以下に、自立型メッセージハンドラー関数への呼び出しをC++オブジェクトのメンバー関数に渡す方法を示します。
これは多くのコードですが、通常は再利用可能なモジュールでこれを抽象化し、最も基本的なものはほんの小さなクラスgui::api_level::window_subclasser_t
です。
私はあまりエラー処理を示していませんし、このコードは外部を介したC ++オブジェクトのプログラムによる破棄もサポートしていdelete
ません(これを行うための正しい方法はDestroyWindow
、APIレベルのウィンドウだけで、それを自己破棄まで伝播させることだと思いますC ++オブジェクトですが、前回これを行ってから何年も経っています)。
#undef UNICODE
#define UNICODE
#undef NOMINMAX
#define NOMINMAX
#undef STRICT
#define STRICT
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
//#include <windowsx.h>
#include <commctrl.h> // SetWindowSubclass
#include <assert.h> // assert
#include <stdexcept> // std::exception, std::runtime_error
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
#include <string> // std::string
#ifndef IS_DELETED
# define IS_DELETED = delete // C++11
#endif
namespace cpp {
using namespace std;
bool hopefully( bool condition ) { return condition; }
bool throw_x( string const& s ) { throw runtime_error( s ); }
} // namespace cpp
namespace winapi {
using cpp::hopefully;
using cpp::throw_x;
bool get( MSG& m )
{
int const code = ::GetMessage( &m, 0, 0, 0 );
hopefully( code >= 0 )
|| throw_x( "winapi::get( MSG ): GetMessage failed" );
return !!code;
}
} // namespace winapi
namespace gui {
using cpp::hopefully;
using cpp::throw_x;
namespace api_level
{
class message_handler_t
{
public:
virtual LRESULT window_proc( MSG const& message ) = 0;
};
LRESULT CALLBACK main_window_subclassproc(
HWND const window,
UINT const message_id,
WPARAM const w_param,
LPARAM const l_param,
UINT_PTR const subclass_id,
DWORD_PTR const data
)
{
(void) subclass_id; struct subclass_id;
auto const p_handler = reinterpret_cast< message_handler_t* >( data );
MSG const message = { window, message_id, w_param, l_param, DWORD(), POINT() };
return p_handler->window_proc( message );
}
class window_subclasser_t
{
private:
enum { subclass_id = 1 };
HWND window_handle_;
window_subclasser_t( window_subclasser_t const& ) IS_DELETED;
window_subclasser_t& operator=( window_subclasser_t const& ) IS_DELETED;
public:
HWND handle() const { return window_handle_; }
LRESULT pass_to_superclass( MSG const& m )
{
return ::DefSubclassProc( m.hwnd, m.message, m.wParam, m.lParam );
}
~window_subclasser_t()
{
::RemoveWindowSubclass(
window_handle_,
&main_window_subclassproc,
subclass_id
)
|| throw_x( "gui::api_level::window_subclass_t::<destroy>(): RemoveWindowSubclass failed" );
}
window_subclasser_t(
HWND const api_window,
message_handler_t* cpp_window
)
: window_handle_( api_window )
{
assert( cpp_window != 0 );
::SetWindowSubclass(
window_handle_,
main_window_subclassproc,
subclass_id,
reinterpret_cast<DWORD_PTR>( cpp_window )
)
|| throw_x( "gui::api_level::window_subclass_t::<init>(): SetWindowSubclass failed" );
}
};
ATOM create_main_window_class()
{
WNDCLASS params = {};
params.hbrBackground = reinterpret_cast<HBRUSH>( COLOR_WINDOW + 1 );
params.hCursor = ::LoadCursor( 0, IDC_ARROW );
params.hIcon = ::LoadIcon( 0, IDI_APPLICATION );
params.hInstance = ::GetModuleHandle( nullptr );
params.lpfnWndProc = &::DefWindowProc;
params.lpszClassName = L"MainWindow";
ATOM const result = ::RegisterClass( ¶ms );
hopefully( result != 0 )
|| throw_x( "gui::api_level::create_main_window_class: RegisterClass failed" );
return result;
}
ATOM main_window_class()
{
static ATOM const the_class = create_main_window_class();
return the_class;
}
HWND create_main_window()
{
HWND const window = ::CreateWindow(
MAKEINTATOM( main_window_class() ),
L"My main window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
400, 300,
HWND(), // Parent.
HMENU(), // Menu.
::GetModuleHandle( nullptr ),
nullptr // Param.
);
hopefully( window != 0 )
|| throw_x( "gui::api_level::create_main_window: CreateWindow failed" );
return window;
}
} // api_level
class window_t
: private api_level::message_handler_t
{
private:
window_t( window_t const& ) IS_DELETED;
window_t& operator=( window_t const& ) IS_DELETED;
api_level::window_subclasser_t subclasser_;
virtual LRESULT window_proc( MSG const& m ) override
{
switch( m.message )
{
case WM_DESTROY:
delete this;
::PostQuitMessage( 0 );
return 0;
default:
return subclasser_.pass_to_superclass( m );
}
}
protected:
struct api_object_factory_t
{
virtual HWND create() const
{
return api_level::create_main_window();
}
};
virtual ~window_t() {}
window_t( api_object_factory_t const& factory )
: subclasser_( factory.create(), this )
{}
public:
HWND handle() const { return subclasser_.handle(); }
void show() { ::ShowWindow( handle(), SW_SHOW ); }
};
} // namespage gui
// ---------------------------------------------------------------------------------------
// Usage:
class main_window_t
: public gui::window_t
{
public:
main_window_t()
: gui::window_t( api_object_factory_t() )
{}
};
void cpp_main()
{
auto const main_window = new main_window_t();
main_window->show();
MSG msg;
while( winapi::get( msg ) )
{
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
assert( msg.message == WM_QUIT );
}
#include <iostream>
auto main() -> int
{
using namespace std;
try { cpp_main(); return EXIT_SUCCESS; }
catch( exception const& x ) { wcerr << "!" << x.what() << endl; }
return EXIT_FAILURE;
}
これをVisualC++ 11.0でコンパイルするには、プリプロセッサシンボルIS_DELETED
を何も定義しないでください。