打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Hello Qt(三十一)——QT进程间通信

一、TCP/IP

其实就是通过网络模块实现的IPC。不过Qt对其进行了封装,并提供了两个层次的API,包括应用程序级的QNetworkAccessManager, QFtp等和底层的QTcpSocket, QTcpServer, QSslSocket等。

二、QShared Memory(共享内存)

Qt提供的基于共享内存的IPC有QSharedMemory类和QSystemSemaphore类,QSharedMemory可以访问共享内存区域,以及多线程和进程的共享内存区域,而QSystemSemaphore类用于访问系统共享资源,以实现独立进程间的通信。

1、QSharedMemory

QSharedMemory读写内存时,可以使用lock()实现同步。如果同步完成,必须使用unlock()为共享内存区域解锁。

QSharedMemory可以使用attach()访问共享内存,通过指定参数来设置共享内存的访问模式。如果使用的是QSharedMemory::ReadOnly模式,则只能通过只读模式访问共享内存,如果使用QSharedMemory::ReadWrite模式则可以通过读写模式访问共享内存。

QSharedMemory拥有进程并提供可以返回共享内存区域指针的成员函数。在共享内存区域,成员函数constData()可以通过void 类型返回进程正在使用的内存区域指针。创建共享时,QSharedMemory可以以字节为单位分配共享内存区域,还可以通过第二个参数设置函数 attach()提供的模式。

QSharedMemory可以设置特定共享内存的固定key。函数setNativeKey()可以设置共享内存对象的key,setNativeKey()函数使用从属平台的共享内存的key进行相关设置。使用函数setKey()可以设置独立于平台的key。函数setKey()创建与平台本地key(Native Key)映射的key。

初始化QSharedMemory时,必须指定一个唯一的标识Key,进程的Key必须保持一致。可以使用setKey来设置。

2、QSystemSemaphore

QSystemSemaphore可以提供普通系统的信号量。信号量使用互斥体,而互斥体只可以使用1次锁定(Block)。因此,QSemaphore类不能对有效资源使用多线程,而QSystemSemaphore类可以在多进程或多线程中实现。

QSystemSemaphore与QSemaphore类不同,可以访问多进程。这表示QSystemSemaphore是一个重量级的类。因此,使用单一线程或进程时,建议使用QSemaphore。获得资源前,成员函数acquire()始终阻塞。函数release()用于释放资源,且该函数可以设置参数。该函数的参数>1时,释放资源。

3、QSharedMemory编程流程

共享内存中数据提供方:

A、定义QSharedMemory shareMemory,并设置标志名shareMemory.setKey();

B、将共享内存与主进程分离 shareMemory.detach();

C、创建共享内存 shareMemory.create();

D、将共享内存上锁shareMemory.lock();

E、将进程中要共享的数据拷贝到共享内存中;

F、将共享内存解锁shareMemory.unlock();

共享内存中数据使用方:

A、定义QSharedMemory shareMemory,并设置与共享内存提供方一致的标志名shareMemory.setKey()。

B、将共享内存上锁shareMemory.lock();

C、将共享内存与主进程绑定shareMemory.attach(),使主进程可以访问共享内存的数据;

D、从共享内存中取数据;

E、使用完后将共享内存解锁shareMemory.unlock(),并将共享内存与进程分离shareMemory.detach();

4、QSharedMemory使用实例

共享内存数据提供方代码:

Widget.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QtGui/QWidget>
  4. #include <QSharedMemory>
  5. class Widget : public QWidget
  6. {
  7.     Q_OBJECT
  8. public:
  9.     Widget(QWidget *parent = 0);
  10.     ~Widget();
  11. private:
  12.     QSharedMemory sharememory;
  13. private slots:
  14.     void WriteShareMemory();
  15. };
  16. #endif // WIDGET_H

Widget.cpp文件:

  1. #include 'Widget.h'
  2. #include <QBuffer>
  3. #include <QDebug>
  4. #include <QDataStream>
  5. Widget::Widget(QWidget *parent): QWidget(parent)
  6. {
  7.     sharememory.setKey('share');
  8.     this->WriteShareMemory();
  9. }
  10. Widget::~Widget()
  11. }
  12. void Widget::WriteShareMemory()
  13. {
  14.     if(sharememory.isAttached())
  15.     {
  16.         sharememory.detach();
  17.     }
  18.     QBuffer buffer;
  19.     QDataStream out(&buffer);
  20.     buffer.open(QBuffer::ReadWrite);
  21.     buffer.write('hello QT!');
  22.     int size = buffer.size();
  23.     if(!sharememory.create(size))
  24.     {
  25.         qDebug() << sharememory.errorString();
  26.         return ;
  27.     }
  28.     sharememory.lock();
  29.     char *dest = reinterpret_cast<char *>(sharememory.data());
  30.     const char *source = reinterpret_cast<const char *>(buffer.data().data());
  31.     memcpy(dest, source, qMin(size, sharememory.size()));
  32.     sharememory.unlock();
  33. }

