重温WIN32API------最简单的Windows窗⼝封装类
1 开发语⾔抉择
1.1 关于开发Win32 程序的语⾔选择 C还是C++
在决定抛弃MFC,⽽使⽤纯Win32 API 开发Window桌⾯程序之后,还存在⼀个语⾔的选择,这就是是否使⽤C++。C++作为C的超集,能实现所有C能实现的功能。其实反之亦然,C本⾝也能完成C++超出的那部分功能,只是可能需要更多⾏的代码。就本⼈理解⽽⾔,
对于巨⼤型项⽬,还是使⽤纯C来架构更加稳妥;
对于中⼩型项⽬来说,C++可能更⽅便快捷。由于⽬前做的是中⼩项⽬,所以决定把C++作为主要开发语⾔。
1.2 关于C++特性集合的选择
在决定使⽤C++之后,还有⼀个⾄关重要的抉择,那就是C++特性集合的选择。C++实在是太复杂了,除了⽀持它的⽼祖先C的所有开发模式,还⽀持基于对象开发(OB)、⾯向对象开发(OO)、模板技术。可以说,C++是个真正全能型语⾔,这同时也造成了C++的⾼度复杂性。使⽤不同的开发模式,就
相当于使⽤不同的编程语⾔。就本⼈⽽⾔,对C++的模板编程也根本没有任何经验。综合过去的经验教训和本⼈对C++的掌握程度,决定:
使⽤基于对象和⾯向对象两种开发模式,如果⼀个功能两种都可以实现,则优先选择基于对象。倾向于OB的技术观点来⾃对苹果Object-C开发经验。
尽量避免多继承,此观点来⾃Java和开发经验。
数据结构和容器,使⽤C++标准模板库(STL),模板编程本⾝复杂,但是使⽤STL却⾮常容易。
2 Windows窗⼝对象的封装类
对Windows桌⾯程序⽽⾔,Window和Message的概念是核⼼。⾸先需要封装的就是窗⼝,例如MFC就是⽤CWnd类封装了窗⼝对象。我们当初抛弃MFC的原因,就是因为它太复杂不容易理解,所以对基本窗⼝对象的封装⼀定要做到最简单化。
switch函数用法举例2.1 封装原则
⾸要的原则就是“简单”。能⽤⼀个Win32API直接实现的功能,绝不进⾏⼆次包装,如移动窗⼝可以使⽤ MoveWindow()⼀个函数实现,类中就不要出现同样功能的MoveWindow()函数。MFC⾥有很多
这种重复的功能,其实只是可以少写⼀个hwnd参数⽽已,却多加了⼀层调⽤。我就是要让HWND句柄到处出现,绝不对其隐藏,因为这个概念对于Windows来说太重要了,开发者使⽤任何封装类都不应该对其视⽽不见。
其次,同样功能多种技术可以实现时,优先选择容易理解的技术,“可理解性”⽐“运⾏效率”更重要。
2.2 源码
头⽂件 XqWindow.h
#pragma once
#include <vector>
class XqWindow
{
public:
XqWindow(HINSTANCE hInst);
~XqWindow();
private:
HWND hWnd;    // 对外只读,确保安全
HINSTANCE hInstance;
public:
// 返回窗⼝对象句柄
HWND GetHandle();
// 消息处理。需要后续默认处理则需要返回0;停⽌该消息后续处理,则返回1
virtual int HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); private:
// 原始窗⼝过程
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); private:
// 已注册过的类集合
static std::vector<void*> registeredClassArray;
public:
// 创建窗⼝
void Create();
};
实现⽂件 XqWindow.cpp
#include "stdafx.h"
#include "XqWindow.h"
std::vector<void*> XqWindow::registeredClassArray;
// 创建窗⼝
void XqWindow::Create()
{
wchar_t szClassName[32];
wchar_t szTitle[128];
void* _vPtr = *((void**)this);
::wsprintf(szClassName, L"%p", _vPtr);
std::vector<void*>::iterator it;
for (it = registeredClassArray.begin(); it != d(); it++)  // 判断对象的类是否注册过
{
if ((*it) == _vPtr)
break;
}
if (it == d())                // 如果没注册过,则进⾏注册
{
//注册窗⼝类
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = XqWindow::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = this->hInstance;
wcex.hIcon = NULL;
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szClassName;
wcex.hIconSm = NULL;
if (0 != ::RegisterClassEx(&wcex))    // 把注册成功的类加⼊链表
{
registeredClassArray.push_back(_vPtr);
}
}
// 创建窗⼝
if (this->hWnd == NULL)
{
::wsprintf(szTitle, L"窗⼝类名(C++类虚表指针):%p", _vPtr);
HWND hwnd = ::CreateWindow(szClassName,
szTitle,
WS_OVERLAPPEDWINDOW,
0, 0, 800, 600,
NULL,
NULL,
hInstance,
(LPVOID)this
);
if (hwnd == NULL)
{
this->hWnd = NULL;
wchar_t msg[100];
::wsprintf(msg, L"CreateWindow()失败:%ld", ::GetLastError());
::MessageBox(NULL, msg, L"错误", MB_OK);
return;
}
}
}
XqWindow::XqWindow(HINSTANCE hInst)
{
this->hWnd = NULL;
this->hInstance = hInst;
}
XqWindow::~XqWindow()
{
if ( this->hWnd!=NULL && ::IsWindow(this->hWnd) ) // C++对象被销毁之前,销毁窗⼝对象
{
::DestroyWindow(this->hWnd);  // Tell system to destroy hWnd and Send WM_DESTROY to wndproc
}
}
HWND XqWindow::GetHandle()
{
return this->hWnd;
}
// 消息处理。需要后续默认处理则需要返回0;停⽌该消息后续处理,则返回1
int XqWindow::HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return 0;
}
// 原始窗⼝过程
LRESULT CALLBACK XqWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
XqWindow* pObj = NULL;
if (message == WM_CREATE) // 在此消息收到时,把窗⼝对象句柄赋给C++对象成员,同时把C++对象地址赋给窗⼝对象成员 {
pObj = (XqWindow*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
pObj->hWnd = hWnd;                                          // 在此处获取HWND,此时CreateWindow()尚未返回。
:
:SetWindowLong(hWnd, GWL_USERDATA, (LONG)pObj);            // 通过USERDATA把HWND和C++对象关联起来
}
pObj = (XqWindow*)::GetWindowLong(hWnd, GWL_USERDATA);
switch (message)
{
case WM_CREATE:
pObj->HandleMessage(hWnd, message, wParam, lParam);
break;
case WM_DESTROY:
if (pObj != NULL)  // 此时,窗⼝对象已经销毁,通过设置hWnd=NULL,来通知C++对象
{
pObj->hWnd = NULL;
}
break;
default:
pObj = (XqWindow*)::GetWindowLong(hWnd, GWL_USERDATA);
if (pObj != NULL)
{
if (pObj->HandleMessage(hWnd, message, wParam, lParam) == 0) // 调⽤⼦类的消息处理虚函数
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
else
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
}
return 0;
}
2.3 使⽤举例
基本⽤法为,创建⼀个TestWindow类,继承⾃XqWindow,然后重新虚函数 HandleMessage()。所有
业务处理代码都要在HandleMessage()⾥调⽤,由于该函数是成员函数,所有⾥⾯可以直接使⽤this来引⽤TestWindow类对象的成员。⼀个例⼦代码如下:
TestWindow.h
#pragma once
#include "XqWindow.h"
class TestWindow :
public XqWindow
{
public:
TestWindow(HINSTANCE);
~TestWindow();
protected:
int HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
private:
// 业务数据部分
int rectWidth;
int rectHeight;
};
TestWindow.cpp
#include "stdafx.h"
#include "TestWindow.h"
TestWindow::TestWindow(HINSTANCE hInst) :XqWindow(hInst)
{
rectWidth = 300;
rectHeight = 200;
}
TestWindow::~TestWindow()
{
}
int TestWindow::HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::Rectangle(hdc, 0, 0, this->rectWidth, this->rectHeight);
::EndPaint(hWnd, &ps);
return 1;
default:
break;
}
return 0;
}
调⽤部分:
pTest = new TestWindow(theApp.m_hInstance);
pTest->Create();
::ShowWindow(pTest->GetHandle(), SW_SHOW);
::UpdateWindow(pTest->GetHandle());
运⾏效果:
2.4 技术要点
这个XqWindow类对窗⼝对象做了最⼩的封装,主要实现了消息处理函数和C++对象的关联。内存布局如下:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。