QT信号槽分析 – 作者:雷石安全实验室

环境安装

默认安装好了VS2015 和QT

下载QT pdb和QT src压缩包(选择自己的版本,我是5.9.8)

qt下载网址

1604046055_5f9bcce76a6564b05594d.png!small?1604046015895

拷贝自己需要的文件,qt目录中D:\Qt\Qt5.9.8\5.9.8我是这个

1604046063_5f9bccef222eb8c3a5f16.png!small?1604046023458

并且把源码也拷贝进同一目录,当然其他的目录也行

设置VS选项

工具->选项->调试->符号

1604046071_5f9bccf71a96625ba5caf.png!small

项目->xxxx属性->vc++目录(添加源码路径)

1604046091_5f9bcd0b51eda34b37659.png!small?1604046051698

测试

对着qt的函数或者宏按F12

1604046103_5f9bcd17b01c0815ab8d5.png!small?16040460640821604046111_5f9bcd1f9908e0de8ee49.png!small?1604046072274

安装查看源码的软件(Source Insight)

这个工具可以很好的分析源码

1604046180_5f9bcd64d7a2b65a1c8d8.png!small?1604046141496

分析QT信号槽

分析moc生成的文件

案例代码

#include <QObject>class QtClass : public QObject{Q_OBJECTpublic:QtClass(QObject *parent);~QtClass();void Test(){emit Sig1(5);}signals:void Sig1(int nVal);     //void Sig2(bool b);//void Sig3(bool b);//void Sig4(bool b, int nVal);private slots:void Slot1(int nVal0){qDebug() << nVal0;}}

信号函数只有声明,没有实现?

emit Sig1(5);这句代码调用信号,但是我们没有实现信号这个函数呀。我们没有实现,那么是什么能够帮我们实现呢?当然是编译器。

调试信号函数

F11发现是有实现代码的吧

1604046255_5f9bcdaf279ef33e6648c.png!small?1604046216864

打开文件所在目录

1604046261_5f9bcdb57d02f4bc272c2.png!small?1604046221815

查看moc文件夹,分析编译器都给我们生成了什么

定义了一个结构体

qt_meta_stringdata_QtClass_t

struct qt_meta_stringdata_QtClass_t {     QByteArrayData data[6];     char stringdata0[31];};

后面创建了一个结构体静态变量里面存放一些类、参数、函数名等信息。这个第一个参数应该是ID,字符串的开始位置,字符串size

QT_MOC_LITERAL(0, 0, 7), // “QtClass”

