[英]cpp WinAPI WndProc wrapped in the class
i want to create class to instantiate windows on the fly, but since few weeks cant get it to work, and any help would be highly appreciated 我想创建可即时实例化窗口的类,但由于数周无法使其正常工作,因此我们将不胜感激任何帮助
As i was not able to retrieve correct data with GetObjectFromHandle function, i tried to use std::map to store class instances, and in constructor i can access data from the map as expected, but from message loop i can access only garbage while HWND hWnd is correct. 由于我无法使用GetObjectFromHandle函数检索正确的数据,因此我尝试使用std :: map存储类实例,并且在构造函数中我可以按预期从映射访问数据,但是从消息循环中,我只能在HWND时访问垃圾hWnd是正确的。
here is the code 这是代码
.h 。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
and .cpp 和.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 ) );
}
i use visual studio 2005 我使用Visual Studio 2005
You are missing a destructor to destroy your HWND
and clean up any references to it. 您缺少销毁您的HWND
并清理所有引用的析构函数。 An HWND
can be reused after it has been destroyed. HWND
销毁后可以重新使用。 If you do not remove a destroyed HWND from your std::map
, you will end up with stale cWin*
pointers. 如果不从std::map
删除被破坏的HWND,则最终将出现陈旧的cWin*
指针。
For that matter, your std::map
is unnecessary. 因此,您的std::map
是不必要的。 You are relying on GetObjectFromHandle()
returning a valid cWin*
pointer before you access the std::map
, but you say GetObjectFromHandle()
is not working correctly to begin with. 在访问std::map
之前,您依靠的是GetObjectFromHandle()
返回有效的cWin*
指针,但是您说GetObjectFromHandle()
cWin*
无法正常工作。 So just get rid of the std::map
, you don't need it. 因此,只需摆脱std::map
,就不需要它了。
Try something more like this instead: 尝试类似这样的方法:
.h 。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 .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 );
}
}
A class' member function is not a part of a class instance. 类的成员函数不属于类实例。 You should look at class member functions to work like this: 您应该查看类成员函数,使其工作如下:
class Foo {
};
namespace Foo {
void Bar(Foo *this, ...);
};
Of course there can not be a namespace on the same scope as a class with the same name. 当然,不能在名称空间与具有相同名称的类相同的作用域内。 This is just for illustration. 这仅用于说明。 When you define a class member function, the compiler translates this into "a function that is placed the namespace of the class definition and has a first implicit parameter this
that's set to the pointer of the class instance upon which the function is called with the .
or ->
operator. static
class member functions are merely placed into the class namespace but don't have the implicit this
parameter… that's the whole difference. 当您定义类成员函数时,编译器将此转换为“一个函数,该函数位于类定义的名称空间中,并且具有第一个隐式参数, this
参数设置为使用调用该函数的类实例的指针.
或->
运算符, static
类成员函数仅放置在类名称空间中,但没有隐式this
参数……这就是全部。
So in this line 所以在这一行
wcex.lpfnWndProc = (WNDPROC)this->internal_WndProc;
the problem is, that there's no instance member "function" variable internal_WndProc. 问题是,没有实例成员“函数”变量internal_WndProc。 You have instead to use just the function from the class namespace: 相反,您只能使用类名称空间中的函数:
wcex.lpfnWndProc = G::cWin::internal_WndProc;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.