Mailslotを使ってプログラム間で通信を行いたいのですが、VB5/6では問題なくデータの送受信が行われるのに、これをVB.NETにすると、受信側のバッファにデータが通知されません。
送信側がデータを送ったタイミングで受信側には受信の完了は通知され、受信データ数も正しく入っているのですが、受信バッファにはデータが入っていません。
送受信の関数コールWriteFile/ReadFileで指定する送受信バッファは、VB5/6ではAnyですが、VB.NETではAnyを使えないので型を明示する必要があります。Byte/Integer/String等いろいろとやってみましたが、どれにしてもデータが通知されません。
どのようにすれば正しくデータが通知されるのでしょうか。
受信側のバッファには、一般的には StringBuilder、IntPtr、ByRef の構造体、OutAttribute 属性を付けた Byte 配列、辺りのいずれかを使います。
それ以上はコードを見ないことには何とも。
プログラム該当箇所(変更箇所は★印、他にByVal・ByRefの追加と削除)
VB5/6 .......................................
送信側
関数宣言
Private Declare Function WriteFile Lib "kernel32" ( _
ByVal hFile As Long, _
lpBuffer As Any, _ ★
ByVal nNumberOfBytesToWrite As Long, _
lpNumberOfBytesWritten As Long, _
lpOverlapped As Long) As Long
Dim byteString() As Byte '送信メッセージバッファ
lngRet = WriteFile(hFile, byteString(0), _
ByVal UBound(byteString), lngWritten, ByVal 0)
受信側
関数宣言
Private Declare Function ReadFile Lib "kernel32" ( _
ByVal hFile As Long, _
lpBuffer As Any, _ ★
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
lpOverlapped As Long) As Long
Dim tbyteBuffer() As Byte '読み込みデータバッファ
lngRet = ReadFile(P_hMailslot, tbyteBuffer(0), _
lngNextSize, lngReadSize, ByVal 0)
VB.NET ......................................
送信側
関数宣言
Private Declare Function WriteFile Lib "kernel32" ( _
ByVal hFile As Integer, _
ByRef lpBuffer As Integer, _ ★
ByVal nNumberOfBytesToWrite As Integer, _
ByRef lpNumberOfBytesWritten As Integer, _
ByRef lpOverlapped As Integer) As Integer
Dim byteString() As Byte '送信メッセージバッファ
lngRet = WriteFile(hFile, byteString(0), _
UBound(byteString), ngWritten, 0)
受信側
関数宣言
Private Declare Function ReadFile Lib "kernel32" ( _
ByVal hFile As Integer, _
ByRef lpBuffer As Integer, _ ★
ByVal nNumberOfBytesToRead As Integer, _
ByRef lpNumberOfBytesRead As Integer, _
ByRef lpOverlapped As Integer) As Integer
Dim tbyteBuffer() As Byte '読み込みデータバッファ
lngRet = ReadFile(P_hMailslot, tbyteBuffer(0), _
lngNextSize, lngReadSize, 0)
これ以外に関数宣言の送受信バッファの型を配列にしたりStringにしてやってみましたが、データは通知されません。
> lngRet = WriteFile(hFile, byteString(0), _
> UBound(byteString), ngWritten, 0)
ん〜……。
まず、旧 VB の知識は捨ててください。それらはおおよそ使えません。
配列をアンマネージドの関数に渡すのなら、先頭アドレスとか考える必要はなく、Declare 文のパラメータにそのまま配列を(ByValで)指定して、呼び出す側も配列の変数をそのまま渡せばいいです。
また配列の長さは、UBound 関数は使わず、配列の Length プロパティを使ってください。
アンマネージドの関数から配列を受け取るのなら(配列の要素をコピーするなら)、それも渡すのと同じように Declare 文のパラメータにそのまま配列を(ByValで)指定します。関数を呼び出すときも、配列の変数そのままを使います。
ただし、デフォルトでは呼び出し先(アンマネージド関数)での配列要素の書き換えを認めませんので、書き換えできるように <OutAttribute> 属性を Declare 文のその配列型のパラメータに付ける必要があります。
> Dim tbyteBuffer() As Byte '読み込みデータバッファ
> lngRet = ReadFile(P_hMailslot, tbyteBuffer(0), _
lngNextSize, lngReadSize, 0)
tbyteBuffer が初期化されていませんが。lngNextSize 以上のサイズを事前に確保しておかなければなりません。
P/Invoke、プラットフォーム呼び出し技術は便利ですがその分色々考えねばならないことが多々あります。
一度 MSDN のこの辺りを読んでおくべきです。
http://msdn2.microsoft.com/ja-jp/library/04fy9ya1.aspx
> 配列をアンマネージドの関数に渡すのなら、先頭アドレスとか考える必要はな
> く、Declare 文のパラメータにそのまま配列を(ByValで)指定して、呼び出す
> 側も配列の変数をそのまま渡せばいいです。
ByRefをByValにし、呼び出し側も配列をそのまま渡してできるようになりました。
バッファにデータが書き込まれるからByRefという訳ではないのですね。
ありがとうございました。
> tbyteBuffer が初期化されていませんが。lngNextSize 以上のサイズを事前に
> 確保しておかなければなりません。
あっ、これはやっています。特に記載する必要がないと思って省略しましたが、原因追求のために考えなくてすむ条件も書いておいた方がよいですね。
細かいところまで見ていただいてありがとうございました。
Visual Studio.NET 2003 でPLCとの通信を行いたいのですが、送信ができません。パソコンとPLCの設定も正しいのですが・・・
PLCは、三菱のFX3Uです。
Dim A As String
Private Sub Form1_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load
MSComm1.CommPort = 1
MSComm1.Settings = "9600,n,8,1"
MSComm1.Portopen = True
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
MSComm1.Output = "00FFBW0M0001011" & vbCrLf
A = MSComm1.Input
TextBox1.Text = A
End sub
End Class
ツイート | ![]() |