- PR -

C#からC++へのプロセス間通信

投稿者投稿内容
なな
会議室デビュー日: 2005/10/12
投稿数: 13
投稿日時: 2006-06-08 11:15
独立したC#のアプリとC++のアプリ間で値の受け渡しを行いたいと思ってます。
具体的には、C#からPostMessageを用いて文字もしくは数値を受け渡そうと考えていたのですが、
C++側でうまく値を受け取ることができません。
良い方法をご存知でないでしょうか?

---------------サンプルの抜粋----------------------------------

[C#]送信側

[DllImport("user32.dll", SetLastError = true)]
private static extern int PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

System.IntPtr HWND_BROADCAST = new IntPtr(0xffff);
private UInt32 MyMsg = RegisterWindowMessage("MY_MSG");

//処理を行って送りたい情報(strInfo)をポストする
string strInfo = "送りたい情報が処理によって格納される予定";
PostMessage(HWND_BROADCAST, MyMsg, 0, Marshal.StringToHGlobalAnsi(strInfo));


[C++]受信側
UINT MyMsg = RegisterWindowMessage(L"MY_MSG");

INT CALLBACK Proc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
 if(Message == MyMsg)
 {
 //受け取った情報を目視するためにエディットボックスに表示
  SendDlgItemMessage(hDlg, IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)lParam);
 }
}
---------------ここまで----------------------------------

私なりに、送受信する情報の型が悪いのだろうと思っているのですが、対処法が見当が付かない状態です。
 送信側のIntPtrへの変換
 受信側のlParamの処理 など

・イベントの受信はできていることは確認できています。
 SendDlgItemMessageをBeep関数に置き換えてBeep音を確認より。

・またイベント情報を無視したエディットボックスの表示(TEST)も確認しています。
 SendDlgItemMessage(hDlg, IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)L"TEST"); //表示OK

何か良い方法や解決方法があればご教授お願いいたします。

Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-06-08 11:24
プロセスが異なればメモリ空間も異なるので、A プロセスの M というメモリアドレスに存在する文字列を指定しても、B プロセスの方の M というメモリアドレスには文字列は存在しません。
  • VirtualAllocEx などを使って相手プロセスのメモリ空間を操作できるようにする
  • WM_COPYDATA を使う
  • Mailslot や Pipe を使う
などして下さい。
Elle
常連さん
会議室デビュー日: 2004/09/29
投稿数: 23
投稿日時: 2006-06-08 11:29
文字列が短いなら、GlobalAtomという手もあるね。
甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2006-06-08 11:30
引用:

ななさんの書き込み (2006-06-08 11:15) より:
独立したC#のアプリとC++のアプリ間で値の受け渡しを行いたいと思ってます。
具体的には、C#からPostMessageを用いて文字もしくは数値を受け渡そうと考えていたのですが、
C++側でうまく値を受け取ることができません。


#単なるコーディングミスだと思う人 ノシ

別プロセスである以上、メモリ空間も独立しています。メモリアドレスを、別のプロセスに単純に渡したって、データを取得できるわけありません。これは言語に関係ない話です。WM_SETTEXTとWM_COPYDATAだけが例外で、メモリアドレスを渡すことでデータをコピーできます。これはAPI内部でデータを受け渡すための特別な処理が行われているからです。

したがって方法は二つあります。

一つ目はユーザー定義メッセージを使用するのを止めて、WM_SETTEXTとWM_COPYDATAを用いる方法。

二つ目はGlobalAddAtom等のプロセス間でメモリを共有するための仕組みを併用することです。
_________________
甕星 <mikahosi@abox9.so-net.ne.jp>
http://blogs.msmvp.jp/mikahosi/
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2006-06-08 12:05
引用:

PostMessage(HWND_BROADCAST, MyMsg, 0, Marshal.StringToHGlobalAnsi(strInfo));



プロセス間で HGLOBAL を共有できるのは Win16 時代の話です。
Win32 以降では通用しません。

