青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-90  評(píng)論-947  文章-0  trackbacks-0

由于 C++ 成員函數(shù)的調(diào)用機(jī)制問(wèn)題,對(duì)C語(yǔ)言回調(diào)函數(shù)的 C++ 封裝是件比較棘手的事。為了保持C++對(duì)象的獨(dú)立性,理想情況是將回調(diào)函數(shù)設(shè)置到成員函數(shù),而一般的回調(diào)函數(shù)格式通常是普通的C函數(shù),尤其是 Windows API 中的。好在有些回調(diào)函數(shù)中留出了一個(gè)額外參數(shù),這樣便可以由這個(gè)通道將 this 指針傳入。比如線程函數(shù)的定義為:

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
    LPVOID lpThreadParameter
    );
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;

這樣,當(dāng)我們實(shí)現(xiàn)線程類的時(shí)候,就可以:

class Thread
{
private:
    HANDLE m_hThread;

public:
    BOOL Create()
    {
        m_hThread = CreateThread(NULL, 0, StaticThreadProc, (LPVOID)this, 0, NULL);
        return m_hThread != NULL;
    }

private:
    DWORD WINAPI ThreadProc()
    {
        // TODO
        return 0;
    }

private:
    static DWORD WINAPI StaticThreadProc(LPVOID lpThreadParameter)
    {
        ((Thread *)lpThreadParameter)->ThreadProc();
    }
};

不過(guò),這樣,成員函數(shù) ThreadProc() 便喪失了一個(gè)參數(shù),這通常無(wú)傷大雅,任何原本需要從參數(shù)傳入的信息都可以作為成員變量讓 ThreadProc 來(lái)讀寫。如果一定有些什么是非從參數(shù)傳入不可的,那也可以,一種做法,創(chuàng)建線程的時(shí)候傳入一個(gè)包含 this 指針信息的結(jié)構(gòu)。第二種做法,對(duì)該 class 作單例限制——如果現(xiàn)實(shí)情況允許的話。

所以,有額外參數(shù)的回調(diào)函數(shù)都好處理。不幸的是,Windows 的窗口回調(diào)函數(shù)沒(méi)有這樣一個(gè)額外參數(shù):

typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

這使得對(duì)窗口的 C++ 封裝變得困難。為了解決這個(gè)問(wèn)題,一個(gè)很自然的想法是,維護(hù)一份全局的窗口句柄到窗口類的對(duì)應(yīng)關(guān)系,如:

#include <map>

class Window
{
public:
    Window();
    ~Window();
   
public:
    BOOL Create();

protected:
    LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);

protected:
    HWND m_hWnd;

protected:
    static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    static std::map<HWND, Window *> m_sWindows;
};

在 Create 的時(shí)候,指定 StaticWndProc 為窗口回調(diào)函數(shù),并將 hWnd 與 this 存入 m_sWindows:

BOOL Window::Create()
{
    LPCTSTR lpszClassName = _T("ClassName");
    HINSTANCE hInstance = GetModuleHandle(NULL);

    WNDCLASSEX wcex    = { sizeof(WNDCLASSEX) };
    wcex.lpfnWndProc   = StaticWndProc;
    wcex.hInstance     = hInstance;
    wcex.lpszClassName = lpszClassName;

    RegisterClassEx(&wcex);

    m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    if (m_hWnd == NULL)
    {
        return FALSE;
    }

    m_sWindows.insert(std::make_pair(m_hWnd, this));

    ShowWindow(m_hWnd, SW_SHOW);
    UpdateWindow(m_hWnd);

    return TRUE;
}

在 StaticWindowProc 中,由 hWnd 找到 this,然后轉(zhuǎn)發(fā)給成員函數(shù):

LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    std::map<HWND, Window *>::iterator it = m_sWindows.find(hWnd);
    assert(it != m_sWindows.end() && it->second != NULL);

    return it->second->WndProc(message, wParam, lParam);
}

(m_sWindows 的多線程保護(hù)略過(guò),下同)

據(jù)說(shuō) MFC 采用的就是類似的做法。缺點(diǎn)是,每次 StaticWndProc 都要從 m_sWindows 中去找 this。由于窗口類一般會(huì)保存窗口句柄,回調(diào)函數(shù)里的 hWnd 就沒(méi)多大作用了,如果這個(gè) hWnd 能夠被用來(lái)存 this 指針就好了,那么就能寫成這樣:

LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}

這樣看上去就爽多了。傳說(shuō)中 WTL 所采取的 thunk 技術(shù)就是這么干的。之前,只是聽(tīng)過(guò)這遙遠(yuǎn)的傳說(shuō),今天,終于有機(jī)會(huì)走進(jìn)這個(gè)傳說(shuō)去看一看。參考資料是一篇不知原始出處的文章《深入剖析WTL—WTL框架窗口分析》,以及部分 WTL 8.0 代碼,還有其他亂七八糟的文章。

