我做了个自动化型的COM,里面封装了些东西,其中就有一个连接socket。现在不同的应用程序调用此COM,我的目标:
   A:要么此COM只有唯一的实例,并可将事件分发给各自的应用程序
   B:如果A不能满足,则退而求次:保证COM的唯一实例,只有第一个调用此COM的程序能运行,其他调用此COM的程序则不让运行那位大大知道解决方法不?

解决方案 »

  1.   

    我觉得你的设计有问题;COM里不能用SOCKET的;
      

  2.   

    COM没用过!不懂!不过我会去学~  up
      

  3.   

    楼上的大师,在COM除了不能用Socket,还有一些什么限制啊?
    我上次做了个,想在COM里用ado,结果编译都通过了,应用到程序时就发生错误。
      

  4.   

    首先,你的COM至少是进程外COM。。
    也要选择一下合适的实例模式和线程模式。。具体做法可以查资料,多自己思考一点。
      

  5.   

    在COM里面使用SOCKET,ADO都没有问题的。具体做的时候要细心些。
      

  6.   

    COM实现过程原创:吴剑明(foxnt)前言COM已经成为一个必需的东西了。在我们周围,可以说处处充满了COM - 如果你是在使用WINDOWS,并在其下面编写程序的话。然而,无论你是用VC,还是使用DELPHI进行COM编程时,在大多数情况下,编程工具的IDE已经向你隐藏了COM的大部分实现过程,使得程序员根本不需要了解COM,只专心致志地写其所关心的逻辑代码。这就意味着,我们很少有机会,能够揭开COM的神秘面纱,来看到它下面到底是什么东西。这对于一个WINDOWS程序员来说,不能不是个遗憾。因此,本文的宗旨,就是抛开现有的IDE提供的各种向导工具,引导大家从最基本的地方入手,完整地从一个空白的程序里,建立起一个COM程序,从而达到能够比较清晰地了解一个COM,到底是如何生成出来并实现在程序中。本文假设,您是一个有COM编程经验的DELPHI/VC程序员,并希望了解COM的基本实现过程。限于篇幅和时间,我们只讨论进程内的COM(DLL)的实现,并引导大家亲手建立起一个最简单的COM程序。COM是什么?COM有各种表现形式,可以是进程内,也可以是进程外;可以在本机调用,也可以远程调用。记得国外有个研究COM的组织,他的主题就叫作:COM就是爱! 这当然是外国人的幽默,他只是想说明,COM是个多么重要的东西。那么COM到底是个什么东西呢?很早以前,在我刚开始学习COM的时候,身边就有些程序员告诉我:COM不是DLL,虽然它通常也是以DLL来作为扩展名的,可他完全与DLL完全不同。那么,这种说法是否正确呢?我们来看看,要实现一个进程内的COM,到底需要经过哪些步骤,那么,我们就能很清楚的知道答案了。完成一个进程内的COM,通常需要以下几步:1. 建立一个DLL项目,并导出以下四个函数:DllGetClassObject,DllCanUnloadNow,DllRegisterServer,DllUnregisterServer;2. 定义自定义的接口,同时必须实现Iunknown接口。3. 建立GUID,以标识这个组件以及自定义的接口。4. 在注册表中注册以标记这个DLL。大家都看到了,在第一个步骤里,需要建立一个DLL项目,那么,是不是意味着,COM就是一个DLL呢?在这里,我可以明确地告诉大家,从技术上讲,一个进程内的COM完全可以被认为就是一个普通的DLL-动态连接库!如果你抛弃常用的COM API,比如DELPHI中常用的:CreateCOMObject()或者CreateOLEObject()那么您完全可以直接采用加载普通DLL的方式来调用这个COM组件,比如说,您可以直接用LoadLibrary()函数来加载这个DLL,然后使用GetProcAddress来调用从这个DLL里输出的接口,从而完成各项操作。这是完全可行的。然而,我不得不告诉大家,把一个COM仅仅看成一个DLL,那是非常肤浅的看法 - DLL仅仅是一种表现形式而已。更重要的是,COM实现了一种规则。因此我们可以说:l COM是一种包含了许多处理逻辑、符合了某种接口规范(如Iunknown规范)的DLL组件。(注:如果没有特别说明,我在本文里所指的COM,都是指进程内的DLL形式的COM)l COM实现了Iunknown接口。因此,任何只要符合Iunknown规范,实现了Iunknown接口的DLL组件,我们都可以把他看成是一个COM。那么,什么是Iunknown接口呢?如何实现一个Iunknown接口呢?我们看看,在DELPHI中是如何定义一个Iunknown接口的:IInterface = interface['{00000000-0000-0000-C000-000000000046}']function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;function _AddRef: Integer; stdcall;function _Release: Integer; stdcall;end;IUnknown = IInterface;简单一点看,我们直接这样理解就行了:IUnknown = interface['{00000000-0000-0000-C000-000000000046}']function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;function _AddRef: Integer; stdcall;function _Release: Integer; stdcall;end;在DELPHI里,interface是编译器所认识的一种类型。如果是在VC++中,Iunknown将被定义为一种结构(struct)。如果要实现一个Iunknown接口,我们必须用一个类来实现它,比如在DELPHI中,要实现Iunknown接口可以写成为:TMyCOMObject = class (Tobject, Iunknown)……end;有心的读者可能会立即问:这个Iunknown接口由Tobject来实现,那么,可不可以是由其他类来实现呢?比如说用Tcomponent类来实现?答案是: 完全可以!!例如,我们要实现一个自定义的接口IMyCOM,可以写成这样:IMyCOMTest = interface(Iunknown);TMyCOMTest = class(Tcomponent, IMyCOMTest)…….End;这样是完全可以的!因为COM关注的只是如何实现一个接口,至于程序员使用什么类来实现,COM是不管的。后面我们要实现一个COM的例子,而且我打算就用这个IMyCOMTest接口来做。所以我们把这个接口声明成为例1,以便后面使用。COM的产生假如我们已经完成了一个COM,并且已经在系统中注册了。那么,一个客户端需要来调用这个COM,这时,系统中发生了哪些事呢?一般来说,以DELPHI为例,客户程序使用CreateCOMObject或者CreateOLEObject调用COM组件时,会发生以下几个步骤:1. CreateCOMObject或者CreateOLEObject的动作。我们看看这两个函数都干了些什么:function CreateComObject(const ClassID: TGUID): IUnknown;beginOleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER orCLSCTX_LOCAL_SERVER, IUnknown, Result));end;CreateOLEObject稍微复杂些:function CreateOleObject(const ClassName: string): IDispatch;varClassID: TCLSID;beginClassID := ProgIDToClassID(ClassName);OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER orCLSCTX_LOCAL_SERVER, IDispatch, Result));end;看到了吗?CreateOLEObject多了一个ProgIDToClassID函数。这意味着,如果我们要用CreateOLEObject来调用我们的COM组件,我们将要多一些步骤来编写我们的COM。这个我将会在后面说明。现在,我们要关注的是CoCreateInstance API函数。2. CoCreateInstance API函数将调用CoGetClassObject API,这个调用过程我们是看不到相关的代码的,因为微软已经把他封装好了。而CoGetClassObject函数的作用是什么呢?它将调用LoadLibrary来寻找我们指定的COM组件(DLL),然后使用GetProcAddress 来寻找组件的入口函数 - 还记得我们上面说过的那四个被导出的函数吗?对,其中的DllGetClassObject 函数就在这里将被调用。该函数的原形在DELPHI中是:function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult;其中第三个参数:Obj ,将向我们返回COM中的定义的接口。但是,要注意,这个接口并不是我们自定义的接口,而是向我们返回了一个被成为是“类工厂”接口的IclassFactory的接口。当我们获得类工厂接口后,就可以获得我们所需要的、那个我们自定义的接口了。看看IclassFactory 的接口声明:IClassFactory = interface(IUnknown)['{00000001-0000-0000-C000-000000000046}']function CreateInstance(const unkOuter: IUnknown; const iid: TIID;out obj): HResult; stdcall;function LockServer(fLock: BOOL): HResult; stdcall;end;看到那个CreateInstance 的方法了吗?对了,它的第三个参数 obj 将向我们返回那个我们定义的接口,比如是我们的IMyCOMTest接口(例1)。这样,我们就可以调用我们自定义的接口方法了。以上的众多步骤看起来有点让人迷惑。那么我们就用一个简单的流程来描绘我们刚才所发生的步骤。不要被那些步骤吓倒,其实他们是非常简单的。l CreateCOMObject --à CoCreateInstance。 CoCreateInstance 在注册表中查找COM的注册信息。l CoCreateInstance -à CoGetClassObject 。注册信息被交给CoGetClassObject。这时候CoGetClassObject将知道COM组件在磁盘上的位置。l CoGetClassObject -à LoadLibrary 。LoadLibrary 将寻找COM DLL的入口,然后GetProcAddress调用其输出函数DllGetClassObjectl DllGetClassObject 的输出参数将向我们返回“类工厂”接口IClassFactory。l IclassFactory --à CreateInstance 。CreateInstance方法建立其我们实现接口的类。该类将调用自身的QueryInterface 方法,查看用户指定的接口是否被自己实现,如果实现了,则向返回自定义的接口。l 调用结束后,COM客户将调用COM的DLL输出函数DllCanUnloadNow 。如果该函数返回S_OK,则释放该组件。实际的COM例子下面我们来做一个实际的例子。包括如何建立一个COM Server和一个COM Client。对于COM Server,我们将实现以下功能:l 单线程,单客户支持。l 实现自定义的接口l 能够使用Regsvr32 在系统中注册和反注册。l 能够被DELPHI或者VC++程序调用。我们只关注实现最基本的功能。当大家清楚整个流程后,自然就能写出更高级的功能,比如多线程支持等。下面,让我们开始COM实现之旅。 COM Server程序l 在DELPHI中,新建一个DLL工程。注意是DLL,而不是 Activex Library。并把工程名保存为MyCOM。然后依次建立两个单元文件:MyCOMServer 单元: 此单元描述了COM的逻辑实现COMDef 单元: 此单元描述了COM的输出函数定义。l 在MyCOM单元里,我们定义DLL的输出函数,整个代码:
      

  7.   

    接上面library MyCOM;usesSysUtils,Classes,COMDef,MyCOMServer in 'MyCOMServer.pas';//在这里导出四个函数。exportsDllGetClassObject,DllCanUnloadNow,DllRegisterServer,DllUnregisterServer;{$R *.res}beginend.先做好定义,不要考虑他们是如何实现的。这个在后面我会做详细解说。在这里我先说明这四个函数的作用:DllGetClassObject : 返回类工厂接口。DllCanUnloadNow : 告诉客户端该COM是否可以被正常卸载。DllRegisterServer : 向系统注册COM组件信息。Regsvr32.exe 就是调用这个函数来进行注册的。DllUnregisterServer :从系统中反注册一个COM。Regsvr32.exe 就是调用这个函数来进行反注册的。l 现在为我们的COM定义GUID。注意,在一个COM组件里,有三种GUID:CLASS_xx 类型的GUID,是用来标识COM组件的。IID_xx类型的GUID,是用来标识一个接口的。可以有多个。LIBID_xx类型的GUID,是用来标识一个TypeLib的。关于如何实现一个TypeLib,我会在COM实现过程(2)中会讲到。在这里先略过。现在,我们定义我们的COM组件的GUID,在COMDef单元里加入的代码为:constClass_MyCOM: TGUID = '{CE38847E-A386-4753-89F1-34BE80042107}';LIBID_MyCOM: TGUID = '{C2387E2A-0F08-442E-8947-D1AB36A9BDD0}';l 开始定义我们的接口。我们采用例一所定义的接口,在MyCOMServer单元里加入代码:IMyCOMTest = interface(IUnknown)['{D1C4A022-7F6F-42F0-A9B0-4A91703EB124}']function msg: integer;stdcall;end;注意,在这里,我们为IMyCOMTest 定义了一个IID类型的GUID,它唯一表示我们的接口。同时我们定义了一个方法 msg ,它向我们返回一个整数值。定义了接口,当然要实现它:TMyCOMServer = class(TObject, IMyCOMTest)protectedFLock: integer;function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;function _AddRef: Integer; stdcall;function _Release: Integer; stdcall;publicConstructor Create;function msg: integer;stdcall;end;现在,我们一个个的来说明并实现TMyCOMServer 的几个方法。_AddRef 方法:该方法用于增加一个COM的计数器。我们知道COM Server是需要支持多客户调用的,在COM中每实现一次调用,就必须进行计数。如果COM的计数>0 ,则COM继续生存。否则,该COM接口实例就必须被注销。_Release 方法:该方法和_AddRef方法相反,用于减少一个计数,当计数为0,则注销该接口。QueryInterface 方法:该方法用于查询一个接口是否存在,如果存在则在输出参数里返回该接口指针。为实现这几个方法,所添加的代码如下:function TMyCOMServer._AddRef: Integer;beginInc(FLock);end;function TMyCOMServer._Release: Integer;beginDec(FLock);if FLock = 0 thenFree;end;function TMyCOMServer.QueryInterface(const IID: TGUID; out Obj): HResult;begin//在类工厂中实现的一个MC接口,在这里返回。由于我们只有一个接口,所以那个IID参数没有用。当然,如果我们实现了多个接口,就需要维护一个数组,用个CASE语句,根据不同的IID,来返回不同的接口。Pointer(Obj) := Pointer(MC);Result := S_OK;end;其中,MC是一个IMyCOMTest 接口,是个全局变量:varMC: IMyCOMTest;CF: IClassFactory;别忘了还有我们的构造器:constructor TMyCOMServer.Create;beginInc(FLock);end;在一开始就增加一个引用计数,使得COM计数大于1,这样COM就不会自动销毁。然后去实现我们的接口方法:function TMyCOMServer.msg: integer;beginresult := 1978;end;该方法直接向我们返回一个整数值。l 类工厂的实现正如我前面所说的,一个类工厂必须去建立我们自定义的接口。在上面,我们定义了自定义的接口,并由类TMyCOMServer 去实现。那么,现在我们还要做的是,实现类工厂,然后由类工厂建立一个TMyCOMServer 的接口实例。类工厂接口定义如下:IClassFactory = interface(IUnknown)['{00000001-0000-0000-C000-000000000046}']function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;out Obj): HResult; stdcall;function LockServer(fLock: Boolean): HResult; stdcall;end;注意,IclassFactory是系统预先定义了的,在ACTIVEX单元有,所以不需要自己再去定义一次。我们只要去实现它就是:TClassFactory = class(TObject, IClassFactory)protectedFLock: integer;function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;function _AddRef: Integer; stdcall;function _Release: Integer; stdcall;publicConstructor Create;function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;out Obj): HResult; stdcall;function LockServer(fLock: Boolean): HResult; stdcall;end;我们只关注CreateInstance 方法。LockServer 用于在多客户调用COM时,锁定COM,以免一个客户退出时销毁了COM,那么其他客户的调用将发生错误。但是我们在这里只实现单客户,所以不考虑这个函数,把他置空就是。function TClassFactory.CreateInstance(const UnkOuter: IInterface;const IID: TGUID; out Obj): HResult;begin//我们的自定义接口,就是在这里被创建的。MC := TMyCOMServer.Create;Pointer(Obj) := Pointer(MC);end;function TClassFactory.LockServer(fLock: Boolean): HResult;beginend;同样的,TclassFactory也必须实现引用计数,因为它也实现了Iunknown接口。function TClassFactory._AddRef: Integer;beginInc(FLock);end;function TClassFactory._Release: Integer;beginDec(FLock);if FLock = 0 thenFree;end;function TClassFactory.QueryInterface(const IID: TGUID; out Obj): HResult;beginend;其中,QueryInterface 我把它置空,因为在这个例子中,不需要向它查询什么接口。如果以后读者需要向它查询接口时,自己实现相关代码。同样,在它的构造器中,也预先对计数加1constructor TClassFactory.Create;beginInc(FLock);end;到目前为止,我们已经基本实现了一个COM需要的大部分功能。现在,我们需要把它注册到系统中,以便被其他程序调用。l COM的注册和反注册我们回过头来,看看如何去实现那四个DLL的输出函数。这四个函数的原形如下:function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult;stdcall;function DllCanUnloadNow: HResult;stdcall;function DllRegisterServer: HResult;stdcall;function DllUnregisterServer: HResult;stdcall;
      

  8.   

    接上面我们上面所说的类工厂的实例,就是在DllGetClassObject 中创建的。代码如下:function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult;beginCF := TClassFactory.Create;Pointer(obj) := Pointer(CF);Result := S_OK;end;同样的,我们只有一个类工厂,所以可以不理会前面那两个参数。否则,就要根据不同GUID,来创建不同的类工厂对象。在这里,我们直接把类工厂对象给返回了。函数DllCanUnloadNow 用来注销一个COM。在正常使用中,要根据引用计数,来判断是否允许用户注销。在这里我们直接返回S_OK,让用户直接注销。function DllCanUnloadNow: HResult;beginResult := S_OK;end;函数DllRegisterServer 用来向注册表注册一个COM组件信息。要注册一个COM,用户必须知道COM在注册表中的信息是如何组织的。结构如下:HKEY_CLASSES_ROOT---- CLSID---- GUID----- InprocServer32 标明 COM所在磁盘的路径以及线程模型----- ProgID 标明COM所实现的接口----- TypeLib 标明 COM 的类型库的GUID----- Version 标明 COM的版本号。当发生CreateCOMObject()调用时,输入参数为COM的CLASS类型的GUID,系统将在注册表中搜索到相关信息,然后就可以找到该COM的位置,就可以开始调用了。注意,如果您希望COM组件支持客户端的CreateOLEObject()函数的调用,您必须还要注册一个信息:HKEY_CLASSES_ROOT----- 接口声明----- CLSID 标明 COM 接口和CLASS类型的GUID的对应关系。那么,当发生 CreateOLEObject 调用时,系统将会根据输入参数(一个COM接口声明,如a.b),去查找和接口对应的CLASS GUID,然后就可以读到COM的相关信息了。全部代码如下:function DllRegisterServer: HResult;varlp: pchar;ns: Dword;beginResult := S_FALSE;Reg := TRegistry.Create;GetMem(lp, 255);tryReg.RootKey := HKEY_CLASSES_ROOT;if Reg.OpenKey('.MyCOMTest',true) thenbeginReg.CreateKey('CLSID');if Reg.OpenKey('CLSID',true) thenReg.WriteString('',GUIDToString(Class_MyCOM));end;if Reg.OpenKey(' + GUIDToString(Class_MyCOM), true) thenbeginif Reg.CreateKey('InprocServer32') = false orReg.CreateKey('ProgID') = false orReg.CreateKey('TypeLib') = false orReg.CreateKey('Version') = false thenExit;Reg.WriteString('','MyCOM');if Reg.OpenKey(' + GUIDToString(Class_MyCOM) +'', false) thenbeginWindows.GetModuleFileName(HInstance,lp, 255);Reg.WriteString('', lp);Reg.WriteString('ThreadingModel', 'Single');end;if Reg.OpenKey(' + GUIDToString(Class_MyCOM) + '', false) thenReg.WriteString('','MyCOM.MyCOMTest');if Reg.OpenKey(' + GUIDToString(Class_MyCOM) + '', false) thenReg.WriteString('','1.0');if Reg.OpenKey(' + GUIDToString(Class_MyCOM) + '', false) thenReg.WriteString('',GUIDToString(LIBID_MyCOM));Reg.CloseKey;Result := S_OK;end;finallybeginFreeMem(lp);Reg.Free;end;end;end;函数DllUnregisterServer 则向系统注销一个COM的注册信息。它比较简单,直接把COM的相关注册键给删除就是:function DllUnRegisterServer: Hresult;beginResult := S_False;Reg := TRegistry.Create;tryReg.RootKey := HKEY_CLASSES_ROOT;Reg.DeleteKey(' + GUIDToString(Class_MyCOM));Reg.CloseKey;FinallyReg.Free;end;end;l 最后工作。现在,我们编译程序,然后生成一个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);beginshowmessage(inttostr(a.msg));end;在窗口的OnCLose事件里加上:procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);begina := 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)”,主要讲述如何实现类型库的。
      

  9.   

    关注,我也为这个问题头痛过很一阵子
    现在不头痛了
    不是因为解决这个问题,而是我不用COM了:)正如有句广告词:不用COM,背了不痛了!脚也不抽筋了!
    *_^
      

  10.   

    拜托,saien(有问必答),贴东西也贴点相关的吧....
      

  11.   

    在COM中调用REGISTERACTIVEOBJECT()注册.
    运行时先调用GETACTIVEOBJECT(),看获取的接口是否为NIL,是说明已经有实例在运行,否则创建
    COM实例.
      

  12.   

    在COM中加入事件,好像98和2000下不太一样,或者是D5和D6中实现方法不一样.
    我也有些问题,请高手解答.
    http://expert.csdn.net/Expert/topic/2394/2394876.xml?temp=.3526575
      

  13.   

    我想来想去,觉得
    1:COM要是采用进程内自动化,实现只有一个实例的情况下配发事件,由于进程间调用的限制,传统方式是很难的,我听说过一种能欺骗进程获取实例地址的方式,不过没有实现过。但,COM做成进程外自动化,是可以完全满足要求的。自动化中增加多点接收事件,调用端实现接收器类,还是可行的。不过看上去不太优美。不知道大大们还有没有更好的建议。
    2:由于自动化对象中不能重载Create,而自动化对象是一定要创建的,故我决定强在创建自动化对象之前下手。我想到了一种解决方法,就是很土。在进程内自动化中,再增加一个自动化接口,此接口中引出方法Init(),无非做的就是在里面产生一个Mutex.先检测有没有Mutex,有,好返回失败,调用此COM的程序自觉中止下步操作,没有Mutex,产生并继续执行。这种方式增加了判断步骤,并且调用者不按规矩调用,那就好看啦。当然还可以使用其他的进程间通讯方式来实现。
      大大们有没有别的好的思路呢?指教!