【问题标题】:cpp WinAPI WndProc wrapped in the classcpp WinAPI WndProc 封装在类中
【发布时间】:2015-11-30 23:31:48
【问题描述】:

我想创建类来即时实例化窗口,但由于几周后无法让它工作,任何帮助将不胜感激

由于我无法使用 GetObjectFromHandle 函数检索正确的数据,我尝试使用 std::map 来存储类实例,并且在构造函数中我可以按预期从地图访问数据,但从消息循环中我只能访问垃圾,而 HWND hWnd 是正确的。

这里是代码

.h

#ifndef BASE_WINDOW_H
#define BASE_WINDOW_H

#include "GlobalApp.h"
#include <string>
#include <map>

namespace G{
    class cWin;

    static void SetObjectToHandle( HWND hWnd, LPARAM lParam );
    static cWin *GetObjectFromHandle( HWND hWnd );

    class cWin{
        static LRESULT CALLBACK internal_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    public:
        static std::map<HWND, cWin*> hwndMap;
        LRESULT WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
        cWin();

        int     registerCls();
        HWND    createWnd();
        HWND    hWnd;
    };
}

#endif

和.cpp

#include "stdafx.h"
#include "BaseWindow.h"

HWND G::cWin::createWnd(){
    HWND hWnd;
    hWnd = ::CreateWindowEx(WS_EX_WINDOWEDGE, L"div", NULL,
        WS_CHILD | WS_VISIBLE,
        50, 50, 50, 50,
        G::hWnd, NULL, G::hInst, this );

    ::UpdateWindow( hWnd );

    return hWnd;
}

int G::cWin::registerCls(){
    WNDCLASSEX wcex;

    if ( !::GetClassInfoEx(G::hInst, L"div", &wcex) ){
        wcex.cbSize = sizeof(WNDCLASSEX);

        wcex.style          = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc    = (WNDPROC)this->internal_WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = DLGWINDOWEXTRA;
        wcex.hInstance      = G::hInst;
        wcex.hIcon          = LoadIcon(G::hInst, MAKEINTRESOURCE(IDI_WINAPITWO));
        wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+3);
        wcex.lpszMenuName   = NULL;
        wcex.lpszClassName  = L"div";
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

        if ( ::RegisterClassEx(&wcex) == 0 ){
            G::Console( L"wcex_ERR" );
            return -1;
        }
    }
    return 0;
}

G::cWin::cWin(){
    this->registerCls();

    this->hWnd = this->createWnd();
    G::Console( L"wndCreated", this->hWnd );

    this->hwndMap.insert( std::pair< HWND, G::cWin*>( this->hWnd, this ) );

    G::cWin *pWnd = this->hwndMap[ this->hWnd ];
    G::Console( L"map:", pWnd->hWnd ); //point to correct data
}

