一个WTL程序包含一个CComModule类型的全局变量_Module,这和MFC的程序都有一个CWinApp类型的全局变量theApp有些类似,唯一不同的是在ATL中这个变量必须命名为_Module。
下面是代码的开始部分:
#define STRICT
#define VC_EXTRALEAN
//#define IDD_DIALOG1 1001
#include <atlbase.h> // 基本的ATL类
CComModule _Module; // 全局_Module
#include <atlwin.h> // ATL窗口类
#include "resource.h"
atlbase.h已经包含最基本的Window编程的头文件,所以我们不需要在包含windows.h,tchar.h之类的头文件。在代码中声明并且定义了_Module变量:
CComModule _Module;
CComModule含有程序的初始化和关闭函数,需要在WinMain()中显示的调用,让我们从这里开始:
CComModule _Module;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,
LPSTR szCmdLine, int nCmdShow)
{
_Module.Init(NULL, hInst);
_Module.Term();
}
Init()的第一个参数只有COM的服务程序才有用,由于我们的EXE不含有COM对象,我们只需将NULL传递给Init()就行了。ATL不提供自己的WinMain()和类似MFC的消息泵,所以我们需要创建CMyWindow对象并添加消息泵才能使我们的程序运行。































上面的代码唯一需要说明的是CWindow::rcDefault,这是CWindow中的成员(静态数据成员),数据类型是RECT。和调用CreateWindow() API时使用CW_USEDEFAULT指定窗口的宽度和高度一样,ATL使用rcDefault作为窗口的最初大小。
在ATL代码内部,ATL使用了一些类似汇编语言的魔法将主窗口的句柄与相应的CMyWindow对象联系起来,在外部看来就是可以毫无问题的在线程之间传递CWindow对象,而MFC的CWnd却不能这样作。
这就是我们的窗口:
我们将添加一个About菜单并显示一个对话框,主要是为它增加一些情趣。
ATL中的对话框
我们前面提到过,ATL有两个对话框类,我们的About对话框使用CDialogImpl。生成一个新对话框和生成一个主窗口几乎一样,只有两点不同:
1. 窗口的基类是CDialogImpl而不是CWindowImpl。
2. 你需要定义名称为IDD的公有成员用来保存对话框资源的ID。
现在开始为About对话框定义一个新类:
class CAboutDlg : public CDialogImpl<CAboutDlg>
{
public:
enum { IDD = IDD_ABOUT };
BEGIN_MSG_MAP(CAboutDlg)
END_MSG_MAP()
};
ATL没有在内部实现对“OK”和“Cancel”两个按钮的响应处理,所以我们需要自己添加这些代码,如果用户用鼠标点击标题栏的关闭按钮,WM_CLOSE的响应函数就会被调用。我们还需要处理WM_INITDIALOG消息,这样我们就能够在对话框出现时正确的设置键盘焦点,下面是完整的类定义和消息响应函数。
class CAboutDlg : public CDialogImpl<CAboutDlg>
{
public:
enum { IDD = IDD_ABOUT };
BEGIN_MSG_MAP(CAboutDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
COMMAND_ID_HANDLER(ID_OK, OnOKCancel)
COMMAND_ID_HANDLER(ID_CANCEL, OnOKCancel)
END_MSG_MAP()
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CenterWindow();
return TRUE; // let the system set the focus
}
LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
EndDialog(IDCANCEL);
return 0;
}
LRESULT OnOKCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
EndDialog(wID);
return 0;
}
};
我使用一个消息响应函数同时处理“OK”和“Cancel”两个按钮的WM_COMMAND消息,因为命令响应函数的wID参数就已经指明了消息是来自“OK”按钮还是来自“Cancel”按钮。
显示对话框的方法与MFC相似,创建一个新对话框类的实例,然后调用DoModal()方法。现在我们返回主窗口,添加一个带有File菜单项的菜单用来显示我们的对话框,这需要再添加两个消息响应函数,一个是响应WM_CREATE,另一个是响应菜单的IDC_ABOUT命令。
class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
{
public:
DECLARE_WND_CLASS(_T("Demo_WTL_MainForm"))
BEGIN_MSG_MAP(CMyWindow)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
COMMAND_ID_HANDLER(ID_FILE, OnAbout)
END_MSG_MAP()
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HMENU hmenu = LoadMenu ( _Module.GetResourceInstance(),
MAKEINTRESOURCE(IDR_MENU1) );
SetMenu ( hmenu );
return 0;
}
LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
CAboutDlg dlg;
dlg.DoModal();
return 0;
}
LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
DestroyWindow();
return 0;
}
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
PostQuitMessage(0);
return 0;
}
};
在指定对话框的父窗口的方式上有些不同,MFC是通过构造函数将父窗口的指针传递给对话框而在ATL中是将父窗口的指针作为DoModal()方法的第一个参数传递给对话框的,如果象上面的代码一样没有指定父窗口,ATL会使用GetActiveWindow()得到的窗口(也就是我们的主框架窗口)作为对话框的父窗口。
对LoadMenu()方法的调用展示了CComModule的另一个方法-GetResourceInstance(),它返回你的EXE的HINSTANCE实例,和MFC的AfxGetResourceHandle()方法相似。(当然还有CComModule::GetModuleInstance(),它相当于MFC的AfxGetInstanceHandle()。)
这就是主窗口和对话框的显示效果: