COM实现过程3

最后工作。
现在,我们编译程序,然后生成一个DLL文件,在命令行下,使用:
regsvr32 MyCOM.dll
向系统注册COM。


COM Client程序
在DELPHI中调用
新建一个项目,然后在单元中,定义接口信息:
IMyCOMTest = interface(IUnknown)
['{D1C4A022-7F6F-42F0-A9B0-4A91703EB124}']
function msg: integer;stdcall;
end;
定义变量:
class_wjm: TGUID = '{CE38847E-A386-4753-89F1-34BE80042107}';
a: IMyCOMTest;
然后在窗口的OnCreate 事件里,添加如下代码:
procedure TForm1.FormCreate(Sender: TObject);
begin
//随便用哪个都可以
a := createcomobject(class_wjm) as IMyCOMTest;
//或者使用 a := createoleobject('MyCOM.MyCOMTest') as IMyCOMTest;
end;
然后,放一个按钮,并在其事件里添加代码:
procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(inttostr(a.msg));
end;

在窗口的OnCLose事件里加上:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
a := nil;
end;
注意一定要释放接口,否则可能是个难看的 AV 错误哦。
运行我们的程序,点下按钮,你将看到输出信息“1978”。
如果你看不到正确的信息,请仔细查看你的代码是否和文中一致。你也可以直接向我索要源代码。

在VC6中调用
稍微复杂点。先把GUID翻译过来:
//{CE38847E-A386-4753-89F1-34BE80042107};
static const CLSID CLSID_MyCOM = {0xCE38847E,0xA386,0x4753,
{0x89,0xF1,0x34,0xBE,0x80,0x04,0x21,0x07}};
//{D1C4A022-7F6F-42F0-A9B0-4A91703EB124}
static const IID IID_MyCOM = {0xD1C4A022,0x7F6F,0x42F0,
{0xA9,0xB0,0x4A,0x91,0x70,0x3E,0xB1,0x24}};
然后在声明一次接口的定义:
struct IMyCOMTest : public IUnknown
{
virtual LONG __stdcall msg();
};
IMyCOMTest* pLink;
然后放个按钮上去,并在相关事件里写代码:
void CMyvcView::OnButton6()
{
pLink = NULL;
int a =0;
CoInitialize(NULL);
a = CoCreateInstance(CLSID_MyCOM, NULL,
CLSCTX_INPROC_SERVER,IID_MyCOM, (void**)&pLink);
if (a==S_OK){
LONG a= pLink->msg();
};
}
注意,一定要记住调用 CoInitialize(NULL); 这个函数,否则COM无法使用的。
编译运行,你应该能看到 a 是等于1978 的。
总结
到目前为止,我们成功的编写了一个最简单的COM组件,并且在DELPHI和VC中成功调用。这都说明我们的工作是成功的。同时我们也看到,实现一个COM,并不难。
关于进程外的COM以及DCOM,前者是基于LPC 本地过程调用,后者是基于RPC远程过程调用。除了协议不同外,其他的都一样。大家有兴趣,可以以后继续讨论。
关于COM的线程模式,我曾经以为,是COM向导中自动会产生对应的线程代码,来实现多线程的。但是我后来又认为,根本没有这回事,COM只是做了个标记,告诉操作系统他的线程模型,至于如何产生线程,则是操作系统做的。有关这方面的讨论,还需要进一步研究。
一个小尾巴
我们知道,在DELPHI里,有一个Import Type Library 的功能。可以把一个COM组件导到DELPHI中直接使用。但是,如果我们试图把我们刚才写的那个组件,也ADD进去的时候,DELPHI会提示:
加载类型库/DLL时出错。
这是怎么回事呢? 原来,这是MS/BORLAND的一个小花招。我们看看VCL的代码就知道了,在DELPHI的向导为你创建一个COM时,它偷偷地加了一个 IprovideClassInfo 的接口进去,该接口使用ItypeInfo 接口,主要用于向外提供COM的类型信息的。大家仔细跟下TtypedComObject 这个类,就会发现这个奥秘了。在前例中,我们没有实现这个接口,那么当然无法被DELPHI加载了。关于如何实现这个接口,已经超出了本文的范围,所以不于讨论。
有兴趣的朋友,可以继续关注 “COM实现过程(2)”,主要讲述如何实现类型库的。


2002/6/27
版权所有
转载时请包括作者姓名    

