//========================================================================
//TITLE:
// 四论在C++类中实现Windows窗口的创建
//AUTHOR:
// norains
//DATE:
// Tuesday 23-October-2007
//Environment:
// EVC4.0 + Windows CE 5.0 Standard SDK
//========================================================================
之前写过三篇关于在类中封装window窗口的文章,其中的代码无一例外都是只能有一个窗口实例.一般来说,在普通的单界面使用场合中是足够了,也不会有什么很大的问题,但如果是多界面的环境中则将无能为力,比如浏览器的多页面浏览,如果按照之前文章所封装的方法,那么多页面也只能是虚假----因为永远只有一个窗口实例.
C++类中封装窗口的创建一个关键在于,WNDCLASS->lpfnWndProc这个窗口过程处理函数必须不属于任何对象,也就是说只能属于类,亦即必须是static类型.而如果函数为static,会带来一个问题,就是不能直接读取实例对象的变量.之前的文章为了解决这个问题,特意设置了一个static的实例对象指针m_pInstance,然后在WndProc()函数中通过m_pInstance->Function()方式来调用对象的函数和变量(具体可参见之前文章的WndProc()函数代码).由此可见,如果要实现同一个类能创建多个实例对象,就必须要解决这个static WndProc()函数读取实例对象的变量问题.
在本文中,C++类中还是存在一个static的WndProc()函数(以下以示例代码的StaticWndProc()函数替代),不过此时这个函数功能和之前文章的截然不同,而是用来向不同的实例发送窗口消息.要实现这点,我们首先要知道这个消息需要传送给哪个窗口实例,这个就需要一点小技巧.
在窗口创建完毕之后,我们将实例对象指针存储在窗口的GWL_USERDATA地址中:
然后在StaticWndProc通过GetWindowLong()函数获取该实例对象指针,并且使用该指针调用实例对象的WndProc()函数:
OK,就是这么简单,简简单单的两个代码,就完成了静态窗口函数向对象窗口函数的联结,同时也解决了本文创建多个对象窗口的难题.
我们来看看一个简单的封装好的窗口类代码:
/
// SimpleWnd.h: interface for the CSimpleWnd class.
//
/ /
#ifndef SIMPLEWND_H
#define SIMPLEWND_H
class CSimpleWnd
{
public :
BOOL Create(HINSTANCE hInst,HWND hWndParent);
BOOL ShowWindow(BOOL bShow);
CSimpleWnd();
virtual ~ CSimpleWnd();
protected :
BOOL RegisterWnd(HINSTANCE hInst,HWND hWndParent);
LRESULT WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
static LRESULT StaticWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
HWND m_hWnd;
HWND m_hWndParent;
HINSTANCE m_hInst;
};
#endif // #ifndef SIMPLEWND_H
/ /
// SimpleWnd.cpp: implementation of the CSimpleWnd class.
//
/ /
#include " stdafx.h "
#include " SimpleWnd.h "
// =====================================================================
// Macro define
#define WND_NAME TEXT("SimpleWnd_name")
#define WND_CLASS TEXT("SimpleWnd_cls")
// =====================================================================
/ /
// Construction/Destruction
/ /
CSimpleWnd::CSimpleWnd():
m_hWnd(NULL),
m_hWndParent(NULL),
m_hInst(NULL)
{
}
CSimpleWnd:: ~ CSimpleWnd()
{
}
// ----------------------------------------------------------------------
// Description:
// Initialize
// ----------------------------------------------------------------------
BOOL CSimpleWnd::RegisterWnd(HINSTANCE hInst, HWND hWndParent)
{
static BOOL s_bRegistered = FALSE;
WNDCLASS wc;
if (s_bRegistered == FALSE)
{
wc.style = 0 ;
wc.lpfnWndProc = (WNDPROC)CSimpleWnd::StaticWndProc;
wc.cbClsExtra = 0 ;
wc.cbWndExtra = 0 ;
wc.hInstance = m_hInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = WND_CLASS;
if (RegisterClass( & wc))
{
s_bRegistered = TRUE;
}
}
return s_bRegistered;
/*
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = (WNDPROC)CSimpleWnd::StaticWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = m_hInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = WND_CLASS;
if (RegisterClass(&wc) == FALSE)
{
return FALSE;
}
return TRUE;
*/
}
// ----------------------------------------------------------------------
// Description:
// Static WndProc wrapper and actual WndProc
//
// ----------------------------------------------------------------------
LRESULT CSimpleWnd::StaticWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CSimpleWnd * pObject = (CSimpleWnd * )GetWindowLong(hWnd, GWL_USERDATA);
if (pObject)
{
return pObject -> WndProc(hWnd,msg,wParam,lParam);
}
else
{
return DefWindowProc(hWnd,msg,wParam,lParam);
}
}
// ----------------------------------------------------------------------
// Description:
// Actual WndProc
//
// ----------------------------------------------------------------------
LRESULT CSimpleWnd::WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}
// ----------------------------------------------------------------------
// Description:
// Show the window
//
// ----------------------------------------------------------------------
BOOL CSimpleWnd::ShowWindow(BOOL bShow)
{
if (m_hWnd == NULL)
{
return FALSE;
}
if (bShow == TRUE)
{
::ShowWindow(m_hWnd,SW_SHOW);
SetForegroundWindow(m_hWnd);
}
else
{
::ShowWindow(m_hWnd,SW_HIDE);
}
return TRUE;
}
// ----------------------------------------------------------------------
// Description:
// Create the window
//
// ----------------------------------------------------------------------
BOOL CSimpleWnd::Create(HINSTANCE hInst,HWND hWndParent)
{
m_hInst = hInst;
m_hWndParent = hWndParent;
RegisterWnd(m_hInst,m_hWndParent);
m_hWnd = CreateWindowEx( 0 ,
WND_CLASS,
WND_NAME,
WS_VISIBLE,
0 ,
0 ,
800 ,
480 ,
m_hWndParent,
NULL,
m_hInst,
0 );
if (IsWindow(m_hWnd) == FALSE)
{
return FALSE;
}
// If the window is created successfully, store this object so the
// static wrapper can pass calls to the real WndProc.
SetWindowLong(m_hWnd, GWL_USERDATA, (DWORD) this );
return TRUE;
}
来看看调用类实例对象的代码:
OK,就是这么简单创建了两个类窗口实例.虽然两个窗口名相同,但要更改也是一件很简单的事情,不是么?
最后我们来看看在<三论在C++类中实现Windows窗口的创建>(http://blog.csdn.net/norains/archive/2007/01/16/1485063.aspx)一文中窗口的调用方法:
相比之下,是不是本文的窗口调用方式更为简单明了?不用调用GetInstance()获取对象实例,然后再调用Initialize()进行初始化,而仅仅是通过Create()即可完成以上两步的功能.
那么在<三论>中所提到的方式是否就无用武之地了呢?也不尽然.如果在程序代码中,在不同的类都需要调用相同的窗口代码实现特定的功能,并且要保存上一次窗口关闭时的参量,那么采用<三论>中的方法则可以免去频繁地在不同类中传递全局变量的痛苦,同时也使得同步变得更为简便.
//TITLE:
// 四论在C++类中实现Windows窗口的创建
//AUTHOR:
// norains
//DATE:
// Tuesday 23-October-2007
//Environment:
// EVC4.0 + Windows CE 5.0 Standard SDK
//========================================================================
之前写过三篇关于在类中封装window窗口的文章,其中的代码无一例外都是只能有一个窗口实例.一般来说,在普通的单界面使用场合中是足够了,也不会有什么很大的问题,但如果是多界面的环境中则将无能为力,比如浏览器的多页面浏览,如果按照之前文章所封装的方法,那么多页面也只能是虚假----因为永远只有一个窗口实例.
C++类中封装窗口的创建一个关键在于,WNDCLASS->lpfnWndProc这个窗口过程处理函数必须不属于任何对象,也就是说只能属于类,亦即必须是static类型.而如果函数为static,会带来一个问题,就是不能直接读取实例对象的变量.之前的文章为了解决这个问题,特意设置了一个static的实例对象指针m_pInstance,然后在WndProc()函数中通过m_pInstance->Function()方式来调用对象的函数和变量(具体可参见之前文章的WndProc()函数代码).由此可见,如果要实现同一个类能创建多个实例对象,就必须要解决这个static WndProc()函数读取实例对象的变量问题.
在本文中,C++类中还是存在一个static的WndProc()函数(以下以示例代码的StaticWndProc()函数替代),不过此时这个函数功能和之前文章的截然不同,而是用来向不同的实例发送窗口消息.要实现这点,我们首先要知道这个消息需要传送给哪个窗口实例,这个就需要一点小技巧.
在窗口创建完毕之后,我们将实例对象指针存储在窗口的GWL_USERDATA地址中:
SetWindowLong(m_hWnd, GWL_USERDATA, (DWORD)
this
);
然后在StaticWndProc通过GetWindowLong()函数获取该实例对象指针,并且使用该指针调用实例对象的WndProc()函数:
//
获取实例对象指针
CSimpleWnd * pObject = (CSimpleWnd * )GetWindowLong(hWnd, GWL_USERDATA);
// 调用实例对象的窗口处理函数
pObject -> WndProc(hWnd,msg,wParam,lParam);
CSimpleWnd * pObject = (CSimpleWnd * )GetWindowLong(hWnd, GWL_USERDATA);
// 调用实例对象的窗口处理函数
pObject -> WndProc(hWnd,msg,wParam,lParam);
OK,就是这么简单,简简单单的两个代码,就完成了静态窗口函数向对象窗口函数的联结,同时也解决了本文创建多个对象窗口的难题.
我们来看看一个简单的封装好的窗口类代码:
/
// SimpleWnd.h: interface for the CSimpleWnd class.
//
/ /
#ifndef SIMPLEWND_H
#define SIMPLEWND_H
class CSimpleWnd
{
public :
BOOL Create(HINSTANCE hInst,HWND hWndParent);
BOOL ShowWindow(BOOL bShow);
CSimpleWnd();
virtual ~ CSimpleWnd();
protected :
BOOL RegisterWnd(HINSTANCE hInst,HWND hWndParent);
LRESULT WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
static LRESULT StaticWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
HWND m_hWnd;
HWND m_hWndParent;
HINSTANCE m_hInst;
};
#endif // #ifndef SIMPLEWND_H
/ /
// SimpleWnd.cpp: implementation of the CSimpleWnd class.
//
/ /
#include " stdafx.h "
#include " SimpleWnd.h "
// =====================================================================
// Macro define
#define WND_NAME TEXT("SimpleWnd_name")
#define WND_CLASS TEXT("SimpleWnd_cls")
// =====================================================================
/ /
// Construction/Destruction
/ /
CSimpleWnd::CSimpleWnd():
m_hWnd(NULL),
m_hWndParent(NULL),
m_hInst(NULL)
{
}
CSimpleWnd:: ~ CSimpleWnd()
{
}
// ----------------------------------------------------------------------
// Description:
// Initialize
// ----------------------------------------------------------------------
BOOL CSimpleWnd::RegisterWnd(HINSTANCE hInst, HWND hWndParent)
{
static BOOL s_bRegistered = FALSE;
WNDCLASS wc;
if (s_bRegistered == FALSE)
{
wc.style = 0 ;
wc.lpfnWndProc = (WNDPROC)CSimpleWnd::StaticWndProc;
wc.cbClsExtra = 0 ;
wc.cbWndExtra = 0 ;
wc.hInstance = m_hInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = WND_CLASS;
if (RegisterClass( & wc))
{
s_bRegistered = TRUE;
}
}
return s_bRegistered;
/*
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = (WNDPROC)CSimpleWnd::StaticWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = m_hInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = WND_CLASS;
if (RegisterClass(&wc) == FALSE)
{
return FALSE;
}
return TRUE;
*/
}
// ----------------------------------------------------------------------
// Description:
// Static WndProc wrapper and actual WndProc
//
// ----------------------------------------------------------------------
LRESULT CSimpleWnd::StaticWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CSimpleWnd * pObject = (CSimpleWnd * )GetWindowLong(hWnd, GWL_USERDATA);
if (pObject)
{
return pObject -> WndProc(hWnd,msg,wParam,lParam);
}
else
{
return DefWindowProc(hWnd,msg,wParam,lParam);
}
}
// ----------------------------------------------------------------------
// Description:
// Actual WndProc
//
// ----------------------------------------------------------------------
LRESULT CSimpleWnd::WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}
// ----------------------------------------------------------------------
// Description:
// Show the window
//
// ----------------------------------------------------------------------
BOOL CSimpleWnd::ShowWindow(BOOL bShow)
{
if (m_hWnd == NULL)
{
return FALSE;
}
if (bShow == TRUE)
{
::ShowWindow(m_hWnd,SW_SHOW);
SetForegroundWindow(m_hWnd);
}
else
{
::ShowWindow(m_hWnd,SW_HIDE);
}
return TRUE;
}
// ----------------------------------------------------------------------
// Description:
// Create the window
//
// ----------------------------------------------------------------------
BOOL CSimpleWnd::Create(HINSTANCE hInst,HWND hWndParent)
{
m_hInst = hInst;
m_hWndParent = hWndParent;
RegisterWnd(m_hInst,m_hWndParent);
m_hWnd = CreateWindowEx( 0 ,
WND_CLASS,
WND_NAME,
WS_VISIBLE,
0 ,
0 ,
800 ,
480 ,
m_hWndParent,
NULL,
m_hInst,
0 );
if (IsWindow(m_hWnd) == FALSE)
{
return FALSE;
}
// If the window is created successfully, store this object so the
// static wrapper can pass calls to the real WndProc.
SetWindowLong(m_hWnd, GWL_USERDATA, (DWORD) this );
return TRUE;
}
来看看调用类实例对象的代码:
CSimpleWnd sWnd1,sWnd2;
sWnd1.Create(hInstance,NULL);
sWnd1.ShowWindow(TRUE);
sWnd2.Create(hInstance,NULL);
sWnd2.ShowWindow(TRUE);
sWnd1.Create(hInstance,NULL);
sWnd1.ShowWindow(TRUE);
sWnd2.Create(hInstance,NULL);
sWnd2.ShowWindow(TRUE);
OK,就是这么简单创建了两个类窗口实例.虽然两个窗口名相同,但要更改也是一件很简单的事情,不是么?
最后我们来看看在<三论在C++类中实现Windows窗口的创建>(http://blog.csdn.net/norains/archive/2007/01/16/1485063.aspx)一文中窗口的调用方法:
//
声明一个对象指针:
CDrawWnd * pDrawWnd;
// 获取一个对象实例:
pDrawWnd = CDrawWnd::GetInstance();
// 初始化窗口:
if (pDrawWnd -> Initialize(hInstance) == FALSE)
{
return 0x05 ;
}
// 如果窗口初始化成功,我们就可以让它显示了
if (pDrawWnd -> ShowWindow(TRUE) == FALSE)
{
return 0x10 ;
}
CDrawWnd * pDrawWnd;
// 获取一个对象实例:
pDrawWnd = CDrawWnd::GetInstance();
// 初始化窗口:
if (pDrawWnd -> Initialize(hInstance) == FALSE)
{
return 0x05 ;
}
// 如果窗口初始化成功,我们就可以让它显示了
if (pDrawWnd -> ShowWindow(TRUE) == FALSE)
{
return 0x10 ;
}
相比之下,是不是本文的窗口调用方式更为简单明了?不用调用GetInstance()获取对象实例,然后再调用Initialize()进行初始化,而仅仅是通过Create()即可完成以上两步的功能.
那么在<三论>中所提到的方式是否就无用武之地了呢?也不尽然.如果在程序代码中,在不同的类都需要调用相同的窗口代码实现特定的功能,并且要保存上一次窗口关闭时的参量,那么采用<三论>中的方法则可以免去频繁地在不同类中传递全局变量的痛苦,同时也使得同步变得更为简便.