WTL 的思路是,每次在系統(tǒng)調(diào)用 WndProc 的時(shí)候,讓它鬼使神差地先走到我們的另一處代碼,讓我們有機(jī)會(huì)修改堆棧中的 hWnd。這處代碼可能是類似這樣的:

__asm
{
    mov dword ptr [esp+4], pThis  ;調(diào)用 WndProc 時(shí),堆棧結(jié)構(gòu)為:RetAddr, hWnd, message, wParam, lParam, ... 故 [esp+4]
    jmp WndProc
}

由于 pThis 和 WndProc 需要被事先修改(但又無(wú)法在編譯前定好),所以我們需要運(yùn)行的時(shí)候去修改這部分代碼。先弄一個(gè)小程序探測(cè)下這兩行語(yǔ)句的機(jī)器碼:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return 0;
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    MessageBox(NULL, NULL, NULL, MB_OK);

    __asm
    {
        mov dword ptr [esp+4], 1
        jmp WndProc
    }

    return 0;
}

最前面的 MessageBox 是為了等下調(diào)試的時(shí)候容易找到進(jìn)入點(diǎn)。

然后使用 OllyDbg,在 MessageBoxW 上設(shè)置斷點(diǎn),執(zhí)行到該函數(shù)返回:

image

這里我們看到,mov dword ptr [esp+4] 的機(jī)器碼為 C7 44 24 04,后面緊接著的一個(gè) DWORD 是 mov 的第二個(gè)操作數(shù)。jmp 的機(jī)器碼是 e9,后面緊接著的一個(gè) DWORD 是跳轉(zhuǎn)的相對(duì)地址。其中 00061000h - 0006102Bh = FFFFFFD5h。

于是定義這樣一個(gè)結(jié)構(gòu):

#pragma pack(push,1)
typedef struct _StdCallThunk
{
    DWORD   m_mov;          // = 0x042444C7
    DWORD   m_this;         // = this
    BYTE    m_jmp;          // = 0xe9
    DWORD   m_relproc;      // = relative distance
} StdCallThunk;
#pragma pack(pop)

這個(gè)結(jié)構(gòu)可以作為窗口類的成員變量存在。我們的窗口類現(xiàn)在變成了這樣子:

class Window
{
public:
    Window();
    ~Window();

public:
    BOOL Create();

protected:
    LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);

protected:
    HWND         m_hWnd;
    StdCallThunk m_thunk;

protected:
    static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

似乎少了點(diǎn)什么……創(chuàng)建窗口的時(shí)候,我們是不能直接把回調(diào)函數(shù)設(shè)到 StaticWndPorc 中去的,因?yàn)檫@個(gè)函數(shù)是希望被寫成這樣子的:

LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}

那么至少需要一個(gè)臨時(shí)的回調(diào)函數(shù),在這個(gè)函數(shù)里去設(shè)置新的回調(diào)函數(shù)(設(shè)到 m_thunk 上),再由 m_thunk 來(lái)調(diào)用 StaticWndProc,StaticWndProc 再去調(diào)用 WndProc,這樣整個(gè)過(guò)程就通了。

但是,臨時(shí)回調(diào)函數(shù)還是需要知道從 hWnd 到 this 的對(duì)應(yīng)關(guān)系。可是現(xiàn)在我們不能照搬用剛才的 m_sWindows 了。因?yàn)榇翱谠趧?chuàng)建過(guò)程中就會(huì)調(diào)用到回調(diào)函數(shù),需要使用到 m_sWindows 里的 this,而窗口被成功創(chuàng)建之前,我們沒(méi)法提前拿到 HWND 存入 m_sWindows。現(xiàn)在,換個(gè)方法,存當(dāng)前線程 ID 與 this 的對(duì)應(yīng)關(guān)系。這樣,這個(gè)類變成了:

#include <map>

class Window
{
public:
    Window();
    ~Window();

public:
    BOOL Create();

protected:
    LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);

protected:
    HWND         m_hWnd;
    StdCallThunk m_thunk;

protected:
    static LRESULT CALLBACK TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

    static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    static std::map<DWORD, Window *> m_sWindows;
};

然后實(shí)現(xiàn) Create 和 TempWndProc:

BOOL Window::Create()
{
    LPCTSTR lpszClassName = _T("ClassName");
    HINSTANCE hInstance = GetModuleHandle(NULL);

    WNDCLASSEX wcex    = { sizeof(WNDCLASSEX) };
    wcex.lpfnWndProc   = TempWndProc;
    wcex.hInstance     = hInstance;
    wcex.lpszClassName = lpszClassName;

    RegisterClassEx(&wcex);

    DWORD dwThreadId = GetCurrentThreadId();
    m_sWindows.insert(std::make_pair(dwThreadId, this));

    m_thunk.m_mov = 0x042444c7;
    m_thunk.m_jmp = 0xe9;

    m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    if (m_hWnd == NULL)
    {
        return FALSE;
    }
   
    ShowWindow(m_hWnd, SW_SHOW);
    UpdateWindow(m_hWnd);

    return TRUE;
}