借你宝地,我翻译了下COM中的TClassInstancing, TThreadingModel,不过只是加点自已的意思,可能没怎么对,我只是想弄彻底弄明白这两个东西.你怎么看这两个东东

{ Instancing mode for COM classes }
实例模式
TClassInstancing = (ciInternal, ciSingleInstance, ciMultiInstance);
通过COM类厂决定如何实例一个COM对象

Description

TClassInstancing说明了类厂确定一个实例化的COM对象加载在进程的空间,并决定单进程中是否创建多个实例,下列是可能是值选项:

ciInternal
COM对象是创建在COM服务器中的同一个进程里。这种情况,外部程序不能直接创建一个实例,外部程序的处理必须通过已经创建起来的对象来调用。
The COM object is created by the same process as the COM server. That is, an external application cannot create an instance of this object directly. Instead, external processes must call a method of the application that creates the document object.

ciSingleInstance
在COM对象服务器中只允许一个单独的实例来响应每一个应用程序。如果这个实例不共享给多层的客户端(即当有客户端已经使用了这个实例时),那其它的客户端需要等待占用实例的客户释放实例。
Allows only a single instance of the COM object for each executable (application). If this single instance is not shared across multiple clients, then each client must launch its own instance of the executable.

ciMultiInstance
EXE/DLL中将会创建多个COM对象实例,不同的COM对象实例将响应任何时刻客户端请求服务
The COM object is created as one of multiple instances within the same executable. Any time a client requests service, a separate instance of the object gets invoked.

***********************************************************************************
***********************************************************************************
***********************************************************************************
***********************************************************************************

{ Threading model supported by COM classes }
TThreadingModel = (tmSingle, tmApartment, tmFree, tmBoth, tmNeutral);
线程模式类型
线程模式说明了SERVER中是如何连接调用加载COM对象

Description
线程模式说明了SERVER中是如何连接调用加载COM对象,应用程序必需确保COM对象实现了自已安全线程模式才行,即说在自已方法和函数中的调用是线程安全的。


tmSingle
COM对象由客户端发出请求,每个COM对象不支持对线程的运作,即每个客户端请求是一个接一个,连继的,不能有多线程、多个客户端的连接的存在,一个客户端请求完成,下一个客户端才能请求服务。在服务端中只能有一个COM对象、一个客户连接的实例。
COM serializes all client requests. The object does not need to provide thread support.

tmApartment
这种类型确保了在同一时刻只能向所有的COM对象中的一个发出一个请求。
在每一个客户端实例调用COM对象,服务端将分配一个COM实例给这个客户端,即COM对象和客户端实例是一一对应,所以这些实例的数据是线程安全的.而全局的数据变量必须用Critical Section(临界资源)或相同的功能将它保护起来访问。(线程变量(threadvar)能够在这里使用)
COM ensures that any instance of the COM object services one request at a time. Different objects from the same server can be called on different threads, but each object is called only from that one thread. Instance data is safe, global data must be protected using critical sections or some other form of serialization. The thread抯 local variables are reliable across multiple calls.

tmFree
类似于多线程方式,COM对象能够在任意时刻任意线程进行调用,但必须使用临界资源(Critical Section或类似)保护所有实例和全局变量中的数据。(线程变量(threadvar)不能够在这里使用)
Also called multi-threaded apartment. The COM object can receive calls from any thread at any time. Objects must protect all instance and global data using critical sections or some other form of serialization. Thread local variables are not reliable across multiple calls.

tmBoth
身具tmApartment或tmFree的两种访问方式。它支持这两种模式,当客户通过单线程或Free线程模式访问时
Objects can support clients that use either apartment or free threading models. It supports both threading models when clients may be using either single-threaded or free threading models.

tmNeutral
分布式客户能够在不同的线程中同一时间来访问对象,但COM对象必须保证调用不冲突,所以你必须预防两个互斥的线程访问全局变量数据时的冲突,以及预防所有可通过一个方法、函数来访问的实例数据。这种对象模式不能够使用一个用户接口,仅仅在COM+才有效,如果是COM使用这种模式,将会被映射成 tmApartment模式。
Multiple clients can call the object on different threads at the same time, but COM ensures that no two calls conflict. You must guard against thread conflicts involving global data and any instance data that is accessed by more than one method. This model should not be used with objects that have a user interface. This model is only available under COM+. Under COM, it is mapped to the Apartment model.

posted @ 2009-07-27 12:59  Handll  阅读(433)  评论(0编辑  收藏  举报