`
yangyou230
  • 浏览: 1649166 次
文章分类
社区版块
存档分类

ATL7窗口类剖析

 
阅读更多

目录:

ATL是微软继MFC之后提供的一套C++模板类库,小巧、精妙、效率极高。它的主要作用是为我们编写COM/DOM/COM+程序提供了丰富的支持。但是ATL只能写COM么?我以前只是MFC程序员的时候,一直有此误解。但其实ATL提供了很多类用来帮助编写WIN32窗口程序,可能没有MFC使用的广泛和方便(当然啦,因为ATL本来难度就较一般的C++类库大)。用ATL编写WIN32窗口程序有什么好处?小巧、效率这些好处之外,还有一个我认为非常大的好处,写一个EXE形式的COM服务程序,该程序拥有自己的窗口可以和用户交互。你想象一下,一个友好的窗口程序,同时暴露了一些COM接口使得可以和其他程序跨进程通信,是不是非常的便利呢?
使用ATL编写WIN32窗口应用程序你具备以下基础知识,包括WIN32SDK编程能力、C++模板技术、COM编程的能力。要求很高啊,正因为这样,才萌发了写这篇文章的念头。
第一章 HWND和CWindow
HWND是WINDOWS窗口的灵魂,每个窗口都对应一个HWND变量,称为窗口句柄。
我们可以通过HWND向窗口发送消息,让窗口做一些我们想要的动作或者获取窗口的某些信息(比如设置/窗口标题)。
CWindow类保存了窗口句柄,并且包装了一些常用的基于窗口句柄的对窗口的操作。CWindow类定义在atlwin.h文件中。CWindow类提供了很多成员变量和函数,有几个比较重要的:
HWND m_hWnd;//保存了窗口句柄
static RECT rcDefault;//静态变量,保存了默认的窗口的初始位置和大小
_declspec(selectany) RECT CWindow::rcDefault = { CW_USEDEFAULT, CW_USEDEFAULT, 0, 0 };
Create成员函数:
HWND Create(LPCTSTR lpstrWndClass, HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,DWORD dwStyle = 0, DWORD dwExStyle = 0,
_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) throw()
{
ATLASSERT(m_hWnd == NULL);
if(rect.m_lpRect == NULL)
rect.m_lpRect = &rcDefault;
m_hWnd = ::CreateWindowEx(dwExStyle, lpstrWndClass, szWindowName,
dwStyle, rect.m_lpRect->left, rect.m_lpRect->top, rect.m_lpRect->right - rect.m_lpRect->left,
rect.m_lpRect->bottom - rect.m_lpRect->top, hWndParent, MenuOrID.m_hMenu,
_AtlBaseModule.GetModuleInstance(), lpCreateParam);
return m_hWnd;
}
Creat函数第一步,检测窗口是否已经拥有句柄,然后判断;第二步,检测rect参数的变量m_plpRect是否为NULL,rect类型为
class _U_RECT
{
public:
_U_RECT(LPRECT lpRect) : m_lpRect(lpRect)
{ }
_U_RECT(RECT& rc) : m_lpRect(&rc)
{ }
LPRECT m_lpRect;
};
我们可以直接传递一个RECT变量的指针,RECT变量的指针会被用作构造函数的参数创建一个临时的_U_RECT变量,作为参数传递给Create函数。
第三步调用CreateWindowEx函数。这是一个WIN32函数。可以指定扩展窗口风格、已注册窗口类名称、窗口标题、窗口风格、窗口位置矩形、父窗口句柄、菜单资源ID、
进程实例和创建窗口时可以指定的创建参数。
注意,这里的进程实例句柄来自于_AtlBaseModule.GetModuleInstance(),_AtlBaseModule变量声明于atlcore.h文件中:
extern CAtlBaseModule _AtlBaseModule;
CAtlBaseModule的声明也在atlcore.h文件中
class CAtlBaseModule : public _ATL_BASE_MODULE
{
public :
static bool m_bInitFailed;
CAtlBaseModule() throw();
~CAtlBaseModule() throw ();
HINSTANCE GetModuleInstance() throw()
{
return m_hInst;
}
HINSTANCE GetResourceInstance() throw()
{
return m_hInstResource;
}
HINSTANCE SetResourceInstance(HINSTANCE hInst) throw()
{
return static_cast< HINSTANCE >(InterlockedExchangePointer((void**)&m_hInstResource, hInst));
}
bool AddResourceInstance(HINSTANCE hInst) throw();
bool RemoveResourceInstance(HINSTANCE hInst) throw();
HINSTANCE GetHInstanceAt(int i) throw();
};
__declspec(selectany) bool CAtlBaseModule::m_bInitFailed = false;
extern CAtlBaseModule _AtlBaseModule;
CAtlBaseModule类用来取代旧版的ATL中的CComModule类。主要作用是保存进程实例句柄和资源句柄,并且是线程安全的。
使用CWindow
说了这么多,我们先来写一个例子程序。
创建Win32项目CWindow。在stdafx.h中加入代码:#include <atlbase.h>。这样,ATL会在程序一启动就自动实例化_AtlBaseModule对象。也就是说我们不需要自己创建CAtlBaseModule对象。
创建窗口程序首先要注册窗口类,创建窗口,建立消息泵,在窗口过程函数中对消息进行处理。CWindow类可以帮助我们创建窗口。所以Win32代码作如下修改:
在stdafx.h中加入代码:#include <atlwin.h>
_WinMain(...)中调用InitInstance函数的地方改为
//创建窗口
CWindow wnd;
wnd.Create(szWindowClass,0,CWindow::rcDefault,L"Window Application",WS_OVERLAPPEDWINDOW,WS_EX_CLIENTEDGE);
if(!wnd)
return -1;
wnd.CenterWindow();
wnd.ShowWindow(nCmdShow);
wnd.UpdateWindow();
好了,现在全局变量HINSTANCE hInst变量可以删除掉,所有需要使用hInst的地方都可以用_AtlBaseModule.GetModuleInstance()替换。
一切大功告成!
在第一章中,讨论了CWindow类的使用,但是注册窗口类,窗口过程函数仍然是使用的Win32 SDK方式。我们可以通过编写自己的派生自CWindowImpl类的子类达到简化这些工作的目的。
ProcessWindowMessage与消息映射宏
CWindowImpl类是一个最终派生自CWindow类的模板类。它可以在第一次调用Create函数时自动注册窗口类,并且通过thunk机制将窗口类中的窗口过程函数映射到自己派生类的成员函数,同时提供了很多宏用于建立消息映射语句。
CWindowImple类与父类的关系图----





CWindowImplRoot类默认的模板参数TBase为CWindow,所以绝大多数情况下,CWindowImpl类派生自CWindow类。而另一个父类CMessageMap类非常简单
class ATL_NO_VTABLE CMessageMap
{
public:
virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
LRESULT& lResult, DWORD dwMsgMapID) = 0;
};
只是声明了一个纯虚函数,我们的派生类必须实现ProcessWindowMessage函数,否则我们的派生类将不能实例化。我们要实现的ProcessWindowMessage函数是一个非常类似于WindowProc函数的成员函数,里面有大量的switch/case语句,可以根据不同的消息调用其他成员函数进行处理,为了简化这些工作,ATL提供了BEGIN_MSG_MAP/END_MSG_MAP以及MESSAGE_HANDLER宏帮助我们实现这个函数。如下:
BEGIN_MSG_MAP(CMainWindow)
COMMAND_ID_HANDLER(IDM_EXIT, OnFileExit)
END_MSG_MAP()
MESSAGE_HANDLER(msg,func)宏将消息交给指定的函数处理
MESSAGE_RANGE_HANDLER(msgFirst,msgLast,func)宏处理一定范围内的窗口消息
这里的func函数具有下面的形式:
LRESULT MessageHandler(UINT nMsg,WPARAM wparam,LPARAM lparam,BOOL& bHandled)
如果消息没有被func函数处理,则会交给缺省窗口过程处理,如果被func处理,同时又想让消息继续流动下去而不是截断,则可以将bHandled设为FALSE。
为了方便处理,ATL对WM_COMMAND和WM_NOTIFY消息提供了更方便的宏,WM_COMMAND用于菜单被按下、加速键被按下或者WIN32控件发送通知给父窗口;WM_NOTIFY用于WIN32控件通知父窗口。WM_NOTIFY是用于后来增加的新控件的,因为那时WM_COMMAND消息的WPARAM和LPARAM的所有位都已经用完了。
COMMAND_HANDLER(id,code,func)
NOTIFY_HANDLER(id,code,func)
处理函数原型:
LRESULT CommandHandler(WORD wNotifyCode,WORD wID,HWND hWndCtl,BOOL& bHandled)
LRESULT NotifyHandler(int idCtrl,LPNMHDR pnmh,BOOL& bHandled)
有时候消息处理函数不关心code参数,下面的宏更加方便,比如用于菜单
COMMAND_ID_HANDLER(id,func)
NOTIFY_ID_HANDLER(id,func)
还有其他一些宏
COMMAND_RANGE_HANDLER(idFirst,idLast,func)
NOTIFY_RANGE_HANDLER(idFirst,idLast,func)
COMMAND_CODE_HANDLER(code,func)
NOTIFY_CODE_HANDLER(code,func)
CWindowImpl类的Create函数内部注册窗口类,然后创建窗口
HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
DWORD dwStyle = 0, DWORD dwExStyle = 0,
_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
{
if (T::GetWndClassInfo().m_lpszOrigName == NULL)
T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();
ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);
dwStyle = T::GetWndStyle(dwStyle);
dwExStyle = T::GetWndExStyle(dwExStyle);
// set caption
if (szWindowName == NULL)
szWindowName = T::GetWndCaption();
return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,
dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam);
}
T::GetWndClassInfo()函数将返回CWndClassInfo类型,CWndClassInfo定义如下:
#define CWndClassInfo CWndClassInfoW
typedef _ATL_WNDCLASSINFOW CWndClassInfoW;
struct _ATL_WNDCLASSINFOW
{
WNDCLASSEXW m_wc;
LPCWSTR m_lpszOrigName;
WNDPROC pWndProc;
LPCWSTR m_lpszCursorID;
BOOL m_bSystemCursor;
ATOM m_atom;
WCHAR m_szAutoName[5+sizeof(void*)*CHAR_BIT];
ATOM Register(WNDPROC* p)
{
return AtlWinModuleRegisterWndClassInfoW(&_AtlWinModule, &_AtlBaseModule, this, p);
}
};
静态成员函数GetWndClassInfo()是通过宏DECLARE_WND_CLASS定义的
#define DECLARE_WND_CLASS(WndClassName) /
static ATL::CWndClassInfo& GetWndClassInfo() /
{ /
static ATL::CWndClassInfo wc = /
{ /
{ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, /
0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, /
NULL, NULL, IDC_ARROW, TRUE, 0, _T("") /
}; /
return wc; /
}
GetWndClassInfo()完成了CWndClassInfo静态变量的初始化工作。非常重要的一点是,将StartWindowProc函数作为窗口过程保存到m_wc. lpfnWndProc中。
我们可以看到StartWindowProc函数是CWindowImplBaseT类的静态成员函数:
template <class TBase, class TWinTraits>
LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)_AtlWinModule.ExtractCreateWndData();
ATLASSERT(pThis != NULL);
pThis->m_hWnd = hWnd;
pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);
WNDPROC pProc = pThis->m_thunk.GetWNDPROC();
WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);
#ifdef _DEBUG
// check if somebody has subclassed us already since we discard it
if(pOldProc != StartWindowProc)
ATLTRACE(atlTraceWindowing, 0, _T("Subclassing through a hook discarded./n"));
#else
(pOldProc); // avoid unused warning
#endif
return pProc(hWnd, uMsg, wParam, lParam);
}
StartWindowProc函数完成了几个重要的工作:
1)获取我们的派生类对象的指针,该指针在第一次窗口过程被调用时将保存到ATL的列表_AtlCreateWndData*中。
2)保存窗口句柄到m_hWnd中。
3)初始化m_thunk变量,m_thunk是一个类型,内部保存了一个结构变量
struct _stdcallthunk
{
DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
DWORD m_this; //
BYTE m_jmp; // jmp WndProc
DWORD m_relproc; // relative jmp
void Init(DWORD_PTR proc, void* pThis)
{
m_mov = 0x042444C7;//C7 44 24 0C
m_this = PtrToUlong(pThis);
m_jmp = 0xe9;
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
// write block from data cache and
//flush from instruction cache
FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));
}
//some thunks will dynamically allocate the memory for the code
void* GetCodeAddress()
{
return this;
}
};
该结构包含了两个汇编指令:move和jmp。有了它的帮助,StartWindowProc函数内部在执行return pProc(hWnd, uMsg, wParam, lParam);语句之前,就能够不知不觉的在调用栈里将窗口句柄偷换成我们的派生类的指针。
同时,由于SetWindowLongPtr将当前窗口过程修改为静态成员函数WindowProc。所以WindowProc函数将被调用,WindowProc函数内部将调用我们的目的地函数ProcessWindowMessage,消息路由完成。
我们总结一下消息路有的经过:
窗口创建时,将StartWindowProc注册为窗口过程;
窗口的第一个消息到来时,ATL将窗口指针保存到全局列表中;
第二个窗口消息到来时,StartWindowProc将句柄保存,从全局列表中获得窗口类的指针,同时将WindowProc成员函数指定为窗口过程,并且用thunk技术将调用栈里面的句柄替换成this指针,然后调用WindowProc;
WindowProc内部调用ProcessWindowMessage函数,该函数是通过消息映射宏帮助建立的,该函数内部根据不同的消息调用对应的消息映射函数。
后续的消息到来时,ATL将直接调用WindowProc函数。
最后一个问题是,窗口类注册时需要指定一个名字以便日后引用,CWindowImpl的Create函数第一次被调用时将检测是否创建,如果没有则创建注册窗口类,同时也指定窗口类的名称。注册窗口类的函数是_ATL_WNDCLASSINFOW结构的Register成员函数。Register内部调用了类AtlModuleRegisterWndClassInfoParamW的成员函数
static void FormatWindowClassName(PXSTR szBuffer, void* unique)
{
#if defined(_WIN64) // || or Windows 2000
::wsprintfW(szBuffer, L"ATL:%p", unique);
#else
::wsprintfW(szBuffer, L"ATL:%8.8X", reinterpret_cast<DWORD_PTR>(unique));
#endif
}
获得了窗口类名称,其实就是把WINCLASSEX变量的内存地址转换成字符串作为窗口类的名字。最后注册窗口类还是依赖于API RegisterClassExW下面也是AtlModuleRegisterWndClassInfoParamW类的成员函数:
ATLINLINE ATLAPI_(ATOM) AtlWinModuleRegisterClassExW(_ATL_WIN_MODULE* pWinModule, const WNDCLASSEXW *lpwc)
{
if (pWinModule == NULL || lpwc == NULL)
return 0;
ATOM atom = ::RegisterClassExW(lpwc);
BOOL bRet = pWinModule->m_rgWindowClassAtoms.Add(atom);
ATLASSERT(bRet);
(bRet);
return atom;
}
窗口类风格的设定,归根到底就是CreateWindowEx函数接收的两个参数dwExStyle和dwStyle的设定。在CWindowImpl类的Create函数内部,有这么几行代码:
dwStyle = T::GetWndStyle(dwStyle);
dwExStyle = T::GetWndExStyle(dwExStyle);
// set caption
if (szWindowName == NULL)
szWindowName = T::GetWndCaption();
return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,
dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam);
类型T是通过模板参数传递进来的,是我们的派生类,我们的派生类从CWindowImplBaseT继承了GetWndStyle和GetWndExStyle函数。CWindowImplBaseT类是通过调用模板参数类的静态成员函数来实现这两个函数的。
template <class TBase = CWindow, class TWinTraits = CControlWinTraits>
class ATL_NO_VTABLE CWindowImplBaseT : public CWindowImplRoot< TBase >
{
public:
WNDPROC m_pfnSuperWindowProc;
CWindowImplBaseT() : m_pfnSuperWindowProc(::DefWindowProc)
{}
static DWORD GetWndStyle(DWORD dwStyle)
{
return TWinTraits::GetWndStyle(dwStyle);
}
static DWORD GetWndExStyle(DWORD dwExStyle)
{
return TWinTraits::GetWndExStyle(dwExStyle);
}
virtual WNDPROC GetWindowProc()
{
return WindowProc;
}
。。。。。。。。。。
TWinTraits模板参数通常是一个CWinTraits类
template <DWORD t_dwStyle = 0, DWORD t_dwExStyle = 0>
class CWinTraits
{
public:
static DWORD GetWndStyle(DWORD dwStyle)
{
return dwStyle == 0 ? t_dwStyle : dwStyle;
}
static DWORD GetWndExStyle(DWORD dwExStyle)
{
return dwExStyle == 0 ? t_dwExStyle : dwExStyle;
}
};
我们只需要将窗口风格和扩展风格作为模板参数传递进去,然后将整个类作为模板参数传递给我们的派生类,就可以创建我们需要的风格的窗口。也可以使用ATL预定义模板类。如下:
typedef CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0> CControlWinTraits;
typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> CFrameWinTraits;
typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_MDICHILD> CMDIChildWinTraits;
typedef CWinTraits<0, 0> CNullTraits;
EXE组件的窗口表现
参见工程GPSRecv,同时注意,如果没有实现一个接口的话,进程会自动结束,所以必须至少实现一个接口。
修改WNDCLASSEX
第一次调用Create成员函数的时候,CWindowImpl类将替我们完成窗口类的注册和窗口的创建工作。
GetWndClassInfo成员函数可以让我们获取到CWindClassInfo结构。CWindClassInfo.m_atom成员标志窗口类是否已被注册,CWindClassInfo.m_wc就是WNDCLASSEX结构。我们可以很方便的获得它并在注册前修改m_wc的成员。如下代码:
CMainWindow::CMainWindow(void)
{
CWndClassInfo& wci=GetWndClassInfo();
if(!wci.m_atom)
{
wci.m_wc.hIcon=LoadIcon(hInst,(LPCTSTR)IDI_ATLWINDOW2);
wci.m_wc.hIconSm=(HICON)LoadImage(hInst,MAKEINTRESOURCE(IDI_SMALL),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
wci.m_wc.hbrBackground=CreateHatchBrush(HS_DIAGCROSS,RGB(0,0,255));
}
}
类似于C++的继承。目的:扩展基类窗口的一些功能。子窗口复制基类窗口的窗口过程,然后替换掉名字和窗口过程,如果消息自己处理完后仍然想交给基类窗口处理,那么可以路由到基类窗口过程。
宏DECLARE_WND_SUPERCLASS(子类窗口名称,基类窗口名称)帮助我们实现这一步骤,下面我们要做的就是编写消息映射宏进行消息处理。
用SetWindowsLong函数将基类窗口的窗口过程替换成子类的窗口过程
如果我们的窗口类的在处理某一个消息的时候发现其实已经有一个另一个类的成员函数能够处理,我们如何办呢。让我们的窗口类派生自这个类,并在消息映射宏中使用该成员函数。这是一个手工造的方法,还有一种相对自动化的方法,就是使用宏CHAIN_MSG_MAP宏,该宏会调用另一个类的ProcessWindowMessage函数。举个例子:
template <typename Deriving>
class CFileHandler {
public:
// Message map in base class
BEGIN_MSG_MAP(CMainWindow)
COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen)
COMMAND_ID_HANDLER(ID_FILE_SAVE, OnFileSave)
COMMAND_ID_HANDLER(ID_FILE_SAVE_AS, OnFileSaveAs)
COMMAND_ID_HANDLER(ID_FILE_EXIT, OnFileExit)
END_MSG_MAP()
LRESULT OnFileNew(WORD, WORD, HWND, BOOL&);
LRESULT OnFileOpen(WORD, WORD, HWND, BOOL&);
LRESULT OnFileSave(WORD, WORD, HWND, BOOL&);
LRESULT OnFileSaveAs(WORD, WORD, HWND, BOOL&);
LRESULT OnFileExit(WORD, WORD, HWND, BOOL&);
};
class CMainWindow :
public CWindowImpl<CMainWindow, CWindow, CMainWinTraits>,
public CFileHandler<CMainWindow>
{
public:
BEGIN_MSG_MAP(CMainWindow)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
COMMAND_ID_HANDLER(ID_HELP_ABOUT, OnHelpAbout)
// Chain to a base class
CHAIN_MSG_MAP(CFileHandler<CMainWindow>)
END_MSG_MAP()
...
};
如果刚好我们的窗口类拥有一个成员变量,也许也是一个窗口对象,它能够帮助我们处理一些消息,这时候我们应该用另一个宏CHAIN_MSG_MAP_MEMBER。这两个宏的唯一区别就是一个使用::调用ProcessWindowMessage函数,另一个使用.符号调用。
关于更加细节的变化,请参考<<ATL Internals>> (2nd Edition)。
ATL提供了宏FORWARD_NOTIFICATIONS来实现这个功能。实际上该宏调用了下面的函数:
static LRESULT Atl3ForwardNotifications(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
LRESULT lResult = 0;
switch(uMsg)
{
case WM_COMMAND:
case WM_NOTIFY:
#ifndef _WIN32_WCE
case WM_PARENTNOTIFY:
#endif // !_WIN32_WCE
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_COMPAREITEM:
case WM_DELETEITEM:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
case WM_HSCROLL:
case WM_VSCROLL:
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
lResult = ::SendMessage(::GetParent(hWnd), uMsg, wParam, lParam);
break;
default:
bHandled = FALSE;
break;
}
return lResult;
}
因此,这实际上硬编码,只有这些消息才会被反射给父窗口。
CAxHostWindow类帮助我们实现了ActiveX控件包容器所需要支持的各种接口。
该类简化了CAxHostWindow的使用。创建Grid控件的代码如下:
LRESULT CMainWindow::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
RECT rect;
GetClientRect(&rect);
LPCTSTR pszName=__T("SimpleGrid.Grid");
HWND hwndContainer=m_ax.Create(m_hWnd,rect,pszName,WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
if(!hwndContainer)
return -1;
return 0;
}
CMainWindow:public CWindowImpl<...>
{
private:
CAxWindow m_ax;
}
或者可以分两部创建:
LRESULT CMainWindow::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
RECT rect;
GetClientRect(&rect);
//创建控件容器
HWND hwndContainer=m_ax.Create(m_hWnd,rect,0,WS_CHILD|WS_VISIBLE);
if(!hwndContainer)
return -1;
//创建控件
CComBSTR pszName("SimpleGrid.Grid");
HRESULT hr=m_ax.CreateControl(pszName);
if(hr!=S_OK)
return -1;
return 0;
}
CAxHostWindow类提供了MoveWindow方法移动窗口的位置和大小。
CAxHostWindow类提供了QueryControl方法查询控件的接口。然后我们就可以调用控件提供的方法。
分享到:
评论

相关推荐

    ATL实现的CDHtmlDialog模板类

    &lt;br&gt;基于这个原因,通过理解分析MFC中CDHtmlDialog类的功能和实现行为,这里完全使用ATL一样的实现机制来模仿MFC中实现的功能编写了一个头文件,使ATL爱好者在无需MFC庞大的支持库的情形下实现跟CDHtmlDialog...

    深入解析ATL(第2版).pdf

     ·使用ATL窗口类和控件建立独立的应用程序和UI组件。  ·使用ATL Servet开发可以运行在微软IIS上的Web应用程序。 虽然.NET系统从1998年才开始其发展进程。但它已经使很多开发人员的编程发生了革命性的变化,并在...

    深入剖析WTL PDF

    深入剖析WTL PDF WTL 是Windows Template Library 的缩写。最初,WTL 是由微软的ATL(Active Template Library)小组成员开发的一个SDK 例子。主要是基于ATL 的对Win32 API 的封装。从 2.0 后,功能逐步完善,成为...

    彻底深入剖析WTL精髓

    深入剖析WTL 一.Win32模型 二.如何封装Windows界面程序 三.WTL框架窗口分析 深入剖析WTL

    深入剖析WTL

    从2.0后,功能逐步完善,成为了一个完整的支持窗口的框架(windows framework)。 与MFC相比较,功能并没有MFC完善。比如MFC支持doc/view架构,而WTL并不支持。同时,WTL也没有Microsoft的官方支持。但是,WTL是...

    VC6风格界面

    ...................\MFC窗口位置管理详细分析及实例.doc ...................\Toolbar制作菜单条过程详解.doc ...................\VC++6.0定制窗口的方法.doc ...................\VC++中MFC窗口对象的清除....

    Visual.C#2010从入门到精通

    18.4.7 CDaoRecordset类常用函数 376 18.5 使用MFC DAO实现通讯录 377 18.5.1 创建工程 377 18.5.2 添加CDaoRecordset类 378 18.5.3 系统主界面 379 18.5.4 显示联系人信息 381 18.5.5 删除联系人信息 382 18.5.6 ...

    McGraw C++程序调试实用手册

    书中深入地分析了开发不包含逻辑和语法错误的代码技巧以及调试程序的基本原理,介绍了开发和调试命令行代码的过程和方法,说明了关于定位、分析及修复编程错误的方法,介绍了开发 Visual C++程序时所遇到的特殊...

    vc++ 应用源码包_1

    使用了六个类五个模块类演示了atl的调用方法 autoplaysnd mp3 播放器源码 重载了自带的控件进行播放 aviplayer avi播放器源码 引用了atl控件播放 beautifulskin 源码 演示了各种控件方法 Browser.Net源码 C#的一...

    vc++ 应用源码包_2

    使用了六个类五个模块类演示了atl的调用方法 autoplaysnd mp3 播放器源码 重载了自带的控件进行播放 aviplayer avi播放器源码 引用了atl控件播放 beautifulskin 源码 演示了各种控件方法 Browser.Net源码 C#的一...

    vc++ 应用源码包_6

    使用了六个类五个模块类演示了atl的调用方法 autoplaysnd mp3 播放器源码 重载了自带的控件进行播放 aviplayer avi播放器源码 引用了atl控件播放 beautifulskin 源码 演示了各种控件方法 Browser.Net源码 C#的一...

    vc++ 应用源码包_5

    使用了六个类五个模块类演示了atl的调用方法 autoplaysnd mp3 播放器源码 重载了自带的控件进行播放 aviplayer avi播放器源码 引用了atl控件播放 beautifulskin 源码 演示了各种控件方法 Browser.Net源码 C#的一...

    vc++ 应用源码包_3

    使用了六个类五个模块类演示了atl的调用方法 autoplaysnd mp3 播放器源码 重载了自带的控件进行播放 aviplayer avi播放器源码 引用了atl控件播放 beautifulskin 源码 演示了各种控件方法 Browser.Net源码 C#的一...

    vc++ 开发实例源码包

    使用了六个类五个模块类演示了atl的调用方法 class CDHtmlSinkHandler; // Events Sink Base class CDHtmlEventSink; // IHTMLDocument2 Events Sink // IDispatch class CDHtmlControlSink; // ActiveX Control ...

    金山卫士开源代码----消息机制浅析

    今天简化了金山的开源代码,用来学习一下,先谢谢金山的开源精神了,呵呵···直接弄最简单的,窗口见下图,关键代码如下,全部的代码放附件里吧。分析中关于累的继承机制等没有详细的说明了,在函数申明中我会用...

    应用程序支持扩展之鼠标手势

    手势时候创建一窗口,将内容拷贝到窗口上,对当前窗口操作,相当于是截图,所以操作甚好。 分析若有出入地方,别见怪就是。 由于实现方式、及部分资源参考两者,望别有版权之事,只要研究学习而已。 手势时...

    COM与COM+从入门到精通(pdf版本,含源码)

    第7章 用VB建立InternetCOM组件 无窗口控件 ActiveX控件容器的线程模型 ActiveX控件的安全性 Web页面访问 VBDHTML项目 DHTML项目基础 DHTML应用程序样本 VBIIS应用程序 WebClass 一个IIS应用程序样本 ...

    Whole.Tomato.Visual.Assist.X.10.7.1925.0 破解补丁 dll Cracked 最新破解版

    Visual Assist X只收集您工程中的头文件和对象信息,这些头文件和对象可以包含在系统、第三方库、MFC或ATL/WTL/STL中。这意味着Intellisence的活动更加频繁,结果也更加准确。  不止是C/C++,对于所有编程语言,...

    Visual Assist X 10.4

    Visual Assist X只收集您工程中的头文件和对象信息,这些头文件和对象可以包含在系统、第三方库、MFC或ATL/WTL/STL中。这意味着Intellisence的活动更加频繁,结果也更加准确。 不止是C/C++,对于所有编程语言,...

    Whole.Tomato.Visual.Assist.X.10.7.1925.0 2012年12月14日 破解补丁 dll Cracked 最新破解版

    Visual Assist X只收集您工程中的头文件和对象信息,这些头文件和对象可以包含在系统、第三方库、MFC或ATL/WTL/STL中。这意味着Intellisence的活动更加频繁,结果也更加准确。  不止是C/C++,对于所有编程语言,...

Global site tag (gtag.js) - Google Analytics