■ プロセス間通信(SendMessage) ■ ■ プロセス間通信とは
同一コンピューター上で起動して居るアプリケーション間でデータを受け渡し度い事は時々有る。 Framework には『リモート処理』と謂う方法でデータの受け渡しを行なう方法が有る。此処では、此
の方法では無く、従来の方法のAPI を使用したプロセス間通信を紹介する。
此の方法は、送信側はAPI の SendMessage で送り、受信側は WndProc のメッセージループで受け取
る事に成る。難しい事は余り無いのだが、文字列の送受信では構造体のCOPYDATASTRUCT を使うの で、此の処理が少々面倒で有る。 ■ 送信側 仕様 先ず相手のアプリケーション名を入力して、送るメッセージと送る数値を2個入力して。送信ボタンを 押すと、相手にメッセージが投げられる物とする。 ■ コードの説明 宣言
API は全部で3個使用する。先ず相手の Window のハンドルを取得する FindWindow と、相手に数値 を送るSendMessage と、文字列を送る SendMessage で有る。SendMessage は、同じ名前の関数で有 るが、引数の形が異なる。 始めの関数は、引数として、1 番目に相手の Window のハンドルを、2 番目、3 番目に数値を持つ。一 方2番目のSendMessage は、1 番目は同じで有るが、2 番目は 0 を、3 番目には COPYDATASTRUCT と謂う構造体を持つ。COPYDATASTRUCT は、値渡しではなく、参照渡し(ref)なので、注意され 度い。
プ
プ
ロ
ロ
セ
セ
ス
ス
間
間
通
通
信
信
Visual Basic
' COPYDATASTRUCT 構造体
Public Structure COPYDATASTRUCT
Public dwData As Int32 ' 送信するビット値 Public cbData As Int32 ' lpData のバイト数
Public lpData As String ' 送信するデータへのポインタ(0 も可能) End Structure
Public Const WM_COPYDATA As Int32 = &H4A Public Const WM_USER As Int32 = &H400
"user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Private Shared Function FindWindow( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr End Function
"user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Public Shared Function SendMessage( _
ByVal hWnd As IntPtr, _ ByVal wMsg As Int32, _ ByVal wParam As Int32, _
ByVal lParam As Int32) As Integer End Function
"user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Public Shared Function SendMessage( _
ByVal hWnd As IntPtr, _ ByVal wMsg As Int32, _ ByVal wParam As Int32, _
ByRef lParam As COPYDATASTRUCT) As Integer End Function
C#
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName); [DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern Int32 SendMessage(Int32 hWnd, Int32 Msg, Int32 wParam, ref COPYDATASTRUCT lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern Int32 SendMessage(Int32 hWnd, Int32 Msg, Int32 wParam, Int32 lParam);
public const Int32 WM_COPYDATA = 0x4A; public const Int32 WM_USER = 0x400;
// COPYDATASTRUCT 構造体 public struct COPYDATASTRUCT {
public Int32 dwData; // 送信する 32 ビット値 public Int32 cbData; // lpData のバイト数
public string lpData; // 送信するデータへのポインタ(0 も可能) }
■ データの送信
Visual Basic
' 送信ボタン押下
Private Sub butSend_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles butSend.Click
Dim result As Int32 = 0
' 相手のウィンドウハンドルを取得する
Dim hWnd As Int32 = FindWindow(Nothing, txtName.Text) If hWnd = 0 Then ' ハンドルが取得出来なかった MessageBox.Show("相手 Window のハンドルが取得出来ません") Return End If ' 文字列メッセージを送信する
If txtMessage.Text <> String.Empty Then ' 送信データを Byte 配列に格納
Dim bytearry() As Byte = System.Text.Encoding.Default.GetBytes(txtMessage.Text) Dim len As Int32 = bytearry.Length
Dim cds As COPYDATASTRUCT
cds.dwData = 0 ' 使用しない
cds.lpData = txtMessage.Text ' テキストのポインターをセット cds.cbData = len + 1 ' 長さをセット
' 文字列を送る
result = SendMessage(hWnd, WM_COPYDATA, 0, cds) End If
' 数値メッセージを送信する
If txtInt1.Text <> String.Empty And txtInt2.Text <> String.Empty Then Dim int1 As Int32 = 0
Dim int2 As Int32 = 0 Try
' 数値に正しく変換出来るか? int1 = CType(txtInt1.Text, Int32) int2 = CType(txtInt2.Text, Int32) Catch
MessageBox.Show("入力された数値が正しく有りません") Return
End Try ' 数値を送る
result = SendMessage(hWnd, WM_USER, int1, int2) End If
End Sub
C#
// 送信ボタン押下
private void butSend_Click(object sender, EventArgs e) {
Int32 result = 0;
// 相手のウィンドウハンドルを取得する
Int32 hWnd = FindWindow(null, txtName.Text); if (hWnd == 0) { // ハンドルが取得出来なかった MessageBox.Show("相手 Window のハンドルが取得出来ません"); return; } // 文字列メッセージを送信する if (txtMessage.Text != string.Empty) { // 送信データを Byte 配列に格納
byte[] bytearry = System.Text.Encoding.Default.GetBytes(txtMessage.Text); Int32 len = bytearry.Length;
COPYDATASTRUCT cds;
cds.dwData = 0; // 使用しない
cds.lpData = txtMessage.Text; // テキストのポインターをセット cds.cbData = len + 1; // 長さをセット
// 文字列を送る
result = SendMessage(hWnd, WM_COPYDATA, 0, ref cds); }
// 数値メッセージを送信する
if (txtInt1.Text != string.Empty && txtInt2.Text != string.Empty) { Int32 int1 = 0; Int32 int2 = 0; try { // 数値に正しく変換出来るか? int1 = int.Parse(txtInt1.Text); int2 = int.Parse(txtInt2.Text); }
catch { MessageBox.Show("入力された数値が正しく有りません"); return; } // 数値を送る
result = SendMessage(hWnd, WM_USER, int1, int2); } } サンプルコード:sendmessagesendc.lzh、sendmessagesendvb.lzh ■ 受信側のコード 受信側のコードは簡単で有る。.NET では、簡単に Form に送られて来るメッセージをフックする事が 出来る。 其れには、Form クラスの WndProc メソッドを、自分のフォームでオーバーライドすれば良い。後は メッセージの種類に依り、数値データか、文字データかを判断して、数値データで有れば、其の儘、文 字にして表示する。文字列の場合は、WinProc には、引数と仕て System.Windows.Forms.Message 構 造体が参照で渡されて来るので、此の参照データから Message 構造体が持つ GetLParam メソッドで LParam を取得し、OPYDATASTRUCT でキャストして文字列を取り出す。 ■ 仕様 フォームの構造は簡単で、送られたデータを表示する丈で有る。 Visual Basic
Public Class FomGetMessage ' COPYDATASTRUCT 構造体
Public Structure COPYDATASTRUCT
Public dwData As IntPtr ' 送信するビット値 Public cbData As Int32 ' lpData のバイト数
Public lpData As String ' 送信するデータへのポインタ(0 も可能) End Structure
Public Const WM_COPYDATA As Int32 = &H4A Public Const WM_USER As Int32 = &H400
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) Select Case m.Msg Case WM_USER ' 数値が送信されて来た txtInt1.Text = m.WParam.ToString() txtInt2.Text = m.LParam.ToString() Case WM_COPYDATA ' 文字が送信されて来た
Dim mystr As COPYDATASTRUCT = New COPYDATASTRUCT() Dim mytype As Type = mystr.GetType()
mystr = CType(m.GetLParam(mytype), COPYDATASTRUCT) txtMessage.Text = mystr.lpData End Select MyBase.WndProc(m) End Sub ' テキストボックスのクリア
Private Sub butCls_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles butCls.Click txtMessage.Text = String.Empty txtInt1.Text = String.Empty txtInt2.Text = String.Empty End Sub End Class C# using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace getmessagec {
public partial class FomGetMessage : Form {
// COPYDATASTRUCT 構造体 public struct COPYDATASTRUCT {
public Int32 dwData; // 送信する 32 ビット値 public Int32 cbData; // lpData のバイト数
public string lpData; // 送信するデータへのポインタ(0 も可能) }
public const int WM_COPYDATA = 0x4A; public const int WM_USER = 0x400; public FomGetMessage()
{
InitializeComponent(); }
protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_USER: // 数値が送信されて来た txtInt1.Text = m.WParam.ToString(); txtInt2.Text = m.LParam.ToString(); break; case WM_COPYDATA: // 文字が送信されて来た
COPYDATASTRUCT mystr = new COPYDATASTRUCT(); Type mytype = mystr.GetType();
mystr = (COPYDATASTRUCT)m.GetLParam(mytype); txtMessage.Text = mystr.lpData ; break; } base.WndProc(ref m); } // テキストボックスのクリア
private void butCls_Click(object sender, EventArgs e) { txtMessage.Text = string.Empty; txtInt1.Text = string.Empty; txtInt2.Text = string.Empty; } } } サンプルコード:sendmessagegetc.lzh、sendmessagegetvb.lzh