用标准C编写COM(一)
下载例程-17.6kb内容:
简介
COM 对象和虚表
GUID
QueryInte易做图ce(), AddRef(), and Release()
IClassFactory对象
打包到DLL中
C++/C包含文件
定义文件( DEF)
安装DLL,注册对象
C实例程序
C++实例程序
修改代码
接下来是什么?
简介:
有大量例子教大家如何使用和创建COM/OLE/ActiveX组件,但这些例子大都使用了微软基础类(MFC)、.NET、C#、WTL,至少会使用了ATL,因为这些框架会提供给你一些已经“封装” 好了的模板代码。不幸的是,这些框架对程序员隐藏了所有底层细节,所以你永远不会真正明白使用COM组件的本质。更好的掌握如何使用一个特定的框架是建立在你熟练掌握COM的基础上。
如果你正尝试不使用MFC、WTL、.NET、ATL、C#、或者甚至一些C++代码,只使用标准的C来处理COM对象,则这方面的例子和资料非常匮乏。本文就是介绍在不使用其他框架,只使用标准C创建COM组件文章系列的第一部分。
对于标准的Win32控件,例如Static、Edit、ListBox、ComboBox等,你可以获得一个控件的句柄(也就是一个HWND)并通过发消息(通过 SendMessage)给它来操纵它。同时当这个控件要通知你一些事情或给你一些数据时,它也通过消息的形式返回给你(也就是通过把它们放入你自己的消息队列中,你再通过GetMessage获取他们)。
对于一个OLE/COM对象而言则完全不是这样。你不能来回发消息,取而代之的是,COM对象给你一些可以调用来操纵这个对象的一些函数指针。例如,一个IE浏览器对象会给你一 个函数指针,通过这个指针你可以调用来引发浏览器在你的一个窗口中去加载并显示Web页面。一个Office的对象会给你一个函数指针,你可以通过它加载一个文档。如果COM对象要通知你一些事情或发给你一些数据,那么你需要在你的程序中写特殊的函数,提供这些函数的指针(给COM对象)以便对象在需要的 时候调用它们。换句话说你需要在你的程序中创建你自己的COM对象。其中在C中真正麻烦是怎么定义你自己的COM对象。为了这样做,你需要知道一个COM对象的每个细节-这些原始的东西在预制的框架中对你而言则是隐藏的,在接下来的一系列文章中我将展示它。
总之,你通过调用COM对象中的函数来操纵它,同时它在你的程序中也是通过函数调用来通知你一些事情或传给你一些数据或通过其他方式与你的程序交互。这个方法类似于DLL中的函 数调用,就像在你的C程序中DLL也要调用函数一样-有几分像“回调”。但是与DLL不同的是,你不能用LoadLibrary()和GetProcAddress()去获得这个COM对象的函数指针。我们马上就会揭示它,你需要使用一个与之不同的操作系统函数来获得一个对象指针,然后 用这个对象去获得指向它的函数的指针。
COM对象和虚表
在学习怎样使用COM对象之前,我们首先需要了解一下COM对象是什么。认识它的最好的方式是创建我们自己的COM对象。但在我们这样做之前,让我们给出一个C结构数据类型。作为一个C程序员,你应该对它相当熟悉。这是一个例子的定义,一个简单的结构(叫“IExample”),它包含两个成员-一个DWORD(通过“count” 成员名来存取)和一个80个字符长度的数组(通过“buffer” 成员名来存取)。
[cpp]
DWORD count;
char buffer[80];
};
让我们用typedef来使它可以提前使用:
[cpp]
typedef struct {
DWORD count;
char buffer[80];
}IExample;
接下来是一个对于这个结构分配一个实例的例子(忽略了错误检查),同时初始化它的成员:
[cpp]
IExample * example;
example = (IExample*)GlobalAlloc(GMEM_FIXED, sizeof(IExample));
example->count = 1;
example->buffer[0] =0;
你知道一个结构可以存储一个指向函数的指针嘛?希望你知道,这是个例子。我们有一个参数是char*的函数,返回值是个long。这是我们的函数:
[cpp]
long SetString(char *str)
{
return(0);
}
现在我们需要把这个指向这个函数的指针存储在IExample中。在这里我们定义IExample,添加一个成员(“SetString”)来存储指向上面的函数的指针(并且我也用了一个typedef来使它提前可用):
[cpp]
typedef longSetStringPtr(char *);
typedef struct {
SetStringPtr * SetString;
DWORD count;
char buffer[80];
} IExample;
接下来是我们在分配的IExample中给SetString指针赋值,然后用这个指针调用来调用SetString:
[cpp]
example->SetString =SetString;
long value =example->SetString("Some text");
好,可能我们需要存储两个函数指针。这是第二个函数:
[cpp]
longGetString(char *buffer, long length)
{
return(0);
}
让我们重新定义IExample,添加另一个函数成员(“GetString”)来存储指向第二个函数的指针:
[cpp]
typedeflong GetStringPtr(char *, long);
typedef struct {
SetStringPtr * SetString;
GetStringPtr * GetString;
DWORD count;
char buffer[80];
} IExample;
接下来我们初始化这个成员:
[cpp]
example->GetString= GetString;
但你可能会说我不想把函数指针直接存储在IExample中。相反的,我们更愿意使用一个函数指针数组。例如,让我们定义第二个结构来达到存储我们的两个函数指针的目的。我们将叫它IExampleVtbl结构,它的定义是这样的:
[cpp]
typedef struct {
SetStringPtr * SetString;
GetStringPtr * GetString;
} IExampleVtbl;
现在,我们把指向上面的数组的指针存储在IExample中。我们要添加一个叫“lpVtbl”的新成员来达到这个目的(当然,我们得删除SetString和GetString成员,因为他们已经挪到IExampleVtbl结构中了)
[cpp]
typedef struct {
IExampleVtbl * lpVtbl;
DWORD count;
char buffer[80];
} IExample;
所以下面是一个分配内存并初始化IExample的例子(当然,包括IExampleVtbl):
[cpp]
// 由于IExample_Vtbl的内容永远不会改变,
// 所以我把它定义为静态的并且用以下方法初始化它。
// 它可以被大量的IExample实例复制。
static const IExampleVtblIExample_Vtbl = {SetString, GetString};
IExample * example;
// 创建 (分配内存) 一个IExample结构.
example = (IExample*)GlobalAlloc(GMEM_FIXED, sizeof(IExample));
// 初始化IExample(也就是把指向IExample_Vtbl赋值给它).
example->lpVtbl =&IExample_Vtbl;
example->count = 1;
example->buffer[0] =0;
接着可以这样调用我们的函数:
[cpp]
char buffer[80];
example->lpVtbl->SetString("Sometext");
example->lpVtbl->GetString(buffer,sizeof(buffer));
补充:软件开发 , C++ ,