static const qt_meta_stringdata_QtClass_t qt_meta_stringdata_QtClass = {     {QT_MOC_LITERAL(0, 0, 7), // "QtClass" //类名QT_MOC_LITERAL(1, 8, 4), // "Sig1"      //信号名QT_MOC_LITERAL(2, 13, 0), // ""        QT_MOC_LITERAL(3, 14, 4), // "nVal" //参数名QT_MOC_LITERAL(4, 19, 5), // "Slot1" //槽函数名QT_MOC_LITERAL(5, 25, 5) // "nVal0"     //槽参数名 },"QtClass\0Sig1\0\0nVal\0Slot1\0nVal0"};

qt_meta_data_QtClass : 存储类中函数相关的信息

static const uint qt_meta_data_QtClass[] = {  // content:7,       // revision  //qt对应版本0,       // classname        0,    0, // classinfo2,   14, // methods   //2个函数 context长度140,    0, // properties0,    0, // enums/sets0,    0, // constructors0,       // flags1,       // signalCount //信号数量// signals: name, argc, parameters, tag, flags1,    1,   24,    2, 0x06 /* Public */,//name:对应qt_meta_stringdata_QtClass_t结构体的ID,是"Sig1"//argc:是参数个数//parameters:该函数的具体声明在在qt_meta_data_QtClass结构体的偏移// slots: name, argc, parameters, tag, flags4,    1,   27,    2, 0x08 /* Private */,//上面的parameters就是指向这里// 返回值   参数1... 这个结构的大小24 // signals: parametersQMetaType::Void, QMetaType::Int,    3,27 // slots: parametersQMetaType::Void, QMetaType::Int,    5,0        // eod};

qt_static_metacall类的信号/槽函数调用的实现地址判断传进来的ID然后调用函数

void QtClass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, {if (_c == QMetaObject::InvokeMetaMethod) {QtClass *_t = static_cast<QtClass *>(_o);         Q_UNUSED(_t)         switch (_id) {case 0: _t->Sig1((*reinterpret_cast< int(*)>(_a[1]))); break;         case 1: _t->Slot1((*reinterpret_cast< int(*)>(_a[1]))); break;         default: ;}} else if (_c == QMetaObject::IndexOfMethod) {         int *result = reinterpret_cast<int *>(_a[0]);{typedef void (QtClass::*_t)(int );             if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&QtClass::*result = 0;                 return;}}}}

staticMetaObject : 类信息的总和

红色框是父类地址,蓝色是存储字符串的结构体的地址,黑色是存放信号槽函数信息的结构体,类的信号/槽函数调用的实现地址

1604046293_5f9bcdd556fca1b683a51.png!small?1604046253727

metaObject:判断静态/动态调用

1604046302_5f9bcdde17d212920802d.png!small?1604046262616

qt_metacast:返回类名称

1604046319_5f9bcdef751f88f96a3c7.png!small

+qt_metacall:函数调用

void** _a:数组指针, 每个指针指向一个函数的地址

_c:实现函数的类型

_id : 函数id, 判断是否实现 & 以何种方式实现该函数

1604046334_5f9bcdfe72d58b40fb95f.png!small?1604046295008

信号函数的实现

void* _a[]是一个信号对应的槽函数的地址

一个信号可以对应多个槽,所有这里是void* _a[]数组

1604046359_5f9bce172163e00c70fe0.png!small?1604046319518

Q_OBJECT

从这里看是不是很明显的能看出,这里写了虚函数,然后生成moc文件帮我们实现

* qmake ignore Q_OBJECT */#define Q_OBJECT \ public: \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static const QMetaObject staticMetaObject; \     virtual const QMetaObject *metaObject() const; \     virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \     QT_TR_FUNCTIONS \ private: \Q_OBJECT_NO_ATTRIBUTES_WARNING \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, Q     QT_WARNING_POP \struct QPrivateSignal {}; \QT_ANNOTATE_CLASS(qt_qobject, "")

分析connect函数

检查函数

判断参数是否为NULL

1604046377_5f9bce29474baa8fea6e6.png!small?1604046337637

给Sender赋值获取名字、对象、参数等

1604046383_5f9bce2fc540505f357f0.png!small?1604046344250

获取sender的Index,并且判断是否有效

1604046394_5f9bce3a3b4350a297f91.png!small?1604046354899

接收方信息

1604046400_5f9bce40c5521a71d469d.png!small?1604046361226

判断信号槽是否被关联

1604046409_5f9bce490bd4224690ef4.png!small?1604046369647

判断接收方是否有效可以被关联

1604046415_5f9bce4f1f88999f8ed9d.png!small?1604046375791

检查发送者和接收者参数是否一致

1604046421_5f9bce55e4477545950cc.png!small?1604046382344

执行关联操作函数

1604046428_5f9bce5cb5f5d4f8fa938.png!small?1604046389307

进入实现函数

先是强转了sender 和receiver保存在两变量里面

1604046439_5f9bce67ebccdf3335870.png!small?1604046400368

检测版本

1604046445_5f9bce6ddc11a574fd000.png!small?1604046406511

估计是为了防止多线程,给了锁

1604046451_5f9bce73dd0d14a09d3b3.png!small?1604046412244

生成信号槽的对应关系Connection,并把Connection插入ConnectionList中

1604046461_5f9bce7d85dc219088fc8.png!small?1604046422077

为对应的connect赋值

1604046468_5f9bce8476e6b8da74c5d.png!small?1604046429038

解锁,并且检查是否关联成功

1604046475_5f9bce8b49e68415bb6f5.png!small?1604046435593

分析activate函数

当我们发Sig1信号时调用的函数

1604046483_5f9bce9314b7845b24974.png!small?1604046443783

红框计算父类的信号

1604046488_5f9bce98b7e80e984c73b.png!small?1604046449107

前面做了一系列检查,是否还关联着

1604046494_5f9bce9ea1c54f8f57d25.png!small?1604046455173

构造一个ConnectListRef的结构体,类似ConnectList引用

1604046501_5f9bcea58e42a7e7ba8c4.png!small?1604046462216

这里用上面的Index找到关联链表

1604046508_5f9bceacba0af595ae796.png!small?1604046469072

遍历表找到对应得connect

1604046514_5f9bceb2b70f958bff5f0.png!small?1604046476552

调用链表得槽函数

1604046521_5f9bceb9ec506c831106e.png!small?1604046482567

1604046529_5f9bcec140abaed15070f.png!small?1604046490072

大体图

1604046545_5f9bced110a675f3ea766.png!small?1604046505427

end

来源:freebuf.com 2020-10-30 16:19:10 by: 雷石安全实验室

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论