用标准C编写COM(一)(三)

2014-11-24 09:46:32 · 作者: · 浏览: 4
face的第三个参数,注意AddRef和Release也传递同样的我们用于调用他们的结构指针。
好,在我们没有忘记前,让我们添加HRESULTSTDMETHODCALLTYPE到SetString和GetString:
[cpp]
typedef HRESULTSTDMETHODCALLTYPE SetStringPtr(IExample *, char *);
typedef HRESULTSTDMETHODCALLTYPE GetStringPtr(IExample *, char *, long);
HRESULT STDMETHODCALLTYPESetString(IExample *this, char * str)
{ ...
return(0);
}
HRESULT STDMETHODCALLTYPEGetString(IExample *this, char *buffer, long value)
{
...
return(0);
}
总之,一个COM对象基本上是一个C++类。这个C++类是一个总是以它的虚表指针(一个函数指针数组)为起点的结构。并且在虚表中最开始的三个函数总是被命名为QueryInterface、AddRef和Release。额外的函数也可以出现在虚表中,它们的名字依赖对象它自身的定义。(你决定要加入你的 COM对象中的函数)。例如,IE的Browser对象勿庸置疑有与播放音乐对象不同的函数。但是所有的COM对象都以它们的虚表指针开始,最开始的三个虚表指针指向对象的QueryInterface、AddRef、和Release函数。一个对象的函数的第一个参数是一个指向对象(结构)自身的指针。 这是一个约定,一定要遵守。
GUID
让我们继续我们的构造IExample为一个真正的COM对象之旅。现在要写我们的QueryInterface、AddRef和Release函数。但在我们动手之前,我们必须谈谈一个叫全 局唯一表示符(GUID)的东东。哦,它是什么?它是一个用特殊的一连串字节填充的16字节数组。当我说它是特殊的时候,我的意思是唯一。一个GUID(也就是16字节数组)不能与另一个GUID有同样的字节序列,无论何时何地。每个GUID在任何时候被创建都有唯一的16位序列数。
那么你怎样创建这个唯一的16位序列呢?你可以用一个微软的GUIDGEN.EXE工具。它打包在你的编译器中,或者你也可以在SDK找到它。运行它你会看到这个窗口:

当你一运行GUIDGEN时,它自动生成一个新的GUID给你,显示在Result框中。注意在你的Result框中看到的会与上面的有所不同。毕竟,每个 单一的GUID生成与其他的是不同的。所以你最好看到一些与我看到的不同的东东。继续单击“NewGUID”按钮会看到一些不同的数字出现在Result框中。单击一整天,看看是否会生成同一个序列数超过一次,不会。同时,也没人会生成一些与你生成的序列相同的数。
你可以单击“Copy”按钮来把这个信息传输到剪切板上,然后把它粘贴到其它地方(像你的源代码中)。这是我这样做,粘贴完的东东:
[cpp]
//{0B5B3D8E-574C-4fa3-9010-25B8E4CE24C2}
DEFINE_GUID(<>,0xb5b3d8e, 0x574c, 0x4fa3,
0x90, 0x10, 0x25, 0xb8, 0xe4, 0xce,0x24, 0xc2);
上面是一个宏,一个#define在微软的包含文件中,它会告诉你的编译器把上面的内容编译成一个16位数组。
但是有一个事情我们必须做。我们必须用一些我们要用的这个GUID的C变量名来替换<>。我们叫它CLSID_IExample.
[cpp]
//{0B5B3D8E-574C-4fa3-9010-25B8E4CE24C2}
DEFINE_GUID(CLSID_IExample,0xb5b3d8e, 0x574c, 0x4fa3,
0x90, 0x10, 0x25, 0xb8, 0xe4, 0xce, 0x24,0xc2);
现在我们有了一个可以用于IExample的GUID。
我们还需要一个GUID给IExample的虚表(“接口”),也就是,我们的IExampleVtble结构。所以继续单击GUIDGEN.EXE的“NewGUID”按钮,并拷贝、粘贴到其他地方。这次,我们将用一个命名为IID_IExample的C变量名来替换<>。下面是我粘贴、编辑过的结果:
[cpp]
//{74666CAC-C2B1-4fa8-A049-97F3214802F0}
DEFINE_GUID(IID_IExample,0x74666cac, 0xc2b1, 0x4fa8,
0xa0, 0x49, 0x97, 0xf3,0x21, 0x48, 0x2, 0xf0);
总之,每个COM对象有它自己GUID,每个GUID是由不同的16位字节数组组成。一个GUID可以通过GUIDGEN.EXE工具生成。一个COM对象的虚表(也就是接口)也得有一个GUID。
QueryInterface(),AddRef(), and Release()
当然我们要允许其他程序来获得我们创建、初始化的IExample结构(也就是一个COM对象),那么这个程序就可以调用我们的函数了。(我们先不给出另一个程序怎样来获得我们的IExample。我们将在后面讨论它)。
除我们自己的COM对象以外,可能有很多其他COM组件安装在一个特定的计算机上。(再次,我们将推后讨论怎样安装我们的COM组件。)不同的计算机可能安装了不同的COM组件。一个程序怎样确定我们的IExampleCOM对象是否已经安装了,怎样来把它与其他所有COM对象区别开来?
记住每个COM对象有一个完全唯一的GUID,我们的IExample对象也是。我们的IExample虚表也有一个GUID。我们需要做的是告诉这个程序的开发者 IExample对象和它的虚表的GUID。通常,你给他一个包含上面你用GUIDGEN.EXE获得的两个GUID的宏的文件(.H)。OK,这样其它程序就知道IExample和它的虚表的GUID。它可以用他们做什么呢?
在这我们的QueryInterface函数派上用场了。记住每个COM对象必须有一个QueryInterface函数(也得有AddRef和Release)。其它程序会传递我们 IExample的虚表的GUID给我们的QueryInterface函数,我们检查它并确认它是IExample虚表的GUID。如果它是,那么我们会返回一些值来让这个程序知道它确实拥有了一个IExample对象。如果传入一个错误的GUID,我们会返回一些错误值让它知道它没有获得这个 IExample对象。所以,一台计算机上的所有COM对象,除了我们自己的QueryInterface,如果传给他们的QueryInterface一个IExample虚表的