Ошибка SendMessage с использованием API RegisterWindowMessage - PullRequest
0 голосов
/ 18 сентября 2018

Я отправляю сообщение окна из приложения 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 для C ++ WinProc

Отправить сообщение hwnd на 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 (); }

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Вы можете передать строку с вашим собственным сообщением с глобальным атомом - см. GlobalAddAtom APIВы получаете целочисленное значение из GlobalAddAtom (номер атома) и даете строку в качестве входного параметра.Затем SendMessage передает атом как WPARAM или LPARAM.Другая сторона декодирует атом в строку с GlobalGetAtomName.Наконец, позвоните GlobalDeleteAtom, чтобы освободить ресурс.

0 голосов
/ 18 сентября 2018

Вы не можете отправлять необработанные указатели памяти через границы процесса, как вы пытаетесь это сделать.Даже если вы преобразуете данные строки C # в BSTR, выделенный ОС, выделенная память по-прежнему действительна только в адресном пространстве процесса, который ее выделил.

Строковые данные должны быть маршалировал из адресного пространства одного процесса в адресное пространство другого процесса.COM обрабатывает это автоматически, когда передает значения BSTR через границы процесса.Но в случае оконных сообщений ОС автоматически маршалирует только определенные сообщения, а сообщения, созданные с помощью RegisterWindowMessage(), не маршалируются.

Для того, что вы пытаетесь использовать, вместо этого используйте WM_COPYDATA,который маршалед.Однако вы НИКОГДА не должны транслировать (используйте (IntPtr)0xffff aka 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);
}
...