|
类型库
在 WinTel 平台, COM接口获得关于它存放在系统注册信息。这些接口可能被呼叫MIDL(微软接口定义语言)编译器编辑的"接口定义语言"(IDL)建立,命令行app 。 我不假设对您充分地理解IDL并能够在笔记里定义接口。 但是,因为MIDL工具只被MSVC装载,如果你有它,那么你就有VC,因此我使用Visual Studio 工具建立我原先的接口定义文件。
命名MyComApp,我开始一项ATL工程,并且插入一个新ATL对象并且选择简单的对象名称MyCom(我的app将使用相同的术语)。然后类向导(wizard)创造了空白 IMyCom 接口。 ATL 属性需要被设置单线, 习惯接口, 并且没有集成。 然后我由在类浏览器合适的点击IMyCom 接口建立了接口, 和使用增加属性插入SetValue, GetValue属性, 和 RaiseValue方式。 然后我保存并且关闭app把MyComApp.idl 文件复制到我的汇编程序文件夹。
这VC ATL接口定义文件的输出(.idl):
//MyCom.idl : MyCom.dll的IDL资源
//
//MIDL工具将处理这个文件
//生产类型信息库(MyCom.tlb)并且设置代码
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(F8CE5E41-1135-11d4-A324-0040F6D487D9),
helpstring("IMyCom Interface"),
pointer_default(unique)
]
interface IMyCom : IUnknown
{
[propget, helpstring("property Value")]
HRESULT Value([out, retval] long *pVal);
[propput, helpstring("property Value")]
HRESULT Value([in] long newVal);
[helpstring("method Raise")]
HRESULT Raise(long Value);
};
[
uuid(F8CE5E42-1135-11d4-A324-0040F6D487D9),
version(1.0),
helpstring("MyComApp 1.0 Type Library")
]
library MyComLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(F8CE5E43-1135-11d4-A324-0040F6D487D9),
helpstring("MyCom Class")
]
coclass MyCom
{
[default] interface IMyCom;
};
};
这个文件可能为进一步的接口定义作为原型使用。 注意它包含3 GUIDs, 每一个接口、coclass, 和类型库。为新应用这些必须是能被改变和分明。
除了接口本身,这个定义文件应该看起来几乎不言而喻:
[propget, helpstring("property Value")] HRESULT Value([out, retval] long *pVal); [propput, helpstring("property Value")] HRESULT Value([in] long newVal); [helpstring("method Raise")] HRESULT Raise(long Value);
这接口同样被定义在MASM:
GetValue PROTO:DWORD,:DWORD
SetValue PROTO:DWORD,:DWORD
RaiseValue PROTO:DWORD,:DWORD
区别大... 但简单很原因。 为类型库写的接口可能一样全面, 并且针对象Visual Basic那样的客户, 并且VB尽量被设计握程序员的助手。为了保持对VB 用户接口简单操作,"特性"的概念被使用。可以"确定(set)"或者"得到(get)"特性值,因此这两个功能好像对VB 程序员(物体参考只移到等于操作者的另一边)是相同的。一种"方法"做一些改变或者执行与物体有关的一些行动。
建立连接类型(type lib), 在命令行使用MIDL如下:
MIDL MyCom.idl
这主要产生您能忽略的几份输出文件,以及最重要MyCom.tlb, 我们的类型库。 这库应该被增加到资源提交给的dll
1 typelib MyCom.tlb
做它第一资源要素是重要的, 因为我们以后将使用 LoadTypeLib API功能提取这个库, 这个功能准备发现库在位置1 (除非告诉做其他) 。 如此简单, 我们保留它在位置1 。
记录组成部分
---------------------------------------------------------------------------------------------------------------------The DllRegisterServer 和DllUnregisterServer 为我们记录组成部分。dll(或ocx,真正好的加设计者扩展的dll) 。这些被做为登记入口:
HKEY_CLASSES_ROOT\CMyCom
(Default) "CMyCom simple client"
HKEY_CLASSES_ROOT\CMyCom\CLSID
(Default) "{A21A8C43-1266-11D4-A324-0040F6D487D9}"
HKEY_CLASSES_ROOT\CLSID\{A21A8C43-1266-11D4-A324-0040F6D487D9}
(Default) "CMyCom simple client"
HKEY_CLASSES_ROOT\CLSID\{A21A8C43-1266-11D4-A324-0040F6D487D9}\CMyCom
(Default) "CMyCom"
HKEY_CLASSES_ROOT\CLSID\{A21A8C43-1266-11D4-A324-0040F6D487D9}\InprocServer32
(Default) "C:\MASM32\MYCOM\MYCOM.DLL"
ThreadingModel "Single"
HKEY_CLASSES_ROOT\TypeLib\{A21A8C42-1266-11D4-A324-0040F6D487D9}
(Default) (value not set)
HKEY_CLASSES_ROOT\TypeLib\{A21A8C42-1266-11D4-A324-0040F6D487D9}\1.0
(Default) "MyCom 1.0 Type Library"
HKEY_CLASSES_ROOT\TypeLib\{A21A8C42-1266-11D4-A324-0040F6D487D9}\1.0\0
(Default) (value not set)
HKEY_CLASSES_ROOT\TypeLib\{A21A8C42-1266-11D4-A324-0040F6D487D9}\1.0\0\win32
(Default) " C:\masm32\COM\MyCom \MYCOM.DLL"
HKEY_CLASSES_ROOT\TypeLib\{A21A8C42-1266-11D4-A324-0040F6D487D9}\1.0\FLAGS
(Default) "O"
HKEY_CLASSES_ROOT\TypeLib\{A21A8C42-1266-11D4-A324-0040F6D487D9}\1.0\HELPDIR
(Default) "C:\masm32\COM\MyCom" 这里一关键价值是易变的, 那是服务器dll的路径和名字。 在我的系统我安置它在"C:\MASM32\COM\MYCOM\MYCOM.DLL "当我记录组成部分时,这被发现, 因为DllRegisterServer的另外一个功能是通过产生GetModuleFileName储存路径发现dll本身。
这是许多一个人台小服务器的信息。我们需要给实例知道我们服务器的全部身份验证{A21A8C43-1266 11D-4 A324-0040F-6D-487D-9 }以及CoCreateInstance的一个有效ID接口。我们不必知道它那些零部件在哪里,亦不设置它在特别目录里。 CoCreate API将通过登记设置追踪, 开始以CLSID 发现所有它需要知道建立的组成部分。一旦它有组成部分,如有必要它可能获悉装载类型库。
我们是幸运的,最后5 个登记入口是为我们通过RegisterTypeLib API做的。 在DllRegisterServer 我们呼叫一系列的登记功能设置前5个通道和价值, 然后产生RegisterTypeLib。DllUnregisterServer只间接通过这构造和删除它做的全部入口,然后产生UnRegisterTypeLib。当删除键时, 一定小心不要删除整个HKEY_CLASSES_ROOT\CLSID\tree, 否则你将在你系统完全搞乱你系统和uninstall部分隔activeX 零部件。
类型库被定义为"密集的一块(dense black blob)" 二进制数据。 微软公司显示的的内部结构唯一特性是前4个字节将是"MSFT"的ASCII 代码。为了获悉里面是什么,必须使用API方法。再次,这保持COM获得语言中立。
不为人知的执行
---------------------------------------------------------------------------------------------------------------------
MyCom 是非常简单的对象, 它impliments唯一有二个接口, IUnknown 和IMyCom 。因为这两个接口重叠, 返回的ppv指针不必派成两个接口中的任一个, 并且我们将满足非常简单的对象结构。如果您延伸到CoLib (部分库), 如果成倍增加您将看见一个非重复接口支持的,更复杂的对象结构被注册。
对象使用期由IUnknown 接口处理。 这些三个表面上简单的AddRef,Release, 和QueryInterface用法是相当强有力的, 而且被使用,因此每一个的功能性从未在另一个部分被复制。
这几次非重复功能或许促进为什么这样命名IUnknown。当DllGetClassObject产生时, 对象CLSID 和一个具体接口IID 是通过定义需要什么建立的。 想一下: 在我们建立它之前,我们实际上要求 DllGetClassObject在对象里执行QueryInterface. 那不是所发生的,我们不想要复制(即,QueryInterface两相同实施,一处是在QueryInterface它自己,一处是DllGetClassObject)的功能性。 如果不是别的,我们不想保持有相似代码的二个部分。
相反, 当DllGetClassObject产生时, 我们仅仅建立CLSID定义的对象。 实际上, 我们创造一个未知的对象。未知的是: 这个物体能支持我们需要的接口吗?
这个问题很容易回答。在未知的对象DllGetClassObject将产生QueryInterface。 如果它真地支持接口, 这参考将返回。 如果它不支持它, 对象被删除, 并且DllGetClassObject返回一条失败代码。
AddRef实现相当简单。因为我们有一个简单的对象结构, 并且"this"是这个结构基地址, 我们能直接地访问所有成员对象。
AddRef_MC proc this_:DWORD
mov eax, this_
inc (MyComObject ptr [eax]).nRefCount
mov eax, (MyComObject ptr [eax]).nRefCount
ret ; note we return the object count
AddRef_MC endp
AddRef 有点异常,因为它不返回HRESULT (失败代码), 返回对象数。在COM内返回值未定义,但是返回数字是常规的。 上一页 [1] [2] [3] 下一页 |