LRESULT CALLBACK Window::TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    std::map<DWORD, Window *>::iterator it = m_sWindows.find(GetCurrentThreadId());
    assert(it != m_sWindows.end() && it->second != NULL);

    Window *pThis = it->second;
    m_sWindows.erase(it);

    WNDPROC pWndProc = (WNDPROC)&pThis->m_thunk;

    pThis->m_thunk.m_this = (DWORD)pThis;
    pThis->m_thunk.m_relproc = (DWORD)&Window::StaticWndProc - ((DWORD)&pThis->m_thunk + sizeof(StdCallThunk));

    m_hWnd = hWnd;
    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pWndProc);

    return pWndProc(hWnd, message, wParam, lParam);
}

差不多可以了,調(diào)試一下。結(jié)果,在 thunk 的第一行出錯(cuò)了。我原以為地址算錯(cuò)了神馬的,嘗試把 thunk.m_mov 改為 0x90909090,再運(yùn)行,還是出錯(cuò)。于是傻掉了……過(guò)了好一會(huì)兒才意識(shí)到,可能是因?yàn)?thunk 在數(shù)據(jù)段,無(wú)法被執(zhí)行。可是,很久很久以前偶滴一個(gè)敬愛(ài)的老師在 TC 中鼓搗程序運(yùn)行時(shí)改變自身代碼時(shí),貌似無(wú)此問(wèn)題啊。。。然后查呀查,原來(lái)是 Windows 在的數(shù)據(jù)執(zhí)行保護(hù)搞的鬼。于是,需要用 VirtualAlloc 來(lái)申請(qǐng)一段有執(zhí)行權(quán)限的內(nèi)存。WTL 里面也是這么做的,不過(guò)它似乎維護(hù)了一塊較大的可執(zhí)行內(nèi)存區(qū)作為 thunk 內(nèi)存池,我們這里從簡(jiǎn)。最后,整個(gè)流程終于跑通了。最終代碼清單如下:

#include <Windows.h>
#include <assert.h>
#include <map>
#include <tchar.h>

#pragma pack(push,1)
typedef struct _StdCallThunk
{
    DWORD   m_mov;
    DWORD   m_this;
    BYTE    m_jmp;
    DWORD   m_relproc;

} StdCallThunk;
#pragma pack(pop)

class Window
{
public:
    Window();
    ~Window();

public:
    BOOL Create();

protected:
    LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);

protected:
    HWND          m_hWnd;
    StdCallThunk *m_pThunk;

protected:
    static LRESULT CALLBACK TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    static std::map<DWORD, Window *> m_sWindows;
};

std::map<DWORD, Window *> Window::m_sWindows;

Window::Window()
{

}

Window::~Window()
{
    VirtualFree(m_pThunk, sizeof(StdCallThunk), MEM_RELEASE);
}

