DOBON.NET DOBON.NETプログラミング掲示板過去ログ

sendmessageから別アプリ側でWM_COPYDATAが受け取れません。

環境/言語:[Window7 Pro 64bit , VB.Net 2010 , .Net Framework 4.0]
分類:[.NET]

こんにちは、よろしくお願い致します。

あまり使用したことのない32bit APIの処理で困っています。

・実現したい事
あるWindows Formアプリから文字列を送信し、別アプリで受信し表示する。

・コード
--- 送信側 ---------------------------------------------

'COPYDATASTRUCT構造体
Private Structure COPYDATASTRUCT
Public dwData As Integer
Public cbData As Integer
Public lpData As StringBuilder
End Structure

Private Const WM_COPYDATA As Integer = &H4A

<System.Runtime.InteropServices.DllImport("user32.dll", _
CharSet:=System.Runtime.InteropServices.CharSet.Ansi)> _
Private Shared Function SendMessage( _
ByVal hWnd As IntPtr, _
ByVal wMsg As Integer, _
ByVal wParam As Integer, _
ByRef lParam As COPYDATASTRUCT) As Integer
End Function

Private Sub Process_Boot(ByVal exe_nm As String, ByVal app_nm As String)
'プロセスを開始
Dim hProcess As System.Diagnostics.Process = System.Diagnostics.Process.Start(GetAppPath() + exe_nm)
Try
'↓ ウィンドウハンドル
'アイドル状態になるまで待機する
hProcess.WaitForInputIdle()

While (hProcess.MainWindowHandle = IntPtr.Zero And hProcess.HasExited = False)
System.Threading.Thread.Sleep(1)
hProcess.Refresh()
End While

Dim hWnd As Integer = 0
hWnd = GetHwndFromPid(hProcess.Id)

'↓ 文字列メッセージを送信
Dim str_Send As String = "ABCDEFG"


Dim bytearry() As Byte = System.Text.Encoding.Default.GetBytes(str_Send)
Dim len As Int32 = bytearry.Length
Dim cds As COPYDATASTRUCT = New COPYDATASTRUCT
Dim result As Int32 = 0

Dim buf As New StringBuilder()
Try
buf.Append(str_Send)
Finally
buf = Nothing
End Try

cds.dwData = 0
cds.cbData = len + 1
cds.lpData = buf

'送信
SendMessage(hWnd, WM_COPYDATA, 0, buf)

Catch ex As Exception
Throw New Exception(app_nm & "は起動できませんでした。" & ex.Message)
Finally
hProcess.Close()
hProcess.Dispose()
End Try
End Sub

'ボタン押下げ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Try
Me.Cursor = Cursors.WaitCursor
'起動 & プロセス通信
Process_Boot("\Recieve_Window.exe", "受信アプリ")
Me.Cursor = Cursors.Default
Catch ex As Exception
Me.Cursor = Cursors.Default
MsgBox(ex.Message, MsgBoxStyle.OkOnly + MsgBoxStyle.Critical, "起動エラー")
End Try
End Sub


・コード
--- 受信側 ---------------------------------------------
'COPYDATASTRUCT構造体
Private Structure COPYDATASTRUCT
Public dwData As Integer
Public cbData As Integer
Public lpData As String
End Structure

Private Const WM_COPYDATA As Integer = &H4A
Private Const WM_GETTEXT As Integer = &HD

Private targetString As String

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Select Case m.Msg
Case WM_GETTEXT
targetString = "何故か、ここを通る?"

Case WM_COPYDATA
Dim mystr As COPYDATASTRUCT = New COPYDATASTRUCT()
Dim mytype As Type = mystr.GetType()

mystr = CType(m.GetLParam(mytype), COPYDATASTRUCT)
targetString = mystr.lpData
Return
End Select
MyBase.WndProc(m)
End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.Text = targetString
End Sub

コード 以上 ------------------------------------------

・結果 受信側アプリのMe.Textに「何故か、ここを通る?」が表示されてしまう。

以上ですが、
1. なぜ、WM_GETTEXT なのでしょう?
2. 試しに WM_GETTEXT の中にWM_COPYDATA内の処理を置いてみましたが、
System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。エラーとなりました。

よろしくお願い致します。
2012/02/04(Sat) 19:36:10 編集(投稿者)
2012/02/04(Sat) 19:36:03 編集(投稿者)

> '送信
> SendMessage(hWnd, WM_COPYDATA, 0, buf)

  おかしいところ1点
  WPARAM が、『0』になっています。
  ここは、自アプリのメインウィンドウハンドル等になります。

  他のプログラムの詳細は確認していませんので・・・

  それと、管理者権限の有無はどうなっていますか?
  両アプリとも、管理者権限なし?
  片方が、ありで片方がなしの場合、応答しません。
  (OSの仕様なはずです)

以上。参考まで
気になった点を追加でいくつか。

・WM_COPYDATA の LPARAM は StringBuilder を渡したらだめです。
・バイト配列を作った後、結局 StringBuilder で渡している。
 どの文字エンコードになっているか確認できていますか?
 合わせるなら Byte[] 配列を lpData に突っ込むべきだとは思いますが…。
 (今のコードで問題がないかはぱっとわからないので、OK/NG はわかりません)
オショウさん、Azuleanさん
早速のご返答ありがとうございます。

また、ご覧頂いた方々に図表モードでない投稿だったことをお詫びします。

