﻿<?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++博客-Leo</title><link>http://www.cppblog.com/leo-chen/</link><description /><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 09:39:37 GMT</lastBuildDate><pubDate>Mon, 13 Apr 2026 09:39:37 GMT</pubDate><ttl>60</ttl><item><title> 如何让API回调你的VC类成员函数而不是静态函数 </title><link>http://www.cppblog.com/leo-chen/archive/2007/05/16/24201.html</link><dc:creator>LeoChen</dc:creator><author>LeoChen</author><pubDate>Wed, 16 May 2007 05:32:00 GMT</pubDate><guid>http://www.cppblog.com/leo-chen/archive/2007/05/16/24201.html</guid><wfw:comment>http://www.cppblog.com/leo-chen/comments/24201.html</wfw:comment><comments>http://www.cppblog.com/leo-chen/archive/2007/05/16/24201.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/leo-chen/comments/commentRss/24201.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/leo-chen/services/trackbacks/24201.html</trackback:ping><description><![CDATA[<div class=postTitle>&nbsp;</div>
<div class=postText>
<p>只要在函数声明前加static就好了，哈哈哈哈哈~~~~~&nbsp;&nbsp; </p>
<p>。。。开个玩笑。以前确实大家都是这样做的，在静态的成员函数中再查找this指针，它多半是全局变量，或者是回调函数提供的附加参数。如果是前者，就会大大破坏程序的结构。而现在，随着社会生产力的发展，偶们已经能做到将成员函数映射成为一个临时的静态函数了。本文就来演示一下这种实现方式。</p>
<p>首先需要包含一个由yzwykkldczsh同志编写的模板类-----万能多用自适应无限制回调模板（为纪念友人fishskin，此模板又称为H&gt;W模板）&nbsp;</p>
<p>/**************************************************************************<br>&nbsp;*&nbsp;&nbsp; ACCallback.h<br>&nbsp;*&nbsp;&nbsp; Helper class of Member function callback mechanism<br>&nbsp;**************************************************************************/<br>#include "stdafx.h"<br>#include "windows.h"</p>
<p>#pragma pack(push, 1)<br>struct _ACCallbackOpCodes<br>{<br>&nbsp;unsigned char tag;&nbsp;&nbsp;// CALL e8<br>&nbsp;LONG_PTR offset;&nbsp;&nbsp;// offset (dest - src - 5, 5=sizeof(tag + offset))<br>&nbsp;LONG_PTR _this;&nbsp;&nbsp;&nbsp;// a this pointer<br>&nbsp;LONG_PTR _func;&nbsp;&nbsp;&nbsp;// pointer to real member function address<br>};<br>#pragma pack(pop)</p>
<p>static __declspec( naked ) int STDACJMPProc()<br>{<br>&nbsp;_asm<br>&nbsp;{<br>&nbsp;&nbsp;POP ECX&nbsp; <br>&nbsp;&nbsp;MOV EAX, DWORD PTR [ECX + 4]&nbsp;// func<br>&nbsp;&nbsp;MOV ECX, [ECX]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// this&nbsp;&nbsp;<br>&nbsp;&nbsp;JMP EAX<br>&nbsp;}<br>}</p>
<p>static LONG_PTR CalcJmpOffset(LONG_PTR Src, LONG_PTR Dest)<br>{<br>&nbsp;return Dest - (Src + 5);<br>}</p>
<p>/*<br>&nbsp;* NOTE: _TPStdFunc: a type of function pointer to API or Callbacks, *MUST* be _stdcall<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _TPMemberFunc: a type of function pointer to class member function, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *MUST* be the *DEFAULT* calling conversation, *NO* prefix should be added,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; that is, using ECX for "this" pointer, pushing parameters from right to left, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and the callee cleans the stack.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _TClass: the class who owns the callback function. The caller should only own the _stdcall function pointer<br>&nbsp;&nbsp; LIFE TIME:&nbsp; It is important to keep the ACCallback object alive until the CALLBACK is not required!!!<br>&nbsp;*/<br>template&lt;typename _TPStdFunc, class _TClass, typename _TPMemberFunc&gt;<br>class ACCallback<br>{<br>public:<br>&nbsp;_TClass *m_pThis;<br>&nbsp;_TPMemberFunc m_pFunc;</p>
<p>private:<br>&nbsp;_TPStdFunc m_pStdFunc;</p>
<p>&nbsp;void MakeCode()<br>&nbsp;{<br>&nbsp;&nbsp;if (m_pStdFunc) ::VirtualFree(m_pStdFunc, 0, MEM_RELEASE);<br>&nbsp;&nbsp;m_pStdFunc = (_TPStdFunc)::VirtualAlloc(NULL, sizeof(_ACCallbackOpCodes), MEM_COMMIT, PAGE_EXECUTE_READWRITE);<br>&nbsp;&nbsp;_ACCallbackOpCodes *p = (_ACCallbackOpCodes *)m_pStdFunc;<br>&nbsp;&nbsp;p-&gt;_func = *(LONG_PTR *)&amp;m_pFunc;<br>&nbsp;&nbsp;p-&gt;_this = (LONG_PTR)m_pThis;<br>&nbsp;&nbsp;p-&gt;tag = 0xE8;<br>&nbsp;&nbsp;p-&gt;offset = CalcJmpOffset((LONG_PTR)p, (LONG_PTR)STDACJMPProc);<br>&nbsp;}</p>
<p>public:<br>&nbsp;ACCallback&lt;_TPStdFunc, _TClass, _TPMemberFunc&gt;()<br>&nbsp;{<br>&nbsp;}<br>&nbsp;ACCallback&lt;_TPStdFunc, _TClass, _TPMemberFunc&gt;(_TClass* pThis,<br>&nbsp;&nbsp;_TPMemberFunc pFunc<br>&nbsp;&nbsp;)<br>&nbsp;{<br>&nbsp;&nbsp;m_pFunc = pFunc;<br>&nbsp;&nbsp;m_pThis = pThis;<br>&nbsp;&nbsp;m_pStdFunc = NULL;<br>&nbsp;&nbsp;MakeCode();<br>&nbsp;}<br>&nbsp;void Assign(_TClass* pThis,<br>&nbsp;&nbsp;_TPMemberFunc pFunc<br>&nbsp;&nbsp;)<br>&nbsp;{<br>&nbsp;&nbsp;m_pFunc = pFunc;<br>&nbsp;&nbsp;m_pThis = pThis;<br>&nbsp;&nbsp;m_pStdFunc = NULL;<br>&nbsp;&nbsp;MakeCode();<br>&nbsp;}<br>&nbsp;~ACCallback&lt;_TPStdFunc, _TClass, _TPMemberFunc&gt;()<br>&nbsp;{<br>&nbsp;&nbsp;::VirtualFree(m_pStdFunc, 0, MEM_RELEASE);<br>&nbsp;}<br>&nbsp;operator _TPStdFunc()<br>&nbsp;{<br>&nbsp;&nbsp;return m_pStdFunc;<br>&nbsp;}<br>};</p>
<p>/********************************** EXAMPLE **********************************<br>class CClass1<br>{<br>public:<br>&nbsp;TCHAR m_Buf[255];<br>&nbsp;BOOL EnumWindowProc(HWND hwnd, LPARAM lp)<br>&nbsp;{<br>&nbsp;&nbsp;GetWindowText(hwnd, m_Buf, 255);<br>&nbsp;&nbsp;printf("Enum window=%s\n", m_Buf);<br>&nbsp;&nbsp;return TRUE;<br>&nbsp;}<br>&nbsp;typedef BOOL (CClass1::*CLASSWNDENUMPROC)(HWND, LPARAM);<br>};</p>
<p>TO USE:<br>&nbsp;CClass1 c1;<br>&nbsp;ACCallback&lt;WNDENUMPROC, CClass1, CClass1::CLASSWNDENUMPROC&gt; cb(&amp;c1, &amp;CClass1::EnumWindowProc);<br>&nbsp;EnumWindows(cb, 0);</p>
<p>************************* END OF EXAMPLE *********************************/<br></p>
<p>模板的三个参数分别是：API函数指针的类型，类名字，类成员函数指针的类型（两种函数指针在参数和返回值上应该一样，只是前者声明为_stdcall，后者不加任何调用修饰，即默认的__thiscall方式）<br>该项头文件的注释中给了一个调用API函数EnumWindows的例子。现在偶们来试试调用SetTimer。</p>
<p>class CTestCallback<br>{<br>private:<br>&nbsp;/* A callback of SetTimer, mirrored into member OnTimer */<br>&nbsp;typedef void (CTestCallback::*CLASSTIMERPROC)(HWND, UINT, UINT_PTR, DWORD);<br>&nbsp;void OnTimer (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);<br>&nbsp;ACCallback&lt;TIMERPROC, CTestCallback, CLASSTIMERPROC&gt; m_DOnTimer;<br>}</p>
<p>调用时，只要这样写：<br>/*&nbsp;初始化回调结构 */<br>m_DOnTimer.Assign(this, &amp;CTestCallback::OnTimer);<br>m_uid = ::SetTimer( NULL, 0, 1000, m_DOnTimer);</p>
<p>最后记得在CTestCallback的析构函数中KillTimer。由于m_DOnTimer会实现转化到静态函数指针类型的操作符，所以调用的地方只要直接写回调结构的名字就可以了。</p>
<p>使用该模板需要注意两点：<br>1.API函数应当是_stdcall类型的（这一点绝大部分API都满足）。类成员函数必须是默认的调用方式，不要加_stdcall或_cdecl之类的修饰。此方式的重要条件就在于_stdcall和__thiscall之间只相差了一个ECX指出的this指针，所以我们才能实现这种映射（这种方式在VCL和ATL的窗口类中都有使用到）；<br>2.回调结构的生存周期应当是在整个回调函数有效的时间内。因此，对于EnumWindows这样的函数，只要声明在栈上就可以了；但对于SetTimer，就必须定义为类成员变量，同时，在类的析构函数中必须及时销毁这个timer。</p>
</div>
<img src ="http://www.cppblog.com/leo-chen/aggbug/24201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/leo-chen/" target="_blank">LeoChen</a> 2007-05-16 13:32 <a href="http://www.cppblog.com/leo-chen/archive/2007/05/16/24201.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to use SetTimer() with callback to a non-static member function</title><link>http://www.cppblog.com/leo-chen/archive/2007/05/16/24186.html</link><dc:creator>LeoChen</dc:creator><author>LeoChen</author><pubDate>Wed, 16 May 2007 02:31:00 GMT</pubDate><guid>http://www.cppblog.com/leo-chen/archive/2007/05/16/24186.html</guid><wfw:comment>http://www.cppblog.com/leo-chen/comments/24186.html</wfw:comment><comments>http://www.cppblog.com/leo-chen/archive/2007/05/16/24186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/leo-chen/comments/commentRss/24186.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/leo-chen/services/trackbacks/24186.html</trackback:ping><description><![CDATA[<h2>Quick update...</h2>
<p nd="1">After reviewing the comments and suggestions from a few people, I made the solution better. Look for an update to this article which uses a better approach, namely using the functions:</p>
<ul>
    <li><code nd="2">CreateWaitableTimer()</code>
    <li><code nd="3">SetWaitableTimer()</code>
    <li><code nd="4">WaitForMultipleObjects()</code> </li>
</ul>
<p nd="5">The solution based on these functions will allow multiple instances of the <code nd="6">CSleeperThread</code> class to run (instead of just one using the current example). So stay tuned, I'll have this article updated as soon as possible. :-)</p>
<h2>Introduction</h2>
<p nd="7">I have seen many questions on the boards about how to properly use <code nd="8">SetTimer()</code>. I've also noticed that most of these questions are around how to put a thread to sleep for X seconds. One obvious answer would be to use the <code nd="9">Sleep()</code> function. The main drawback is, how do you gracefully shut down your thread, or cancel the <code nd="10">Sleep()</code> operation before the time expires.</p>
<p nd="11">This article is meant to address all of the above. I give an example of putting a thread to sleep using <code nd="12">SetTimer()</code>. The <code nd="13">SetTimer()</code> calls back to a non-static function. This is key, because normally you have to pass a static member to <code nd="14">SetTimer()</code> which means it can't access any other non-static variables or member functions of the class.</p>
<h2>Details</h2>
<p nd="15">Since implementing a non-static callback member is key to this, we'll go into this first. Implementing a callback to a static member function doesn't require anything different from implementing a regular C callback function. Since static member functions have the same signature as C functions with the same calling conventions, they can be referenced using just the function name.</p>
<p nd="16">Making a non-static callback member function is a different story, because they have a different signature than a C function. To make a non-static member function, it requires the use of two additional items:</p>
<ul>
    <li nd="17">A global (<code nd="18"><span class=cpp-keyword>void</span>*</code>) pointer, referencing the class of the callback function
    <li nd="19">A wrapper function which will be passed to <code nd="20">SetTimer()</code> </li>
</ul>
<p nd="21">This is actually a fairly simple implementation. First, you need to define your class:</p>
<pre nd="22"><span class=cpp-keyword>class</span> CSleeperThread : <span class=cpp-keyword>public</span> CWinThread {
<span class=cpp-keyword>public</span>:
<span class=cpp-keyword>static</span> VOID CALLBACK TimerProc_Wrapper( HWND hwnd, UINT uMsg,
UINT idEvent, DWORD dwTime );
VOID CALLBACK TimerProc( HWND hwnd,
UINT uMsg, UINT idEvent, DWORD dwTime );
<span class=cpp-keyword>void</span> ThreadMain();
<span class=cpp-keyword>void</span> WakeUp();
<span class=cpp-keyword>private</span>:
<span class=cpp-keyword>static</span> <span class=cpp-keyword>void</span> * pObject;
UINT_PTR pTimer;
CRITICAL_SECTION lock;
};</pre>
<p nd="23">Then, don't forget to include the following line in your class implementation file:</p>
<pre nd="24"><span class=cpp-keyword>void</span> * CSleeperThread::pObject;</pre>
<p nd="25">Now that we have our class declared, we can look at the wrapper function, the non-static member function and the member function that will call <code nd="26">SetTimer()</code>:</p>
<pre nd="27">VOID CALLBACK CSleeperThread::TimerProc_Wrapper( HWND hwnd, UINT uMsg,
UINT idEvent, DWORD dwTime ) {
CSleeperThread *pSomeClass = (CSleeperThread*)pObject; <span class=cpp-comment>// cast the void pointer</span>
pSomeClass-&gt;TimerProc(hwnd, uMsg, idEvent, dwTime); <span class=cpp-comment>// call non-static function</span>
}</pre>
<p nd="28">The wrapper function first initializes a <code nd="29">CSleeperThread</code> pointer with <code nd="30">pObject</code>. Since <code nd="31">pSomeClass</code> is a local pointer, we can access it within the static wrapper function.</p>
<pre nd="32">VOID CALLBACK CSleeperThread::TimerProc(HWND hwnd,
UINT uMsg, UINT idEvent, DWORD dwTime) {
::EnterCriticalSection(&amp;lock);
<span class=cpp-keyword>if</span>(idEvent == pTimer) {
KillTimer(NULL, pTimer);  <span class=cpp-comment>// kill the timer so it won't fire again</span>
ResumeThread();  <span class=cpp-comment>// resume the main thread function</span>
}
::LeaveCriticalSection(&amp;lock);
}</pre>
<p nd="33">The <code nd="34">TimerProc</code> member function isn't static, so we can access other non-static functions like <code nd="35">ResumeThread()</code> and we can access the private variable <code nd="36">lock</code>. Notice that I've entered a critical section which prevents a second timer event to enter the callback, thus ensuring that the first execution of <code nd="37">TimerProc()</code> will cancel out the timer.</p>
<p nd="38">Next, let's take a look at the main execution function, <code nd="39">ThreadMain()</code>.</p>
<pre nd="40"><span class=cpp-keyword>void</span> CSleeperThread::ThreadMain()
{
pObject = <span class=cpp-keyword>this</span>; <span class=cpp-comment>// VERY IMPORTANT, must be initialized before</span>
<span class=cpp-comment>// calling SetTimer()</span>
<span class=cpp-comment>// call SetTimer, passing the wrapper function as the callback</span>
pTimer = SetTimer(NULL, NULL, <span class=cpp-literal>10000</span>, TimerProc_Wrapper);
<span class=cpp-comment>// suspend until the timer expires</span>
SuspendThread();
<span class=cpp-comment>// the timer has expired, continue processing </span>
}</pre>
<p nd="41">The first step in <code nd="42">ThreadMain()</code> is absolutely critical. We need to assign the class instance pointer (<code><span class=cpp-keyword>this</span></code>) to the <code nd="43">pObject</code> variable. This is how the wrapper callback function will gain access to execute the non-static member function.</p>
<p nd="44">Next, we just call <code nd="45">SetTimer()</code> passing in a function pointer to our wrapper function. <code nd="46">SetTimer()</code> will call the wrapper function when the timer expires. The wrapper function in turn, will execute the non-static function <code nd="47">TimerProc()</code>, by accessing the static variable <code nd="48">pSomeClass</code>.</p>
<p nd="49"><strong>NOTE:</strong> I chose to implement a main function that will create the timer, go to sleep, continue processing and then exit when finished. This is in effect a function that will only execute once per timer. You could easily add a loop to <code nd="50">ThreadMain()</code> which would execute once for each timer event.</p>
<p nd="51">One last little function. Since we used <code nd="52">SuspendThread()</code> in <code nd="53">ThreadMain()</code>, if we need to wake up the thread (for whatever reason), all we have to do is make a call to <code nd="54">ResumeThread()</code>. So, I've added an access function like so:</p>
<pre nd="55"><span class=cpp-keyword>void</span> WakeUp() {
::EnterCriticalSection(&amp;lock);
KillTimer(NULL, pTimer);
ResumeThread(); <span class=cpp-comment>// wake the thread up</span>
}</pre>
<h2>Buh dee buh dee, that's all folks...</h2>
<p nd="56">And there we have it. A thread safe class that goes to sleep using <code nd="57">SetTimer()</code> and a non-static callback function; which also has the ability to wake up before the timer expires.</p>
<p nd="58">Hopefully, you have found this helpful. I've actually used this code in a project I'm working on now, and was in hopes someone else would get some good use out of it.</p>
<p nd="59">Someone once told me "you'll like programming if you like banging your head against the wall repeatedly". I've found that to be true, it took me literally several days to figure out what I've put into this article, I'm just slow I guess.</p>
<p nd="60">Whew, my head hurts, time for some Advil...or Ibooprofin.. or asssprin.... or something.</p>
<h2>Credits...</h2>
<p nd="61">I probably learned way more in the process of writing this article. So, much thanks goes to Lars Haendel for creating a web-site dedicated to understanding function pointers, without which I wouldn't know didley.</p>
<p nd="62"><a href="http://www.function-pointer.org/CCPP/callback/callback.html#example2" target=_blank><u><font color=#0000ff>www.function-pointer.org</font></u></a>.</p>
<!-- Article Ends -->
<script src="/script/togglePre.js" type=text/javascript></script>
<img src ="http://www.cppblog.com/leo-chen/aggbug/24186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/leo-chen/" target="_blank">LeoChen</a> 2007-05-16 10:31 <a href="http://www.cppblog.com/leo-chen/archive/2007/05/16/24186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>