tags:

views:

368

answers:

4

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 contains 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.
A: 

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.

superjoe30
unfortunately I'm not willing to pay... :( is there another way?
superjoe30
Actually Qt solutions has an LGPL license - you don't have to pay for a license.
superjoe30
+1  A: 

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.

OregonGhost
A: 

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.

Idan K
I need "Set class name." Also, winEventFilter gives me the parameters MSG * msg, long * result, which does not include lParam, which contains the actual message sent with WM_COPYDATA.
superjoe30
upon further investigation, it looks like MSG is a structure that includes message id as well as lParam, so this solution is great. stackoverflow's security won't let me change my vote for your answer.
superjoe30
Also, this still doesn't solve the problem that I need to be able to SET the class name.
superjoe30
Since Qt is the one who registers your window, it doesn't seem likely that it's configurable. That's why I recommended to let Qt use whatever class name it chooses and use GetClassName in runtime to figure out what it is then use that in your plugin.
Idan K
A: 

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");
    
superjoe30