BOOL Window::Create()
{
    LPCTSTR lpszClassName = _T("ClassName");
    HINSTANCE hInstance = GetModuleHandle(NULL);

    WNDCLASSEX wcex    = { sizeof(WNDCLASSEX) };
    wcex.lpfnWndProc   = TempWndProc;
    wcex.hInstance     = hInstance;
    wcex.lpszClassName = lpszClassName;
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    RegisterClassEx(&wcex);

    DWORD dwThreadId = GetCurrentThreadId();
    m_sWindows.insert(std::make_pair(dwThreadId, this));

    m_pThunk = (StdCallThunk *)VirtualAlloc(NULL, sizeof(StdCallThunk), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    m_pThunk->m_mov = 0x042444c7;
    m_pThunk->m_jmp = 0xe9;

    m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    if (m_hWnd == NULL)
    {
        return FALSE;
    }
   
    ShowWindow(m_hWnd, SW_SHOW);
    UpdateWindow(m_hWnd);

    return TRUE;
}

LRESULT Window::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONUP:
        MessageBox(m_hWnd, _T("LButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
        break;
    case WM_RBUTTONUP:
        MessageBox(m_hWnd, _T("RButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        break;
    }

    return DefWindowProc(m_hWnd, message, wParam, lParam);
}

LRESULT CALLBACK Window::TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    std::map<DWORD, Window *>::iterator it = m_sWindows.find(GetCurrentThreadId());
    assert(it != m_sWindows.end() && it->second != NULL);

    Window *pThis = it->second;
    m_sWindows.erase(it);

    WNDPROC pWndProc = (WNDPROC)pThis->m_pThunk;

    pThis->m_pThunk->m_this = (DWORD)pThis;
    pThis->m_pThunk->m_relproc = (DWORD)&Window::StaticWndProc - ((DWORD)pThis->m_pThunk + sizeof(StdCallThunk));

    pThis->m_hWnd = hWnd;
    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pWndProc);

    return pWndProc(hWnd, message, wParam, lParam);
}

LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    Window wnd;
    wnd.Create();

    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

剛才有一處,存 this 指針的時(shí)候,我很武斷地把它與當(dāng)前線程 ID 關(guān)聯(lián)起來(lái)了,其實(shí)這正是 WTL 本身的做法。它用 CAtlWinModule::AddCreateWndData 存的 this,最終會(huì)把當(dāng)前線程 ID 和 this 作關(guān)聯(lián)。我是這么理解的吧,同一線程不可能同時(shí)有兩處在調(diào)用 CreateWindow,所以這樣取回來(lái)的 this 是可靠的。

好了,到此為止,邊試驗(yàn)邊記錄的,不知道理解是否正確。歡迎指出不當(dāng)之處,也歡迎提出相關(guān)的問(wèn)題來(lái)考我,歡迎介紹有關(guān)此問(wèn)題的新方法、新思路,等等,總之,請(qǐng)各位看官多指教哈。

posted on 2010-10-24 16:44 溪流 閱讀(6654) 評(píng)論(40)  編輯 收藏 引用 所屬分類: C++Windows

評(píng)論:
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 17:43 | OwnWaterloo
virtualalloc不是這樣用的啊, 同學(xué)……
virtualalloc的粒度很大(保留按64k, 提交按頁(yè))。
也就是說(shuō), 如果直接用virtualalloc, 每個(gè)window instance, 要占用64k地址空間, 并使用至少4k內(nèi)存。


ATL不同版本處理這個(gè)問(wèn)題好像采取的策略不同。
有你提到的使用virtualalloc分配, 然后再劃分。
還有直接使用HeapCreate創(chuàng)建一個(gè)分配可執(zhí)行內(nèi)存的win32 heap。


這些技術(shù)很炫, 研究起來(lái)也很有成就感。
玩具代碼當(dāng)然可以隨便用, 但成熟代碼盡可能選取成熟的技術(shù)。

比如全局table。它的效率問(wèn)題真的無(wú)法忍受嗎? 或者全局帶來(lái)的其他問(wèn)題(多線程相關(guān))很要緊嗎?

如果窗口類是自己注冊(cè)的, 可以用cbExtra。
如果不是, 而且GWL_USERDATA是開(kāi)放的(對(duì)話框和MDI是保留給自己用的), 那可以用GWL_USERDATA。

還可以用Set/GetWindowProp。

如果thunk機(jī)器相關(guān)的缺陷不要緊, 而且上述的這些成熟的方案都不合適, 也不一定非要自己寫內(nèi)存池, HeapCreate在windows上總是可用的。  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 18:00 | 溪流
@OwnWaterloo
啊。。我真的沒(méi)打算寫這方面輪子~~~只是想順著 WTL 的做法自己跑一遍,以了解僅僅看別人文章所看不出的細(xì)節(jié)。。。VirtualAlloc確實(shí)沒(méi)用過(guò)哈,于是剛才胡亂搞了一通,只求拿到內(nèi)存,慚愧~  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 18:02 | 溪流
@OwnWaterloo
嗯對(duì)了,你覺(jué)得WTL本身算成熟技術(shù)嗎?  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 18:08 | OwnWaterloo
@溪流
沒(méi)什么好慚愧的呀……
實(shí)驗(yàn)、 玩具代碼這很正常。 為了突出重點(diǎn)(機(jī)器代碼的構(gòu)造)而非內(nèi)存管理。
造輪子也是很好的練習(xí)。

只是, 輪子在投入正規(guī)使用前, 一定要謹(jǐn)慎。
玩具代碼中忽略的非重點(diǎn), 恰恰可能占了很多代碼, 繁瑣、 無(wú)趣、 bug叢生……
如果真有恒心把這些問(wèn)題處理好, 再和現(xiàn)有的輪子客觀的比較權(quán)衡之后, 再考慮使用。


因?yàn)榭催^(guò)許多把玩具代碼 —— 而且是一些相當(dāng)沒(méi)有技術(shù)含量, 僅僅是滿足個(gè)人審美觀而已 —— 的代碼放入實(shí)際工程中, 還很自鳴得意的……
慘不忍睹啊……
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 18:13 | OwnWaterloo
@溪流
WTL我不熟, 深入研究的也就是和thunk相關(guān)的東西……

給人的感覺(jué)是模板使用過(guò)度。
比如, 居然用一個(gè)模板參數(shù)去傳遞兩個(gè)窗口風(fēng)格參數(shù)。
感覺(jué)是C++的混沌年代, 大家都還不知道如何合理使用模板的情況下, 產(chǎn)生的東西……
所以也不愿意多看……