LRESULT G::cWin::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
    if ( !this->hwndMap.count( hWnd ) ){
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    G::cWin *pWnd = this->hwndMap[ hWnd ];

    switch (message){
        case WM_LBUTTONDOWN:
            G::Console( L"ButtonDown", pWnd->hWnd ); // not correct, why?
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

std::map<HWND, G::cWin*> G::cWin::hwndMap;


LRESULT CALLBACK G::cWin::internal_WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ){
    if( uMsg == WM_NCCREATE ){
        G::SetObjectToHandle( hWnd, lParam );
    }

    G::cWin *pWnd = G::GetObjectFromHandle( hWnd );

    if( pWnd ){
        return pWnd->WindowProc( hWnd, uMsg, wParam, lParam );
    } else
        return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

void G::SetObjectToHandle( HWND hWnd, LPARAM lParam ){
    LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>( lParam );
    G::cWin *pWnd = reinterpret_cast<G::cWin*>( cs->lpCreateParams );

    SetLastError( 0 );

    if( !SetWindowLongPtr( hWnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>( pWnd ) ) && GetLastError() ){
        G::Console( L"Error" );
    }
}

G::cWin *G::GetObjectFromHandle( HWND hWnd ){
    return reinterpret_cast<G::cWin*>( GetWindowLongPtr( hWnd, GWL_USERDATA ) );
}

我使用 Visual Studio 2005

【问题讨论】:

    标签: c++ winapi


    【解决方案1】:

    您缺少一个析构函数来销毁您的 HWND 并清理对它的任何引用。 HWND 可以在销毁后重新使用。如果您不从 std::map 中删除已损坏的 HWND,您将得到陈旧的 cWin* 指针。

    就此而言,您的std::map 是不必要的。在访问std::map 之前,您依赖GetObjectFromHandle() 返回一个有效的cWin* 指针,但您说GetObjectFromHandle() 一开始就无法正常工作。所以只要去掉std::map,你就不需要它了。

    尝试类似的方法:

    .h

    #ifndef BASE_WINDOW_H
    #define BASE_WINDOW_H
    
    #include "GlobalApp.h"
    #include <string>
    
    namespace G
    {
        class cWin
        {
        private:
            HWND hWnd;
            LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
    
            static LRESULT CALLBACK internal_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    
        public:
            cWin();
            ~cWin();
    
            HWND    getWnd();
            int     registerCls();
            int     createWnd();
        };
    }
    
    #endif
    

    .cpp

    #include "stdafx.h"
    #include "BaseWindow.h"
    
    G::cWin::cWin()
    {
        registerCls();
        createWnd();
        G::Console( L"wndCreated", hWnd );
    }
    
    G::cWin::~cWin()
    {
        if ( hWnd )
        {
            DestroyWindow( hWnd );
        }
    }
    
    HWND G::cWin::getWnd()
    {
        return hWnd;
    }
    
    int G::cWin::createWnd()
    {
        hWnd = ::CreateWindowEx(WS_EX_WINDOWEDGE, L"div", NULL,
            WS_CHILD | WS_VISIBLE,
            50, 50, 50, 50,
            G::hWnd, NULL, G::hInst, this );
    
        if ( !hWnd )
        {
            G::Console( L"hwnd_ERR" );
            return -1;
        }
    
        ::UpdateWindow( hWnd );
    
        return 0;
    }
    
    int G::cWin::registerCls()
    {
        WNDCLASSEX wcex;
    
        if ( !::GetClassInfoEx(G::hInst, L"div", &wcex) )
        {
            wcex.cbSize = sizeof(WNDCLASSEX);
    
            wcex.style          = CS_HREDRAW | CS_VREDRAW;
            wcex.lpfnWndProc    = &internal_WndProc;
            wcex.cbClsExtra     = 0;
            wcex.cbWndExtra     = DLGWINDOWEXTRA;
            wcex.hInstance      = G::hInst;
            wcex.hIcon          = LoadIcon(G::hInst, MAKEINTRESOURCE(IDI_WINAPITWO));
            wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
            wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+3);
            wcex.lpszMenuName   = NULL;
            wcex.lpszClassName  = L"div";
            wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
            if ( !::RegisterClassEx(&wcex) )
            {
                G::Console( L"wcex_ERR" );
                return -1;
            }
        }
    
        return 0;
    }
    
    LRESULT G::cWin::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
            case WM_LBUTTONDOWN:
                G::Console( L"ButtonDown", hWnd );
                break;
    
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
    
            case WM_NCDESTROY:
                this->hWnd = NULL;
                break;
    
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
        }
    
        return 0;
    }
    
    LRESULT CALLBACK G::cWin::internal_WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
    {
        G::cWin *pWnd;
    
        if( uMsg == WM_NCCREATE )
        {
            LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>( lParam );
            pWnd = reinterpret_cast<G::cWin*>( cs->lpCreateParams );
    
            SetLastError( 0 );
            if( !SetWindowLongPtr( hWnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>( pWnd ) ) )
            {
                if( GetLastError() != 0 )
                    G::Console( L"Error" );
            }
        }
        else
        {
            pWnd = reinterpret_cast<G::cWin*>( GetWindowLongPtr( hWnd, GWL_USERDATA ) );
        }
    
        if( pWnd )
        {
            return pWnd->WindowProc( uMsg, wParam, lParam );
        }
        else
        {
            return DefWindowProc( hWnd, uMsg, wParam, lParam );
        }
    }
    

    【讨论】:

    • 析构函数实现给了我一个答案,为什么我要检索垃圾,简单地说,类实例超出范围,回调没有什么可检索的...... :(
    • 那么你需要弄清楚是什么导致你的窗口提前被破坏。登录WM_DESTROY。还有PostQuitMessage() in WM_DESTROY is a bug waiting to happen(在您的情况下,当您添加工具箱等子窗口时会发生这种情况,然后想知道为什么关闭工具箱会退出整个应用程序)。
    【解决方案2】:

    类的成员函数不是类实例的一部分。你应该看看类成员函数是这样工作的:

    class Foo {
    };
    
    namespace Foo {
        void Bar(Foo *this, ...);
    };
    

    当然,命名空间不能与具有相同名称的类在同一范围内。这只是为了说明。当您定义一个类成员函数时,编译器将其转换为“一个放置在类定义的命名空间并具有第一个隐式参数this 的函数,该参数设置为调用该函数的类实例的指针. 或 -&gt; 运算符。static 类成员函数只是放置在类命名空间中,但没有隐含的 this 参数……这就是全部区别。

    所以在这一行

        wcex.lpfnWndProc    = (WNDPROC)this->internal_WndProc;
    

    问题是,没有实例成员“函数”变量 internal_WndProc。您必须只使用类命名空间中的函数:

        wcex.lpfnWndProc    = G::cWin::internal_WndProc;
    

    【讨论】:

    • (WNDPROC) 演员表不是必需的,仅用于隐藏错误。它可以安全地移除。
    • 我都同意,但不幸的是这并不能解决问题,结果相同
    • 你从来没有说过你做了得到了什么。另请参阅this。
    • @IInspectable:是的。把它编辑掉了。
    猜你喜欢
    • 2016-05-16
    • 2011-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多