开发者

Receive WM_COPYDATA messages in a Qt app

I am working on a Windows-only Qt application, and I need to receive data from a Microsoft OneNote plugin. The plugin is written in C#, and can send WM_COPYDATA messages. How do I receive these messages in a C++ Qt app?

I need to:

  • Be able to specify the "class name" a window registers as when it calls RegisterClassEx, so that I can make sure the plugin sends WM_COPYDATA messages to the correct window.
  • Have access to the message id to check if it's WM_COPYDATA and lParam, which contai开发者_如何转开发ns the COPYDATASTRUCT with the actual data. This information is passed in WndProc, but I am unable to find a hook where I can intercept these messages.


This can all be handled within Qt:

  1. Extend QWidget with a class that will capture the WM_COPYDATA messages:

        class EventReceiverWindow : public QWidget
    {
        Q_OBJECT
    public:
        EventReceiverWindow();
    
    signals:
        void eventData(const QString & data);
    
    private:
        bool winEvent ( MSG * message, long * result );
    };
    
  2. Generate a GUID to set as the QWidget's windowTitle:

    EventReceiverWindow::EventReceiverWindow()
    {
        setWindowTitle("ProjectName-3F2504E0-4F89-11D3-9A0C-0305E82C3301::EventReceiver");
    }
    
  3. Override winEvent to handle the WM_COPYDATA structure and emit a signal when you get it:

    bool EventReceiverWindow::winEvent ( MSG * message, long * result )
    {
            if( message->message == WM_COPYDATA ) {
                // extract the string from lParam
                COPYDATASTRUCT * data = (COPYDATASTRUCT *) message->lParam;
    
                emit eventData(QString::fromAscii((const char *)data->lpData, data->cbData));
    
                // keep the event from qt
                *result = 0;
                return true;
            }
    
            // give the event to qt
            return false;
    }
    
  4. In another class, you can use this class to receive the message strings:

    EventReceiverWindow * eventWindow = new EventReceiverWindow;
    QObject::connect(eventWindow, SIGNAL(eventData(const QString &)), this, SLOT(handleEventData(const QString &)));
    

    ...

    void OneNoteInterface::handleEventData(const QString &data)
    {
        qDebug() << "message from our secret agent: " << data;
    }
    
  5. And in the program that is sending the messages, simply find the window by the unique window caption. Here's an example in C#:

    private struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int cbData;
        [MarshalAs(UnmanagedType.LPStr)]
        public string lpData;
    }
    
    private const int WM_COPYDATA = 0x4A;
    
    [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
    static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
    
    [DllImport("User32.dll", EntryPoint = "SendMessage")]
    private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
    
    private void sendMessageTo(IntPtr hWnd, String msg)
    {
        int wParam = 0;
        int result = 0;
    
        if (hWnd != IntPtr.Zero )
        {
            byte[] sarr = System.Text.Encoding.Default.GetBytes(msg);
            int len = sarr.Length;
            COPYDATASTRUCT cds;
            cds.dwData = IntPtr.Zero;
            cds.lpData = msg;
            cds.cbData = len + 1;
            result = SendMessage(hWnd, WM_COPYDATA, wParam, ref cds);
        }
    }
    

    then you can:

    IntPtr hwnd = FindWindowByCaption(IntPtr.zero, "ProjectName-3F2504E0-4F89-11D3-9A0C-0305E82C3301::EventReceiver");
    sendMessageTo(hwnd, "omg hai");
    


You can also create a dummy window just for receiving that message with the Win32 API. I guess you won't have access to a Qt-Window's window proc, so this should be the easiest way.

You could (I wouldn't) also subclass the window by setting a new WndProc (with SetWindowLong(Ptr), the window's handle can be obtained with QWidget::winId()). In this WndProc, you could just handle your specific WM_COPYDATA and pass all other window messages to the old WndProc.


To handle messages your window receives, override your QCoreApplication::winEventFilter. If that doesn't work you can take a look at QAbstractEventDispatcher.

For the class name you could try using QWidget::winId along with Win32 API. I would try and find it for you but I can't right now, maybe try GetClassName.


You can use QWinHost from Qt solutions to create a dummy window. Following the guide will show you how to specify your class name and check the event loop for your message.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