想研究gui的話, cppblog有個(gè) cexer, 看過(guò)很多gui的框架, 可以向他咨詢。  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 21:33 | 陳梓瀚(vczh)
@溪流
好快……其實(shí)我是看錯(cuò)了,所以把留言刪掉了你竟然在這幾秒鐘內(nèi)……  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 21:34 | 溪流
@陳梓瀚(vczh)
哈哈,碰巧在刷~  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 21:36 | 陳梓瀚(vczh)
一般來(lái)說(shuō),如果你自己想用滿足各種要求的函數(shù)對(duì)象,就去用std::function哈。當(dāng)然這個(gè)實(shí)現(xiàn)起來(lái)比較麻煩,假設(shè)支持10個(gè)參數(shù),你就要特化0-10個(gè)參數(shù),而且返回void和其他的各一份,一共22個(gè)類,還得考慮寫程序或者用宏搞代碼生成……然后如果你想實(shí)現(xiàn)bind和curry,那就更多類了……

幸好std已經(jīng)有function了,哇哈哈  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 21:38 | 溪流
@陳梓瀚(vczh)
function 現(xiàn)在 std 了?我土了。。  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 21:53 | OwnWaterloo
@陳梓瀚(vczh)
這問(wèn)題用bind解決不了。

bind生成的對(duì)象, 需要template配合。
比如: for_each(begin, end, bind( ... ) );
for_each是一個(gè)函數(shù)模板。

而WndProc必須是一個(gè)函數(shù)指針。
lpWndProc = bind( ... ); 是不行的。
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 21:57 | 溪流
@OwnWaterloo
剛才逛了下cexer的博客,開(kāi)場(chǎng)白太好太強(qiáng)大了,盡看開(kāi)場(chǎng)白去了哈  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 22:02 | 溪流
@OwnWaterloo
我以為你說(shuō) WTL 模板使用過(guò)度神馬的可能會(huì)引人打架,怎么這么久都沒(méi)有人來(lái)打醬油呢~ ^_^  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-24 22:10 | OwnWaterloo
@溪流
因?yàn)橛玫娜松侔?nbsp; 回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 09:26 | waiting4you
打醬油的來(lái)了~~希望沒(méi)打攪幾位,呵呵.
我只是有一點(diǎn)小疑問(wèn):這種方法在64位系統(tǒng)下可以工作嗎?StdCallThunk里放在代碼塊是否x64系統(tǒng)兼容?
WTL應(yīng)該可以編譯為64位程序,不知道WTL的源碼中有沒(méi)有針對(duì)x64的編譯開(kāi)關(guān)  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 09:33 | OwnWaterloo
@waiting4you
有。
Windows支持的體系結(jié)構(gòu)里, ATL的stdthunk都有實(shí)現(xiàn)。
編譯switch也不用自己操心, 都寫好了的。
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 10:13 | waiting4you
@OwnWaterloo
謝謝,看來(lái)這種實(shí)現(xiàn)的可移植性不太好,了解一下它的實(shí)現(xiàn)可以開(kāi)闊視野.只是自己應(yīng)用這種技術(shù)來(lái)編碼就得好好考慮一下了.像ATL/WTL這樣有強(qiáng)大的微軟或開(kāi)源社區(qū)(偶不確定這個(gè)thunk部分應(yīng)該歸哪個(gè)"部門"管,開(kāi)源的WTL基于商業(yè)的ATL~!@#$)支持的代碼來(lái)說(shuō),這不是問(wèn)題.但是我們自己寫就...  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 10:29 | 空明流轉(zhuǎn)
WTL的高手還沒(méi)發(fā)話沒(méi)得打架的。
我沒(méi)覺(jué)得WTL模板用的多。。。  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 10:42 | 空明流轉(zhuǎn)
順便說(shuō)一下,那個(gè)什么所謂的Thunk,也就是一個(gè)JIT的雛形。。。  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk[未登錄](méi) 2010-10-25 12:34 | dd
請(qǐng)問(wèn)下:(DWORD)&Window::StaticWndProc - ((DWORD)pThis->m_pThunk + sizeof(StdCallThunk));這里面的(DWORD)m_pThunk + sizeof(StdCallThunk)是正確的嗎?sizeof(StdCallThunk)得到的是這個(gè)結(jié)構(gòu)體的字節(jié)數(shù) 13 byte,然后把字節(jié)數(shù)的單位變成DWORD加上,即 sizeof(DWORD)*13...  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 13:03 | 溪流
@dd
(DWORD)(pThis->m_pThunk),一個(gè)很原始很普通的數(shù)字,再加上 sizeof(StdCallThunk),也就是加上13。沒(méi)有sizeof(DWORD)*13。嗯。  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 22:36 | 路過(guò)
boost::function即可  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 23:01 | 路過(guò)
@OwnWaterloo

class Thread
{
LONG ThreadProc(LPVOID lParam)
{
boost::function<void> *pFunc = (boost::function<void> *) lParam;
(*pFunc)();
delete pFunc;
}

template<typename Callable>
void Run(Callable callable)
{
boost::function<void> *pFunc = new boost::function<void>;
*pFunc = callable;
::CreateThread(NULL, 0, StaticThreadProc, (LPVOID) pFunc, 0, NULL);
}
}

int _tmain(int argc, _TCHAR* argv[])
{
Task task;
Thread thread;

thread.Run(boost::bind(&Task::DoSomething, &task, 1, 2, ... n));
}
}  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 23:09 | OwnWaterloo
@路過(guò)
喔唷! 好華麗, 好耀眼哦!
class, thread, callable, run什么的, 我看不懂也!

