简体   繁体   中英

SendMessage fail using RegisterWindowMessage API

I am sending a window message from a C# application to a C++ Win32 application. I am using a message via the RegisterWindowMessage() API.

The string value transfers from C# to C++, but on the C++ side I could not convert it back to a string.

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

Please let me know how to fix this issue?

I also refer below link for fix issue but it could't help.

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

You cannot send raw memory pointers across process boundaries like you are attempting to do. Even though you are converting your C# string data to a BSTR allocated by the OS, the allocated memory is still only valid in the address space of the process that allocated it.

Your string data must be marshaled from one process's address space to the other process's address space. COM handles that for you automatically when it passes BSTR values over process boundaries. But with window messages, only certain messages are marshaled automatically by the OS, and messages created with RegisterWindowMessage() are not marshaled.

For what you are attempting, use WM_COPYDATA instead, which is marshaled. However, you should NEVER broadcast (use (IntPtr)0xffff aka HWND_BROADCAST as the target window) a WM_COPYDATA message! Bad things can happen if unsuspecting apps receive WM_COPYDATA messages they are not prepared to handle correctly.

Have your C# code find the specific window it is actually interested in for your C++ app (from FindWindow/Ex() , etc) and then send WM_COPYDATA to only that window and no others. You can use RegisterWindowMessage() to create a unique value for use in the COPYDATASTRUCT::dwData field to differentiate your use of WM_COPYDATA from other people's use of WM_COPYDATA . Make your C++ code ignore any WM_COPYDATA message whose dwData value is not recognized.

Try something more like this:

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

You can transfer string with your custom message with global atom - see GlobalAddAtom API. You get integer value from GlobalAddAtom (atom number) and give the string as input parameter. Then SendMessage transmits the atom as WPARAM or LPARAM . The other side decodes the atom to string with GlobalGetAtomName . Finally call GlobalDeleteAtom to free the resource.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM