环境安装
默认安装好了VS2015 和QT
下载QT pdb和QT src压缩包(选择自己的版本,我是5.9.8)
qt下载网址
拷贝自己需要的文件,qt目录中D:\Qt\Qt5.9.8\5.9.8我是这个
并且把源码也拷贝进同一目录,当然其他的目录也行
设置VS选项
工具->选项->调试->符号
项目->xxxx属性->vc++目录(添加源码路径)
测试
对着qt的函数或者宏按F12
安装查看源码的软件(Source Insight)
这个工具可以很好的分析源码
分析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发现是有实现代码的吧
打开文件所在目录
查看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 : 类信息的总和
红色框是父类地址,蓝色是存储字符串的结构体的地址,黑色是存放信号槽函数信息的结构体,类的信号/槽函数调用的实现地址
metaObject:判断静态/动态调用
qt_metacast:返回类名称
+qt_metacall:函数调用
void** _a:数组指针, 每个指针指向一个函数的地址
_c:实现函数的类型
_id : 函数id, 判断是否实现 & 以何种方式实现该函数
信号函数的实现
void* _a[]是一个信号对应的槽函数的地址
一个信号可以对应多个槽,所有这里是void* _a[]数组
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
给Sender赋值获取名字、对象、参数等
获取sender的Index,并且判断是否有效
接收方信息
判断信号槽是否被关联
判断接收方是否有效可以被关联
检查发送者和接收者参数是否一致
执行关联操作函数
进入实现函数
先是强转了sender 和receiver保存在两变量里面
检测版本
估计是为了防止多线程,给了锁
生成信号槽的对应关系Connection,并把Connection插入ConnectionList中
为对应的connect赋值
解锁,并且检查是否关联成功
分析activate函数
当我们发Sig1信号时调用的函数
红框计算父类的信号
前面做了一系列检查,是否还关联着
构造一个ConnectListRef的结构体,类似ConnectList引用
这里用上面的Index找到关联链表
遍历表找到对应得connect
调用链表得槽函数
大体图
end
来源:freebuf.com 2020-10-30 16:19:10 by: 雷石安全实验室
请登录后发表评论
注册