thunkをいじる

公開:2010-12-08 06:30
更新:2020-02-15 04:36
カテゴリ:windows

64bit環境でウィンドウを1枚表示してみた。thunkコードを書きなおした以外はほぼそのままで動いている。thunkコードの中身はウィンドウプロシージャとC++クラスのインスタンスをつなぐコードである。ATL Windowクラスで使われている手法で、私はその仕組みを拝借して独自(というほどでもないけど)にwindowラップクラスを作りthunkコード部分をXbyakで実装している。しかしこのthunkって本来のthunkではないような気もするが。64bitになって関数の呼び出し規約が変わっているのでthunkコードも変えることになったのだが、32bit版に比べてコードが短くなった。


// thisとhwndをつなぐthunkクラス
struct hwnd_this_thunk : public Xbyak::CodeGenerator {
hwnd_this_thunk(base_window* impl,WNDPROC proc)
{
// rcxにhwndが格納されているので、それをimpl->hwndに保存
mov(qword[&(impl->hwnd_)],rcx);
// 代わりにthisのアドレスをrcxに格納
mov(rcx,(LONG_PTR)impl);
// r10にproc(Window プロシージャ)へのアドレスを格納
mov(r10,(LONG_PTR)proc);
// Window プロシージャへへジャンプ
jmp(r10);
}
};

hwnd_thisthunk.GetCode()を呼び出すとアセンブルされ、アセンブルされたコードの先頭アドレスが戻り値で返される。そのアドレスをRegisterClassするときにWindowプロシージャのアドレスとしてセットする。
で、CreateWindow()以降上記コードが呼ばれて、本来hwndが格納されるところにthisが格納され、hwndはimpl->hwnd
メンバに格納されてwindowプロシージャ(proc)が呼ばれる

windowプロシージャではhwndをthisにキャストして、thisのメンバメソッドを呼び出す。結果としてウィンドウハンドル(HWND)とC++クラスのインスタンスが結び付けられることになる。


static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
base_window* ptr = reinterpret_cast<base_window*>(hwnd);
hwnd = ptr->hwnd_;
return ptr->window_proc(hwnd,message,wParam,lParam);
};

本当はjmp(proc)としたいところなんだれどどうもできないのでr10レジスタに一旦格納してからjmp(r10)としている。
また上記のstaticメソッドもXbyakで置き換えられそうな気もするがアセンブラ・C++内部動作に詳しくないのでよくわからない。

base_windowラップクラスのソースコード