LPARAM, WPARAM (=実質 32bit 値)以上のユーザ定義データを送信する時は、独自定義のメッセージではなく、WM_COPYDATA メッセージを SendMessage() します。
なな
会議室デビュー日: 2005/10/12
投稿数: 13
投稿日時: 2006-06-09 09:50
Hongliangさん、Elleさん、甕星さん、渋木宏明さん、ありがとうございます。
いろいろとアドバイスをいただいて、それを頼りに調べてみました。

WM_COPYDATAで試したところ、イベントは受信できているのですが、表示される文字は化けてしまっています。
まだ、作りが悪いのでしょうか?それとも何か型変換が間違っているのでしょうか?
(基本的な事柄ですみません)

---------------サンプルの抜粋----------------------------------
[C#]送信側
struct COPYDATASTRUCT
{
 public IntPtr dwData;
 public UInt32 cbData;
 public string lpData;
}

[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, ref COPYDATASTRUCT lParam);


//処理を行って送りたい情報(testTEST)をポストする
COPYDATASTRUCT cs = new COPYDATASTRUCT();
string strTest = "testTEST";
cs.dwData = new IntPtr(0);
cs.cbData = (uint)strTest.Length + 1;
cs.lpData = strTest;

SendMessage(HWND_BROADCAST, WM_COPYDATA, new IntPtr(0), ref cs);

[C++]受信側

INT CALLBACK Proc(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
case WM_COPYDATA:
 {
  COPYDATASTRUCT *pcs = (COPYDATASTRUCT*)lParam;
 //受け取った情報を目視するためにエディットボックスに表示
  SendDlgItemMessage(hDlg, IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)pcs->lpData);
  ::Beep(3000, 500);
 }
}

---------------ここまで----------------------------------



渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2006-06-09 11:06
引用:

WM_COPYDATAで試したところ、イベントは受信できているのですが、表示される文字は化けてしまっています。
まだ、作りが悪いのでしょうか?それとも何か型変換が間違っているのでしょうか?



COPYDATASTRUCT の使いこなし?間違ってます。

COPYDATASTRUCT の定義は

コード:
struct COPYDATASTRUCT 
{ 
 public IntPtr dwData; 
 public UInt32 cbData; 
 public IntPtr lpData; 
} 



です。

lpData メンバは char 型配列ではなく LPVOID 型であるため、lpData メンバに string 型をあてがっても、期待しているような動作は得られません。

このような場合は

コード:
string s = "ほげ";

COPYDATASTRUCT cds = new COPYDATASTRUCT();

cds.lpData = Marshal.StringToHGlobal(s);
cds.cbData = s.Length;

SendMessage (適宜);

Marshal.FreeHGlobal (cds.lpData);



のように、手動でマーシャリングして WM_COPYDATA メッセージを送信ます。

あと、WM_COPYDATA をブロードキャストしてはいけません。
どっかに「駄目だ」と書いてあるはずです。
とっちゃん
大ベテラン
会議室デビュー日: 2005/07/19
投稿数: 203
投稿日時: 2006-06-09 11:45
とっちゃんです。

あと、文字列を送る場合は、その文字コードの変換は送受信するアプリケーション間で取り決めしておく必要があります(もちろん、dwData部分も)。

WM_COPYDATA は、lpData から cbData 分のバイナリイメージをデッドコピーして送るので
対象は文字列に限っていませんからね。

>あと、WM_COPYDATA をブロードキャストしてはいけません。
>どっかに「駄目だ」と書いてあるはずです。

WM_COPYDATAのドキュメントの最後のほうに書かれています。

_________________
// とっちゃん(高萩 俊行)@わんくま同盟
// とっちゃん’Blog
// MS-MVP for Developer Tools - Visual C++
// WindowsInstallerの話題はhttp://www.freeml.com/msiまで

スキルアップ/キャリアアップ(JOB@IT)