Main.cpp文件:

  1. #include <QtGui/QApplication>
  2. #include 'Widget.h'
  3. int main(int argc, char *argv[])
  4. {
  5.     QApplication a(argc, argv);
  6.     Widget w;
  7.     w.show();
  8.     return a.exec();
  9. }

共享内存数据使用方代码:

Widget.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QtGui/QWidget>
  4. #include <QSharedMemory>
  5. class Widget : public QWidget
  6. {
  7.     Q_OBJECT
  8. public:
  9.     Widget(QWidget *parent = 0);
  10.     ~Widget();
  11. private:
  12.     QSharedMemory sharememory;
  13. private slots:
  14.     void ReadShareMemory();
  15. };
  16. #endif // WIDGET_H

Widget.cpp文件:

  1. #include 'Widget.h'
  2. #include <QBuffer>
  3. #include <QDebug>
  4. #include <QDataStream>
  5. Widget::Widget(QWidget *parent): QWidget(parent)
  6. {
  7.     sharememory.setKey('share');
  8.     this->ReadShareMemory();
  9. }
  10. Widget::~Widget()
  11. {
  12. }
  13. void Widget::ReadShareMemory()
  14. {
  15.     if(!sharememory.attach())
  16.     {
  17.         qDebug() << 'cann't attach sahrememory!';
  18.     }
  19.     QBuffer buffer;
  20.     sharememory.lock();
  21.     buffer.setData((char*)sharememory.constData(),sharememory.size());
  22.     buffer.open(QBuffer::ReadWrite);
  23.     buffer.readAll();
  24.     sharememory.unlock();
  25.     sharememory.detach();
  26.     qDebug() << buffer.data().data();
  27. }

Main.cpp文件:

  1. #include <QtGui/QApplication>
  2. #include 'Widget.h'
  3. int main(int argc, char *argv[])
  4. {
  5.     QApplication a(argc, argv);
  6.     Widget w;
  7.     w.show();
  8.     return a.exec();
  9. }

三、D-Bus

D_BUS是一种低开销、低延迟的进程间通信机制。Qt提供了QtDBus模块,QtDBus模块使用D-Bus协议,把信号与槽机制(Signal and Slot)扩展到进程级别,使得开发者可以在一个进程中发出信号,可以再其他进程定义槽来响应其他进程发出的信号。

D-Bus是一种高级的进程间通信机制,由freedesktop.org项目提供,使用GPL许可证发行。D-Bus最主要的用途是在Linux桌面环境为进程提供通信,同时能将Linux桌面环境和Linux内核事件作为消息传递到进程。D-Bus的主要概率为总线,注册后的进程可通过总线接收或传递消息,进程也可注册后等待内核事件响应,例如等待网络状态的转变或者计算机发出关机指令。目前,D-Bus已被大多数Linux发行版所采用,开发者可使用D-Bus实现各种复杂的进程间通信任务。

D-Bus是一个消息总线系统,其功能已涵盖进程间通信的所有需求,并具备一些特殊的用途。D-Bus是三层架构的进程间通信系统,其中包括:

接口层:接口层由函数库libdbus提供,进程可通过libdbus库使用D-Bus的能力。

总线层:总线层实际上是由D-Bus总线守护进程提供的。在Linux系统启动时运行,负责进程间的消息路由和传递,其中包括Linux内核和Linux桌面环境的消息传递。

包装层:包装层一系列基于特定应用程序框架的Wrapper库。

在QT中的Dbus是使用的Dbus的包装层libdbus-qt.

要查看Dbus总线上的服务和对象可以借助d-feet 和qdbusviewer

要发送信号可以使用dbus-send,要查看Dbus上的消息流可以使用dbus-monitor

四、QCOP(Qt COmmunications Protocol )

QCOP 是 Qt 内部的一种通信协议,这种协议用于不同的客户之间在同一地址空间内部或者不同的进程之间的通信。目前,这种机制还只在 Qt 的嵌入式版本中提供。

为 实现这种通信机制,Qt 中包括了由 QObject 类继承而来的 QCopChannel 类,该类提供了诸如 send()、isRegistered() 等静态函数,它们可以在脱离对象的情况下使用。为了在 channel 中接收通信数据,用户需要构造一个 QCopChannel 的子类并提供 receive() 函数的重载函数,或者利用 connect() 函数与接收到的信号相联系。值得一提的是,在 Qt 系统中,只提供了 QCOP 协议机制和用于接收消息的类,而如何发送消息则没有提供相应的类供用户使用。

