[2018/09/03] 共有メモリとしてのメモリマップドファイルの使い方3・配列を持つ構造体の書込と読込 (No.55)
[2018/08/24] 共有メモリとしてのメモリマップドファイルの使い方2・構造体の書込と読込 (No.54)
[2018/08/20] 共有メモリとしてのメモリマップドファイルの使い方(MemoryMappedFile,CreateOrOpen,CreateViewAccessor) (No.53)
[2018/08/13] Nullableな変数の使い方(GrapeCityのInputManのコントロールに関連して) (No.51)
[2018/06/07] プログラム実行中のEXEファイルのファイル名変更ができる! (No.50)
-
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
-
前回は構造体をそのままで読み書きする例を示しましたが、 その中で構造体に配列を持っている場合にはマーシャルの考え方を使うとありましたので、 今回はバイト配列を2個持つ構造体をメモリマップドファイルに書込み、読込む例を示したいと思います。
今回も、メモリマップドファイルの指定で、実際のファイルへの書込みを行いますので、 書込んだ後で、エディタで確認することで、書き込んだデータの値の確認を行います。
以下のソースにその関数を記します。
このフォームは、ボタンを2個配置します。 Button1 は構造体を宣言し、中身を設定してから、構造体をマーシャリングにより、べたなバイト配列として展開します。
そのバイト配列をメモリマップドファイルに書込みを行います。 処理手順は以下の様になります。
1.オブジェクトのアンマネージのバイトサイズでバイト配列宣言
バイト配列を使ってメモリマップドファイルに書込みを行うので、最初に宣言する。
2.アンマネージメモリからメモリを割り当て
アンマネージメモリ(連続したバイト領域としての領域)を(1.)と同じ容量でメモリを確保する。
3.マネージオブジェクトからアンマネージメモリにデータをマーシャリング
構造体の内容を、アンマネージメモリに連続したバイト領域としてコピーする。
4.アンマネージメモリポインターのデータをマネージバイト配列にコピー
アンマネージメモリ(連続したバイト領域としての領域)の内容を、マネージバイト配列に展開する。 (マネージバイト配列はメモリ的には連続した領域に存在するわけでは無く、各配列バイトデータがバラバラに管理されている)
5.アンマネージ メモリから割り当てられたメモリを解放
アンマネージメモリに確保したメモリ領域を解放する。
6.アクセサを使ってメモリマップドファイルへの書込み
マネージバイト配列の内容をメモリマップドファイルへ書込みする。
Button2 はメモリマップドファイルから全体をバイト配列として読込みを行います。 そのバイト配列を、マーシャリングにより構造体に変換する処理を行っています。
処理手順は以下の様になります。
1.オブジェクトのアンマネージのバイトサイズでバイト配列宣言
バイト配列を使ってメモリマップドファイルに書込みを行うので、最初に宣言する。
2.アクセサを使ってメモリマップドファイルから読込
メモリマップドファイルからマネージバイト配列に読込みする。
3.アンマネージメモリからメモリを割り当て
アンマネージメモリ(連続したバイト領域としての領域)を(1.)と同じ容量でメモリを確保する。
4.マネージバイト配列をアンマネージメモリポインターにコピー
メモリマップドファイルから読込まれたマネージバイト配列の内容を、アンマネージメモリ(連続したバイト領域としての領域)に展開する。
5.アンマネージメモリブロックから、指定した型の、新しく割り当てられたマネージオブジェクトにデータをマーシャリング
連続したバイト領域のアンマネージメモリブロックから、指定された型の、マネージな領域にマーシャリングコピーする。
6.アンマネージメモリから割り当てられたメモリを解放
アンマネージメモリに確保したメモリ領域を解放する。共有メモリとしてのメモリマップドファイルの使い方3・配列を持つ構造体の書込と読込
Imports System.IO Imports System.IO.MemoryMappedFiles Imports System.Text Imports System.Runtime.InteropServices Public Class frmMemMapStruc2 '1バイト境界の構造体 <StructLayout(LayoutKind.Sequential, Pack:=1)> _ Public Structure StrucTest Const SIZE1 = 8 Const SIZE2 = 16 <MarshalAs(UnmanagedType.ByValArray, SizeConst:=SIZE1)> _ Public arrData1() As Byte 'Byte型配列データ1 <MarshalAs(UnmanagedType.ByValArray, SizeConst:=SIZE2)> _ Public arrData2() As Byte 'Byte型配列データ2 '初期化メソッド Sub New(ByVal intDummy As Integer) Me.arrData1 = New Byte(SIZE1 - 1) {} Me.arrData2 = New Byte(SIZE2 - 1) {} End Sub End Structure 'バイト型配列を文字列に変換する Private Function ConvBytesToString(ByVal pbytDatas() As Byte) As String If pbytDatas(0) = 0 Then Return "" End If 'Trimされる文字(NULL文字、半角空白、全角空白) Dim pchrTrim() As Char = {ControlChars.NullChar, " "c, " "c} 'Shift JISとして文字列に変換 Return System.Text.Encoding.GetEncoding(932).GetString(pbytDatas).Trim(pchrTrim) End Function '文字列をバイト配列に展開する Private Sub ConvStringToBytes(ByVal astrData As String, ByRef abytArr() As Byte) '文字列をShift-JISとしてバイト配列に変換 Dim pbytDatas() As Byte = System.Text.Encoding.GetEncoding(932).GetBytes(astrData) '格納先バイト配列を0x00でクリア Array.Clear(abytArr, 0, abytArr.Length) 'コピーバイト数の調整 Dim pintCopy As Integer = pbytDatas.Length If pintCopy > abytArr.Length Then pintCopy = abytArr.Length End If '格納先にコピー Array.Copy(pbytDatas, abytArr, pintCopy) End Sub 'テキストボックスの内容をメモリマップドファイルに書込む Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf = MemoryMappedFile.CreateFromFile("MemMapFileTest.dat", FileMode.OpenOrCreate, "MemMapFileTest", 64) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() '書込用の構造体の宣言 Dim StrucT As New StrucTest(0) 'ASCII文字列をバイト配列に展開する ConvStringToBytes("1234567", StrucT.arrData1) '8バイト領域に7バイト設定 ConvStringToBytes("ABCDEFGHIJ", StrucT.arrData2) '16バイト領域に10バイト設定 '1.オブジェクトのアンマネージのバイトサイズでバイト配列宣言 Dim size As Integer = Marshal.SizeOf(StrucT) Dim bytes(size) As Byte '2.アンマネージメモリからメモリを割り当て Dim ptr As IntPtr = Marshal.AllocHGlobal(size) '3.マネージオブジェクトからアンマネージメモリにデータをマーシャリング Marshal.StructureToPtr(StrucT, ptr, False) '4.アンマネージ メモリ ポインターのデータを マネージバイト配列にコピー Marshal.Copy(ptr, bytes, 0, size) '5.アンマネージ メモリから割り当てられたメモリを解放 Marshal.FreeHGlobal(ptr) '6.アクセサを使ってメモリマップドファイルへの書込み acc.WriteArray(0, bytes, 0, bytes.Length) 'アクセサ、メモリマップドファイルの廃棄 acc.Dispose() mmf.Dispose() End Sub 'メモリマップドファイルの内容を読込んで表示する Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf = MemoryMappedFile.CreateFromFile("MemMapFileTest.dat", FileMode.OpenOrCreate, "MemMapFileTest", 64) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() '読込用の構造体の宣言 Dim StrucT2 As StrucTest = Nothing '1.オブジェクトのアンマネージのバイトサイズでバイト配列宣言 Dim size As Integer = Marshal.SizeOf(StrucT2) Dim bytes(size) As Byte '2.アクセサを使ってメモリマップドファイルから読込 acc.ReadArray(0, bytes, 0, size) '3.アンマネージメモリからメモリを割り当て Dim ptr As IntPtr = Marshal.AllocHGlobal(size) '4.マネージバイト配列をアンマネージメモリポインターにコピー Marshal.Copy(bytes, 0, ptr, size) '5.アンマネージメモリブロックから、指定した型の、新しく割り当てられたマネージオブジェクトにデータをマーシャリング StrucT2 = CType(Marshal.PtrToStructure(ptr, GetType(StrucTest)), StrucTest) '6.アンマネージ メモリから割り当てられたメモリを解放 Marshal.FreeHGlobal(ptr) 'アクセサ、メモリマップドファイルの廃棄 acc.Dispose() mmf.Dispose() 'バイト型配列を文字列に変換する Dim strData As String = "" strData &= "arrData1:" & ConvBytesToString(StrucT2.arrData1) & vbCrLf strData &= "arrData2:" & ConvBytesToString(StrucT2.arrData2) & vbCrLf MsgBox("Read Text:" & vbCrLf & strData) End Sub End Class
MemMapFileTest.dat のファイルの内容をバイナリ表示で行うと以下の様になっています。 (「秀丸エディタ」で表示)
関連する記事
⇒共有メモリとしてのメモリマップドファイルの使い方(MemoryMappedFile,CreateOrOpen,CreateViewAccessor)
⇒共有メモリとしてのメモリマップドファイルの使い方2・構造体の書込と読込
PR -
メモリマップドファイルを使ったバイト配列の読み書きの記事は前回紹介しましたが、 今回は構造体をそのままで読み書きする例を示します。
尚、今回は、実際のデータファイルを指定する方法で、メモリマップドファイルを使います。 ファイルに書込んだ後で、「秀丸」などのエディタで内容を確認する為です。
以下のソースにその関数を記します。
このフォームは、ボタンを2個と、テキストボックスを1個配置します。 Button1 はテキストボックスの文字列をInteger型データに設定し、メモリマップドファイルへの書込み処理を行い、 Button2 はメモリマップドファイルから読込みを行います。
MemoryMappedFile.CreateFromFile は実際のファイル名とマップ名とメモリ領域のサイズを指定し、メモリマップドファイルのオープンを行います。 その後、MemoryMappedFile.CreateViewAccessor のアクセサを使って、メモリ空間への読み書きを行います。共有メモリとしてのメモリマップドファイルの使い方2・構造体の書込と読込
Imports System.IO Imports System.IO.MemoryMappedFiles Imports System.Runtime.InteropServices Public Class frmMemMapStruc '簡単な構造体 <StructLayout(LayoutKind.Sequential, Pack:=1)> _ Public Structure StrucTest Public bytData As Byte 'Byte型データ Public intData As Integer 'Integer型データ Public lngData As Long 'Long型データ Public dblData As Double 'Double型データ Public decData As Decimal 'Decimal型データ End Structure 'テキストボックスの内容をメモリマップドファイルに書込む Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf = MemoryMappedFile.CreateFromFile("MemMapFileTest.dat", FileMode.OpenOrCreate, "MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() '構造体宣言と値設定 Dim StrucT As StrucTest StrucT.bytData = &H55 StrucT.intData = CInt(Me.TextBox1.Text) StrucT.lngData = 1000000L StrucT.dblData = 200.123 StrucT.decData = 1000.456 'アクセサで構造体をメモリマップドファイルへ書込み acc.Write(0, StrucT) 'アクセサ、メモリマップドファイルの廃棄 acc.Dispose() mmf.Dispose() End Sub 'メモリマップドファイルの内容を読込んで表示する Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf = MemoryMappedFile.CreateFromFile("MemMapFileTest.dat", FileMode.OpenOrCreate, "MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() 'アクセサで構造体にメモリマップドファイルから読込み Dim StrucT As StrucTest acc.Read(0, StrucT) 'アクセサ、メモリマップドファイルの廃棄 acc.Dispose() mmf.Dispose() '構造体の値を表示 Dim strData As String = "" strData &= "bytData:" & StrucT.bytData.ToString("X2") & vbCrLf strData &= "intData:" & StrucT.intData.ToString & vbCrLf strData &= "lngData:" & StrucT.lngData.ToString & vbCrLf strData &= "dblData:" & StrucT.dblData.ToString & vbCrLf strData &= "decData:" & StrucT.decData.ToString & vbCrLf MsgBox("Read:" & strData) End Sub End Class
MemMapFileTest.dat のファイルの内容をバイナリ表示で行うと以下の様になっています。 (「秀丸エディタ」で表示)
このソースで注意するのは、構造体のデータの読み書き処理が終わったところで、 アクセサ及び、メモリマップドファイルのインスタンスの廃棄を行っているところです。
この処理をしないと、各ボタンクリック処理でのプロセスがデータファイルを捕まえたままになって、 MemoryMappedFile.CreateFromFile のところでエラーが発生します。(ファイルのロックが掛かった様な状態)
以下がその様子を示す画像です。(ボタン1のクリック処理を行い、その後で、ボタン2のクリックを行った時の様子です)
書込み、及び読込みの処理を別々のプロセスに分けた場合に、書込み処理中に読込み処理が行われる可能性があります。 また、その逆も考えられますので、MemoryMappedFile.CreateFromFile でエラーが発生した場合には、その後の処理はしない方がいいと思います。
今回の構造体は単純なInteger型やLong型などの全て値型のデータ型ばかりなので、 配列データは出来ないものかと思って以下の様にソースを変更しました。 (Byte型配列データの部分を追加)Imports System.IO Imports System.IO.MemoryMappedFiles Imports System.Runtime.InteropServices Public Class frmMemMapStruc '簡単な構造体 <StructLayout(LayoutKind.Sequential, Pack:=1)> _ Public Structure StrucTest Public bytData As Byte 'Byte型データ Public intData As Integer 'Integer型データ Public lngData As Long 'Long型データ Public dblData As Double 'Double型データ Public decData As Decimal 'Decimal型データ Public arrData() As Byte 'Byte型配列データ End Structure 'テキストボックスの内容をメモリマップドファイルに書込む Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf = MemoryMappedFile.CreateFromFile("MemMapFileTest.dat", FileMode.OpenOrCreate, "MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() '構造体宣言と値設定 Dim StrucT As StrucTest StrucT.bytData = &H55 'StrucT.intData = 100 StrucT.intData = CInt(Me.TextBox1.Text) StrucT.lngData = 1000000L StrucT.dblData = 200.123 StrucT.decData = 1000.456 StrucT.arrData = {&H0, &H11, &H55, &HAA} 'アクセサで構造体をメモリマップドファイルへ書込み acc.Write(0, StrucT) 'アクセサ、メモリマップドファイルの廃棄 acc.Dispose() mmf.Dispose() End Sub 'メモリマップドファイルの内容を読込んで表示する Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf = MemoryMappedFile.CreateFromFile("MemMapFileTest.dat", FileMode.OpenOrCreate, "MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() 'アクセサで構造体にメモリマップドファイルから読込み Dim StrucT As StrucTest acc.Read(0, StrucT) 'アクセサ、メモリマップドファイルの廃棄 acc.Dispose() mmf.Dispose() '構造体の値を表示 Dim strData As String = "" strData &= "bytData:" & StrucT.bytData.ToString("X2") & vbCrLf strData &= "intData:" & StrucT.intData.ToString & vbCrLf strData &= "lngData:" & StrucT.lngData.ToString & vbCrLf strData &= "dblData:" & StrucT.dblData.ToString & vbCrLf strData &= "decData:" & StrucT.decData.ToString & vbCrLf MsgBox("Read:" & vbCrLf & strData) End Sub End Class
このソースを実行すると以下の様にVBから叱られてしまいました。
アクセサの構造体の書き込みは、構造体の中に参照データが存在する時にはできない様です。 配列データは構造体の arrData 変数は配列データの実体を持っているわけでは無く、 ポインタ的な参照を持っているだけなので、当然だとは思います。
しかし、参照データを含む構造体の場合にはマーシャルの考え方を使えばできる様ですので、 後日また公開したいと思います。関連する記事
⇒共有メモリとしてのメモリマップドファイルの使い方(MemoryMappedFile,CreateOrOpen,CreateViewAccessor)
⇒共有メモリとしてのメモリマップドファイルの使い方3・配列を持つ構造体の書込と読込
-
プロセス間での通信を簡単に行いたい場合には、メモリマップドファイルが使えます。 メモリマップドファイルはその名の通り、プログラム上のメモリ空間にファイルを読込んで、連続したメモリ空間として取り扱えます。 そのメモリ空間を読み書きすることで、プロセス間の通信ができます。
今回は、実際のデータファイルを指定しない方法での、メモリマップドファイルの使い方を示します。
以下のソースにその関数を記します。
このフォームは、ボタンを2個と、テキストボックスを1個配置します。 Button1 はテキストボックスの文字列をメモリマップドファイルへの書込み処理を行い、 Button2 はメモリマップドファイルから読込みを行います。
MemoryMappedFile.CreateOrOpen はマップ名とメモリ領域のサイズを指定し、メモリマップドファイルのオープンを行います。 その後、MemoryMappedFile.CreateViewAccessor のアクセサを使って、メモリ空間への読み書きを行います。共有メモリとしてのメモリマップドファイルの使い方
Imports System.IO Imports System.IO.MemoryMappedFiles Imports System.Text Public Class frmMemMap 'テキストボックスの内容をメモリマップドファイルに書込む Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateOrOpen("MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() 'ASCII文字列をバイト配列に展開する Dim asciiBytes As Byte() = Encoding.ASCII.GetBytes(Me.TextBox1.Text.Trim) 'バイト配列の最後尾にNULL(0x00)設定 ReDim Preserve asciiBytes(UBound(asciiBytes) + 1) asciiBytes(UBound(asciiBytes)) = 0 'アクセサを使ってメモリマップドファイルへの書込み acc.WriteArray(0, asciiBytes, 0, asciiBytes.Length) End Sub 'メモリマップドファイルの内容を読込んで表示する Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateOrOpen("MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() Dim byteVal As Byte Dim index As Integer = 0 Dim sb As StringBuilder = New StringBuilder() Do 'アクセサを使ってメモリマップドファイルから位置指定での読込 byteVal = acc.ReadByte(index) If (byteVal = 0) Then 'NULLの時点で終了 Exit Do End If 'バイト値を文字に変換 Dim ascChar As Char = ChrW(byteVal) '文字列に加算 sb.Append(ascChar, 1) 'メモリマップドファイルの位置を進める index += 1 Loop MsgBox("Read Text:" & sb.ToString) End Sub End Class
このソースでは少し問題が有ります。テキストとしてASCII文字列しか扱えないので、 全角文字をテキストボックスに入れた場合には、期待した結果が得られません。
この件に関しては、後日解決したいと思います。
本日時間が在ったので Shift-JIS が扱える様に変更してみました。
メモリマップドファイルに書込むところで、文字列をバイト配列に展開する処理で、 エンコーディングを Shift-JIS として取得し、バイト配列に展開する様にします。
また、メモリマップドファイルから読込むところでは、バイト配列に取得した後で、 上記同様にエンコーディングを Shift-JIS としてバイト配列から文字列への変換を行います。
以下にそのソースを示します。共有メモリとしてのメモリマップドファイルの使い方
Imports System.IO Imports System.IO.MemoryMappedFiles Imports System.Text Public Class frmMemMap 'テキストボックスの内容をメモリマップドファイルに書込む Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateOrOpen("MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() 'Shift-JIS文字列をバイト配列に展開する Dim asciiBytes As Byte() = Encoding.GetEncoding("shift-jis").GetBytes(Me.TextBox1.Text.Trim) 'バイト配列の最後尾にNULL(0x00)設定 ReDim Preserve asciiBytes(UBound(asciiBytes) + 1) asciiBytes(UBound(asciiBytes)) = 0 'アクセサを使ってメモリマップドファイルへの書込み acc.WriteArray(0, asciiBytes, 0, asciiBytes.Length) End Sub 'メモリマップドファイルの内容を読込んで表示する Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'メモリ上にメモリマップドファイルをオープンする Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateOrOpen("MemMapFileTest", 1024) 'メモリマップドファイルのビューに対応するアクセサ生成 Dim acc As MemoryMappedViewAccessor = mmf.CreateViewAccessor() Dim byteVal As Byte Dim index As Integer = 0 Dim arrBytes(0) As Byte Do 'アクセサを使ってメモリマップドファイルから位置指定での読込 byteVal = acc.ReadByte(index) If (byteVal = 0) Then 'NULLの時点で終了 Exit Do End If '配列に退避 ReDim Preserve arrBytes(index) arrBytes(index) = byteVal 'メモリマップドファイルの位置を進める index += 1 Loop 'バイト型配列を文字列に変換する Dim strData As String = Encoding.GetEncoding("shift-jis").GetString(arrBytes) MsgBox("Read Text:" & strData) End Sub End Class
関連する記事
⇒共有メモリとしてのメモリマップドファイルの使い方2・構造体の書込と読込
⇒共有メモリとしてのメモリマップドファイルの使い方3・配列を持つ構造体の書込と読込
-
データ入力を行うフォームを作成する時に、システム標準のTextBox等でできればいいのですが、 より細かな入力制限などを行いたい場合に市販されているコントロールを利用するのも手だと思います。
GrapeCity かたはいろんなコントロールが販売されていて、フォーム上の入力を楽にするものの一つとして InputMan があります。 InputMan には日付、文字列、数値、コンボボックスなどの拡張入力コントロールが揃っています。 一からコントロールを作成するよりは買った方が早いのでこれを使用しています。 (私は特に GrapeCity の回し者でもありませんが)
InputManのコントロールですが、一つ気をつけないといけない点があります。
例として数値入力のコントロールである GcNumber を取り上げてみます。 GcNumber は数値入力ですので、値を設定・取得する場合のプロパティとして Value が利用します。 この Value のデータ型がNULLを許すNullable型のDecimalとして定義されています。
この例を示すために、 フォーム上に GcNumber を1個と、ボタンを2個貼り付けます。GcNumber の Value について
Public Class frmDataNullable 'フォームロード時イベント Private Sub frmDataNullable_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'GcNumber1に数値を削除した時にNULLを許可 Me.GcNumber1.AllowDeleteToNull = True End Sub 'Nullableな結果を通常の変数で受ける Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Try 'GcNumber1の値を受ける変数 Dim decNrm As Decimal '通常のDecimal型 decNrm = Me.GcNumber1.Value Dim strMsg As String = String.Format("GcNumber1.Value = {0}", decNrm.ToString) MsgBox(strMsg, MsgBoxStyle.OkOnly, "通常のDecimal型") Catch ex As Exception MsgBox(ex.Message) End Try End Sub 'Nullableな結果をNullableな変数で受ける Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Try 'GcNumber1の値を受ける変数 Dim decNul As Decimal? 'NullableなDecimal型 decNul = Me.GcNumber1.Value Dim strMsg As String '値を表示 strMsg = String.Format("GcNumber1.Value = {0}", decNul.ToString) MsgBox(strMsg, MsgBoxStyle.OkOnly, "NullableなDecimal型") '値の有効状態を表示 strMsg = String.Format("GcNumber1.HasValue = {0}" & vbCrLf & "GcNumber1.Valus IsNothing = {1}", decNul.HasValue, Not decNul Is Nothing) MsgBox(strMsg, MsgBoxStyle.OkOnly, "NullableなDecimal型") Catch ex As Exception MsgBox(ex.Message) End Try End Sub End Class
結果これを実行し、「通常の変数」(Button1)を押下すると以下の様になります。
この時の処理は、通常のDecimal型の変数を用意し、その変数に GcNumber の Value を設定しています。 コントロールに数値が入っているときは問題無く動作します。
GcNumber の内容を全て削除して空白にしてから、「通常の変数」(Button1)を押下すると以下の様になります。
GcNumber の Value がNullになる様で、変数の代入時にエラーが発生し、Catch でエラーメッセージが表示されます。
このエラーを回避するには当然なのですが、GcNumber の Value と同じデータ型の変数に代入することです。 「Nullableの変数」(Button2)を押下すると以下の様になります。
Nullableの変数を宣言する時にはデータ型の最後に ? (クエスチョンマーク)を付加します。
この変数は、状態として数値が入っている場合と、NULLの場合がありますので、 この変数をそのまま計算式に使う場合には注意が必要です。 少し面倒ですが、変数がNULLかどうかで計算を分けてやる必要があります。
ただ、データベースのテーブルでは数値項目でもNULLが許可されている場合には、 Nullableの変数は使えると思います。テーブルのカラムに値を設定するSQL文を作成する時には 変数がNULLかどうかで、値にNULLとするのか、それとも値そのものを設定するのかの処理を分けることが 必要かと思います。
今回は GcNumber を例にとりましたが、日付入力コントロールの GcDate も同様に Value プロパティを持っていて、このデータ型が Date? となっていますので 変数で受ける場合はそれに合わせる必要があります。
-
プログラム実行中のEXEファイルを削除しようとすると以下の様にシステムから、 「BlogTestによってファイルは開かれているため、操作を完了できません。」というエラーが返されます。
EXEファイルの例として 「SerialPortコントロールの使い方その2」 の実行を行いました。
プログラム実行中はそのEXEファイルが、システムによってロックが掛けられていて削除できないと思います。
しかし、削除は出来ないのですが、ファイル名の変更は可能なのです。 プログラム実行中にできるとは驚きなのですが、この機能を使うことでプログラム実行中にファイルの更新ができるのです。
プログラムの更新手順は以下の様に行えると思います。- 実行中のEXEファイルを別の名前に変更する。
- 実行ファイルの更新版をコピーする。
- 別の名前に変更されたファイルを削除する。(もしくは別のフォルダにコピーする。)
このEXEファイルのファイル名変更ですが、DLLファイルでも可能な様です。
Windowsのシステムアップデートなどもこの機能が無いとできない気がします...