问题
I have two classes defined as:
class Control
{
private:
std::vector<int> Info;
public:
Control(..);
virtual ~Control();
LRESULT __stdcall SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};
class Button: public Control
{
//...
};
Control::Control(..)
{
SetWindowSubclass(..., SubClass, ...); //Need to pass member function as callback..
}
LRESULT __stdcall Control::SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//Use "Info" data member in here.. thus I cannot have static callback in order to use it.
}
And so I came up with:
class Control
{
private:
std::vector<int> Info;
static LRESULT __stdcall SetCallback(void* ThisPtr) {return static_cast<Control*>(ThisPtr)->SubClass;};
public:
//all the same stuff..
};
Control::Control(..)
{
SetWindowSubclass(..., SetCallback, ...);
}
But the above throws a whole bunch of errors. Is there anyway to either have my static callback access other datamembers OR have my callback non-static? I do not want to have to do something like the following for every instance created (which I've seen as suggestions all over the internet):
Control F;
F.SetCallback(&F::SubClass, &F); //Externally sets member as callback which I don't want.
I'm trying to keep everything in the constructor or the class itself.
回答1:
This is probably the most common question when it comes to Win32 API UI programming. See:
http://msdn.microsoft.com/en-us/library/windows/desktop/ff381400(v=vs.85).aspx
Basically the trick is to call SetWindowLongPtr with GWLP_USERDATA as the first parameter and this as the second. Then in the WindowProc callback use GetWindowLongPtr to get it from the HWND.
回答2:
The following shows how to pass calls to the freestanding message handler function, on to a member function of your C++ object.
It's a lot of code, but normally you would abstract this away in some reusable module, and the most basic stuff is just the little class gui::api_level::window_subclasser_t
.
I haven't shown very much error handling, and neither does this code support programmatic destruction of the C++ object via external delete
(I think the sane way to do this is to just DestroyWindow
the API level window and let that propagate up to self-destruction of the C++ object, but it's many years since last time I did this).
#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;
}
To compile this with Visual C++ 11.0, define the preprocessor symbol IS_DELETED
as nothing.
来源:https://stackoverflow.com/questions/15306694/pass-non-static-member-without-externally-setting-it