天天看点

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);
}