• <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>
            隨筆-90  評(píng)論-947  文章-0  trackbacks-0

            Timer這玩意兒很常用,卻又很煩人。煩人之處有四:

            1.         如果將其設(shè)到HWND上,則

            a)         必須手工維護(hù)TimerID,小心翼翼地保證這些ID不重復(fù),可能有人(比如我)就不怎么喜歡手工維護(hù)硬編碼的ID

            b)         必須跟一個(gè)HWND關(guān)聯(lián),在沒(méi)有HWND的時(shí)候,或者HWND不方便用的時(shí)候,就麻煩了。比如前公司有個(gè)GUI系統(tǒng),是個(gè)類似HWND控件和DirectUI控件混合支持的系統(tǒng)(看上去很強(qiáng)大是不?),在HWND控件上使用SetTimer很方便,直接用原生的就行了,但如果在DirectUI控件上想要搞個(gè)Timer,就傻了。

            2.         如果不將其設(shè)到HWND上,則

            a)         ID倒是可以讓它生成,雖然我很喜歡,但不一定所有人喜歡,這與1.a兩者必居其一,無(wú)法兩全。

            b)         回調(diào)函數(shù)又涉及成員化的問(wèn)題,不然在一個(gè)對(duì)象化的系統(tǒng)里就很難寫(xiě)。

            就我個(gè)人而言,我喜歡2.a特性,因此著眼于解決2.b問(wèn)題。

             

            回調(diào)函數(shù)成員化,看著好像很眼熟。不錯(cuò),我們?cè)?jīng)在《學(xué)習(xí)下WTLthunk》里面干過(guò)這事情。因此幾個(gè)月前我就覺(jué)得對(duì)于Timer也是可以做到的,但由于各種原因沒(méi)時(shí)間去弄,同時(shí)也很遺憾前公司“架構(gòu)師”沒(méi)有采用這種方案。

             

            今天剛剛打眼到公司有人也做了這件事(1.a + 2.b 模式),趁目前還沒(méi)去很仔細(xì)地去研究,趕緊自己先寫(xiě)一個(gè)差異化版本,以避免不必要的版權(quán)糾紛^_^

             

            Thunk需要占用一個(gè)正常參數(shù)。我們觀察一下Timer的回調(diào)函數(shù)格式:

            VOID CALLBACK TimerProc(

                _In_  HWND hWnd,

                _In_  UINT uMsg,

                _In_  UINT_PTR idEvent,

                _In_  DWORD dwTime

            );

             

            很不錯(cuò),前面三個(gè)參數(shù)幾乎都是沒(méi)用的(至少第一個(gè)是沒(méi)用的,這就夠了)。

             

            先把原先為了WNDPROCThunk改得通用些,WNDPROC改成LPVOID或者模版化,所有出現(xiàn)“Wnd”的地方都去掉“Wnd”字樣,改完后變成:

            http://xllib.codeplex.com/SourceControl/latest#SourceCode/xl/Win32/GUI/xlThunk.h

             

            然后寫(xiě)Timer的實(shí)現(xiàn)。代碼比較短,我先全貼了:

            typedef Function<void (DWORD dwTime)> TimerCallback;

             

            class Timer

            {

            public:

                Timer() : m_uTimerId(0)

                {

                   

                }

             

                ~Timer()

                {

                    Kill();

                }

             

            public:

                bool Set(UINT uElapse, TimerCallback fnCallback)

                {

                    if (m_uTimerId != 0)

                    {

                        return false;

                    }

             

                    m_fnCallback = fnCallback;

                    m_thunk.SetObject(this);

                    m_thunk.SetRealProc(StaticTimerProc);

             

                    m_uTimerId = SetTimer(nullptr, 0, uElapse, m_thunk.GetThunkProc());

             

                    if (m_uTimerId == 0)

                    {

                        return false;

                    }

             

                    return true;

                }

             

                void Kill()

                {

                    if (m_uTimerId != 0)

                    {

                        KillTimer(nullptr, m_uTimerId);

                        m_uTimerId = 0;

                    }

                }

             

            protected:

                static VOID CALLBACK StaticTimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)

                {

                    return ((Timer *)hWnd)->m_fnCallback(dwTime);

                }

             

            protected:

                UINT_PTR m_uTimerId;

                Thunk<TIMERPROC> m_thunk;

                TimerCallback m_fnCallback;

            };

             

            注意Timer::Set里面,設(shè)好Thunk的數(shù)據(jù)以后,直接把Timer創(chuàng)建在Thunk上就可以了,比起窗口那個(gè)處理干凈利落多了。咦?窗口里為什么要搞個(gè)StartProc,然后再在StartProc里把回調(diào)函數(shù)設(shè)到Thunk上呢?

             

            是這樣的,注冊(cè)窗口類的時(shí)候就需要一個(gè)回調(diào)函數(shù),此時(shí)窗口未創(chuàng)建。CreateWindow的過(guò)程中,會(huì)調(diào)用到回調(diào)函數(shù)(WM_CREATE),如果沒(méi)有特殊處理,需要調(diào)用回DefWindowProc,其第一個(gè)參數(shù)是HWND,而我們此時(shí)如果使用Thunk的話,就會(huì)篡改掉系統(tǒng)調(diào)用回調(diào)函數(shù)時(shí)給出的HWND,從而沒(méi)法正確調(diào)用DefWindowProc。也就是說(shuō),如果第一次被調(diào)用需要使用第一個(gè)參數(shù)的,就需要像窗口的處理一樣,搞個(gè)StartProc第一次用。

             

            這里我們使用SetTimer(NULL, ...),這第一個(gè)參數(shù)任何時(shí)候都不需要使用,所以可直接將Timer創(chuàng)建在Thunk上。

             

            用例:

            int main()

            {

                xl::Timer t;

                t.Set(1000, [](DWORD dwTime)

                    {

                        printf("%u\n", dwTime);

                    });

             

                MSG msg = {};

             

                while (GetMessage(&msg, nullptr, 0, 0))

                {

                    TranslateMessage(&msg);

                    DispatchMessage(&msg);

                }

             

                return 0;

            }

             

            運(yùn)行結(jié)果:

            clip_image001

             

            源代碼見(jiàn):

            http://xllib.codeplex.com/SourceControl/latest#SourceCode/xl/Win32/Timer/xlTimer.h

             

            流浪了近一個(gè)月,我又開(kāi)始上班啦!

            posted on 2013-06-25 00:18 溪流 閱讀(4911) 評(píng)論(6)  編輯 收藏 引用 所屬分類: C++Windows

            評(píng)論:
            # re: 將 Timer 對(duì)象化[未登錄](méi) 2013-06-25 12:35 | cexer
            其實(shí)可以徹底擺脫那個(gè)static,辦法就是在直接(或者使用JITAssembler)手動(dòng)生成static的代碼,跟thunk替換棧上數(shù)據(jù)一樣的道理。只是這樣生成的代碼同時(shí)與編譯器和CPU綁定了,與編譯器綁定是因?yàn)楦鱾€(gè)編譯器生成的調(diào)用成員函數(shù)的方式可能不一樣。
              回復(fù)  更多評(píng)論
              
            # re: 將 Timer 對(duì)象化[未登錄](méi) 2013-06-25 12:37 | cexer
            #include <iostream>
            #include <string>
            #include <windows.h>
            #include <cstdio>
            using namespace std;


            template<class T, class S>
            inline T union_cast( S s )
            {
            union
            {
            T t;
            S s;
            } u;
            u.s = s;
            return u.t;
            }

            #define __CODE1( a ) \
            *(proc++) = a

            #define __CODE2( a1, a2 ) \
            *(proc++) = a1; \
            *(proc++) = a2

            #define __CODE3( a1, a2, a3 ) \
            *(proc++) = a1; \
            *(proc++) = a2; \
            *(proc++) = a3

            #define __CODE4( a1, a2, a3, a4 ) \
            *(proc++) = a1; \
            *(proc++) = a2; \
            *(proc++) = a3; \
            *(proc++) = a4

            #define __CODE5( a1, a2, a3, a4, a5 ) \
            *(proc++) = a1; \
            *(proc++) = a2; \
            *(proc++) = a3; \
            *(proc++) = a4; \
            *(proc++) = a5

            #define __CODE6( a1, a2, a3, a4, a5, a6 ) \
            *(proc++) = a1; \
            *(proc++) = a2; \
            *(proc++) = a3; \
            *(proc++) = a4; \
            *(proc++) = a5; \
            *(proc++) = a6

            #define __CODE( n, a ) __CODE##n a

            #define __PTR( p ) \
            { \
            *( (void**)proc ) = union_cast<void*>(p);\
            proc += sizeof(void*); \
            }

            #define __CALL( p ) \
            { \
            *(proc++) = 0xE8; \
            unsigned char* pfunc = union_cast<unsigned char*>(p);\
            *( (ptrdiff_t*)proc ) = pfunc - proc - 4; \
            proc += 4; \
            }  回復(fù)  更多評(píng)論
              
            # re: 將 Timer 對(duì)象化[未登錄](méi) 2013-06-25 12:38 | cexer
            class CTest
            {
            public:
            CTest()
            : m_value( 0x12345678 )
            {
            build_proc();
            }

            public:
            LRESULT CALLBACK member_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
            {
            printf( "hwnd:%d, msg:%d, wparam:%d, lparam:%d\n", (int)hwnd, (int)msg, (int)wparam, (int)lparam );
            printf( "CTest::value: 0x%x\n", m_value );
            return 0;
            }

            void build_proc()
            {
            unsigned char* proc = m_proc;
            ///////////////////////// Prolog.
            __CODE( 1, ( 0x55 ) ); // 55 push ebp
            __CODE( 2, ( 0x8B, 0xEC ) ); // 8B EC mov ebp,esp
            __CODE( 6, ( 0x81, 0xEC, 0xC0, 0x00, 0x00, 0x00 ) ); // 81 EC C0 00 00 00 sub esp,0C0h
            __CODE( 1, ( 0x53 ) ); // 53 push ebx
            __CODE( 1, ( 0x56 ) ); // 56 push esi
            __CODE( 1, ( 0x57 ) ); // 57 push edi
            __CODE( 6, ( 0x8D, 0xBD, 0x40, 0xFF, 0xFF, 0xFF ) ); // 8D BD 40 FF FF FF lea edi,[ebp+FFFFFF40h]
            __CODE( 5, ( 0xB9, 0x30, 0x00, 0x00, 0x00 ) ); // B9 30 00 00 00 mov ecx,30h
            __CODE( 5, ( 0xB8, 0xCC, 0xCC, 0xCC, 0xCC ) ); // B8 CC CC CC CC mov eax,0CCCCCCCCh
            __CODE( 2, ( 0xF3, 0xAB ) ); // F3 AB rep stos dword ptr es:[edi]

            ////////////////////// Codes
            __CODE( 3, ( 0x8B, 0x45, 0x14 ) ); // 8B 45 14 mov eax,dword ptr [ebp+14h] [lparam]
            __CODE( 1, ( 0x50 ) ); // 50 push eax
            __CODE( 3, ( 0x8B, 0x45, 0x10 ) ); // 8B 45 10 mov eax,dword ptr [ebp+10h] [wparam]
            __CODE( 1, ( 0x50 ) ); // 50 push eax
            __CODE( 3, ( 0x8B, 0x55, 0x0C ) ); // 8B 55 0C mov edx,dword ptr [ebp+0Ch] [msg]
            __CODE( 1, ( 0x52 ) ); // 52 push edx
            __CODE( 3, ( 0x8B, 0x45, 0x08 ) ); // 8B 45 08 mov eax,dword ptr [ebp+8] [hwnd]
            __CODE( 1, ( 0x50 ) ); // 50 push eax
            __CODE( 1, ( 0xB9 ) ); __PTR( this ); // B9 ?? ?? ?? ?? mov ecx, this
            __CODE( 1, ( 0x51 ) ); // 51 push ecx
            __CALL( &CTest::member_proc ); // E8 ?? ?? ?? ?? call CTest::member_proc

            /////////////////////// Epilog.
            __CODE( 1, ( 0x5F ) ); // 5F pop edi
            __CODE( 1, ( 0x5E ) ); // 5E pop esi
            __CODE( 1, ( 0x5B ) ); // 5B pop ebx
            __CODE( 6, ( 0x81, 0xC4, 0xC0, 0x00, 0x00, 0x00 ) ); // 81 C4 C0 00 00 00 add esp,0C0h
            __CODE( 2, ( 0x8B, 0xE5 ) ); // 8B E5 mov esp,ebp
            __CODE( 1, ( 0x5D ) ); // 5D pop ebp
            __CODE( 3, ( 0xC2, 0x10, 0x00 ) ); // C2 10 00 ret 10h

            DWORD old = 0;
            VirtualProtect( &m_proc, sizeof(m_proc), PAGE_EXECUTE_READWRITE, &old );
            }

            WNDPROC get_proc()
            {
            return (WNDPROC)(void*)m_proc;
            }

            public:
            char m_proc[1024];
            int m_value;
            };


            int main( int argc, char** argv )
            {
            CTest test;
            WNDPROC proc = test.get_proc();
            proc( (HWND)1, 2, 3, 4 );

            return 0;
            }  回復(fù)  更多評(píng)論
              
            # re: 將 Timer 對(duì)象化 2013-06-25 13:37 | WXX
            如果我說(shuō)chromium的base庫(kù)的延遲任務(wù)更好呢??
              回復(fù)  更多評(píng)論
              
            # re: 將 Timer 對(duì)象化 2013-06-25 21:10 | 溪流
            @cexer
            學(xué)習(xí)了  回復(fù)  更多評(píng)論
              
            # re: 將 Timer 對(duì)象化 2013-06-25 21:10 | 溪流
            @WXX
            有更好的很正常。  回復(fù)  更多評(píng)論
              
            久久久久国产精品嫩草影院| 久久精品国产久精国产| 狠狠色噜噜狠狠狠狠狠色综合久久| 丰满少妇高潮惨叫久久久| 国产精品久久久久影院色| 久久夜色撩人精品国产| 狠狠色婷婷久久综合频道日韩 | 精品久久久无码人妻中文字幕| 久久Av无码精品人妻系列| 国产激情久久久久影院老熟女| 伊人情人综合成人久久网小说| 久久久精品人妻一区二区三区蜜桃| 久久福利片| 久久99久久99精品免视看动漫| 久久精品国产色蜜蜜麻豆| 国内精品久久久久久99| 伊人精品久久久久7777| 久久国产免费直播| 国产欧美一区二区久久| 亚洲精品乱码久久久久久自慰| 久久国产综合精品五月天| 99久久精品国产麻豆| 亚洲中文字幕无码久久精品1| 久久人人超碰精品CAOPOREN | 亚洲中文字幕久久精品无码APP| 国产亚洲成人久久| 久久精品人人做人人爽97 | 尹人香蕉久久99天天拍| 亚洲а∨天堂久久精品| 久久性生大片免费观看性| 亚洲国产成人久久综合一| 久久精品国产91久久综合麻豆自制| 日韩人妻无码一区二区三区久久 | 亚洲国产成人久久综合一区77 | 亚洲∧v久久久无码精品| 久久棈精品久久久久久噜噜| 无码人妻久久久一区二区三区 | 久久人人爽爽爽人久久久| 一本久久a久久精品vr综合| 亚洲中文字幕无码久久2017 | 久久99精品久久久久久噜噜 |