在基于Qt的桌面系统Qtopia(QPE)中,则提供了相应的发送类:QCopEnvelope。用户可以通过该类利用channel向其他进程发送消息。该类将通过 QCopChannel 发送 QCop 消息的过程进行了封装,用户只需要调用该类中的相关函数就可以方便地实现进程之间的通信。

五、QProcess

跨平台类QProcess可以用于启动外部程序作为子进程,并与它们进行通信。QProcess提供了用于监测和控制该子进程状态的API。另外,QProcess为从QIODevice继承的子进程提供了输入/输出通道。

QProcess可以在应用程序内部与其它进程通信,或启动其它应用程序。与在终端机之类的命令输入窗口上使用名称和参数是一样的,可以使用QProcess提供的函数start()启动进程。可以注册QStringList处理进程后的参数。

1、带命令行参数启动

进程A-带参启动进程B

A、一般编写程序时,要启动外部程序,需要判断版本是debug还是release,否则,有可能会造成错误。

B、判断将要启动的进程是否存在,如果不存在,则启动;否则,不启动。

C、传参:json格式。

  1. void onSendMessage()
  2. {
  3.     QString strExe('');
  4.     if (m_pProcess == NULL)
  5.         m_pProcess = new QProcess(this);
  6. #if defined(QT_DEBUG)
  7.     strExe = 'ReceiveMessaged.exe';
  8. #   else
  9.     strExe = 'ReceiveMessage.exe';
  10. #  endif
  11.     // 判断进程是否存在
  12.     QProcess tasklist;
  13.     tasklist.start('tasklist',
  14.                    QStringList() << '/NH'
  15.                    << '/FO' << 'CSV'
  16.                    << '/FI' << QString('IMAGENAME eq %1').arg(strExe));
  17.     tasklist.waitForFinished();
  18.     QString strOutput = tasklist.readAllStandardOutput();
  19.     if (!strOutput.startsWith(QString('\'%1').arg(strExe)))
  20.     {
  21.         QJsonObject json;
  22.         json.insert('UserName'QStringLiteral('scorpio'));
  23.         json.insert('Password''123456');
  24.         QJsonDocument document;
  25.         document.setObject(json);
  26.         QByteArray byteArray = document.toJson(QJsonDocument::Compact);
  27.         QStringList arguments;
  28.         arguments << byteArray;
  29.         m_pProcess->startDetached(strExe, arguments);
  30.     }
  31. }

2、命令行读取

进程B-命令行读取

A、在main函数中初始化QApplication以后,获取命令行参数。

B、命令行参数中包含当前程序的名称、接收的参数等信息。

  1. QStringList cmdLineArgs = QCoreApplication::arguments();
  2. QMessageBox::information(NULLQStringLiteral('ReceiveMessage'), 
  3. cmdLineArgs.join(' '));

六、Session Management

在Linux/X11平台上,Qt提供了会话管理的支持。会话允许事件传播到进程,例如,当检测到关机时。进程和应用程序可以执行任何必要的操作,例如:保存打开的文档。

七、Windows消息

Windows消息机制可以实现进程间的通信。

1、发送消息

A、自定义消息类型与接收消息的窗口

包含所需库,定义发送消息的自定义类型、接收消息的窗体标题。自定义类型可以处理消息过多情况下,对消息的区分,如果不需要也可以去掉。

  1. #ifdef Q_OS_WIN
  2. #pragma comment(lib, 'user32.lib')
  3. #include <qt_windows.h>
  4. #endif
  5. const ULONG_PTR CUSTOM_TYPE = 10000;
  6. const QString c_strTitle = 'ReceiveMessage';

B、发送数据

  1.     HWND hwnd = NULL;
  2.     do
  3.     {
  4.         LPWSTR path = (LPWSTR)c_strTitle.utf16();  //path = L'SendMessage'
  5.         hwnd = ::FindWindowW(NULL, path);
  6.     } while (hwnd == (HWND)effectiveWinId()); // 忽略自己
  7.     if (::IsWindow(hwnd))
  8.     {
  9.         QString filename = QStringLiteral('进程通信-Windows消息');
  10.         QByteArray data = filename.toUtf8();
  11.         COPYDATASTRUCT copydata;
  12.         copydata.dwData = CUSTOM_TYPE;  // 用户定义数据
  13.         copydata.lpData = data.data();  // 指向数据的指针
  14.         copydata.cbData = data.size();  // 数据大小
  15.         HWND sender = (HWND)effectiveWinId();
  16.         ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(sender), 
  17. reinterpret_cast<LPARAM>(©data));
  18.     }

2、接收消息

A、设置标题

设置的窗口标题必须与发送消息指定的接收端窗口标题相同。

setWindowTitle('ReceiveMessage');