我就問(wèn)一句, 還望賜教賜教啊~

如果CreateThread使用的線程函數(shù)的簽名是這個(gè)樣子:
unsigned threadproc(void); // 注意, 是void, 而不是void*

你用上面那些華麗的東西給我看看?
全局變量什么的也丑的, 也不許用哦!


WndProc就是這樣一個(gè)場(chǎng)景 —— 回調(diào)的簽名沒(méi)有void* context。
您, 明白了嗎?  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 23:24 | 路過(guò)
@OwnWaterloo

>> 如果CreateThread使用的線程函數(shù)的簽名是這個(gè)樣子:
那是如果,這樣的線程api可用嗎?windows,linux都沒(méi)有

>> WndProc就是這樣一個(gè)場(chǎng)景 —— 回調(diào)的簽名沒(méi)有void* context。
用GWLP_USERDATA不行?

如果設(shè)計(jì)者真的設(shè)計(jì)了這種用戶無(wú)法擴(kuò)展的api,那您是對(duì)的
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-25 23:27 | 路過(guò)
@OwnWaterloo

多嘴說(shuō)句,沒(méi)有如果;

如果真有“如果“,我也不會(huì)回復(fù)你  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-26 00:10 | OwnWaterloo
@路過(guò)
作者討論的是“如何開(kāi)啟一個(gè)線程”嗎?
不, 作者討論的是如何給WndProc塞入一個(gè)context。

哪誰(shuí)在討論ThreadProc?
是你, 將討論從WndProc轉(zhuǎn)移到ThreadProc。

我怕你不懂WndProc和ThreadProc究竟有什么重大區(qū)別,
以及, 也許你不熟悉WndProc,
所以想就近從你熟悉的ThreadProc中給你一點(diǎn)啟發(fā) —— 就是移除ThreadProc的context參數(shù)。
設(shè)想一下ThreadProc如果沒(méi)有void*參數(shù), 編程會(huì)是個(gè)什么樣子, api有多難擴(kuò)展。
以及, 問(wèn)題的關(guān)鍵究竟是什么?

你是不明白啊, 還是不明白啊, 還是不明白?


多說(shuō)一句, GWL_USERDATA我在上面已經(jīng)提到過(guò), 是誰(shuí)回帖不看貼的?
再多提醒一句, GWL_USERDATA并不是你想象的那么簡(jiǎn)單。
究竟是什么, 請(qǐng)繼續(xù)看我在樓上的回復(fù)。
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-26 19:09 | 路過(guò)
@OwnWaterloo

>> 哪誰(shuí)在討論ThreadProc?
請(qǐng)看樓主發(fā)的第一個(gè)例程,

我用threadproc來(lái)舉例一種靈活的方法而已

照這種方法法不能推導(dǎo)出wndproc的用法?需要在這上面挑刺?
你要挑刺我那例程語(yǔ)法也有問(wèn)題,你可以繼續(xù)

>>設(shè)想一下ThreadProc如果沒(méi)有void*參數(shù), 編程會(huì)是個(gè)什么樣子, api有多難擴(kuò)展。

還是“如果”,有意義?

>>多說(shuō)一句, GWL_USERDATA我在上面已經(jīng)提到過(guò), 是誰(shuí)回帖不看貼的?
我知道你提過(guò),既然你知道GWL_USERDATA,那我提的方法就是可行的;但你沒(méi)發(fā)現(xiàn),所以告訴你



最后,請(qǐng)你搞清楚一點(diǎn),

我之所以回復(fù)你,是有人說(shuō)可以用bind,你說(shuō)不行;我告訴怎么做可以行,明白不?都不知道你噴什么
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-26 19:15 | OwnWaterloo
@路過(guò)
lz的標(biāo)題是 WTL thunk。
thunk你懂嗎? thunk解決的是華麗的語(yǔ)法, 還是缺少的context參數(shù)?
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-26 19:20 | 溪流
@路過(guò)
@OwnWaterloo

啊。。。熄火熄火~~~最開(kāi)始提出bind可能原意并非完全針對(duì)我在頂樓說(shuō)的情形,路過(guò)同學(xué)的例子確實(shí)也指出了一個(gè)用bind拐個(gè)彎設(shè)置到原始C函數(shù)指針上去的方法,不過(guò)用不用bind都沒(méi)法解決WndProc少一個(gè)參數(shù)的硬傷……lz是來(lái)打醬油的,請(qǐng)無(wú)視~  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-26 19:27 | OwnWaterloo
>>照這種方法法不能推導(dǎo)出wndproc的用法?
>>還是“如果”,有意義?
 
你有點(diǎn)起碼的邏輯分析能力嗎?
說(shuō)threadproc是幫助你理解這兩者的不同, 既然不同, 那就是不可導(dǎo)出的。
既然好心被當(dāng)作驢肝肺, 那別什么"如果"了, 你自己動(dòng)手導(dǎo)出wndproc試試。
 
然后, 摸著自己的良心說(shuō): wndproc缺少的參數(shù)是靠什么傳入的
是bind嗎? 是function嗎? 是華麗的語(yǔ)法嗎?
 
還是我在前面提到的那些東西: table, cbExtra, GWL_USERDATA, Set/GetProp, thunk?
 
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-26 19:30 | OwnWaterloo
@路過(guò)
最煩這種瞅住機(jī)會(huì)就迫不及待的秀一下自己似是而非的理解, 離題萬(wàn)里
浮躁
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2010-10-26 19:35 | OwnWaterloo

@溪流
>> 路過(guò)同學(xué)的例子確實(shí)也指出了一個(gè)用bind拐個(gè)彎設(shè)置到原始C函數(shù)指針上去的方法

bind生成的可調(diào)用體 —— 一個(gè)instance —— 依然需要通過(guò)某種方式傳遞到callback中
而現(xiàn)在的問(wèn)題就是如何將這個(gè)context傳遞進(jìn)去
 
所以我一直說(shuō), bind根本就不是解決這個(gè)問(wèn)題的方法, 它本身需要這個(gè)問(wèn)題解決之后才能使用
  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2011-03-17 23:11 | 溪流
@OwnWaterloo
又看了一些人對(duì)回調(diào)函數(shù)的理解,覺(jué)得你在這個(gè)問(wèn)題上如此激烈的反應(yīng)是必要的,太多人認(rèn)為function之類的玩意兒就能解決回調(diào)函數(shù)少參數(shù)的問(wèn)題了,甚至沒(méi)意識(shí)到這個(gè)問(wèn)題是什么問(wèn)題。。。  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2011-03-17 23:24 | OwnWaterloo
@溪流
嘿, 多謝理解~  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2011-03-17 23:51 | 溪流
@OwnWaterloo
http://topic.csdn.net/u/20110221/16/86608158-ac06-4a45-b3bb-7f53ba3008fc.html,好多自以為是的人吶,還有人說(shuō)“去看chrome源碼吧”  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2013-04-27 17:37 | 溪流
@OwnWaterloo
最近有點(diǎn)想給TimerProc成員化的欲望~  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk[未登錄](méi) 2015-08-26 11:05 | Mark
是我看過(guò)分析Thunk技術(shù)最清晰的文章  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2015-10-25 00:46 | WXX
這個(gè)做法還是有個(gè)全局的static std::map<DWORD, Window *> m_sWindows
感覺(jué)沒(méi)有比mfc那種查找的做法好多少啊  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2015-10-26 00:27 | 溪流
@WXX
請(qǐng)看完全文吧  回復(fù)  更多評(píng)論
  
