惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
F
Fortinet All Blogs
B
Blog
GbyAI
GbyAI
P
Proofpoint News Feed
量子位
The Register - Security
The Register - Security
宝玉的分享
宝玉的分享
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
V
Visual Studio Blog
B
Blog RSS Feed
WordPress大学
WordPress大学
Recorded Future
Recorded Future
Recent Announcements
Recent Announcements
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Secure Thoughts
雷峰网
雷峰网
Stack Overflow Blog
Stack Overflow Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Webroot Blog
Webroot Blog
AWS News Blog
AWS News Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The GitHub Blog
The GitHub Blog
爱范儿
爱范儿
O
OpenAI News
月光博客
月光博客
H
Hacker News: Front Page
S
Security Affairs
W
WeLiveSecurity
The Hacker News
The Hacker News
aimingoo的专栏
aimingoo的专栏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
MongoDB | Blog
MongoDB | Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
T
The Blog of Author Tim Ferriss
Spread Privacy
Spread Privacy
Blog — PlanetScale
Blog — PlanetScale
J
Java Code Geeks
S
Securelist
Microsoft Azure Blog
Microsoft Azure Blog
TaoSecurity Blog
TaoSecurity Blog
T
Threat Research - Cisco Blogs
M
MIT News - Artificial intelligence
A
About on SuperTechFans

博客园 - 新西兰程序员

C++ 构造函数中的初始化参数列表initializer list C++中的线程thread,以及C++11中的std::atomic学习 Leetcode56 Merge Intervals 合并区间 -- C++实现 C++中的内存对齐 C++中的static关键字使用以及全局变量 C++17中的结构化绑定Structured Binding C++中的函数参数是指针类型, 是按值传递 C++中异常处理机制中的栈展开stack unwinding C++实现链表反转 Linux中的性能分析工具 perf 来分析C++性能 C++17中新建一个类时,编译器默认生成的类成员函数 QT中的元对象系统 Leetcode 114 - 二叉树展开为链表 Leetcode65 有效数字 - 判断给定的字符串是否是一个有效数字 LettCode2289-Steps to Make Array Non-descreasing C#中的委托详解 C++中的std::function C++中的仿函数Functor C++中的const和constexpr异同比较 C++中的左值和右值,以及右值引用,移动语义 C++中传递参数是指针类型以及传入参数是指针的指针(**)详解 C++中GetTickCount函数学习 C#中的System.Security.SecureString学习 C++中的悬挂指针和野指针 C++中四种不同的对象生存方式(in stack, in heap, global, local static) C#中使用Parallel类来进行多线程并发编程 String类型转LPCTSTR -----理解C++中的字符串类型转换 C++中的虚函数和虚函数表 C++中基类指针指向派生类对象 数据结构 - 栈的学习
C++中以类的成员函数作为Windows callback函数需要设置成static函数
新西兰程序员 · 2024-05-29 · via 博客园 - 新西兰程序员

在看代码时,发现很多CALLBACK函数,所以仔细研究了一下C++中的CALLBACK函数

首先,我们来理解一下,什么是C++中的CALLBACK函数  => 凡是由你设计,但是由Windows操作系统调用的函数,我们把它统称为CALLBACK函数,这些函数都有一定的类型,以方便配合Windows的调用动作

某些Windows API函数会要求以callback函数作为其参数之一,这些API 比如SetTimer,LineDDA等。这些Windows API会在进行某种行为之后或者满足某种状态之时调用该callback函数。

看代码时发现,基本上CALLBACK函数都是全局函数,而不是类的成员函数。也就是说通常用全局函数(不要用类的成员函数)来作为CALLBACK函数。 =》 那么问题来了,为什么要这样? 可以采用类的成员函数来作为CALLBACK函数么?  =》答案是可以的,但是你要把类的成员函数设置成static类型  => 为什么会这样呢

这是因为C++编译器为类成员函数多准备了一个隐藏参数 (程序代码中看不到), 这使得函数类型与Windows callback函数的预设类型不符.

我们来看一段代码

class CMyclass
{
private :
int nCount;
int CALLBACK _export
EnumObjectProc(LPSTR lpObj, LPSTR lpData);
public:
void enumIt(CDC& dc);
}

void CMyclass::enumIt(CDC& dc)
{
//注册CALLBACK函数
CDC::EnumObjects(OBJ_BRUSH,EnumObjectProc,NULL

);
}

C++编译器,对上面CMyclass类中的成员函数enumIt实际做出来的代码,就相当于

void CMyclass::enumIt(CDC& dc)
{
    //注册CALLBACK函数
    CDC::EnumObjects(OBJ_BRUSH,EnumObjectProc,NULL, (CDC*)&dc);
}

在这里,你所看到新增加的最后一个参数 (CDC*)&dc, 其实就是一个this指针,类成员函数靠着this指针才得以抓到正确对象的数据.

C++中的类实例化为对象后,会有一个隐晦的this指针来指出正确的对象(代表实例化后的对象), 所以当你写:

nCount = 1;  => 其实它是   this->nCount = 1;

那么,同样的道理,上面类CMyclass的CALLBACK函数 EnumObjectProc也是一个成员函数,所以C++编译器也会为它多准备一个隐藏参数

好,终于说到这里了,问题就出在这个隐藏参数上。 CALLBACK函数是给Windows API调用的, Windows并不经由任何对象调用这个函数,也就无理由传递this指针给callback函数, 这于是导致堆栈中有一个随机变量会成为this指针, 其结果当然是程序的崩溃

所以,如果要把某个函数用作CALLBACK函数,就必须告诉C++编译器,不要放this指针作为该函数的最后一个参数,有两个方法可以做到这一点

1.  就是我们文章开头说的,用全局函数做为CALLBACK函数

2. 使用static成员函数,也就是在函数前面加上static修饰词

第一种作法相当于在 C 语言中使用callback 函数。第二种作法比较接近 OO的精神。

C++中的static成员函数特性是,即使对象还没有产生,static 成员也已经存在(函数或变量都如此)。换句话说对象还没有产生之前你已经可以调用类的static函数或使用类的static变量了。

也就是说,凡声明为static 的东西(不管函数或变量)都并不和对象结合在一起,它们是类的一部分,不属于对象。