VC++窗口类、窗口类对象与窗口

目录

窗口类、窗口类对象与窗口三者之间关系

CWnd类的头文件cwnd.h:

CWnd 类的源文件(cwnd.cpp)

WinMain函数(winmain.cpp):

接上:VC++文档/视类结构 InitInstance函数 帮助对话框类

接下:VC++在窗口中显示按钮 CButton::Create Button Styles Window Styles


窗口类、窗口类对象与窗口三者之间关系

模拟CWnd类的封装过程。在解决方案ch04下添加一个新的空项目,项目名称为:WinMain,在项目创建完成后,将WinMain项目设为启动项目。接下来在WinMain项目中添加一个名为CWnd的类。在该类中定义四个函数:创建窗口函数(CreateEx)、显示窗口函数(ShowWindow)、更新窗口函数(UpdateWindow)和销毁窗口函数(DestroyWindow),并定义一个成员变量(m_hWnd)。CWnd类的头文件(cwnd.h)代码如下所示。

CWnd类的头文件cwnd.h:

#pragma once

#include <Windows.h>
class CWnd
{
public:
	CWnd();
	~CWnd();
public:
	BOOL CreateEx(DWORD dwExStyle,      // extended window style
		LPCTSTR lpClassName,  // registered class name
		LPCTSTR lpWindowName, // window name
		DWORD dwStyle,        // window style
		int x,                // horizontal position of window
		int y,                // vertical position of window
		int nWidth,           // window width
		int nHeight,          // window height
		HWND hWndParent,      // handle to parent or owner window
		HMENU hMenu,          // menu handle or child identifier
		HINSTANCE hInstance,  // handle to application instance
		LPVOID lpParam);        // window-creation data
	BOOL ShowWindow(int nCmdShow);
	BOOL UpdateWindow();
	BOOL DestroyWindow();
public:
	HWND m_hWnd;

};

小技巧: 这些函数的参数可以参照MSDN中相应MFC函数的定义,然后直接复制这些参数即可。

提示: 因为SDK函数数量很多,所以程序员记忆负担很重。MFC中使用的大部分函数名与相应的 SDK 函数名相同,这样做的目的就是为了方便程序员减轻记忆负担。程序员只需要记住两者中的一个就可以了。

接下来在 CWnd 类的源文件(cwnd.cpp)中完成这三个函数的定义,代码如下所示。

CWnd 类的源文件(cwnd.cpp)

#include "cwnd.h"

CWnd::CWnd()
{
	m_hWnd = NULL;
}

CWnd::~CWnd()
{
	DestroyWindow();
}

BOOL CWnd::CreateEx(DWORD dwExStyle,      // extended window style
	LPCTSTR lpClassName,  // registered class name
	LPCTSTR lpWindowName, // window name
	DWORD dwStyle,        // window style
	int x,                // horizontal position of window
	int y,                // vertical position of window
	int nWidth,           // window width
	int nHeight,          // window height
	HWND hWndParent,      // handle to parent or owner window
	HMENU hMenu,          // menu handle or child identifier
	HINSTANCE hInstance,  // handle to application instance
	LPVOID lpParam)        // window-creation data
{
	m_hWnd = ::CreateWindowEx(dwExStyle, lpClassName, lpWindowName, 
		dwStyle, x, y,nWidth, nHeight, hWndParent, hMenu, 
		hInstance,lpParam);
	if (m_hWnd != NULL)
		return TRUE;
	else
		return FALSE;
}

BOOL CWnd::ShowWindow(int nCmdShow)
{
	return ::ShowWindow(m_hWnd, nCmdShow);
}

BOOL CWnd::UpdateWindow()
{
	return ::UpdateWindow(m_hWnd);
}

BOOL CWnd::DestroyWindow()
{
	BOOL bResult = FALSE;
	
	if (m_hWnd != NULL)
	{
		bResult = ::DestroyWindow(m_hWnd);
		m_hWnd = NULL;
	}
	return bResult;
}

我们定义的 CWnd 类的 CreateEx 函数需要完成创建窗口的工作,这可以利用Win32提供的SDK函数:CreateWindowEx函数实现。该函数返回一个句柄,标识它所创建的窗口。这里,我们就可以利用已定义的CWnd类的成员变量m_hWnd来保存这个窗口句柄。因为我们定义的 CreateEx 函数返回值是一个BOOL 型,所以应该判断一下这个窗口句柄。根据其值是否为空来决定函数是返回TRUE值,还是FALSE值。读者应注意的是,在实际开发时,应该初始化m_hWnd变量,这可以在构造函数中实现,给它赋一个初值NULL。这里只是为了演示CWnd类是如何与窗口关联起来的,因此就不进行初始化工作了。

接下来定义ShowWindow函数的实现。同样,需要调用SDK函数,即ShowWindow来完成窗口的显示。为了区分这两个同名函数,在调用这个 SDK 函数时,前面加上作用域标识符(即::)。这种以“::”开始的表示方法表明该函数是一个全局函数,这里表示调用的 ShowWindow 函数是 SDK 函数。因为CreateEx 函数已经获取了窗口句柄并保存到m_hWnd成员变量中,所以ShowWindow函数可以直接把这个句柄变量作为参数来使用。

提示:在定义自己的成员函数时,如果调用的API函数名与自己的函数名不同,那么该 API 函数名前可以加也可以不加“::”符号,编译器会自动识别API函数。但是如果当前定义的成员函数与内部调用的API函数名相同,那么后者前面必须加“::”符号,否则程序在编译或运行时就会出错。