B、重写nativeEvent函数

  1. bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
  2. {
  3.     MSG *param = static_cast<MSG *>(message);
  4.     switch (param->message)
  5.     {
  6.     case WM_COPYDATA:
  7.     {
  8.         COPYDATASTRUCT *cds = reinterpret_cast<COPYDATASTRUCT*>(param->lParam);
  9.         if (cds->dwData == CUSTOM_TYPE)
  10.         {
  11.             QString strMessage = QString::fromUtf8(reinterpret_cast<char*>(
  12. cds->lpData), cds->cbData);
  13.             QMessageBox::information(thisQStringLiteral('提示'), strMessage);
  14.             *result = 1;
  15.             return true;
  16.         }
  17.     }
  18.     }
  19.     return QWidget::nativeEvent(eventType, message, result);
  20. }

3、程序实例

发送端:

Widget.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include <QPushButton>
  5. class Widget : public QWidget
  6. {
  7.     Q_OBJECT
  8. private:
  9.     QPushButton m_send;
  10. private slots:
  11.     void onSendMessage();
  12. public:
  13.     Widget(QWidget *parent = 0);
  14.     ~Widget();
  15. };
  16. #endif // WIDGET_H

Widget.cpp文件:

  1. #include 'Widget.h'
  2. #ifdef Q_OS_WIN
  3. #pragma comment(lib, 'user32.lib')
  4. #include <qt_windows.h>
  5. #endif
  6. const ULONG_PTR CUSTOM_TYPE = 10000;
  7. const QString c_strTitle = 'ReceiveMessage';
  8. Widget::Widget(QWidget *parent):QWidget(parent),m_send(this)
  9. {
  10.     m_send.move(5050);
  11.     m_send.resize(100,30);
  12.     m_send.setText('SendMessage');
  13.     connect(&m_send, SIGNAL(clicked(bool)), thisSLOT(onSendMessage()));
  14. }
  15. void Widget::onSendMessage()
  16. {
  17.     HWND hwnd = NULL;
  18.     do
  19.     {
  20.         LPWSTR path = (LPWSTR)c_strTitle.utf16();  //path = L'SendMessage'
  21.         hwnd = ::FindWindowW(NULL, path);
  22.     } while (hwnd == (HWND)effectiveWinId()); // 忽略自己
  23.     if (::IsWindow(hwnd))
  24.     {
  25.         QString filename = QStringLiteral('进程通信-Windows消息');
  26.         QByteArray data = filename.toUtf8();
  27.         COPYDATASTRUCT copydata;
  28.         copydata.dwData = CUSTOM_TYPE;  // 用户定义数据
  29.         copydata.lpData = data.data();  // 指向数据的指针
  30.         copydata.cbData = data.size();  // 数据大小
  31.         HWND sender = (HWND)effectiveWinId();
  32.         ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(sender), 
  33. reinterpret_cast<LPARAM>(©data));
  34.     }
  35. }
  36. Widget::~Widget()
  37. {
  38. }

接收端:

Widget.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. class Widget : public QWidget
  5. {
  6.     Q_OBJECT
  7. private:
  8.     bool nativeEvent(const QByteArray &eventType, void *message, long *result);
  9. public:
  10.     Widget(QWidget *parent = 0);
  11.     ~Widget();
  12. };
  13. #endif // WIDGET_H

Widget.cpp文件:

  1. #include 'Widget.h'
  2. #include <QMessageBox>
  3. #ifdef Q_OS_WIN
  4. #pragma comment(lib, 'user32.lib')
  5. #include <qt_windows.h>
  6. #endif
  7. const ULONG_PTR CUSTOM_TYPE = 10000;
  8. Widget::Widget(QWidget *parent): QWidget(parent)
  9. {
  10.     setWindowTitle('ReceiveMessage');
  11. }
  12. Widget::~Widget()
  13. {
  14. }
  15. bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
  16. {
  17.     MSG *param = static_cast<MSG *>(message);
  18.     switch (param->message)
  19.     {
  20.     case WM_COPYDATA:
  21.     {
  22.         COPYDATASTRUCT *cds = reinterpret_cast<COPYDATASTRUCT*>(param->lParam);
  23.         if (cds->dwData == CUSTOM_TYPE)
  24.         {
  25.             QString strMessage = QString::fromUtf8(reinterpret_cast<char*>(
  26. cds->lpData), cds->cbData);
  27.             QMessageBox::information(thisQStringLiteral('提示'), strMessage);
  28.             *result = 1;
  29.             return true;
  30.         }
  31.     }
  32.     }
  33.     return QWidget::nativeEvent(eventType, message, result);
  34. }
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
QT之QSharedMemory 详解 进程间通信
记录学习利用 QWidget 类创建桌面壁纸程序
通过鼠标事件来看QT/Embedded 窗体事件是如何派发的
Qt Widget中给Widget加上背景图
将自定义的widget放到Qt Designer中
QT:不规则窗口的实现
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服