一、前言
最近在想Qt如何远程更新程序,Qt将程序打包发布之后,如果在代码中做了一些细微的修改,只需要更新xxx.exe即可更新程序。如果没有自动远程更新程序,则需要我们自己将可执行程序发给对方,这样很麻烦。所以在程序中增加一个更新模块,一启动或按键触发去比对远程服务器上的软件版本号,如果软件版本号高于当前版本号,则弹窗选则更新或取消。然后把可执行文件下载下来,删除原先的可执行文件,把新下载的可执行文件改名,并放到合适的路径即完成远程更新。
问题就在如果我下载完,把程序关闭了(删除原先的可执行文件),谁去做后面这些工作呢?(把新下载的可执行文件改名,并放到合适的路径)
然后我就想到了,委托更新辅助程序去完成这个任务,主程序下载完成之后,使用
QProcess process; process.start ("更新辅助程序.exe");
启动更新辅助程序,退出主程序,然后完成后续操作(把新下载的可执行文件改名,并放到合适的路径)。
所以这就涉及到了进程间通信的问题!
二、效果展示

三、接口详解
PostMessage
是Windows API中的一个常用函数,用于将一条消息放入消息队列中。并且不会等待响应的线程处理消息,而是直接返回,简而言之就是异步。
SendMessage
作用一样,但是会等到结果返回,简而言之就是同步。
BOOL WINAPI PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
-
hWnd
:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:
|值| 含义 |
|:–|:–|
| HWND_BROADCAST | 消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口;消息不被寄送到子窗口 |
| NULL | 此函数的操作和调用函数dwThread设置为当前线程的标识符PostThreadMessage函数一样 |
-
:指定被寄送的消息;Msg
-
:指定附加的消息特定的信息;wParam
-
:指定附加的消息特定的信息;LParam
-
:如果函数调用成功,返回非零,否则函数调用返回值为零;返回值
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
- 同上
-
:指定消息处理的结果,依赖于所发送的消息;返回值
接收消息的时候,使用Qt5中的方法是在的类中,重新实现nativeEvent函数(Qt4的时候使用winEvent,从Qt5开始,就使用nativeEvent),这个方法既可以拦截系统信息,也可以拦截通过
postMessage
、
sendMessage
发送的自定义消息。
[virtual protected] bool QWidget::nativeEvent(const QByteArray &eventType, void *message, long *result);
-
:windows平台的值是eventType
;windows_generic_MSG
-
:类型为MSG*,存储的就是PostMessage中的Msg;message
-
:类型为LRESULT,返回的值;Result
-
:如果返回true,表示停止这个消息;如果返回false,这个消息就继续传递给Qt,Qt会将这个消息转变为Qt event并将它发送给响应的控件;返回值
四、关键代码
void MainWindow::on_pushButton_clicked()
{
//查找窗口句柄(L 将该字符串当做Unicode来编译)
HWND hWnd = FindWindow(NULL, L"接收窗口");
if (hWnd == 0) {
return;
}
if (NULL != hWnd) {
QString command = ui->plainTextEdit->toPlainText();
std::string param = command.toStdString();
COPYDATASTRUCT data; //使用COPYDATA的方式进行数据传递
data.dwData = 0; //指定传输的数据
data.cbData = param.length(); //指定数据大小
data.lpData = ¶m[0]; //指向要传递到接收应用程序的数据的指针。可以为空
SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM)&data);
}
}
bool ReceiveWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
if (eventType == "windows_generic_MSG") //windows平台
{
MSG* msg = reinterpret_cast<MSG*>(message);
if (msg->message == WM_COPYDATA)
{
COPYDATASTRUCT *data = reinterpret_cast<COPYDATASTRUCT*>(msg->lParam);
QTextCodec *gbk = QTextCodec::codecForName("utf-8");
QString recevice = gbk->toUnicode((char *)(data->lpData));//转码
ui->plainTextEdit->appendPlainText(QString("系统收到消息:%1").arg(recevice));
return true;//消息不再进行传递,不再处理
}
}
return QWidget::nativeEvent(eventType, message, result);
}
五、完整代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QWidget>
#include <QTextCodec>
#include "receivewidget.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
ReceiveWidget* rWidget;
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("发送窗口");
rWidget = new ReceiveWidget;
rWidget->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
//查找窗口句柄(L 将该字符串当做Unicode来编译)
HWND hWnd = FindWindow(NULL, L"接收窗口");
if (hWnd == 0) {
return;
}
if (NULL != hWnd) {
QString command = ui->plainTextEdit->toPlainText();
std::string param = command.toStdString();
COPYDATASTRUCT data; //使用COPYDATA的方式进行数据传递
data.dwData = 0; //指定传输的数据
data.cbData = param.length(); //指定数据大小
data.lpData = ¶m[0]; //指向要传递到接收应用程序的数据的指针。可以为空
SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM)&data);
}
}
#ifndef RECEIVEWIDGET_H
#define RECEIVEWIDGET_H
#include <QDebug>
#include <QWidget>
#include <QTextCodec>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
namespace Ui {
class ReceiveWidget;
}
class ReceiveWidget : public QWidget
{
Q_OBJECT
public:
explicit ReceiveWidget(QWidget *parent = nullptr);
~ReceiveWidget();
bool nativeEvent(const QByteArray &eventType, void *message, long *result);
private:
Ui::ReceiveWidget *ui;
};
#endif // RECEIVEWIDGET_H
#include "receivewidget.h"
#include "ui_receivewidget.h"
ReceiveWidget::ReceiveWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ReceiveWidget)
{
ui->setupUi(this);
this->setWindowTitle("接收窗口");
}
ReceiveWidget::~ReceiveWidget()
{
delete ui;
}
bool ReceiveWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
if (eventType == "windows_generic_MSG") //windows平台
{
MSG* msg = reinterpret_cast<MSG*>(message);
if (msg->message == WM_COPYDATA)
{
COPYDATASTRUCT *data = reinterpret_cast<COPYDATASTRUCT*>(msg->lParam);
QTextCodec *gbk = QTextCodec::codecForName("utf-8");
QString recevice = gbk->toUnicode((char *)(data->lpData));//转码
ui->plainTextEdit->appendPlainText(QString("系统收到消息:%1").arg(recevice));
return true;//消息不再进行传递,不再处理
}
}
return QWidget::nativeEvent(eventType, message, result);
}