一、前言
最近在想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);
}