SendMessage使用RegisterWindowMessage API失败

问题描述 投票:0回答:2

我正在从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

Send message to a hwnd in C#

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# c++ sendmessage
2个回答
2
投票

您不能像尝试那样跨进程边界发送原始内存指针。即使您将C#字符串数据转换为OS分配的BSTR,分配的内存仍然只在分配它的进程的地址空间中有效。

您的字符串数据必须从一个进程的地址空间封送到另一个进程的地址空间。当COM在过程边界上传递BSTR值时,COM会自动为您处理。但是对于窗口消息,操作系统只会自动封送某些消息,并且没有封送使用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);
}

0
投票

您可以使用global atom传输带有自定义消息的字符串 - 请参阅GlobalAddAtom API。您从GlobalAddAtom(原子编号)获取整数值,并将该字符串作为输入参数。然后SendMessage传输原子为WPARAMLPARAM。另一边用GlobalGetAtomName将原子解码为字符串。最后打电话给GlobalDeleteAtom来释放资源。

© www.soinside.com 2019 - 2024. All rights reserved.