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的支持做如下修改:

  1. 于 stdatx.h 中 atlbase.h 之后添加 “afxwin.h afxext.h afxdisp.h”
  2. Project Settings - General - Microsoft Foundation Classes: Using MFC in….
  3. DLL Entry Point : DllMain() 函数删除
  4. 在上面删除的地方,添加基于 CWinApp 的应用程序类,具体略

_ATL_MIN_CRT

ATL向导生成的ATL工程,所有的Release版本都预定义了 _ATL_MIN_CRT 宏,Debug版本倒是没有.

如果在编译Release版本时出现: libcmt.lib 未解析符号_main 等错误,则请删除此宏的定义.