名词解释

PostMessage 是 Windows API(应用程序接口) 中的一个常用函数,用于将一条消息放入到消息队列中。并且不会等待响应的线程处理消息,而是直接返回。(简单的理解就是异步)。

而 SendMessage 作用一样,但是会等待结果返回(同步)

我们先来看 PostMessag 函数的原型:

hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:

HWND_BROADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、 被覆盖的窗口和弹出式窗口。消息不被寄送到子窗口

NULL:此函数的操作和调用参数 dwThread 设置为当前线程的标识符 PostThreadMessage 函数一样

Msg:指定被寄送的消息。

wParam:指定附加的消息特定的信息。

LParam:指定附加的消息特定的信息。

返回值:如果函数调用成功,返回非零,否则函数调用返回值为零

接收的时候,使用 QT5 中的方法是 在接收的类中,重新实现 nativeEvent()(Qt4的时候使用的是 winEvent(),从 Qt5 开始,就使用 nativeEvent() ),这个方法既可以拦截系统消息,也可以拦截通过 postMessagesendMessage 发送的自定义消息。

例子

需求:要求程序 A 以 tab 标签形式嵌套到程序 B 中

部分核心代码

嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
auto window = QWindow::fromWinId((WId)hwnd);
if (window) {
qDebug() << "createWindowContainer.............";
window->setFlags(window->flags() | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
QWidget *containerWidget = new QWidget;
containerWidget->setObjectName("containerWidget");
containerWidget->setProperty("processID", processID);
containerWidget->setProperty("winID", jsObj.value("winId").toString());
QHBoxLayout *layout = new QHBoxLayout(containerWidget);
layout->setMargin(0);
if (auto w = QWidget::createWindowContainer(window, containerWidget, Qt::Widget))
layout->addWidget(w);
containerWidget->setLayout(layout);
mw->addTabPage(QIcon(":/resource/images/xx.ico"), title, containerWidget);

::MoveWindow(hwnd, mw->stackedWidget()->x(), mw->stackedWidget()->y(), mw->stackedWidget()->width(), mw->stackedWidget()->height(), true);
mw->raise();

QApplication::processEvents();
}

......
int MainWindow::addTabPage(const QIcon &icon, const QString &text, QWidget *w)
{
return insertTabPage(ui->tabBar->count(), icon, text, w);
}

int MainWindow::insertTabPage(int index, const QIcon &icon, const QString &text, QWidget *w)
{
ui->stackedWidget->insertWidget(index, w);
int ret = ui->tabBar->insertTab(index, text);
ui->tabBar->setTabIcon(index, icon);
ui->tabBar->setCurrentIndex(index);
ui->stackedWidget->setCurrentIndex(index);

m_flh->addExcludeItem(ui->tabBar->tabButton(ret, QTabBar::ButtonPosition::RightSide));

return ret;
}

程序 A 发送 Windows 消息给程序 B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
bool ok = false;
auto strWinId = winID.toULongLong(&ok);
if (!ok) {
qDebug() << __FUNCTION__ << "winid error!";
return false;
}

HWND hwnd = reinterpret_cast<HWND>(strWinId);
if (hwnd == nullptr) {
std::cout << GetLastErrorAsString1() << std::endl;
return false;
}

if (::IsWindow(hwnd)) {
// 单独启动一个线程进行数据传递
std::thread thread([=](){
// 使用 COPYDATA 的方式进行数据传递
COPYDATASTRUCT copyData;
copyData.dwData = CUSTOM_TYPE_YDPLATFORM;
copyData.lpData = (PVOID)msgData.data();
copyData.cbData = (DWORD)msgData.size();

HWND sender = (HWND)effectiveWinId();
LRESULT result = ::SendMessageW(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(sender), reinterpret_cast<LPARAM>(&copyData));
qDebug() << __FUNCTION__ << "result: " << result;
});
// 分离子线程(数据传递线程)与主线程(UI 线程)
thread.detach();

return true;
} else {
qDebug() << "findwindow failed!";
}

return false;

接收 Windows 消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType);

#ifdef Q_OS_WIN
MSG *msg = static_cast<MSG *>(message);
switch (msg->message) {
case WM_COPYDATA: {
COPYDATASTRUCT *copyData = reinterpret_cast<COPYDATASTRUCT*>(msg->lParam);
if (copyData->dwData == CUSTOM_TYPE_YDPLATFORM) {

QString structMsg = QString::fromUtf8(reinterpret_cast<char *>(copyData->lpData), copyData->cbData);
// qDebug() << "CUSTOM_TYPE_YDPLATFORM receive msg: " << structMsg;
parseCustomMsgCommand(structMsg);

*result = 1;
return true;
}
}
break;
}
#endif // Q_OS_WIN

return false;
}