0

次のように定義された2つのクラスがあります。

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

以下に、自立型メッセージハンドラー関数への呼び出しを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;
}

これをVisualC++ 11.0でコンパイルするには、プリプロセッサシンボルIS_DELETEDを何も定義しないでください。

于 2013-03-09T05:20:16.023 に答える
1

これは、Win32APIUIプログラミングに関しておそらく最も一般的な質問です。見る:

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

基本的に、トリックは、最初のパラメーターとしてGWLP_USERDATAを使用し、2番目のパラメーターとしてこれを使用してSetWindowLongPtrを呼び出すことです。次に、WindowProcコールバックで、GetWindowLongPtrを使用してHWNDから取得します。

于 2013-03-09T03:34:02.723 に答える