SendMessage 使用 RegisterWindowMessage API 失败

SendMessage fail using RegisterWindowMessage API

本文关键字:失败 API RegisterWindowMessage 使用 SendMessage      更新时间:2023-10-16

我正在将窗口消息从 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;
}
}

请让我知道如何解决此问题?

我还参考下面的链接来修复问题,但它可能无济于事。

C# SendMessage to C++ WinProc

在 C# 中向 hwnd 发送消息

// 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++代码忽略无法识别其dwData值的任何WM_COPYDATA消息。

尝试更多类似的东西:

[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);
}

您可以使用全局原子将字符串与自定义消息一起传输 - 请参阅GlobalAddAtomAPI。您从 GlobalAddAtom(原子数(获取整数值,并将字符串作为输入参数。然后SendMessageWPARAMLPARAM传输原子。另一侧将原子解码为带有GlobalGetAtomName串。最后调用GlobalDeleteAtom以释放资源。