我正在从 C# 应用程序向 C++ Win32 应用程序发送窗口消息。我正在通过 RegisterWindowMessage()
API 使用消息。
字符串值从 C# 传输到 C++,但在 C++ 端我无法将它转换回字符串。
C#
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
_sendMessageID = RegisterWindowMessage("WM_MSG_TEST");
public void SendMessage()
{
IntPtr buffer = Marshal.StringToBSTR("Hello");
SendMessage((IntPtr)0xffff, (int)_sendMessageID, IntPtr.Zero, buffer);
}
C++
UINT WM_MSG_AA = RegisterWindowMessage("WM_MSG_TEST");
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_MSG_TEST)
{
BSTR* pcds = (BSTR*)lParam;
}
}
请让我知道如何解决这个问题?
我也引用了下面的链接来解决问题,但它无济于事。
// WindowCreationCode
BOOL ProcessNextMessage()
{
MSG msg;
GetMessage(&(msg), _hWnd, 0, 0);
TranslateMessage(&(msg));
DispatchMessage(&(msg));
return TRUE;
}
int Create(){
CoInitialize(NULL);
_hInst = GetModuleHandle(NULL);
WNDCLASS wcex = { 0 };
wcex.lpfnWndProc = WndProc;
wcex.hInstance = _hInst;
wcex.lpszClassName = c_szClassName;
if (!GetClassInfo(wcex.hInstance, wcex.lpszClassName, &wcex))
{
if (!RegisterClass(&wcex))
{
return HRESULT_FROM_WIN32(GetLastError());
}
else
{
return S_OK;
}
}
else
{
return S_OK;
}
_hWnd = CreateWindowEx(
WS_EX_TOPMOST,
c_szClassName,
"ACTXAUTODRIVER",
WS_DLGFRAME ,
1, 1, 1, 1,
NULL,
NULL, _hInst, NULL);
ShowWindow(_hWnd, SW_HIDE);
while (ProcessNextMessage())
{
}
CoUninitialize();
}
最佳答案
您不能像您尝试的那样跨进程边界发送原始内存指针。即使您将 C# 字符串数据转换为操作系统分配的 BSTR
,分配的内存仍然只在分配它的进程的地址空间中有效。
您的字符串数据必须从一个进程的地址空间编码到另一个进程的地址空间。当 COM 通过进程边界传递 BSTR
值时,它会自动为您处理。但是对于窗口消息,只有某些消息会被操作系统自动编码,而使用 RegisterWindowMessage()
创建的消息不会被编码。
对于您正在尝试的内容,请使用 WM_COPYDATA
相反,这是编码的。但是,您永远不应该广播(使用 (IntPtr)0xffff
又名 HWND_BROADCAST
作为目标窗口)WM_COPYDATA
消息!如果毫无戒心的应用收到它们未准备好正确处理的 WM_COPYDATA
消息,就会发生坏事。
让您的 C# 代码为您的 C++ 应用找到它实际感兴趣的特定窗口(来自 FindWindow/Ex()
等),然后发送 WM_COPYDATA
只有那个窗口,没有其他人。您可以使用 RegisterWindowMessage()
创建一个用于 COPYDATASTRUCT::dwData
字段的唯一值,以区分您对 WM_COPYDATA
的使用与其他人的使用 WM_COPYDATA
。让您的 C++ 代码忽略任何 WM_COPYDATA
消息,其 dwData
值无法识别。
尝试更像这样的东西:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
const int WM_COPYDATA = 0x004A;
_cdsDataID = RegisterWindowMessage("WM_MSG_TEST");
public void SendMessage() {
if (_cdsDataID == IntPtr.Zero) return;
IntPtr TargetWnd = ...; // FindWindow(), etc
if (TargetWnd == IntPtr.Zero) return;
string s = "Hello";
COPYDATASTRUCT copyData = new COPYDATASTRUCT();
copyData.lpData = Marshal.StringToHGlobalUni(s);
if (copyData.lpData != IntPtr.Zero)
{
copyData.dwData = _cdsDataID;
copyData.cbData = (s.Length + 1) * 2;
IntPtr copyDataBuff = Marshal.AllocHGlobal(Marshal.SizeOf(copyData));
if (copyDataBuff != IntPtr.Zero)
{
Marshal.StructureToPtr(copyData, copyDataBuff, false);
SendMessage(TargetWnd, WM_COPYDATA, IntPtr.Zero, copyDataBuff);
Marshal.FreeHGlobal(copyDataBuff);
}
Marshal.FreeHGlobal(copyData.lpData);
}
}
const UINT uiDataID = RegisterWindowMessage("WM_MSG_TEST");
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_COPYDATA)
{
LPCOPYDATASTRUCT pcds = (LPCOPYDATASTRUCT) lParam;
if ((pcds->dwData == WM_MSG_TEST) && (WM_MSG_TEST != 0))
{
WCHAR* pstr = (WCHAR*) pcds->lpData;
...
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
关于c# - SendMessage 使用 RegisterWindowMessage API 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52380172/