在 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 消息的主要内容,如果未能解决你的问题,请参考以下文章
跨进程发送消息数据(发送WM_COPYDATA消息,够简单的)