• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Leo

            <2007年5月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            統計

            • 隨筆 - 2
            • 文章 - 0
            • 評論 - 2
            • 引用 - 0

            常用鏈接

            留言簿(1)

            隨筆檔案

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            如何讓API回調你的VC類成員函數而不是靜態函數
             

            只要在函數聲明前加static就好了,哈哈哈哈哈~~~~~  

            。。。開個玩笑。以前確實大家都是這樣做的,在靜態的成員函數中再查找this指針,它多半是全局變量,或者是回調函數提供的附加參數。如果是前者,就會大大破壞程序的結構。而現在,隨著社會生產力的發展,偶們已經能做到將成員函數映射成為一個臨時的靜態函數了。本文就來演示一下這種實現方式。

            首先需要包含一個由yzwykkldczsh同志編寫的模板類-----萬能多用自適應無限制回調模板(為紀念友人fishskin,此模板又稱為H>W模板) 

            /**************************************************************************
             *   ACCallback.h
             *   Helper class of Member function callback mechanism
             **************************************************************************/
            #include "stdafx.h"
            #include "windows.h"

            #pragma pack(push, 1)
            struct _ACCallbackOpCodes
            {
             unsigned char tag;  // CALL e8
             LONG_PTR offset;  // offset (dest - src - 5, 5=sizeof(tag + offset))
             LONG_PTR _this;   // a this pointer
             LONG_PTR _func;   // pointer to real member function address
            };
            #pragma pack(pop)

            static __declspec( naked ) int STDACJMPProc()
            {
             _asm
             {
              POP ECX 
              MOV EAX, DWORD PTR [ECX + 4] // func
              MOV ECX, [ECX]     // this  
              JMP EAX
             }
            }

            static LONG_PTR CalcJmpOffset(LONG_PTR Src, LONG_PTR Dest)
            {
             return Dest - (Src + 5);
            }

            /*
             * NOTE: _TPStdFunc: a type of function pointer to API or Callbacks, *MUST* be _stdcall
                     _TPMemberFunc: a type of function pointer to class member function,
                     *MUST* be the *DEFAULT* calling conversation, *NO* prefix should be added,
                      that is, using ECX for "this" pointer, pushing parameters from right to left,
                      and the callee cleans the stack.
                      _TClass: the class who owns the callback function. The caller should only own the _stdcall function pointer
               LIFE TIME:  It is important to keep the ACCallback object alive until the CALLBACK is not required!!!
             */
            template<typename _TPStdFunc, class _TClass, typename _TPMemberFunc>
            class ACCallback
            {
            public:
             _TClass *m_pThis;
             _TPMemberFunc m_pFunc;

            private:
             _TPStdFunc m_pStdFunc;

             void MakeCode()
             {
              if (m_pStdFunc) ::VirtualFree(m_pStdFunc, 0, MEM_RELEASE);
              m_pStdFunc = (_TPStdFunc)::VirtualAlloc(NULL, sizeof(_ACCallbackOpCodes), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
              _ACCallbackOpCodes *p = (_ACCallbackOpCodes *)m_pStdFunc;
              p->_func = *(LONG_PTR *)&m_pFunc;
              p->_this = (LONG_PTR)m_pThis;
              p->tag = 0xE8;
              p->offset = CalcJmpOffset((LONG_PTR)p, (LONG_PTR)STDACJMPProc);
             }

            public:
             ACCallback<_TPStdFunc, _TClass, _TPMemberFunc>()
             {
             }
             ACCallback<_TPStdFunc, _TClass, _TPMemberFunc>(_TClass* pThis,
              _TPMemberFunc pFunc
              )
             {
              m_pFunc = pFunc;
              m_pThis = pThis;
              m_pStdFunc = NULL;
              MakeCode();
             }
             void Assign(_TClass* pThis,
              _TPMemberFunc pFunc
              )
             {
              m_pFunc = pFunc;
              m_pThis = pThis;
              m_pStdFunc = NULL;
              MakeCode();
             }
             ~ACCallback<_TPStdFunc, _TClass, _TPMemberFunc>()
             {
              ::VirtualFree(m_pStdFunc, 0, MEM_RELEASE);
             }
             operator _TPStdFunc()
             {
              return m_pStdFunc;
             }
            };

            /********************************** EXAMPLE **********************************
            class CClass1
            {
            public:
             TCHAR m_Buf[255];
             BOOL EnumWindowProc(HWND hwnd, LPARAM lp)
             {
              GetWindowText(hwnd, m_Buf, 255);
              printf("Enum window=%s\n", m_Buf);
              return TRUE;
             }
             typedef BOOL (CClass1::*CLASSWNDENUMPROC)(HWND, LPARAM);
            };

            TO USE:
             CClass1 c1;
             ACCallback<WNDENUMPROC, CClass1, CClass1::CLASSWNDENUMPROC> cb(&c1, &CClass1::EnumWindowProc);
             EnumWindows(cb, 0);

            ************************* END OF EXAMPLE *********************************/

            模板的三個參數分別是:API函數指針的類型,類名字,類成員函數指針的類型(兩種函數指針在參數和返回值上應該一樣,只是前者聲明為_stdcall,后者不加任何調用修飾,即默認的__thiscall方式)
            該項頭文件的注釋中給了一個調用API函數EnumWindows的例子。現在偶們來試試調用SetTimer。

            class CTestCallback
            {
            private:
             /* A callback of SetTimer, mirrored into member OnTimer */
             typedef void (CTestCallback::*CLASSTIMERPROC)(HWND, UINT, UINT_PTR, DWORD);
             void OnTimer (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
             ACCallback<TIMERPROC, CTestCallback, CLASSTIMERPROC> m_DOnTimer;
            }

            調用時,只要這樣寫:
            /* 初始化回調結構 */
            m_DOnTimer.Assign(this, &CTestCallback::OnTimer);
            m_uid = ::SetTimer( NULL, 0, 1000, m_DOnTimer);

            最后記得在CTestCallback的析構函數中KillTimer。由于m_DOnTimer會實現轉化到靜態函數指針類型的操作符,所以調用的地方只要直接寫回調結構的名字就可以了。

            使用該模板需要注意兩點:
            1.API函數應當是_stdcall類型的(這一點絕大部分API都滿足)。類成員函數必須是默認的調用方式,不要加_stdcall或_cdecl之類的修飾。此方式的重要條件就在于_stdcall和__thiscall之間只相差了一個ECX指出的this指針,所以我們才能實現這種映射(這種方式在VCL和ATL的窗口類中都有使用到);
            2.回調結構的生存周期應當是在整個回調函數有效的時間內。因此,對于EnumWindows這樣的函數,只要聲明在棧上就可以了;但對于SetTimer,就必須定義為類成員變量,同時,在類的析構函數中必須及時銷毀這個timer。

            posted on 2007-05-16 13:32 LeoChen 閱讀(1769) 評論(2)  編輯 收藏 引用

            評論

            # re: 如何讓API回調你的VC類成員函數而不是靜態函數 [未登錄] 2007-05-16 15:57 walkspeed

            有些意識。
            特別是那個_ACCallbackOpCodes結構。他將m_pStdFunc存儲空間中的頭部分的數據修改了。用的就是_ACCallbackOpCodes結構。不知這個結構的具體含義。然道函數指針空間中存儲了寫信息。呵呵。

            還有那個匯編代碼。然道類的函數調用的匯編代碼及時這個樣的。學習。
              回復  更多評論    

            # re: 如何讓API回調你的VC類成員函數而不是靜態函數  2007-05-16 16:35 空明流轉

            這個寫法很早就有人提了.但是編譯器相關實在太大,換個編譯器就不太一樣了.所以不敢用.
            比較好的方法還是使用functor或者干脆使用COMMAND模式才是C++的上選.
              回復  更多評論    
            久久精品夜色噜噜亚洲A∨| 久久99精品国产自在现线小黄鸭 | 久久亚洲精品视频| 91久久精品国产91性色也| 香港aa三级久久三级| 久久免费视频1| 国产情侣久久久久aⅴ免费| 久久精品成人免费国产片小草| 亚洲欧洲久久av| 色综合合久久天天综合绕视看| 青青草国产97免久久费观看| 精品无码久久久久久尤物| 日本国产精品久久| 亚洲精品高清国产一久久| 久久AV无码精品人妻糸列| 久久久久国产一区二区| 久久九九精品99国产精品| 久久伊人五月天论坛| 丰满少妇人妻久久久久久| 少妇熟女久久综合网色欲| 国产精品久久久99| 久久发布国产伦子伦精品 | 久久国内免费视频| 99久久精品国产毛片| 精品久久久久久中文字幕人妻最新| 欧美性猛交xxxx免费看久久久| 91精品日韩人妻无码久久不卡| 漂亮人妻被黑人久久精品| 狠狠色狠狠色综合久久| 亚洲午夜精品久久久久久app| 国产成人精品综合久久久| 国产精品国色综合久久| 国产情侣久久久久aⅴ免费| 久久青青草原亚洲av无码app| 偷偷做久久久久网站| 久久99热这里只频精品6| 亚洲午夜无码AV毛片久久| 久久久精品久久久久特色影视| 国产精品日韩欧美久久综合| 91久久精品国产91性色也| 国产福利电影一区二区三区久久久久成人精品综合|