■No29762に返信(オショウさんの記事)
> 2012/02/04(Sat) 19:36:10 編集(投稿者)
> 2012/02/04(Sat) 19:36:03 編集(投稿者)
> 
>>        '送信
>>        SendMessage(hWnd, WM_COPYDATA, 0, buf)
> 
>   おかしいところ1点
>   WPARAM が、『0』になっています。
>   ここは、自アプリのメインウィンドウハンドル等になります。

および No29765 Azuleanさんの記事を参考に

    Private Structure COPYDATASTRUCT
        Public dwData As Integer
        Public cbData As Integer
        Public lpData As String
    End Structure

    <System.Runtime.InteropServices.DllImport("USER32.DLL", _
    CharSet:=System.Runtime.InteropServices.CharSet.Ansi)> _
    Private Shared Function SendMessage( _
                            ByVal hWnd As IntPtr, _
                            ByVal wMsg As Integer, _
                            ByVal wParam As Integer, _
                            ByVal lParam As String) As Integer
    End Function

Dim bytearry() As Byte = System.Text.Encoding.Default.GetBytes(str_Send)
Dim len As Int32 = bytearry.Length
Dim cds As COPYDATASTRUCT = New COPYDATASTRUCT
Dim result As Int32 = 0

cds.dwData = 0
cds.cbData = len + 1
cds.lpData = str_Send

SendMessage(hWnd, WM_COPYDATA, Me.Handle, cds)
と変更してみました。


>   それと、管理者権限の有無はどうなっていますか?
>   両アプリとも、管理者権限なし?
>   片方が、ありで片方がなしの場合、応答しません。
>   (OSの仕様なはずです)
どちらも管理者権限で実行しています。

結果:以前と同じでした。

よろしくお願い致します。
2012/02/04(Sat) 22:42:16 編集(投稿者)

■No29766に返信(五十の手習いさんの記事)
>     <System.Runtime.InteropServices.DllImport("USER32.DLL", _
>     CharSet:=System.Runtime.InteropServices.CharSet.Ansi)> _
>     Private Shared Function SendMessage( _
>                             ByVal hWnd As IntPtr, _
>                             ByVal wMsg As Integer, _
>                             ByVal wParam As Integer, _
>                             ByVal lParam As String) As Integer
>     End Function
(略)
> SendMessage(hWnd, WM_COPYDATA, Me.Handle, cds)
> と変更してみました。

これコンパイル通らないのでは?
cds(COPYDATASTRUCT) を暗黙的に String 型に変換することはできないはずなので。
動作が変わらないと言っているのは、コンパイルが通っていないから動きが変わらないか、
実際に書いているコードと貼り付けているコードに差があると疑っています。

あと、Handle を渡すときは Integer ではなく IntPtr 型を渡すことを心がけてください。
■No29768に返信(Azuleanさんの記事)
> 2012/02/04(Sat) 22:42:16 編集(投稿者)
> これコンパイル通らないのでは?
> cds(COPYDATASTRUCT) を暗黙的に String 型に変換することはできないはずなので。
> 動作が変わらないと言っているのは、コンパイルが通っていないから動きが変わらないか、
> 実際に書いているコードと貼り付けているコードに差があると疑っています。
> 
> あと、Handle を渡すときは Integer ではなく IntPtr 型を渡すことを心がけてください。

Azlean様

ありがとうございます。

ご指摘の通り、転記ミスでした。
そこでアドバイス通り下記の様に、変更してみました。

    Private Structure COPYDATASTRUCT
        Public dwData As IntPtr
        Public cbData As Integer
        Public lpData As String
    End Structure

    'SendMessage ------------------------------------------------
    <System.Runtime.InteropServices.DllImport("user32.dll", _
    CharSet:=System.Runtime.InteropServices.CharSet.Ansi)> _
    Private Shared Function SendMessage( _
                            ByVal hWnd As IntPtr, _
                            ByVal wMsg As UInteger, _
                            ByVal wParam As IntPtr, _
                            ByRef lParam As COPYDATASTRUCT) As Integer
    End Function

結果はやはり同じでした。

Webで、Windows Vista,2008 以降のOSに対処 
(ウインドウメッセージを使用した処理がOSによってフィルタリングされるのを解除)
というのを見つけて実装してみましたが、やはり結果は同じでした。
これは現在解除しております。

以上、引き続きよろしくお願い致します。
該当のソースコードをベースに、手元で実行できました。

疑うとすれば、「GetHwndFromPid」が想定しているフォームのハンドルを取得できているか、受け取り側の WM_COPYDATA にきているかどうかの確認方法をどうやっているかでしょうか。

なお、Load イベントの段階までには WM_COPYDATA がこないと思っています。(推測です)
たぶん、Load されてしまった後にメッセージきていませんか?
Azulean様

■No29770に返信(Azuleanさんの記事)
> 該当のソースコードをベースに、手元で実行できました。
> 
> 疑うとすれば、「GetHwndFromPid」が想定しているフォームのハンドルを取得できているか、受け取り側の WM_COPYDATA にきているかどうかの確認方法をどうやっているかでしょうか。
> 
> なお、Load イベントの段階までには WM_COPYDATA がこないと思っています。(推測です)
> たぶん、Load されてしまった後にメッセージきていませんか?

アドバイスの通り、受信Formにボタンを貼り付け

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Text = targetString
    End Sub

実行すると「ABCDEFG」が表示されました。
Loadの後にきていたんですね。

皆さん、どうもありがとうございました。
また精進させて頂きます。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板