0

我有两个类定义为:

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.
}

所以我想出了:

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, ...);
}

但是上面会引发一大堆错误。无论如何,要么让我的静态回调访问其他数据成员,要么让我的回调非静态?我不想为创建的每个实例(我在整个互联网上都视为建议)执行以下操作:

Control F;
F.SetCallback(&F::SubClass, &F); //Externally sets member as callback which I don't want.

我试图将所有内容保留在构造函数或类本身中。

4

2 回答 2

1

这可能是 Win32 API UI 编程中最常见的问题。看:

http://msdn.microsoft.com/en-us/library/windows/desktop/ff381400(v=vs.85).aspx

基本上,诀窍是使用 GWLP_USERDATA 作为第一个参数和this作为第二个参数调用 SetWindowLongPtr 。然后在 WindowProc 回调中使用 GetWindowLongPtr 从 HWND 获取它。

于 2013-03-09T03:34:02.723 回答
1

下面展示了如何将独立消息处理函数的调用传递给 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( &params );
            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;
}

要使用 Visual C++ 11.0 编译它,请将预处理器符号定义IS_DELETED为空。

于 2013-03-09T05:20:16.023 回答