Active Template Library
小规模COM组件的创建,最早被称为:网络组件模版库
早期用MFC开发出来的ActiveX插件代码冗余量很大,而且还必须依赖于MFC运行时库.鉴于此微软提出了ATL技术
应用程序类屏蔽了exe与dll之间的区别:CWinApp::InitInstance()为exe实现了WinMain(),为dll实现了DllMain()
建立一个ATL工程 -> Insert -> Add ATL Componet .idl 文件被(MIDL.exe)编译后生成如下文件: xxx.h xxx_i.c 用于: C++ 开发(#include);其他用于代理占位服务的支持,目前很少用到 (xxx.h, xxx_i.c) + (dlldata.c, xxx_p.c, xxxps.mk) + xxx.tlb , xxxps.def,
demo
// ATL工程的基本包含文件(一般建立一个ATL工程会自动加入以下代码):
#include <atlbase.h> // ATL 工程的基本包含文件
#include <atlcom.h> // ATL 的大部分基本行为(atlimpl.cpp是其基本实现,atlwin.h)
extern CComModule _Module; // VC7或以上版本不需要!
typedef CWinTraits<WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS, WS_EX_APPWINDOW|WS_EX_WINDOWEDGE> CFrameWinTraits;
class CMyWindow : public CWindowImpl <CMyWindow, CWindow, CFrameWinTraits>
{
public:
// My Window Class 是我们这个窗口的类名,如果不写或为NULL则ATL会自动指派一个类名
DECLARE_WND_CLASS(_T("My Window Class"))
// CHIAN_MSG_MAP 负责把任何没有被处理的消息(WM_CLOSE,WM_DESTROY,IDC_ABOUT除外)路由给父窗口: CPaintBkgndBase
// CHIAN_MSG_MAP 会把 CPaintBkgnd<CMyWindow, RGB(0,0,255)> 当成两个参数故此处做了一个类型定义
// 注意 MESSAGE_HANDLER 消息处理函数接收到的两个消息参数(wParam,lParam)都是raw values,必须 unpack 他们(COMMAN,NOTIFY则不必)
// 注意 MESSAGE_HANDLER 消息处理函数接收到的两个消息参数(wParam,lParam)都是raw values,必须 unpack 他们(COMMAN,NOTIFY则不必)
// 在调用消息处理函数之前ATL会先把 bHandled 置为TRUE,当你需要在函数返回后用windows默认的WindowProc再来处理消息需令bHandled=FALSE
typedef CPaintBkgnd<CMyWindow, RGB(0, 0, 255)> CPaintBkgndBase;
BEGIN_MSG_MAP(CMyWindow)
MESSAGE_HANDLER(WM_CREAT, OnCreate)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
CHAIN_MSG_MAP(CPaintBkgndBase)
END_MSG_MAP()
// 创建菜单资源
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
// 在VC7 中,第一个参数是 _AtlBaseModule
// 类似于 MFC: AfxGetResourceHandle().
HMENU hmenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MENU1));
SetMenu(hmenu);
return 0;
}
LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
// ATL 的 DoModal() 是有参数的(指明父窗口).如果为空,则为当前活动窗口
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;
}
};
CComModule _Module;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR szCmdLine, int nCmdShow) {
// 如果这个COM组件是个服务器,则 CComModule::Init() 的第一个参数就不能为 NULL
// 如果是 VC7 或以上版本则无需调用这里的 Init() & Term()
_Module.Init(NULL, hInst);
// Create & show our main window(ATL在内部使用汇编语言将"窗口句柄和窗口类对象"巧妙地联系起来)
CMyWindow wndMain;
if (NULL == wndMain.Create(NULL, CWindow::rcDefault, _T("My First ATL Window")))
return 1;
wndMain.ShowWindow(nCmdShow);
wndMain.UpdateWindow();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
_Module.Term();
return msg.wParam;
}
工程 Build 选项
Release MinSize :该选项定义了 _ATL_DLL,_ATL_MIN_CRT
Release MinDependencies :该选项定义了 _ATL_STATIC_REGISTRY,模块将不必再发布 ATL Registry
最小依赖”,表示编译器会把 ATL 中必须使用的一些函数静态连接到目标程序中
要使用这一特性,删除预定义宏:_ATL_DLL(其实就是静态连接到ATL)
如果在 ATL 组件程序中调用了 CRT 的运行时刻库函数,比如开平方sqrt(),那么编译的时候可能会报错
此时需要删除预定义宏: _ATL_MIN_CRT
组件的注册
编译成功后,IDE会自动为我们注册; 也可以使用“Tools\Register Control”来注册
当我们写一个具有 COM 功能的 EXE 程序时,注册的方法就是运行一次这个程序
当我们需要使用第三方提供的组件程序时,可以命令行来注册(运行:“regsvr32.exe 文件名”)
新建 ATL 工程时,不要选择支持MFC,如果后续想添加MFC的支持做如下修改:
- 于 stdatx.h 中 atlbase.h 之后添加 “afxwin.h afxext.h afxdisp.h”
- Project Settings - General - Microsoft Foundation Classes: Using MFC in….
- DLL Entry Point : DllMain() 函数删除
- 在上面删除的地方,添加基于 CWinApp 的应用程序类,具体略
_ATL_MIN_CRT
ATL向导生成的ATL工程,所有的Release版本都预定义了 _ATL_MIN_CRT 宏,Debug版本倒是没有.
如果在编译Release版本时出现: libcmt.lib 未解析符号_main 等错误,则请删除此宏的定义.