# re: 學(xué)習(xí)下 WTL 的 thunk 2023-05-15 16:39 | 溪流
@Mark
謝謝~!  回復(fù)  更多評(píng)論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            一区二区电影免费观看| 国产亚洲欧美另类中文| 亚洲精品日本| 夜夜夜久久久| 国产最新精品精品你懂的| 久久久久久久综合狠狠综合| 亚洲欧美日产图| 最新国产乱人伦偷精品免费网站| 欧美激情国产精品| 国产精品区免费视频| 久久久久久香蕉网| 欧美日韩免费在线| 久久久久九九视频| 欧美黄色日本| 国产精品初高中精品久久| 久久久久久久综合狠狠综合| 欧美国产三级| 欧美成人精品影院| 国产精品成人播放| 日韩一区二区高清| 亚洲区一区二| 韩日在线一区| 亚洲线精品一区二区三区八戒| 亚洲高清影视| 久久黄色网页| 久久久久综合一区二区三区| 欧美性事免费在线观看| 最新精品在线| 一区二区精品在线| 欧美日韩国产精品专区| 亚洲片国产一区一级在线观看| 激情视频一区| 噜噜噜躁狠狠躁狠狠精品视频 | 亚洲男人第一网站| 亚洲欧美日本另类| 国产欧美一区二区精品忘忧草| 日韩视频精品在线| 欧美一区2区三区4区公司二百| 欧美色一级片| 欧美一区二区三区视频在线 | 欧美一级一区| 欧美国产在线观看| 亚洲综合电影一区二区三区| 欧美天天视频| 久久综合一区二区| 日韩午夜电影av| 免费成人黄色av| 国产视频自拍一区| 亚洲欧美另类中文字幕| 国产精品va在线| 欧美电影电视剧在线观看| 欧美韩日一区| 亚洲美女中文字幕| 欧美一区国产在线| 亚洲无人区一区| 国产精品乱码| 美女亚洲精品| 欧美一区二区三区免费视| 欧美激情一区在线观看| 欧美一区二区三区免费观看| 亚洲黄色免费网站| 国产日韩精品在线播放| 欧美日韩一卡| 你懂的国产精品| 久久人人精品| 欧美一区二区三区成人| 99在线精品观看| 噜噜噜在线观看免费视频日韩| 亚洲理论在线观看| 亚洲人在线视频| 日韩亚洲欧美一区二区三区| 亚洲国产成人av好男人在线观看| 国产精品美女主播| 国产日韩欧美精品在线| 国产精品一卡二| 国内免费精品永久在线视频| 国产精品欧美在线| 国产一区二区三区在线观看免费| 国产亚洲成av人在线观看导航| 国产欧美日韩一区二区三区| 国产伦精品一区二区三区照片91 | 久久频这里精品99香蕉| 最新日韩精品| 在线一区免费观看| 欧美专区福利在线| 欧美激情一区二区久久久| 亚洲精品欧美日韩专区| 午夜精品视频在线观看| 久久午夜精品一区二区| 欧美日韩一区二区在线观看| 国产精品五月天| 99精品久久| 欧美激情亚洲国产| 欧美在线观看视频| 国产精品久久久久久久久久直播| 国产在线乱码一区二区三区| 亚洲精品永久免费| 欧美成va人片在线观看| 亚洲欧美日韩视频二区| 国产精品进线69影院| 亚洲午夜精品一区二区| 亚洲国产小视频| 美女国产精品| 亚洲精品日韩在线| 亚洲第一精品夜夜躁人人爽| 久久爱另类一区二区小说| 国产欧美日韩一区二区三区在线| 亚洲免费电影在线观看| 亚洲第一色中文字幕| 欧美国产成人精品| 夜夜嗨网站十八久久| 这里只有精品视频在线| 国产精品v一区二区三区| 性伦欧美刺激片在线观看| 亚洲影院高清在线| 激情欧美亚洲| 亚洲日本无吗高清不卡| 国产精品久久一卡二卡| 欧美一区二区三区在线视频| 亚洲国产精品一区二区第四页av | 亚洲美女在线国产| 亚洲一区精品在线| 激情综合网址| 一本色道久久综合亚洲精品按摩| 国产精品久久久久久超碰| 欧美日韩另类视频| 亚洲激情视频网站| 欧美日韩另类视频| 狂野欧美激情性xxxx| 欧美日韩另类字幕中文| 美女黄毛**国产精品啪啪 | 亚洲中字在线| 欧美大片va欧美在线播放| 欧美在线一级va免费观看| 欧美高清视频免费观看| 久久综合成人精品亚洲另类欧美| 欧美sm极限捆绑bd| 久久综合给合久久狠狠色| 国产精品视频一| 99精品免费网| 亚洲一区影院| 国产精品久久久久久妇女6080| 欧美xxxx在线观看| 亚洲国产毛片完整版| 麻豆精品视频| 亚洲黄色毛片| 亚洲视屏在线播放| 国产麻豆成人精品| 午夜宅男久久久| 蜜桃av一区| 一本色道**综合亚洲精品蜜桃冫 | 亚洲欧洲日本在线| 性xx色xx综合久久久xx| 欧美精品一区二区久久婷婷| 亚洲欧洲日产国产网站| 亚洲已满18点击进入久久| 国产精品视频一| 久热成人在线视频| 亚洲激情一区二区| 午夜宅男久久久| 伊人蜜桃色噜噜激情综合| 欧美啪啪成人vr| 欧美怡红院视频| 91久久极品少妇xxxxⅹ软件| 亚洲影院污污.| 91久久视频| 国产一区在线看| 欧美午夜精品理论片a级按摩| 亚洲一区三区视频在线观看| 国产偷国产偷精品高清尤物| 嫩草影视亚洲| 久久精品色图| 午夜欧美理论片| 亚洲视频一区在线观看| 欧美黑人国产人伦爽爽爽| 久久精品一区二区国产| 亚洲校园激情| 亚洲一区二区三区视频播放| 亚洲高清在线观看| 在线日本成人| 亚洲福利视频三区| 亚洲激情电影在线| 亚洲人成毛片在线播放| 亚洲国产成人在线| 一区二区三区在线视频观看| 国产日韩欧美视频| 国产欧美午夜| 黄色精品一区二区| 黄色成人av| 亚洲激情网址| 一区二区三区视频在线观看| 亚洲无亚洲人成网站77777| 亚洲男女自偷自拍图片另类| 亚洲无限av看| 免费亚洲网站| 99精品视频免费| 久久精品国产久精国产一老狼| 在线日韩欧美| 夜夜狂射影院欧美极品| 久久精品99无色码中文字幕|