天天看點

Qt程序間使用Windows API通信

一、前言

最近在想Qt如何遠端更新程式,Qt将程式打包釋出之後,如果在代碼中做了一些細微的修改,隻需要更新xxx.exe即可更新程式。如果沒有自動遠端更新程式,則需要我們自己将可執行程式發給對方,這樣很麻煩。是以在程式中增加一個更新子產品,一啟動或按鍵觸發去比對遠端伺服器上的軟體版本号,如果軟體版本号高于目前版本号,則彈窗選則更新或取消。然後把可執行檔案下載下傳下來,删除原先的可執行檔案,把新下載下傳的可執行檔案改名,并放到合适的路徑即完成遠端更新。

問題就在如果我下載下傳完,把程式關閉了(删除原先的可執行檔案),誰去做後面這些工作呢?(把新下載下傳的可執行檔案改名,并放到合适的路徑)

然後我就想到了,委托更新輔助程式去完成這個任務,主程式下載下傳完成之後,使用​

​QProcess process; process.start ("更新輔助程式.exe");​

​啟動更新輔助程式,退出主程式,然後完成後續操作(把新下載下傳的可執行檔案改名,并放到合适的路徑)。

是以這就涉及到了程序間通信的問題!

二、效果展示

Qt程式間使用Windows API通信

三、接口詳解

​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);      
  • ​eventType​

    ​​:windows平台的值是​

    ​windows_generic_MSG​

    ​;
  • ​message​

    ​:類型為MSG*,存儲的就是PostMessage中的Msg;
  • ​Result​

    ​:類型為LRESULT,傳回的值;
  • ​傳回值​

    ​:如果傳回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 = &param[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 = &param[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);
}