自己定义的 UpdateWindow 函数的实现比较简单,直接调用 SDK 函数:UpdateWindow完成更新窗口的工作。从上所示代码可知,定义的 CWnd 类的后两个函数(ShowWindow 和UpdateWindow)内部都需要一个窗口句柄,即需要知道对哪个窗口进行操作。现在就实现了一个窗口类:CWnd。但已知道如果要以类的方式来完成窗口的创建、显示和更新操作,那么首先还需要编写一个WinMain函数。并不需要记忆这个函数的写法,只要机器上有MSDN就可以了,在MSDN中找到该函数的帮助文档,直接复制其定义即可。在项目中添加一个源文件,名字为winmain.cpp,然后编写WinMain函数。这里,我们只是想讲解在这个函数内部所做的工作,并不是真正的实现,因此只是写出其主要的代码,如例下所示。

WinMain函数(winmain.cpp):

#include "cwnd.h"

int WINAPI WinMain(
	HINSTANCE hInstance,      // handle to current instance
	HINSTANCE hPrevInstance,  // handle to previous instance
	LPSTR lpCmdLine,          // command line
	int nCmdShow              // show state
)
{
	//首先是设计窗口类,即定义一个WNDCLASS,并为相应字段赋值。
	WNDCLASS wndcls;
	wndcls.cbClsExtra = 0;
	wndcls.cbWndExtra = 0;
	......
		//注册窗口类
		RegisterClass(&wndcls);

	//创建窗口
	CWnd wnd;
	wnd.CreateEx(...);

	//显示窗口
	wnd.ShowWindow(SW_SHOWNORMAL);

	//更新窗口
	wnd.UpdateWindow();
	//接下来就是消息循环,此处省略
	......
	return 0;
}

回想一下VC++关于WinMain程序的学习笔记(1)利用SDK编程时为创建窗口、显示窗口和更新窗口所编写的代码(如例下所示),并比较上面和下面这两段代码的区别。

    HWND hwnd;
    hwnd = CreateWindowEx();
	::ShowWindow(hwnd, SW_SHOWNORMAL);
	::UpdateWindow(hwnd);

可以发现,SDK 程序中多了一个 HWND 类型的变量 hwnd。该变量用来保存由CreateWindowEx 函数创建的窗口句柄,并将其作为参数传递给随后的显示窗口操作(ShowWindow 函数)和更新窗口操作(UpdateWindow 函数)。而我们自定义的实现代码中,CWnd类定义了一个HWND类型的成员变量:m_hWnd,用于保存这个窗口句柄。首先CWnd类的CreateEx函数创建窗口,并将该窗口句柄保存到这个成员变量,接着在调用CWnd类的ShowWindow函数显示窗口时,就不需要再传递这个句柄了,因为它已经是成员变量,该函数可以直接使用它。CWnd类的UpdateWindow函数也是一样的道理。

许多程序员在进行MFC程序开发时,容易混淆一点:认为这里的CWnd类型的wnd这个C++对象所代表的就是一个窗口。因为在实践中,他们看到的现象是:当C++窗口类对象被销毁时,相应的窗口也就没了。有时正好巧合,当窗口被销毁时,C++窗口类对象的生命周期也到了,从而也被销毁了。正因为如此,许多程序员感觉C++窗口类对象就是窗口,窗口就是这个C++窗口类对象。事实并非如此。可以想像一下,如果我们关闭了一个窗口,这个窗口就被销毁了,那么该窗口对应的C++窗口类对象真的销毁了吗?当然没有。当一个窗口销毁时,它会调用CWnd类的DestroyWindow函数,该函数销毁窗口后,将CWnd成员变量m_hWnd设为NULL。

C++窗口类对象的生命周期和窗口的生命周期不是一致的。当一个窗口被销毁时,与C++窗口类对象没有关系,它们之间的纽带仅仅在于这个 C++窗口类内部的成员变量:m_hWnd,该变量保存了与这个C++窗口类对象相关的那个窗口的句柄。当我们设计的这个 C++窗口类对象被销毁的时候,与之相关的窗口也是应该被销毁的,因为它们之间的纽带(m_hWnd)已经断了。另外,窗口也是一种资源,它也占据内存。这样,在C++窗口类对象析构时,也需要回收相关的窗口资源,即销毁这个窗口。

因此,一定要注意:C++窗口类对象与窗口并不是一回事,它们之间唯一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄。当窗口被销毁时,与之对应的C++窗口类对象被销毁与否,要看其生命周期是否结束。但当C++窗口类对象被销毁时,与之相关的窗口也将被销毁。在我们定义的这个WinMain程序中,当程序运行到WinMain函数的右花括号(})时,该函数内部定义的Wnd窗口类对象的生命周期也就结束了

这是我们自已定义的CWnd类,那么MFC提供的CWnd类是不是这样实现的呢?在 MSDN 中查看 MFC 提供的 CWnd 类,将会发现该类确实定义了一个数据成员:m_hwnd,用来保存与之相关的窗口的句柄。因为MFC中所有的窗口类都是由CWnd类派生的,于是,所有的窗口类(包括子类)内部都有这样的一个成员用来保存与之相关的窗口句柄。所以,不能认为我们前面创建的 MFC 程序 Test 中的 CMainFrame 类和CTestView类的对象就是一个窗口。

接上:VC++文档/视类结构 InitInstance函数 帮助对话框类

接下:VC++在窗口中显示按钮 CButton::Create Button Styles Window Styles

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米酒馆

鼓励鼓励,鼓励很重要啦~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值