`
阅读更多

4.1 COM接口类型概述

描述:除了Vtable结构的接口外,COM还支持另外两种接口类型:即派发接口(Dispinterface)和双向接口(Dual Interface)

4.1.1 Vtable 接口

1)Vtable类型接口的缺点是需要在编译时与客户机程序进行某种形式的绑定,也就是客户机必须清楚编译阶段的接口方法和接口参数.由于这些静态信息并不包含组件中方法的具体实现,所以COM的多态性刚好解决了这一问题.
2)一个接口的函数特征是在编译阶段被定义的,而不是在接口方法的实际实现时被定义的

4.1.2 IDispatch 接口:Dispinterface

1)与Vtable不同,它可以在运行时决定方法的名字和参数,但速度慢.

4.1.3 双向接口

1)一个双向接口(dual-interface)就是一个普通的COM Vtable接口和一个Dispinerface接口的结合


4.2 组件及其接口的描述

描述:为了使客户访问组件的接口,必须对接口进行描述

1)描述Vtable接口的布局,每一个方法参数的类型以及参数的数量
2)对接口的描述可用于产生基于特定语言的绑定信息
3)提供支持调度功能,使客户能够跨进程和跨计算机访问组件


4.2.1 类型信息

1)组件使用IDL对它们的接口进行描述.用MIDL.EXE可将它们编译到类型库中.
2)组件使用一个二进制的,独立于语言的文件对它本身的功能进行描述,也就是type library(类型库)

4.2.1.1 语言绑定

IDL所描述的组件,也可以产生特定语言的文件.MIDL编译器只为C/C++语言产生绑定信息,其他语言则使用类型库来产生编译时绑定.

4.2.1.2 生成PROXY/STUB

MIDL编译IDL文件可得到proxy/stub DLL代码以及make文件.proxy/stub DLL为每一个接口实现标准调度


4.2.2 调度

1)概念:调度是指在进程和计算机之间进行函数参数和返回值传输的一个过程.
2)在多线程应用程序中,COM使用调度来同步对组件的访问,因而即使你的组件只支持在进程内执行,有时也需要提供调度支持.
3)COM在实现调度时使用一个proxy和一个stub.调度时,客户进程空间创建proxy对象,组件进程空间创建一个stub对象.
4)下列情况需要调度
a.访问一台计算机不同进程中的组件时
b.访问不同计算机上的组件时(DCOM).
c.同一个进程组件的不同部分之间传输接口指针时.


4.3 分布式COM

1)概念:分布式COM是指COM可以将它的组件分布在网络中,并能够在不同的计算机上对它进行访问


4.4 标准调度

1)MIDL编译器编译IDL将产生一些文件用于构造proxy/stub DLL.
2)为了能够创建并注册proxy/stub DLL,必须为你的组件提供标准调度
3)proxy/stub必须在每一台客户机上注册

 MIDL 编译产生的proxy/stub文件
 
文件    说明
ProjectNamePS.mk 该make文件生成名为ProjectNamePS.dll的proxy/stub动态链接库
ProjectName.h  与C 和 C++ 兼容的接口声明头文件
ProjectName_p.c  包含实现proxy/stub代码的源文件
ProjectName_i.c  包含接口GUID的C源文件
DLLDATA.C  为proxy/stub代码实现DLL的C源文件


4.4.1 类型库(通用)调度

1)类型库使用标准的自动化调度器实现调度,前提是你的接口方法中只能使用与自动化(Automation)兼容的类型
2)为了使用类型库调度,应确保组件中只使用自动化类型,并在IDL文件的接口声明中添加oleautomation属性

// 由于IMath接口只使用与自动化兼容的类型,因此你可以使用类型库调度
[
 object,
 uuid(8C30BC10-88F2-11D0-A756-B04A12000000),
 oleautomation,
 helpstring("IMath Interface"),
 pointer_default(unique)
]

interface IMath : IUnknown
{
 HRESULT Add([in]long,[in]long,[out,retval]long *);
 HRESULT Subtract([in] long, [in] long, [out,retval]long *);
 HRESULT Multiply([in] long, [in] long, [out,retval]long *);
 HRESULT Divide([in] long, [in] long, [out,retval] long *);
};

3)实现类型库调度所需的另一个步骤是,使用COM提供的RegisterTypeLibrary函数注册组件的类型库
4)ATL可以自动注册你的类型库


4.4.2 自定义调度

1)自定义调度可以使用任何机制实现进程间的通信,如RPC,TCP/IP..等等
2)自定义调度需要在你的组件中实现IMarshal接口


4.5 创建 Proxy/Stub DLL

1)定位 ProjectNamePS.mk 文件,运行命令创建DLL(ProjectNamePS.dll)

C:\MyProject>NMAKE -f ProjectNamePS.mk

2)将其加到注册表中

C:\MyProject>REGSVR32 ProjectNamePS.dll

3)HKCR\Interfaces 注册表键中列出了系统中所有接口所支持的proxy/stub DLL


4.6 接口定义语言

1)IDL 用于对接口以及一些详细的信息进行描述,由它生成的结果文件被另外一种语言处理

4.6.1 基本语法和布局

1)属性值包含在一对方括号中,用于对关键字(如interface)进行修改

[
 uuid(8C30BC10-88F2-11D0-A756-B04A12000000),
 helpstring("IMath Interface"),
 pointer_default(unique)
]

interface IMath:IUnknown {...};

2)如果IDL文件包含 library 关键字,MIDL 编译器将产生一个类型库.
3)每一个IDL关键字被它前面的属性所修改

[
 uuid(8C30BC10-88F2-11D0-A756-B04A12000000),
 version(1.0),
 helpstring("Chapter4_Server 1.0 Type Library")
]

library CHAPTER4_SERVERLib
{
 importlib("stdole32.tlb");
 importlib("stdole2.tlb");
 [
  uuid(5FB0C22F-3343-11D1-883A-444553540000),
  helpstring("Math Class")
 ]
 
 coclass Math
 {
  [default] interface IMath;
  interface IMath2;
  interface IAdvancedMath;
  interface IComponentInfo;
 };
};


4.6.2 接口的声明:方法和属性

1)如果想指定特殊的接口属性,必须在interface关键字之前声明,然后声明该接口所支持的方法

[
 object,
 uuid(8C30BC10-88F2-11D0-A756-B04A12000000),
 oleautomation,
 helpstring("IMath Interface"),
 pointer_default(unique)
]

interface IMath : IUnknown
{
 [helpstring("Method Add")]
 HRESULT Add([in]long,[in]long,[out,retval]long *);
 [helpstring("Method Subtract")]
 HRESULT Subtract([in] long, [in] long, [out,retval]long *);
 HRESULT Multiply([in] long, [in] long, [out,retval]long *);
 HRESULT Divide([in] long, [in] long, [out,retval] long *);
};

2)属性代表组件的数据成员,可以对它直接赋值

interface IMath2:IUnknown
{
...
 [propget,helpstring("property VesionNumber")]
 HRESULT VersionNumber([out,retval]long * pVal);
 [propput,helpstring("property VersionNumber")]
 HRESULT VersionNumber([in] long newVal);
};


4.7 IDL 数据类型

1)使用内部的自动化调度器(automation marshaler)将接口参数的类型限制在自动化类型之内


4.7.1 数组

1)组件预先能够知道树组的大小

HRESULT Sum([in] short sArray[5],[out,retval]long *plResult);

2)通过size_is属性向服务器传送任意大小的数组

HRESULT Sum([in] short sArraySize,
     [in,size_is(sArraySize)]short sArray[],
     [out,retval]long *plResult);
    
而服务器上代码非常简单

STDMETHODIMP CMath::Sum(short sArraySize,short sArray[],long *plResult)
{
 *plResult = 0;
 while(sArraySize)
 {
  *plResult += sArray[--sArraySize];
 }
 return S_OK;
}


4.7.2 字符串

1) 用string属性来描述

typedef struct tagCOMPONENT_INFO
{
 [string]char* pstrAuthor;
 ...
} COMPONENT_INFO;


4.7.3 结构

1)由于IDL可以为组件的方法自动产生proxy/stub代码,因此我们可以构造复杂的数据结构在COM接口进行参数传递

typedef struct COMPONENT_INFO
{
 [string]char * pstrAuthor;
 short sMajor;
 short sMinor;
 BSTR  bstrName;
}COMPONENT_INFO;

interface IComponentInfo:IUnknown
{
 [helpstring("method get_Info")]
 HRESULT get_Info([out]COMPONENT_INFO **pInfo);
 [helpstring("method get_Name")]
 HRESULT get_Name([out] BSTR * bstrName);
};


4.7.4  ENUM 类型

1)IDL支持枚举类型,但它是与语言无关的

typedef
[
 uuid(8C30BC10-88F2-11D0-A756-B04A12000000),
 helpstring("Math Operation Type")
]
enum mathOPERATION
{
 [helpstring("Add")] mathAdd = 0x0001,
 [helpstring("Subtract")] mathSubtract = 0x0002,
 [helpstring("Multiply")] mathMultiply = 0x0003,
 [helpstring("Divide")] mathDivide = 0x0004
} mathOPERATION;

//定义后即可使用

[helpstring("method Compute")]
HRESULT Compute([in] mathOPERATION enumOp,
  [in] long lOp1,
  [in] long lOp2,
  [out,retval]long * plResult );

使用MIDL编译上面代码,将产生一个C construct,并把它作为Chapter4_Server头文件的一部分

2)由MIDL为组件生成的类型苦也包含对结构类型的定义,非C++也可以使用该枚举类型

 

4.8 ATL 及 COM 数据类型  

4.8.1 接口指针

1)接口指针实际是指向一个C++抽象类Vtable的指针
2)有时候返回接口指针是很有必要的
// IDL entry
[propget,helpstring("property AdvancedMath")]
HRESULT AdvancedMath([out,retval]IAdvancedMath **ppVal);

// Implementation
STDMETHODIMP CMath::get_AdvancedMath(IAdvancedMath **ppVal)
{
 GetUnknown()->QueryInterface(IID_IAdvancedMath,(void **)ppVal);
 
 return S_OK;
}


4.8.2 C++ 智能指针

1)智能指针(Smart pointer)指的是在处理指针时隐藏了许多内存管理技术的C++类
2)智能指针封装了QueryInterface()/Release() 和 CoCreateInstance()/Release()两对方法,使用户不必担心COM指针是否被释放.
3)ATL 提供了两个智能指针类:CComPtr 和 CComQIPtr


4.8.3 CComPtr

1)释放CComPtr指针可以明确调用ptrMath.Release 或 ptrMath = 0,或不做处理
2)使用CComPtr

CComPtr<IMath> ptrMath;
HRESULT hr;
// This time use CoCreateInstance
hr = CoCreateInstance(CLSID_Math,
   NULL,
   CLSCTX_LOCAL_SERVER,
   IID_IMath,
   (void **)&ptrMath);
// Access the IMath interface
long lResult;
ptrMath->Add(134,353,&lResult);
cout<<"134 + 353 =" << lResult << endl;
...

3)注意点:由于CComPtr直接指向对外公开的一个Release方法,因此有可能错误地两次释放接口资源.多次调用Release方法可能会导致资源非法访问


4.8.4 CComQIPtr

1)CComQIPtr 当被实例化时将自动调用QueryInterface
2)CComQIPtr需要请求接口的IID作为参数

// Access IAdvancedMath
CComQIPtr <IAdvancedMath,&IID_IAdvancedMath> ptrAdvancedMath(ptrMath);

if (ptrAdvancedMath)
{
 ptrAdvancedMath->Factorial(12,&lResult);
 cout << "12! = " << lResult << endl;
 ptrAdvancedMath->Fibonacci(12,&lResult);
 cout << "The Fibonacci of 12 = " << lResult << endl;
}

3)Visual C++ 为 COM 接口提供了一个智能指针的内部实现._com_ptr_ 类提供了该实现.


4.8.5 BSTR

1)BSTR被声明为OLECHAR *,它是一个Unicode字符串.Win32函数可以方便将Unicode和ANSI字符串转换成BSTR类型

BSTR bstrDescription = 0;
BSTR bstrSource = 0;
pEl->GetDescription(&bstrDecription);
pEl->GetSource(&bstrSource);


USES_CONVERSION;
cout << OLE2T(bstrDecription) << endl;
cout << OLE2T(bstrSource) << endl;

::SysFreeString(bstrDescription);
::SysFreeString(bstrSource);

4.8.6 CComBSTR

1)CComBSTR是COM的BSTR类型的一个简单封装类

  CComBSTR 类方法
成员函数   说明
CComBSTR(...)  针对ANSI,Unicode以及BSTR字符串类型,CComBSTR类提供了多个构造函数
Append   将一个ANSI字符串附加到一个BSTR字符串的后面
AppendBSTR  附加一个BSTR字符串
Copy   返回一个BSTR的复制
Length   返回字符串的长度(字符的个数)

2)比较CComBSTR字符串

// BSTR comparison
CComBSTR bstrA("COM");
CComBSTR bstrB("COM");

if (::SysStringByteLen(bstrA) == ::SysStringByteLen(bstrB) && ::memcmp(bstrA,bstrB,::SysStingByteLen(bstrA)) == 0)
{
 cout << "bstrA == bstrB" << endl;
}


4.9 COM 的内存管理

1)在许多情况下,族件必须分配系统内存,并把它返回给客户机程序,但有时组件并不知道客户机程序什么时候停止使用所分配的内存.COM提供了几个专门的API,来处理这种问题

4.9.1 CoTaskMemAlloc 和 CoTaskMemFree

1)它们提供了内存函数(malloc和free)的一个封装.

COMPONENT_INFO *pInfo = (COMPONENT_INFO *)CoTaskMemAlloc(sizeof(COMPONENT_INFO));
ZeroMemory(pInfo,sizeof(COMPONENT_INFO));
// Do something with the structure
...
// Now free it
CoTaskMemFree(pInfo);


4.9.2 IDL 和内存管理

1)为了弄清楚又谁读一内存的分配和释放负责,必须查看IDL中方法的声明.通过 in 和 out 参数属性来指定.
2)规则如下:
a.对于只带 in 属性的参数,客户机程序负责分配和释放这些参数所需要的内存
b.对于只带 out 属性的参数,服务器负责分配这些参数所需要的内存,而客户机负责释放参数所占用的内存
c.对于具备in/out属性的参数,客户机分配内存并负责释放参数所占用的内存.
3)示例:

// Look up the ProgID

WCHAR * pProgID = 0;
ProgIDFromCLSID(guids[0],&pProgID);

// Add it to the listbox
USES_CONVERSION;
m_ControlList.AddString(W2A(pProgID));

// Free the memory
CoTaskMemFree(pProgID);


4.10  COM 中的错误处理

1)所有的COM接口方法的返回类型要么是void型,要么是HRESULT型.组件向客户机报告错误的基本机制一般是通过HRESULT返回码

4.10.1 ISupportErrorInfo

4.10.2 CreateErrorInfo 和 ICreateErrorInfo

1)当一个组件需要返回错误信息时,它可以通过CreateErrorInfo函数来创建错误信息.
2)在组件内创建并初始化一个错误对象
ICreateErrorInfo *plCEl;
if (SUCCEEDED(CreateErrorInfo(&plCEl)))
{
 // Set the GUID
 plCEl->SetGUID(iid);
 
 // Set the ProgID
 LPOLESTR lpsz;
 ProgIDFromCLSID(clsid,&lpsz);
 
 if (lpsz != NULL)
 {
  plCEl->SetSource(lpsz);
  CoTaskMemFree(lpsz);
 } 
 
 // Set any help infomation
 if (dwHelpID != 0 && lpszHelpFile != NULL)
 {
  plCEl->SetHelpContext(dwHelpID);
  plCEl->SetHelpFile(const_cast<LPOLESTR>(lpszHelpFile));
 }
 
 // Set the actual description of the problem
 plCEl->SetDescription((LPOLESTR)lpszDesc);
 
 // Associate the error with the current execution context
 IErrorInfo *pErrorInfo;
 if (SUCCEEDED(plCEl->QueryInterface(IID_IErrorInfo,(void **)&pErrorInfo)))
  SetErrorInfo(0,pErrorInfo);
  
 // Release the interface
 plCEl->Release();
 pErrorInfo->Release();
}

4.10.3 SetErrorInfo 和 IErrorInfo

1)一个错误对象可以使用CreateErrorInfo 和ICreateErrorInfo 接口来创建
2)创建完错误对象后可以用SetErrorInfo 和 IErrorInfo 将它们与当前正在执行的线程相关联。

分享到:
评论

相关推荐

    ATL学习笔记.doc

    6. 从向导中选择“ATL” -&gt; “ATL简单对象” -&gt; “添加”。 7. 在ATL简单对象向导中,输入类的简称,如“FIRSTclass”,并自定义ProgID(例如:FirstCOM.math)。点击“完成”。 8. 在类视图中,选择新添加的接口...

    windows shell、atl、https学习笔记

    总的来说,这个学习笔记涵盖了Windows系统核心组件、C++组件开发技术以及网络通信安全的关键概念,是一份全面的IT技术学习资料。通过深入学习这些内容,你可以提升自己在Windows平台上的软件开发和网络安全能力。

    ATL——com学习笔记

    在ATL中,默认情况下,每个类都会有一个自动创建的类工厂。然而,在某些情况下,我们可能希望替换这个默认的类厂,以实现更加灵活或者自定义的功能。 #### 实现步骤 1. **定义自己的类工厂**:首先需要继承自`...

    com学习笔记

    在COM学习笔记中,主要涉及的是COM线程管理的概念,包括线程单元(Thread Apartments,简称TA)以及线程如何与COM对象交互。 线程单元是COM中管理线程与对象交互的核心机制。它是一个逻辑上的容器,确保同一单元内...

    Visual C++程序设计学习笔记(1-12 源码)

    本资源包含的是Visual C++程序设计的学习笔记,涵盖了从基础到进阶的12个章节的源代码,对于学习和理解C++编程,特别是使用Visual C++环境编程,具有极高的参考价值。 首先,我们来详细探讨一下Visual C++的关键...

    ATL_OFFICE_COM

    4. **新建 文本文档 (3).txt**: 这可能是一个文本文件,可能包含了代码片段、笔记或其他辅助信息。 通过学习这些资源,开发者可以深入了解ATL和Office COM插件的开发,从而创建出自己的定制化Word插件,提高办公...

    VC学习笔记及项目代码1

    【VC学习笔记及项目代码1】是一份涵盖了多个VC++编程关键领域的学习资源,包括了Win32 API、MFC(面向对象的)框架、COM(Component Object Model,组件对象模型)及其ATL(Active Template Library,活动模板库)...

    COM学习笔记和测试程序

    本学习笔记和测试程序旨在帮助你深入理解COM的工作原理和实践应用。 1. **COM基础概念** - **组件**: 在COM中,组件是实现了COM接口的二进制模块,可以是DLL或EXE。 - **接口**: 接口定义了组件对外提供的服务,...

    vc++技术内幕第四版(清华版)学习笔记

    《VC++技术内幕第四版(清华版)》是C++编程领域的一本经典著作,尤其对于深入理解Microsoft的Visual C++编译器和MFC框架有...这份学习笔记则将书中的精华内容进行了整理,便于读者查阅和复习,是一份宝贵的参考资料。

    MFC学习笔记加windows32

    1. MFC架构:MFC由几个关键部分组成,包括框架类、文档/视图结构、对话框、控件、GDI(图形设备接口)以及ATL(Active Template Library)等。其中,框架类如CWinApp、CDocument、CView等,提供了应用程序的基本结构...

    EMF 自学笔记5

    ### EMF自学笔记知识点详述 #### 一、EMF框架概述 EMF(Eclipse Modeling Framework)是Eclipse项目中的一个子项目,它提供了一套用于构建模型驱动的开发工具和应用程序的框架。EMF的主要目标是简化元数据驱动应用...

    学习mfc的视频笔记

    这篇“学习MFC的视频笔记”显然是针对想要了解或深入学习MFC的初学者设计的,提供了视频教程的笔记内容,旨在帮助初学者快速入门。 1. **MFC基础**:MFC的核心是它的框架,它包括了各种C++类,如CWinApp、CWnd、...

    Matlab学习笔记#01

    在一直以来的学习当中,与matlab这款软件多有接触,但一直没有系统化地研究学习一下这款软件,趁着闲暇之余,整理一下自己有关matlab知识。 matlab技术支持搜索网站 一些基本的符号 ans→answer inf→∞ eps→2.2204...

    个人整理的资料

    "MyLearn.chm"可能包含了作者的学习笔记、教程或项目案例,涵盖了作者在VC++学习过程中的各种理解和感悟。"STL.chm"则很可能是关于标准模板库(Standard Template Library,STL)的详细解释,STL是C++编程中不可或缺...

    matlab学习笔记

    这篇学习笔记主要涉及了几个关键概念,包括标量、矩阵的迹、矩阵维度的获取、单位矩阵的生成、决策变量的定义以及几个相关的函数应用。 1. **标量(Scalar)**:在数学和计算机科学中,标量是一个单一的数值,不...

    YearUp:在 Year Up ATL 创建的文件的存储库

    8. **学习资源**:除了代码,"YearUp-master"可能还包括学习笔记、教程链接、问题解答等辅助学习资料。 9. **团队协作**:Year Up的项目可能涉及多人合作,因此可能包含关于团队分工、沟通和协作的最佳实践。 10. ...

    matlab学习笔记:如何在matlab中如何自定义函数和匿名函数

    matlab的程序语言在拓展性上没有C++齐全,但也足够应付一些简单的程序,而且比C++更易于学习。 input():数据的输入函数。 disp():数据的输出函数。 pause(延迟时间):延迟函数。 其余的程序语句和C++大同小异,当然...

    Windows编程笔记1

    在Windows编程领域,MFC(Microsoft Foundation Classes)是一个强大的C++库,它为开发者提供了构建Windows应用程序的...对于想要学习Windows编程的人来说,这些笔记提供了一个很好的起点,帮助他们理解和应用MFC库。

Global site tag (gtag.js) - Google Analytics