```cpp; highlight:[47,48,49,50,51,52,53,54];

/ window ベースクラス / / (Windowsラップコードの一部なのでこのコードだけでは動作しません。)/ struct base_window { typedef boost::signals2::signal<LRESULT (HWND,boost::uint32_t,WPARAM, LPARAM) > on_message_type; on_message_type on_message; typedef boost::signals2::signal<void ()> on_render_type; on_render_type on_render; operator HWND(); protected: base_window( const std::wstring& title, const std::wstring& name,bool fit_to_display, float width,float height); ~base_window(); void register_class ( wchar_t menu_name, boost::uint32_t style, boost::int32_t cbClsExtra = 0, HICON hIcon = ::LoadIcon(NULL,IDI_APPLICATION), HCURSOR hCursor = ::LoadCursor(NULL, IDC_ARROW), HBRUSH hbrBackground = NULL, HICON hIconSm = NULL ); / デフォルト設定 / void register_class(); void create_window(); void update(); void show(boost::uint32_t show_flag); virtual void discard_device(); virtual void create_device(); virtual void create_device_independent_resources(); virtual LRESULT window_proc(HWND hwnd,boost::uint32_t message, WPARAM wParam, LPARAM lParam); protected: static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { base_window ptr = reinterpret_cast<basewindow*>(hwnd); hwnd = ptr->hwnd; return ptr->window_proc(hwnd,message,wParam,lParam); }; // thisとhwndをつなぐthunkクラス struct hwnd_this_thunk : public Xbyak::CodeGenerator { hwnd_this_thunk(basewindow* impl,WNDPROC proc) { // rcxにhwndが格納されているので、それをimpl->hwndに保存 mov(qword[&(impl->hwnd)],rcx); // 代わりにthisのアドレスをrcxに格納 mov(rcx,(LONG_PTR)impl); // r10にproc(Window プロシージャ)へのアドレスを格納 mov(r10,(LONGPTR)proc); // Window プロシージャへへジャンプ jmp(r10); } }; HWND hwnd; ID2D1FactoryPtr factory_; ID2D1HwndRenderTargetPtr rendertarget; IDWriteFactoryPtr writefactory; IWICImagingFactoryPtr wic_imagingfactory; hwnd_thisthunk thunk; std::wstring title; std::wstring name; float width,height; bool fit_todisplay; boost::shared_ptr<sf::window_class_ex> wndclass; WNDPROC thunkproc; }; LRESULT base_window::window_proc(HWND hwnd,boost::uint32_t message, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; switch (message) { case WM_CREATE: { // TODO: create_device(); break; } case WM_SIZE: { //if (rendertarget) //{ // D2D1_SIZE_U size; // size.width = lParam & 0xFFFF; // size.height = (lParam >> 16) & 0xFFFF; ; // // Note: This method can fail, but it's okay to ignore the // // error here -- it will be repeated on the next call to // // EndDraw. // //rendertarget->Resize(size); //} } case WM_PAINT: { //create_device(); paint_struct begin_paint(hwnd); //if (!(rendertarget->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED)) //{ // // Retrieve the size of the render target. // D2D1_SIZE_F renderTargetSize = rendertarget->GetSize(); // try { // //rendertarget->BeginDraw(); // base_->on_render(); // //THROW_IF_ERR(rendertarget->EndDraw()); // } catch (sf::win32_error_exception& e ) // { // if(e.hresult() == D2DERR_RECREATE_TARGET) // { // discard_device(); // } else { // throw; // } // } //} return FALSE; } case WM_DISPLAYCHANGE: { ::InvalidateRect(hwnd, NULL, FALSE); } case WM_ERASEBKGND: { return FALSE; } case WM_MOUSEMOVE: { // on_mouse_move(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),wParam); } case WM_LBUTTONDOWN: { } } return ::DefWindowProcW(hwnd,message,wParam,lParam); }; void base_window::create_device_independentresources() { // Direct2DFactory の生成 if(!factory){

#if defined(DEBUG) || defined(_DEBUG) D2D1_FACTORY_OPTIONS options; options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION ; THROW_IF_ERR(D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLETHREADED, options, &factory ));

#else THROW_IF_ERR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLETHREADED, &factory));

#endif } if(!writefactory){ THROW_IF_ERR(::DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown>(&writefactory) )); } //wic_imagingfactory.CreateInstance(CLSID_WICImagingFactory); //thunkproc = (WNDPROC)thunk_.getCode(); }; void base_window::register_class ( wchar_t * menu_name, boost::uint32_t style , boost::int32_t cbClsExtra, HICON hIcon , HCURSOR hCursor, HBRUSH hbrBackground , HICON hIconSm ) { wndclass.reset(new sf::window_class_ex(menuname,name,HINST_THISCOMPONENT,thunkproc,style,cbClsExtra,hIcon,hCursor,hbrBackground,hIconSm)); } / デフォルト設定 / void base_window::register_class() { wndclass.reset(new sf::window_classex(0,name,HINST_THISCOMPONENT,thunkproc)); } void base_window::createwindow() { // Create the application window. // // Because the CreateWindow function takes its size in pixels, we // obtain the system DPI and use it to scale the window size. FLOAT dpiX, dpiY; //factory->GetDesktopDpi(&dpiX, &dpiY); // Windowを作成する CreateWindow( name_.cstr(), title.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, static_cast<boost::uint32t>(ceil(width /** dpiX / 96.f/)), static_cast<boost::uint32t>(ceil(height /* dpiY / 96.f/)), NULL, NULL, HINST_THISCOMPONENT, this ); } void base_window::createdevice() { // input.reset(new input(HINSTTHISCOMPONENT,hwnd)); HRESULT hr = SOK; //ウィンドウの現在の幅、高さを求める RECT rc; GetClientRect( hwnd, &rc ); boost::uint32_t width = rc.right - rc.left; boost::uint32_t height = rc.bottom - rc.top; { //wic_imagingfactory.CreateInstance(CLSIDWICImagingFactory); // bitmap = load_bitmap_from_file(rendertarget,wic_imagingfactory,L"myship.png"); } if(!rendertarget) { RECT rc; GetClientRect(hwnd_, &rc); D2D1_SIZE_U size = D2D1::SizeU( rc.right - rc.left, rc.bottom - rc.top ); THROW_IFERR(factory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd_, size,D2D1_PRESENT_OPTIONS_IMMEDIATELY), &rendertarget )); // Create a DC render target //D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( // D2D1_RENDER_TARGET_TYPE_DEFAULT, // D2D1::PixelFormat( // DXGI_FORMAT_B8G8R8A8_UNORM, // D2D1_ALPHA_MODE_IGNORE // ) , 0.0, 0.0, // D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE // ); //THROW_IFERR(factory->CreateDCRenderTarget( // &props, // &rendertarget // )); } } void base_window::discard_device() { / if(rendertarget) { rendertarget.Release(); }/ } void base_window::show(boost::uint32_t show_flag) { //HRESULT hr = S_OK; //BOOL enable; //DwmIsCompositionEnabled (&enable); //if(enable){ // //Create and populate the BlurBehind structre // DWM_BLURBEHIND bb = {0}; // //Enable Blur Behind and Blur Region; // bb.dwFlags = DWM_BBENABLE; // bb.fEnable = true; // bb.hRgnBlur = NULL; // //Enable Blur Behind // hr = DwmEnableBlurBehindWindow(hwnd, &bb); //} ::ShowWindow(hwnd_,show_flag); } void basewindow::update() {::UpdateWindow(hwnd);} base_window::~base_window() { saferelease(factory); safe_release(writefactory); } base_window::base_window(const std::wstring& title,const std::wstring& name,bool fit_todisplay,float width,float height) : title(title),name_(name),fit_todisplay(fit_todisplay), width(width),height(height),thunk(this,basewindow::WndProc),hwnd(0) { thunkproc = (WNDPROC)thunk_.getCode(); //create_device_independent_resources(); } basewindow::operator HWND() { return hwnd; };

```