在 Qt 应用程序中接收 WM_COPYDATA 消息

Posted

技术标签:

【中文标题】在 Qt 应用程序中接收 WM_COPYDATA 消息【英文标题】:Receive WM_COPYDATA messages in a Qt app 【发布时间】:2009-11-18 10:53:37 【问题描述】:

我正在开发一个仅限 Windows 的 Qt 应用程序,我需要从 Microsoft OneNote 插件接收数据。该插件是用 C# 编写的,可以发送 WM_COPYDATA 消息。如何在 C++ Qt 应用程序中接收这些消息?

我需要:

能够指定窗口在调用 RegisterClassEx 时注册的“类名”,这样我可以确保插件将 WM_COPYDATA 消息发送到正确的窗口。 有权访问消息 ID 以检查它是否是 WM_COPYDATA 和 lParam,其中包含带有实际数据的 COPYDATASTRUCT。此信息在 WndProc 中传递,但我找不到可以拦截这些消息的挂钩。

【问题讨论】:

【参考方案1】:

这一切都可以在 Qt 中处理:

    使用将捕获 WM_COPYDATA 消息的类扩展 QWidget:

        class EventReceiverWindow : public QWidget
    
        Q_OBJECT
    public:
        EventReceiverWindow();
    
    signals:
        void eventData(const QString & data);
    
    private:
        bool winEvent ( MSG * message, long * result );
    ;
    

    生成一个 GUID 设置为 QWidget 的 windowTitle:

    EventReceiverWindow::EventReceiverWindow()
    
        setWindowTitle("ProjectName-3F2504E0-4F89-11D3-9A0C-0305E82C3301::EventReceiver");
    
    

    重写 winEvent 来处理 WM_COPYDATA 结构并在你得到它时发出一个信号:

    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;
    
    

    在另一个类中,你可以使用这个类来接收消息字符串:

    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;
    
    

    在发送消息的程序中,只需 find the window 通过唯一的窗口标题。这是 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);
        
    
    

    那么你可以:

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

【讨论】:

【参考方案2】:

您还可以创建一个虚拟窗口,仅用于使用 Win32 API 接收该消息。我猜你将无法访问 Qt-Window 的窗口 proc,所以这应该是最简单的方法。

可以(我不会)还通过设置新的 WndProc(使用SetWindowLong(Ptr),可以使用QWidget::winId() 获得窗口的句柄)来子类化窗口。在这个 WndProc 中,您可以只处理您特定的 WM_COPYDATA 并将所有其他窗口消息传递给旧的 WndProc。

【讨论】:

【参考方案3】:

要处理您的窗口收到的消息,请覆盖您的QCoreApplication::winEventFilter。如果这不起作用,您可以查看QAbstractEventDispatcher。

对于类名,您可以尝试使用QWidget::winId 和 Win32 API。我会尝试为您找到它,但我现在找不到,也许可以试试GetClassName。

【讨论】:

我需要“设置类名”。另外,winEventFilter 给了我参数 MSG * msg, long * result,其中不包括 lParam,其中包含使用 WM_COPYDATA 发送的实际消息。 经过进一步调查,MSG 似乎是一个包含消息 id 和 lParam 的结构,所以这个解决方案很棒。 *** 的安全性不允许我改变我对您的答案的投票。 另外,这仍然没有解决我需要能够设置类名的问题。 由于 Qt 是注册您的窗口的人,它似乎不太可能是可配置的。这就是为什么我建议让 Qt 使用它选择的任何类名并在运行时使用 GetClassName 来确定它是什么,然后在你的插件中使用它。【参考方案4】:

您可以使用 Qt 解决方案中的 QWinHost 创建一个虚拟窗口。关注the guide 将向您展示如何指定您的类名并检查您的消息的事件循环。

【讨论】:

很遗憾我不愿意付钱...... :(还有其他方法吗? 实际上 Qt 解决方案有一个 LGPL 许可证 - 你不必为许可证付费。

以上是关于在 Qt 应用程序中接收 WM_COPYDATA 消息的主要内容,如果未能解决你的问题,请参考以下文章

MFC中WM_COPYDATA消息

WinCE Stack 异常

跨进程发送消息数据(发送WM_COPYDATA消息,够简单的)

从 WM_COPYDATA 消息编组结构

VC++ 在两个程序中 传送字符串等常量值的方法:使用了 WM_COPYDATA 消息(转载)

在 64 位和 32 位 (WOW64) 应用程序之间使用 WM_COPYDATA 是不是安全?