﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-tassard</title><link>http://www.cppblog.com/tassard/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 09 Jun 2026 21:35:29 GMT</lastBuildDate><pubDate>Tue, 09 Jun 2026 21:35:29 GMT</pubDate><ttl>60</ttl><item><title>[原] 怎么将c++的成员函数转为静态函数？</title><link>http://www.cppblog.com/tassard/archive/2009/05/13/82831.html</link><dc:creator>tassard</dc:creator><author>tassard</author><pubDate>Wed, 13 May 2009 08:28:00 GMT</pubDate><guid>http://www.cppblog.com/tassard/archive/2009/05/13/82831.html</guid><wfw:comment>http://www.cppblog.com/tassard/comments/82831.html</wfw:comment><comments>http://www.cppblog.com/tassard/archive/2009/05/13/82831.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tassard/comments/commentRss/82831.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tassard/services/trackbacks/82831.html</trackback:ping><description><![CDATA[<h4 class="beTitle" id="subjcns!C812D47081EEE59C!269">怎么将c++的成员函数转为静态函数？</h4>
<div id="msgcns!C812D47081EEE59C!269" class="bvMsg">
<p>前几天读了一位网友的文章《<a href="http://hi.baidu.com/%BC%D0%B2%E3%D6%D0%B5%C4%BB%B0%CC%E2/blog/item/4b07deb46c725c7b8ad4b241.html">如何让API回调你的VC类成员函数而不是静态函数</a>》。写得很好，到底是高手！可惜也因为是高手，很多细节都略过不谈了（高手嘛，你知道的，懒得说细节了:），所以我决定写这篇小文作为原文的补充。另外，本文写了一个独立的例子，示范了怎么将类成员函数转为消息函数传给windows。例子文件的链接：<strong> <a href="http://cid-c812d47081eee59c.skydrive.live.com/self.aspx/.Public/testwin.rar">testwin</a></strong> 。</p>
<p>一共有4个补充：</p>
<p><strong> 1.成员函数调用与普通函数调用有什么区别？</strong> </p>
<p>区别是：调用成员函数时，ecx寄存器保存的是this指针。</p>
<p><strong> 2.关键代码段：</strong> </p>
<p>抛开乱七八糟的封装，让我把关键代码段展示给大家。整篇文章的核心技术实际上就是下面两个关键代码段。  </p>
<p><strong> 1）代码段1：</strong> <br>代码段1包含了一条指令和两个指针。代码段对应着结构体_ACCallbackOpCodes。  </p>
<p>在本文的例子中<br>wcex.lpfnWndProc = cb;//就是窗口消息函数的地址<br>实际上保存的是指向&#8220;代码段1&#8221;的地址。 </p>
<p>1)call 偏移量 //&#8220;偏移量&#8221;指向的是代码段2<br>2)LONG_PTR _this;//这个指针里保存了this指针<br>3)LONG_PTR _func;//这个指针里保存了成员函数的指针  </p>
<p><strong> 2）代码段2：</strong> <br>代码段2包含了4条指令。代码段2对应着STDACJMPProc函数。<br>1)POP ECX <br>//将栈里保存的this指针的偏移量pop到ecx里去<br>//你可能会觉得奇怪：this指针怎么会跑到栈里去了？<br>//呵呵，怪自己汇编太差吧:P<br>//这是因为call指令会把它的下一条指令压入栈呀！<br>//这也是为什么要把this指针放在call指令后面的原因。<br>//对于call的详细信息请参考汇编语言手册吧。<br>2)MOV EAX, DWORD PTR [ECX + 4] //成员函数的指针存入eax<br>3)MOV ECX, [ECX] //this指针存入ecx<br>4)JMP EAX //跳转到成员函数的起始地址！大功告成！  </p>
<p><strong> 3.为什么要用VirtualAlloc函数？</strong> <br>先
举一个反例：如果在栈上分配一块内存，保存_ACCallbackOpCodes，那么就需要调用FlushInstructionCache函数，因为
_ACCallbackOpCodes需要动态更新某些参数，而cpu不会自动刷新这块内存（这样就必须调用FlushInstructionCache
来刷新）。但是如果用VirtualAlloc就不会有这个问题。 </p>
<p><strong> 4.在CalcJmpOffset中，为什么要计算STDACJMPProc（也就是&#8220;代码段2&#8221;）相对于_ACCallbackOpCodes的偏移量？</strong> <br>我们知道_ACCallbackOpCodes的第一条指令是：<br>call STDACJMPProc（也就是&#8220;代码段2&#8221;）；<br>但
是因为_ACCallbackOpCodes与&#8220;代码段2&#8221;不在同一个段，在执行call指令时，cs寄存器指向的是
_ACCallbackOpCodes所在段的段基址，所以想要跳转到&#8220;代码段2&#8221;上，就必须计算&#8220;代码段2&#8221;相对于
_ACCallbackOpCodes所在段的段基址的偏移量（这个段基址就是_ACCallbackOpCodes的首地址）。</p>
</div>
<br><img src ="http://www.cppblog.com/tassard/aggbug/82831.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tassard/" target="_blank">tassard</a> 2009-05-13 16:28 <a href="http://www.cppblog.com/tassard/archive/2009/05/13/82831.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>