|
分類:[Microsoft Office 全般]
☆やりたいこと
ExcelVBAからSendMessageで、プロセス間通信を行いたい
☆問題
・C#アプリ間(同一アプリ・別アプリ)は行えています
・ExcelVBA→C#の場合、WndProcのメッセージID振り分け直後にブレークをしてもキャッチ出来ない
☆環境
・Win7 x64
・Excel 2007
・VS 2012
☆仕様
・WM_COPYDATAメッセージを利用して、構造体の受け渡しを行う。(dwDataの値により型を指定)
・上記の検証として、文字列(Unicode)の受け渡しを行う。(dwData=0)
☆注意点
・FindWindow及び、SendMessageはAではなくW
・APIの型はStringやAnyではなくLongにし、StrPtrやVarPtrを使用
・C#のコンストラクタで出力しているウインドウハンドルと、ExcelのA2セルの値は10進で同一の為FindWindowは正常
☆C#
public WinProc(AppSetting app)
{
InitializeComponent();
this.Text = app.APIWindowName;
this.textBox1.Text = "";
#if DEBUG
textBox1.Text += string.Format("{0:yyyy/MM/dd HH:mm:ss} {1}{2}", DateTime.Now, this.Handle.ToString(), System.Environment.NewLine);
#endif
}
public delegate void receiveMsgHandler(object sender, receiveMsgEventArgs e);
public event receiveMsgHandler recMsg;
public void OnReceiveMsg(receiveMsgEventArgs e)
{
if (recMsg != null)
{
recMsg(this, e);
}
}
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public UInt32 cbData;
public IntPtr lpData;
}
public const int WM_COPYDATA = 0x4A;
public const int WM_USER = 0x400;
public const int WM_SETTEXT = 0xC;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_COPYDATA:
{
// 受信データ
COPYDATASTRUCT data = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT));
//String msg = Marshal.PtrToStringUni(data.lpData);
byte[] bytes = new byte[data.cbData];
Marshal.Copy(data.lpData, bytes, 0, (int)data.cbData);
string msg = Encoding.Unicode.GetString(bytes);
// イベント
OnReceiveMsg(new receiveMsgEventArgs(msg));
#if DEBUG
textBox1.Text += string.Format("{0:yyyy/MM/dd HH:mm:ss} {1}{2}", DateTime.Now, msg, System.Environment.NewLine);
#endif
m.Result = new IntPtr(1);
break;
}
}
base.WndProc(ref m);
}
☆Excel VBA(標準モジュール)
Option Explicit
Public Declare Function FindWindow Lib "User32.dll" Alias "FindWindowW" _
(ByVal lpClassName As Long, _
ByVal lpWindowName As Long) As Long
Public Declare Function SendMessage Lib "User32.dll" _
Alias "SendMessageW" _
(ByVal hWnd As Long, _
ByVal Msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Public Type COPYDATASTRUCT
dwData As Long
cbData As Long
lpData As Long
End Type
Public Const WM_COPYDATA As Integer = &H4A
Public Const WM_USER As Integer = &H400
☆Excel VBA(テストシート)
Option Explicit
Private Sub CommandButton1_Click()
SendMsg (Sheet1.Range("D5").Value)
End Sub
Private Sub SendMsg(Msg As String)
Dim ret As Long
Dim hWnd As Long: hWnd = 0
Dim sClass As String: sClass = vbNullString
Dim sWindow As String: sWindow = "ReceiverMsg"
Dim data As COPYDATASTRUCT
Dim MsgBytes() As Byte
hWnd = FindWindow(StrPtr(sClass), StrPtr(sWindow))
If (hWnd = 0) Then
MsgBox "起動してください"
Exit Sub
End If
' debug
Sheet1.Range("A2").Value = hWnd
MsgBytes = StrConv(Msg, vbUnicode)
data.dwData = 0
data.lpData = VarPtr(MsgBytes(1))
data.cbData = UBound(MsgBytes)
ret = SendMessage(hWnd, WM_COPYDATA, Me.Application.hWnd, VarPtr(data))
' debug
Sheet1.Range("A1").Value = ret
End Sub
☆
不足が無いようにしたつもりですが、VB6ライクな言語(及びAPIの使用)は久しぶりのため
根本的な勘違いがあるかもしれませんが、よろしくお願いします。
|