ATL接口映射宏详解(中)
16 ATL
8
T* operator=(T* lp){ return (T*)AtlComPtrAssign((IUnknown**)&p, lp); } T* operator=(const CComQIPtr<T,piid>& lp) { return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p); } T* operator=(IUnknown* lp) { return (T*)AtlComQIPtrAssign((IUnknown**)&p, lp, *piid); } ...... } CComQIPtr的第二个模板参数是一个指向接口IID的指针。它有四个构造函数,并且对 “=”进行了三次重载。对IUnkown指针的赋值会调用 AtlComQIPtrAssign函数: _(IUnknown*) AtlComQIPtrAssign(IUnknown** pp, IUnknown* lp, REFIID riid) { IUnknown* pTemp = *pp; *pp = NULL; if (lp != NULL) lp->QueryInterface(riid, (void**)pp); if (pTemp) pTemp->Release(); //注意这里要释放以前的引用! 注意这里要释放以前的引用! 注意这里要释放以前的引用 return *pp; 9 }
在CComQIPtr的支持下,客户的使用变得极为简单: 5void GetLottaPointers(IUnkown* pUnk){ CComQIPtr<IPersist, &IID_IPersist> persist; CComQIPtr<IDispatch, &IID_IDispatch> dispatch; CComPtr<IDataObject, &IID_IDataObject> dataobject;5 dispatch = pUnk; persist = pUnk; dataobject = pUnk; DoIt(persist, dispatch, dataobject); } 或者, void GetLottaPointers(IUnkown* pUnk){ CComQIPtr<IPersist, &IID_IPersist> persist(pUnk); CComQIPtr<IDispatch, &IID_IDispatch> dispatch (pUnk); CComPtr<IDataObject, &IID_IDataObject> dataobject (pUnk); DoIt(persist, dispatch, dataobject); } 所有繁琐的操作都不见了。
ATL编程初级教程
ATL编程初级教程介绍 本教程的⽬的是告诉你如何使⽤ATL创建⼀个COM服务器,并使⽤Visual C++和Visual Basic程序来分别调⽤这个服务器。
我并不想深⼊探讨COM的细节,也不想让你深陷于IDL之中。
这⼀教程只是为VC++的新⼿程序员设计的,告诉他们利⽤ATL来创建⼀个COM对象有多么简单,并让他们能对ATL产⽣更多的兴趣。
第1步:启动ATL COM Wizard 你所需要做的第⼀件事情就是启动Visual C++并创建⼀个新的⼯程,选择“ATL COM Wizard”,⼯程名为“Simple_ATL”。
设置好⼯程的路径之后,单击OK按钮。
你会看到,屏幕上给了你若⼲选项。
第⼀个选项为“Server Type”。
我们将要创建⼀个服务器DLL,所以请确认服务器的类型选为“Dynamic Link Library”。
我们并不需要关⼼下⾯的其它三个复选框,所以我们可以将它们忽略掉。
按下Finish按钮,这样向导就会为你产⽣适当的⽂件了。
之后,⼀个“New Project Information”窗⼝就会出现,你可以从上⾯得知向导都会创建什么⽂件,按下“OK”接受这⼀切。
第2步:创建⼀个新的ATL对象 请确认你能在VC++的IDE中看到Workspace View,如果不能的话则请单击“View”菜单,然后选择“Workspace”。
在这个视图中你会看到三个选项卡,请单击“ClassView”栏,你应该会看到“Simple_ATL Classes”。
请在此右击⿏标键,并在弹出菜单中选择“New ATL Object”,你将会看到下⾯这样的窗⼝: 默认的选择项“Simple Object”就是我们所要的了,请单击next按钮,你会来到“ATL Object Wizard Properties”窗⼝中。
在“Short Name”⽂本框中输⼊“First_ATL”。
请注意,这时候向导就会⾃动地填写其它的⽂本框。
ATL接口映射宏详解(中)
return m_pOwner->QueryInterface(iid, ppvObject);
}
还记得我们创建的分割组件是CComTearOffObject< CTearOff1 >吗?现在执行查询操作的是它的成员函数。它的实现很简单,事实上它什么也没做,仅仅是把它交给它的外部对象(即CComObject< COuter >)去做了。还记得m_pOwner是在构造函数里赋值的吧。现在是否感到有些不妙呢?呵呵
{
//..........
while (pEntries->pFunc != NULL)
{
BOOL bBlind = (pEntries->piid == NULL);
if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK) hRes = p->_InternalQueryInterface(riid, ppv);
{
*pbstrName = ::SysAllocString(L"ITearOff1");
return S_OK;
}
};
class COuter : public ..... //我们真正要实现的组件
Excel宏的使用方法和实例解析
Excel宏的使用方法和实例解析一、什么是Excel宏?Excel宏是一种自动化的处理方法,通过使用宏,用户可以自定义代码来实现特定操作。
宏通常用于自动化重复性的操作,提高工作效率。
在Excel中,宏是由Visual Basic for Applications (VBA)编写的代码模块,可以用于执行一系列动作和任务。
二、Excel宏的基本使用方法1. 启用开发人员选项卡在Excel中,需要先启用“开发人员”选项卡,以便访问VBA编辑器和宏功能。
在Excel的“选项”中选择“自定义功能区”,勾选“开发人员”,然后点击“确认”。
2. 录制宏点击“开发人员”选项卡中的“录制宏”按钮,弹出录制宏对话框。
在对话框中,可以给宏命名,并指定宏的存储位置。
点击“确定”后,Excel会开始记录您在工作表上的操作。
3. 执行宏录制宏后,可以通过按下快捷键、点击按钮或使用相关函数来执行宏。
点击“开发人员”选项卡中的“宏”按钮,选择宏名称,然后点击“运行”,即可执行宏。
4. 停止录制在完成对工作表的操作后,点击“开发人员”选项卡中的“停止录制”按钮即可停止录制宏。
录制停止后,Excel会将所执行的操作转化为可执行的VBA代码。
三、Excel宏的实例解析下面我们以一个实例来解析如何使用Excel宏。
假设我们有一个包含员工姓名、工号和工资的表格,我们需要将所有人的工资增加10%。
我们可以通过宏来实现这个操作。
1. 启用开发人员选项卡按照步骤一中的方法启用“开发人员”选项卡。
2. 录制宏点击“开发人员”选项卡中的“录制宏”按钮,弹出录制宏对话框。
在“宏名称”中输入“增加工资”,选择“个人工作簿”,然后点击“确定”。
3. 执行操作在工作表中选中工资列,点击“开始”选项卡中的“格式”按钮,在弹出的菜单中选择“数值”,然后选择“货币”,设置好货币符号和小数位数,点击“确定”。
4. 编写代码在VBA编辑器中,找到刚才录制的宏,可以看到录制过程中产生的VBA代码。
ATL&WTL第二部分
第二部分- WTL 中的GUI 基础类内容∙第二部分介绍∙WTL 综述∙开始一个 WTL EXE∙WTL 消息映射的增强∙使用 WTL AppWizard 可以得到什么∙通历向导(VC 6)∙通历向导(VC 7)∙检查生成的代码∙CMessageLoop 内幕∙CFrameWindowImpl 内幕∙回到时钟程序∙UI 更新∙控制时钟的新菜单项∙调用 UIEnable()∙关于消息映射的最后注意事项∙下一站,1995∙修订历史第二部分介绍好,是实实在在地讲述 WTL 的时候了!在这部分里,我会介绍写一个主框架窗口的基础知识,以及 WTL 引入的比较受欢迎的改进,比如 UI 更新和更好的消息影射。
为了最大程度地掌握本部分的内容,你应该安装 WTL 以使其头文件处于 VC 的搜索路径中,而且 AppWizard 也在适当的目录下。
WTL 的分发包中附有如何安装 AppWizard 的说明,请参考该文档。
记住,如果你安装 WTL 或者编译示例代码时遇到了任何问题,请在张贴你的问题之前阅读第一部分的 ReadMe 一节。
WTL 综述WTL 的类可以分为几个主要的类别:1.框架窗口的实现 - CFrameWindowImpl, CMDIFrameWindowImpl2.控件封装 - CButton, CListViewCtrl3.GDI 封装 - CDC, CMenu4.特殊的 UI 特性- CSplitterWindow, CUpdateUI, CDialogResize, CCustomDraw5.工具类以及宏 - CString, CRect, BEGIN_MSG_MAP_EX本文将深入到框架窗口中去,顺便提及一些 UI 特性和工具类。
大多数的类都是独立的,不过也有一些像 CDialogResize 这样的嵌入类(mix-in)。
开始一个 WTL EXE如果你不使用 WTL AppWizard (稍后我们就会提到它),那么一个 WTL EXE 一开始会很像一个 ATL EXE。
ATL基本使用
A TL基本使用这一部分将重点介绍ATL的基本使用过程。
由于ATL已经被集成在Microsoft Visulal Studio的Visual C++开发环境中,因此要使用A TL必须先安装Visual C++。
在下面的讨论中有关COM的基本知识请参阅有关的文档,这里不再详细说明。
给出的图是在Microsoft Windows 98平台下Visual Studio 6.0的使用示意图。
使用A TL开发一个COM应用基本可以分为以下几个步骤:创建一个新的A TL工程,并对工程的选项进行适当的配置。
向新创建的工程添加新的ATL类,并对该类进行一些初始配置工作。
根据COM应用的基本要求向新的ATL类加入新的接口定义,并实现相应的接口成员函数。
编译连接工程,注册COM应用。
下面将根据这些步骤依次介绍ATL的基本使用过程。
1. 创建工程首先启动Visual C++集成开发环境,选择“File”菜单下的“New...”命令,在“New”对话框中选择“Project”页,如图1所示。
图1 创建新工程界面示意图选择“A TL COM AppWizard”项,这是创建A TL工程的AppWizard向导入口。
然后在“Project name”编辑框中输入工程的名字,单击“OK”按钮,进入AppWizard对话框。
如图2所示。
图2 ATL COM AppWizard对话框示意图在AppWizard对话框中主要的设置选项有:COM服务程序的类型:- 动态连接库(Dynamic Linking Library)最终产生一个动态连接库(DLL)形式的COM服务程序;- 应用程序(Executable application)最终产生一个可执行程序类型(EXE)的COM服务程序;- NT服务(NT Service):产生一个以NT服务方式运行的COM服务程序。
允许嵌入Proxy/Stub代码。
由Microsoft提供的MIDL编译IDL文件以后,将产生用于对象调度(Marshaling)的Proxy/Stub的代码。
ATL接口映射宏详解
ATL接口映射宏详解序言:这几天看了看A TL的接口映射宏,不知不觉看得比较深入了,突然就萌发了把它写出来的想法。
A TL中定义了很多接口映射宏,有几个还是比较重要的,虽然好象没有必要把它所有的细节都弄得很清楚,但深入学习的过程中也可以顺带学一学其他的A TL类,对它的机制也可以更清楚一些,应该还是会有些好处的吧。
我按照我学习的过程把它写出来,也不知道大家能不能看懂。
想模仿一下侯老师的手笔力争把其内部细节解释清楚,但也不敢大言不惭的美其名曰“深入浅出”,呵呵,只希望能对大家有所帮助了。
以后将分别介绍A TL中各个形式为COM_INTERFACE_ENTRY_XX的接口映射宏并将按照从易到难的顺序讲解,每一部分都将建立在前一部分的基础上。
每一部分都将通过分析实际的调用函数堆栈来进行分析,堆栈的写法是从下向上。
文中所涉及的代码都为略写,只列出相关部分。
一、COM_INTERFA CE_ENTRY(x)首先我们从一个最典型的应用开始:定义一个最简单的A TL DLL:class A TL_NO_VTABLE CMyObject :public CComObjectRootEx,public CComCoClass,public IDispatchImpl{.....BEGIN_COM_MAP(CMyObject)COM_INTERFACE_ENTRY(IMyObject) //一个双接口COM_INTERFACE_ENTRY(IDispatch)END_COM_MAP().....};编写一段最简单的查询接口代码:IUnknown *pUnk;IMyObject *pMyObject;CoCreateInstance(CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pUnk);pUnk->QueryInterface(IID_IMyObject, (void **)&pMyObject);执行客户代码,首先我们看看组件对象是如何被创建的。
深入学习ATL
深入解析ATL(第二版ATL8.0)(1.11-1.13节)1.11 容纳控件1.11.1容纳控件利用 ATL 所支持的控件容纳,可实现容纳控件。
比如, CAxDialogImpl 中的Ax 两字就表示 ActiveX 控件,表示对话框具有容纳控件的能力。
在对话框中实现容纳控件,只需在对话框资源上点击右键,从弹出菜单选择 Insert ActiveX Control ,然后弹出一个对话框,列举了系统安装的所有控件,如图 1-17 所示。
图 1-17 插入 ActiveX 控件对话框插入控件后,点击控件可以在控件的属性窗口设置控件的属性。
如图 1-18 所示。
图 1-18 控件属性对话框在属性对话框的工具栏上点击控件事件按钮,还可以选择处理控件的事件,如图 1-19 所示。
图 1-19 选择处理的控件事件容纳对话框运行显示时,控件被创建,同时根据开发阶段设置的属性初始化控件。
图 1-20 显示了容纳了一个控件的对话框。
图 1-20 容纳 COM 控件的对话框ATL 不但提供了对话框的容纳控件功能,其他窗口也同样支持:声明为对话框资源的 UI 控件(称为复合控件);声明为 HTML 资源的 UI 控件(称为 HTML 控件)。
关于控件容器的更多信息请参考第十二章“控件容器”。
1.11.2 C++ COM 客户端至少在理论上, COM 与 C++ 是一致的。
一个 COM 接口直接映射为一个 C++ 的抽象类。
使用 COM 对象,仅仅需要使用 MIDL 编译器运行 IDL 文件,就可以生成一个头文件,里面包含有所有需要的信息。
所有的这一切都运行正常,直到 VB 团队询问他们也是否可以使用 COM 技术。
VB 开发人员通常不知道,也不想知道 C++ 语言。
IDL 也是一个与 C++ 传统语言相似的语言,其中也支持许多 C/C++ 的特性(比如数组和指针)。
VB 需要一种方法来存储这些 COM 对象的类型信息,以方便 VB 开发人员使用和理解它们。
ATL事件处理
// 声明并实现事件接收类,这个类使用 IDispEventImpl 模板,同时将 typelibrary 作// 为参数传递给模板。
它在事件映射槽中使用 SINK_ENTRY_EX() 宏来为每个事件源指定事// 件处理函数。
class CSinkObj :public IDispEventImpl<IDC_SRCOBJ, CSinkObj,&__uuidof(MyEventsInterface), &LIBID_MyComLibrary, 1, 0>{public:CSinkObj(){// Do nothing}// 事件接口映射接口表声明,表中的每个条目对应接口中的一个事件处理函数BEGIN_SINK_MAP(CSinkObj)SINK_ENTRY_EX(IDC_SRCOBJ, __uuidof(MyEventsInterface), 1, myEventHandler)SINK_ENTRY_EX(IDC_SRCOBJ, __uuidof(MyEventsInterface), 2, myEvent2Handler) END_SINK_MAP()STDMETHODIMP myEventHandler(_bstr_t strParam, long nParam){MessageBox(NULL, "Get myEvent !", " From CSinkObj", MB_OK);return S_OK;}STDMETHODIMP myEvent2Handler(_bstr_t strParam){MessageBox(NULL, "Get myEvent2 !", " From CSinkObj", MB_OK);return S_OK;}};ii> 使用上面实现的事件接收类// 创建事件接收对象CSinkObj *pSink = new CSinkObj();// 创建事件源对象的一个接口MyClassInterface* pClassInterface = NULL;HRESULT hr = CoCreateInstance(__uuidof(myComClass),NULL, CLSCTX_INPROC_SERVER,__uuidof(MyClassInterface),(void**)&pClassInterface);if(SUCCEEDED(hr)){// 通过事件源组件对象的一个接口,将事件接收对象与事件源组件对象连接起来 pSink->DispEventAdvise(pClassInterface);// 在这里我们调用事件源对象的接口中的一个方法,用来激发一个事件pClassInterface->Hello(L"Tom");}// 解除事件接收对象与事件源组件对象的连接if(pSink){if (pSink->m_dwEventCookie != 0xFEFEFEFE)pSink->DispEventUnadvise(pClassInterface);delete pSink; // 释放事件接收对象}。
ATL 教程
ATL 教程Visual Studio .NET 2003ATL 旨在简化创建有效、灵活、轻量的控件的过程。
本教程带领您完成创建ActiveX 控件的过程,以阐释许多ATL 和COM 的基础知识。
本教程不使用属性。
有关使用属性的逐步骤指南,请参见属性教程。
通过遵循本教程,您将了解如何将控件添加到绘制圆形和实心多边形的ATL 项目中。
然后您将添加属性,指示多边形有多少条边,并创建在属性更改时更新此控件的绘图代码。
控件然后将显示在Web 页上,并使用一些VBScript 使它对事件做出响应。
教程分为7 个步骤。
应按顺序执行每个步骤,因为后面的步骤依赖于前面完成的任务。
在开始之前,应该确认您具有在特定计算机上注册ActiveX 组件所需的权限。
通常,只有当您在“终端服务”连接上运行Visual Studio .NET 时,才需要注意这一问题。
步骤 1:创建项目Visual Studio .NET 2003本教程将带领您逐步骤通过一个非属性化ATL 项目,该项目创建一个显示多边形的ActiveX 对象。
此对象包括使用户得以更改组成多边形的边数的选项以及刷新显示的代码。
注意本教程创建与Polygon(多边形)示例相同的源代码。
如果您希望避免手动输入源代码,则可以从多边形示例摘要下载源代码。
然后,您可以一边参考Polygon 源代码一边阅读教程,或者用它来检查自己的项目中是否有错误。
使用“ATL 项目向导”创建初始ATL 项目1.在Visual Studio 开发环境中,在“文件”菜单上单击“新建”,然后单击“项目”。
2.单击“Visual C++ 项目”文件夹并选择“ATL 项目”。
3.键入Polygon作为项目名称:源代码的位置通常默认为My Documents\Visual Studio Projects,并且将自动创建一个新文件夹。
4.单击“确定”,“ATL 项目向导”随即打开。
5.单击“应用程序设置”以查看可用选项:6.在创建控件时,如果控件必须是进程内服务器,则将“服务器类型”保留为DLL。
ATL基础操作手册
陈喜庆 QQ:31444465
ATL 基础操作手册
《手册》详细描述了 ATL 组件式开发的常用操作,系本人经验和心得的总结和完善。对 于想要了解或刚刚接触 ATL COM 的开发人员具有一定的参考价值。由于时间仓促和本人的经 验有限,疏漏之处在所难免,还望斧正。
在此,非常感谢顾学明和李策在工作和生活上对我的关心和指导。感谢赵宏辉反复细心 地阅读本《手册》,并提出了宝贵的修改意见和建议,他为该手册提供了强有力的技术支持。 感谢景翔和何学洲在平日的工作中给予我莫大的帮助。
methodIndex); [propget, helpstring("property MyProperty")] HRESULT MyProperty([out,
retval] BSTR *pVal); [propput, helpstring("property MyProperty")] HRESULT MyProperty([in] BSTR
删除成员变量
在 ClassView 视图中双击需要删除的成员变量,定位后删除该定义。
8
ATL 基础操作手册
陈喜庆 QQ:31444465
编辑接口成员 添加方法
1. 在 ClassView 视图中右键接口对象,单击“Add Method”;
2. 设置返回值类型、方法名和参数。操作过程中,系统将实时显示出即将生成的接口定义, 即“Implementation”里的内容。
if (Enabled == NULL) return E_POINTER;
return E_NOTIMPL; } 5. 切换至组件类头文件(Zoom.h),删除代码段中的实现体,并在定义最后加上分号,如 下所示: STDMETHOD(get_Enabled)(VARIANT_BOOL * Enabled); 6. 重复上面的步骤,完成 ICommand 接口中其它方法和属性的修改。
使用USES_CONVERSION
使用USES_CONVERSION首先,先介绍下USES_CONVERSION为何物。
USES_CONVERSION是ATL中的一个宏定义。
用于编码转换(用的比较多的是CString向LPCWSTR转换)。
通俗的说,就是你用了这个宏后,就可以用一系列的字符串转换宏,有OLE到T,T到OLE,OLE到W,W到OLE等等,非常方便。
或者说,这个宏会告诉编译器,在紧接的代码中我们要用ole 库中代码(如ansi 到unicode 的A2W(...))的转换宏,不加USES_CONVERSION在使用A2W会出错。
要想使用这个宏,因为它是ATL库带的,所以要加上头文件#include <atlconv.h>。
关于USES_CONVERSION的使用,下面是从网上摘来的一段话,讲的很不错。
USES_CONVERSION是ATL中的一个宏定义。
用于编码转换(用的比较多的是CString向LPCWSTR转换)。
在ATL 下使用要包含头文件#include "atlconv.h"使用USES_CONVERSION一定要小心,它们从堆栈上分配内存,直到调用它的函数返回,该内存不会被释放。
如果在一个循环中,这个宏被反复调用几万次,将不可避免的产生stackoverflow。
在一个函数的循环体中使用A2W等字符转换宏可能引起栈溢出。
#include <atlconv.h>void fn(){while(true){{USES_CONVERSION;DoSomething(A2W("SomeString"));}}}让我们来分析以上的转换宏#define A2W(lpa) (\((_lpa = lpa) == NULL) ? NULL : (\_convert = (lstrlenA(_lpa)+1),\ATLA2WHELPER((LPWSTR) alloca(_convert*2), _lpa, _convert)))#define ATLA2WHELPER AtlA2WHelperinline LPWSTR WINAPI AtlA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars, UINT acp){ATLASSERT(lpa != NULL);ATLASSERT(lpw != NULL);// verify that no illegal character present// since lpw was allocated based on the size of lpa// don't worry about the number of charslpw[0] = '\0';MultiByteToWideChar(acp, 0, lpa, -1, lpw, nChars);return lpw;}关键的地方在alloca 内存分配内存上。
VC++ 的MFC 和ATL 及COM 的基本概念解析
VC++ 的MFC 和ATL 及COM 的基本概念解析一、什么是MFC微软基础类(Microsoft Foundation Classes),实际上是微软提供的,用于在C++环境下编写应用程序的一个框架和引擎,VC++是WinOS下开发人员使用的专业C++ SDK(SDK,Standard SoftWare Develop Kit,专业软件开发平台),MFC就是挂在它之上的一个输助软件开发包,MFC作为与VC++血肉相连的部分(注意C++和VC++的区别:C++是一种程序设计语言,是一种大家都承认的软件编制的通用规范,而VC++只是一个编译器,或者说是一种编译器+源程序编辑器的IDE,WS,PlatForm,这跟Pascal和Dephi的关系一个道理,Pascal是Dephi的语言基础,Dephi使用Pascal规范来进行Win下应用程序的开发和编译,却不同于Basic 语言和VB的关系,Basic语言在VB开发出来被应用的年代已经成了Basic语言的新规范,VB新加的Basic语言要素,如面对对象程序设计的要素,是一种性质上的飞跃,使VB既是一个IDE,又成长成一个新的程序设计语言),MFC同BC++集成的VCL一样是一个非外挂式的软件包,类库,只不过MFC类是微软为VC++专配的..MFC是Win API与C++的结合,API,即微软提供的WinOS下应用程序的编程语言接口,是一种软件编程的规范,但不是一种程序开发语言本身,可以允许用户使用各种各样的第三方(如我是一方,微软是一方,Borland就是第三方)的编程语言来进行对Win OS下应用程序的开发,使这些被开发出来的应用程序能在WinOS下运行,比如VB,VC++,Java,Dehpi编程语言函数本质上全部源于API,因此用它们开发出来的应用程序都能工作在WinOS的消息机制和绘图里,遵守WinOS作为一个操作系统的内部实现,这其实也是一种必要,微软如果不提供API,这个世上对Win 编程的工作就不会存在,微软的产品就会迅速从时尚变成垃圾,上面说到MFC是微软对API函数的专用C++封装,这种结合一方面让用户使用微软的专业C++ SDK来进行Win下应用程序的开发变得容易,因为MFC是对API的封装,微软做了大量的工作,隐藏了好多内节程序开发人员在Win下用C++ & MFC编制软件时的大量内节,如应用程序实现消息的处理,设备环境绘图,这种结合是以方便为目的的,必定要付出一定代价(这是微软的一向作风),因此就造成了MFC对类封装中的一定程度的的冗余和迂回,但这是可以接受的..最后要明白MFC不只是一个功能单纯的界面开发系统,它提供的类绝大部分用来进行界面开发,关联一个窗口的动作,但它提供的类中有好多类不与一个窗口关联,即类的作用不是一个界面类,不实现对一个窗口对象的控制(如创建,销毁),而是一些在WinOS(用MFC编写的程序绝大部分都在WinOS中运行)中实现内部处理的类,如数据库的管理类等,学习中最应花费时间的是消息和设备环境,对C++和MFC的学习中最难的部分是指针,C++面向对像程序设计的其它部分,如数据类型,流程控制都不难,建议学习数据结构C++版..二、什么是COM----COM的发展及其局限性所谓COM(Componet Object Model,组件对象模型),是一种说明如何建立可动态互变组件的规范,此规范提供了为保证能够互操作,客户和组件应遵循的一些二进制和网络标准。
用ATL创建COM组件详细解说
用ATL创建COM组件详细解说用ATL创建COM组件详细解说用ATL创建COM组件一、创建一个模型(工程) MyProj。
二、给模型(工程)增加一个组件 MyCom。
三、给组件增加方法(函数) MyF1、MyF2、MyF3、MyF4。
一、创建模型(工程) MyProj在VC++6.0工作平台中,点击菜单 File 下的 New 菜单项,在出现的New 对话框中选中Projects 卡片,在列表框中选中ATL COM AppWizard(活动模板库组件导航)。
在Project Name 编辑框中输入项目名如MyProj ,并选择合适的Location 后,按确认按钮进入下一个对话框:ATL COM Appwizard - step 1 of 1,在Server Type 中选择Dynamic Link Library [ DLL ],即进程内服务器,这是最快的组件。
选中 Support MFC 选择项。
在按下 Finish 和 Ok 按钮后,一个组件的框架已经建立。
二、给模型增加组件MyCom在VC++ 菜单Insert 中选中New ATL Object…菜单项,出现ATL Object Wizard 对话框。
在左边的Category 中选择Objects,右边的Objects 中选中Simple Object 项。
按 Next 按钮。
在出现的 ATL Object Wizard 属性对话框中 Names 卡片中的八个编辑框中左上方的 Short Name 编辑框中输入短名如 MyCom ,其他七个编辑框的内容会自动生成。
然后按确认按钮退出。
三、给组件增加方法(函数) MyF1、MyF2、MyF3、MyF4在 VC++工作平台的左边的 Workspace 的 ClassView 卡片中找到接口IMyCom 项,按右键,在出现的快捷菜单中选择Add Method …,出现 Add Method to Interface 对话框,在对话框中输入要增加的函数的函数名、参数和返回值类型。
ATL消息处理机制
ATL消息处理机制ATL消息机制的探究作者:后知后觉(307817387)任何的框架,包括MFC或者ATL,创建并显示窗口,处理窗口消息都逃不过RegisterClass、CreateWindow和Message Loop。
对于ATL也是一样的道理,下面就来细说一下ATL的消息处理机制。
重要的部分我会用红色标识出来。
1,注册窗口类CWindowImpl类使用一个DECLARE_WND_CLASS(NULL)的宏来定义WNDCLASS的信息#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; \}这里有一个很重要的信息,那就是StartWindowProc,这个是定义的默认的窗口处理函数。
先提醒一下,后面有具体说明。
CWndClassInfo的定义如下: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){returnAtlWinModuleRegisterWndClassInfoW(&_AtlWinModule,&_AtlBaseModule, this, p);}};其中的Register方法会注册一个窗口类。
图文-ATL创建连接点组件(添加EVent)
ATL7.1创建连接点组件分类:COM(+)/ATL/ATL Server2006-09-01 16:44 2458人阅读评论(2) 收藏举报interfacepropertiesmethodsnulleventsimport目录:ATL7.1创建连接点组件目录:一基础理论:1) 源对象和接收器对象2) 建立/断开连接3)连接点4) 同时支持多个连接点连接点容器:连接点映射表激发事件二工程范例:1)创建EventSource的ATL项目2)添加组件类3)添加事件方法一基础理论:1) 源对象和接收器对象接收器对象实现某个接口,源对象拥有该接口的指针,源对象可以调用该接口的方法。
从而形成源对象以事件的方式通知接受器对象的效果。
一个连接包含两部分,源对象和接收器对象。
如图:ISpeaker接口和_ISpeakerEvents接口对于各自的实现对象Demagogue和EarPolitic 对象来说,只是个普通的接口。
但是,由于Demagogue拥有了EarPolitic对象实现的_ISpeakerEvents接口的指针,使得_ISpeakerEvents变得特殊起来,Demagogue可以调用它的方法来通知EarPolitic对象,形成了“事件”。
2) 建立/断开连接连接由EarPolitic对象建立,连接也由EarPolitic对象断开。
如果EarPolitic对象要建立连接,它需要通知Demagogue对象它已经实现了_ISpeakerEvents接口。
如果要断开连接,它也需要通知Demagogue对象。
如何通知呢:Demaogogue对象应该针对_ISpeakerEvents接口实现一个对应接口IConForSpeakerEvents,该接口要提供Advise和UnAdvise两个方法,这样EarPolitic对象通过调用这两个方法来通知Demagogue对象建立连接或者断开连接。
3)连接点IConForSpeakerEvents接口存在的目的只是为了让EarPolitic对象获得建立、断开对_ISpeakerEvents接口的方法。
ATL
ATLVisual Studio .NET 2003活动模板库(ATL) 是一套基于模板的C++ 类,使您可以创建小型、快速的组件对象模型(COM) 对象。
它对主要的COM 功能具有特殊支持,这些功能包括常用实现、双重接口、标准COM 枚举数接口、连接点、分开的接口和ActiveX 控件。
如果需要做大量的ATL 编程工作,则需要了解有关属性的更多内容。
属性是Visual C++ .NET 中的一项新功能,旨在简化COM 编程。
有关更多信息,请参见属性化编程。
本节内容开始ATL 教程带领您完成创建控件的过程,在此过程中将阐释一些ATL 基础知识。
属性教程带领您完成使用属性和事件创建客户端和服务器应用程序的过程。
COMCOM 和ATL 介绍介绍组件对象模型(COM) 背后的主要概念。
本文还简要解释了什么是ATL 以及何时应该使用它。
ATL COM 对象基础知识讨论各种ATL 类之间的关系以及如何实现那些类。
双重接口和ATL从ATL 的角度描述双重接口。
ATL 集合和枚举数描述ATL 中集合和枚举数的实现与创建。
控件复合控件基础知识提供创建复合控件的逐步骤说明。
复合控件是一种ActiveX 控件,它可以包含其他ActiveX 控件或Windows 控件。
ATL 控件包容FAQ讲述有关使用ATL 承载控件的基本问题。
ATL COM 属性页向您说明如何指定和实现COM 属性页。
ATL 对DHTML 控件的支持提供创建DHTML 控件的逐步骤说明。
事件和连接点ATL 连接点说明什么是连接点以及ATL 是如何实现它们的。
事件处理和ATL描述使用ATL 的IDispEventImpl 和IDispEventSimpleImpl 类处理COM 事件所需要的步骤。
线程处理ATL 和自由线程封送拆收器提供有关“ATL 简单对象向导”选项的详细信息,这些选项允许类聚集自由线程封送拆收器(FTM)。
指定项目的线程模型描述可用于控制与项目中的线程处理相关的运行时性能的宏。
ATL接口映射宏详解(下)
ATL接口映射宏详解(下)五.COM_INTERFACE_ENTRY_AGGREGATE(iid, punk) 参ATL例程COMMAP这一节中将介绍ATL中用于聚集对象的宏。
聚集对象的概念请参阅其它参考书。
现在先看一看这个宏的典型用法:class CAgg :public IDispatchImpl< IAgg, &IID_IAgg, &LIBID_AGGREGLib >,public ISupportErrorInfo,public CComObjectRoot,public CComCoClass< CAgg,&CLSID_CAgg >{.....};CAgg是一个聚集类,它的实现与一般的ATL组件没有区别,只是注意在它的类定义中不要加入DECLARE_NO_AGGREGATABLE.class COuter :public CChainBase,public IDispatchImpl< IOuter, &IID_IOuter, &LIBID_COMMAPLib >,public CComCoClass< COuter,&CLSID_COuter >{HRESULT FinalConstruct();void FinalRelease();BEGIN_COM_MAP(COuter)COM_INTERFACE_ENTRY_AGGREGATE(IID_IAgg, m_pUnkAgg.p)END_COM_MAP()DECLARE_GET_CONTROLLING_UNKNOWN()CComPtr< IUnknown > m_pUnkAgg;};COuter包含了聚合组件CAgg,它包含了几个不同之处:(1)加入了COM_INTERFACE_ENTRY_AGGREGATE(IID_IAgg, m_pUnkAgg.p)宏。
ATL与WTL学习
第一部分 - ATL 中的 GUI 类•下载示例工程 - 45.5 KB本章内容•README.TXT•本系列介绍•第一部分介绍•ATL 背景知识o ATL 和 WTL 的历史o ATL 风格的模板•ATL 窗口类•定义窗口实现o填充消息映射•高级消息映射链和嵌入(Mix-in)类•ATL EXE 的结构o VC 6 的情形o VC 7 的情形•ATL 中的对话框•就要到 WTL 了,我保证!•修订历史README.TXT在继续或者在本文的讨论板块中发布帖子之前,我希望你能先阅读以下内容。
本系列原来是为 VC 6 用户写的,介绍 WTL 7.0 的内容。
现在 VC 8 已经出来了,我觉得也到了更新本系列来介绍VC 7.1 的时候了。
;)(不过,VC 7.1 的从 6 到 7 的自动转换工作并不是总能平滑地完成,所以 VC 7.1 的用户在试着使用示例源代码的时候可能会遭遇失败)因而,我将继续下去,持续更新本系列。
文章将更新到可以反映 WTL 7.1 的特性,并会在下载的源代码中包括 VC 7.1 的工程。
针对 VC 2005 用户的重要提示:VC 2005 的 Express 版本并不附带 ATL 或者 MFC,因此不能使用此版本编译ATL 或者 WTL 工程。
如果你在使用 VC 6,那你就需要有 Platform SDK。
没有它你将不能使用 WTL。
你可以使用Web 安装版本或者下载 CAB 文件或者是ISO 映像,然后在本地运行安装程序。
请使用工具把 SDK 的 include 以及 lib 目录加入到 VC 的搜索路径中,该工具可以在 Platform SDK 程序组中的Visual Studio Registration文件夹下找到。
即使你在用 VC 7,使用最新的 Platform SDK 仍然是一个好主意,因为你可以得到最新的头文件和库。
你需要有 WTL。
可以从微软下载版本 7。
在文章"Introduction to WTL - Part 1"以及"Easy installation of WTL"中有一些关于安装的提示。
宏命令语法哈登
宏命令语法哈登一、什么是宏命令有了宏命令,可以很大程度的解放双手,开启懒人脸滚键盘模式,本篇文章就简单讲解一下基本宏指令,并尝试完成一套一件输出宏二、基本宏速查表#showtooltip 多用于宏命令的开头,让按钮显示技能的说明和技能的图标/cast xxx 施放技能,如/cast 意气风发/castsequence reset=6 按顺序释放技能,重置条件为6s。
6s 后技能列表初始化,跳转到第一条技能/use 饰品名字使用饰品三、详解宏指令#showtooltip多用于宏命令的开头,让按钮显示技能的说明和技能的图标,默认是问号。
若后边跟着技能名字,则宏图标则更换为技能图标。
例如:#showtooltip 死亡之握/cast施放技能,如/cast 意气风发可添加参数“!”,如 /cast !自动攻击。
!表示取非的操作,如果自动攻击正在执行中,则不进行此操作。
若没有此技能正在执行,则执行此技能。
/castsequence当你第一次点击这个宏时,它施放列表中的第一个法术,第二次点击的时候,施放第二个法术,以此类推。
当它施放完最后一个时,又回到起始点开始循环。
-如果法术无法施放(冷却,超出距离,法力不足等等情况下),序列就不会走到下一个法术,下一次你再点这个宏的时候,它会再度尝试施放第一个法术.-你可以给这个宏附加之前提到过的条件选项,但只能对整个序列起作用,不能作用到每一个。
/castsequence 附加参数resetreset=N/target/combat/shift/alt/ctrl“reset=target”,target为重置时间,例如6,当超过6s后,/castsequence后的技能列表初始化,再次执行宏,将会重新从第一个技能开始执行。
“reset=target”,那序列将在你改变目标时就重置到起始位置“reset=combat”,每当你脱离战斗,这个序列就会被重置了。
“reset=shift/alt/ctrl”,你使用时是否按住了特定的按键,按住就被重置。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2.COuter::_InternalQueryInterface(...)
1.ATL::CComObject< COuter >::QueryInterface(...)
解释:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
ATLASSERT(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
ATLTRY(p = new T1(pv))
三、COM_INTERFACE_ENTRY_TEAR_OFF(iid, x) 参考ATL例程Beeper、COMMAP
使用这个宏的目的就是为了把一些很少用到的接口放在一个单独的组件中实现,仅当查询到这个接口时,才创建这个组件,并且当它的引用计数减为0时就会被释放掉。我们知道ATL中组件是通过多重继承实现的,每继承一个接口,在为它分配的内存块中就会多一个虚函数表指针,用这个宏就可以为每个组件的实例节省下这一个虚函数表指针来(一个指针4个字节,好象也不多啊,呵呵)
{
return m_pOwner->QueryInterface(iid, ppvObject);
}
还记得我们创建的分割组件是CComTearOffObject< CTearOff1 >吗?现在执行查询操作的是它的成员函数。它的实现很简单,事实上它什么也没做,仅仅是把它交给它的外部对象(即CComObject< COuter >)去做了。还记得m_pOwner是在构造函数里赋值的吧。现在是否感到有些不妙呢?呵呵
1--4:这些代码已经遇到过很多次了,我们还是集中精力看看核心代码:
ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
{
_ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;
return pcd->pFunc(pv, iid, ppvObject);
}
struct _ATL_CREATORDATA
{
_ATL_CREATORFUNC* pFunc;
};
我们继续用我们的老办法来跟踪一下看看它的执行过程。假设pOuter是我们已经获得的组件的IOuter接口指针。
执行pOuter->QueryInterface(IID_ITearOff1, (void **)&pTear1);
函数堆栈一:
7.CTearOff1::_InternalQueryInterface(...)
...........
COM_INTERFACE_ENTRY_TEAR_OFF(IID_ITearOff1, CTearOff1)
END_COM_MAP()
...........
};
CTearOff1实现了Tear-off接口ITearOff1,实现方法与其他组件并无不同。唯一不同的是它从CComTearOffObjectBase继承,CComTearOffObjectBase定义如下:
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK) hRes = p->_InternalQueryInterface(riid, ppv);
}
还记得CTearOff1是从CComTearOffObjectBase继承的吗,这个基类包含了一个成员变量m_pOwner,现在它被赋值为指向它的外部对象的指针了。
7.现在终于把这个实现分割接口的组件创建了,剩下的在CTearOff1中查询ITearOff1的工作已经是重复劳动了,不再赘述。
6.ATL::CComInternalCreator< ATL::CComTearOffObject< CTearOff1 > >::CreateInstance(...)
5.ATL::CComObjectRootBase::_Creator(...)
4.ATL::AtlInternalQueryInterface(...)
if (hRes != S_OK) delete p;
}
return hRes;
}
};
同我们所见到的大多数Creator类一样,它也只有一个静态CreateInstance函数。现在我们终于可以创建我们分割组件了,它不是CTearOff1,它也是经了一层包装的,是 CComTearOffObject! 现在我们再来看看它的构造函数干了些什么事:
2、3: 果然,现在已经不用再看下去了,剩下的将是重复我们在调用第一条查询操作所做的一切。这个过程很简单,但它也隐含说明了一点:若对一个实现分割接口的组件每查询一次它的接口,就会调用一个新的实例!!!在上例中,最后的结果pTear2和pTear1 是不一样的!!这显然是浪费!
在下一节中,我们将介绍一个可以解决这个问题的宏!
四.COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk) 参ATL例程COMMAP
这个宏与上一节所讲的COM_INTERFACE_ENTRY_TEAR_OFF宏最主要的不同就在于,当查询分割对象中其他接口时,不会再新建新的对象。下面还是先看看它的典型用法:
{
public:
CTearOff1(){}
~CTearOff1(){}
BEGIN_COM_MAP(CTearOff1)
COM_INTERFACE_ENTRY(ITearOff1)
END_COM_MAP()
HRESULT STDMETHODCALLTYPE get_Name(BSTR* pbstrName)
我们先来看看COM_INTERFACE_ENTRY_TEAR_OFF的定义:
#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)\
{&iid,\
(DWORD)&_CComCreatorData<\
CComInternalCreator< CComTearOffObject< x > >\
下面我们来看它的典型用法:
class CTearOff1: //该类是专门用来实现分割接口ITearOff1的
public IDispatchImpl< ITearOff1, &IID_ITearOff1, &LIBID_COMMAPLib >,
public CComTearOffObjectBase //外部对象
typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void* pv, REFIID riid, LPVOID* ppv);
template < class Creator >
_ATL_CREATORDATA _CComCreatorData::data = {Creator::CreateInstance};
{
//..........
while (pEntries->pFunc != NULL)
{
BOOL bBlind = (pEntries->piid == NULL);
if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
>::data,\
_Creator},
看不太明白,还是继续我们路由得了
5:原来_Creator是CComObjectRootBase的静态成员函数,它可是COuter的一个基类啊,所以才可以这样写而不会编译出错。看看它的实现吧:
static HRESULT WINAPI _Creator(void* pv, REFIID iid, void** ppvObject,DWORD)
template < class Owner, class ThreadModel = CComObjectThreadModel >
class CComTearOffObjectBase : public CComObjectRootEx
{
public:
typedef Owner _OwnerClass;
执行pTear1->QueryInterface(ITearOff1, (void **)&pTear2)
一个实现分割接口的组件有可能包含多个分割接口,我们来检测一下它的查询过程。
函数堆栈二:
பைடு நூலகம்
4..............
3.COuter::_InternalQueryInterface(...)
{
*pbstrName = ::SysAllocString(L"ITearOff1");
return S_OK;