COM 入门(2)

上一篇文章简单演示了COM组件的编写,注册及调用,本篇实现COM的自注册和反注册,定义一个有实际功能的接口及实现IClassFactory接口。



1. 实现COM组件的自注册和反注册

实现COM组件的自注册和反注册,本质上就是写注册表与删注册表。需要在DLL中引出两个函数:DllRegisterServer和DllUnregisterServer,让这两个函数实现注册表操作。

LPCTSTR RegTable[][3]=

{

{L
"CLSID\{586CDC7B-09F1-4f44-A110-F0E604AED81E}",0, L"BeginningCOM"},

{L
"CLSID\{586CDC7B-09F1-4f44-A110-F0E604AED81E}\InprocServer32",0, (LPCTSTR)-1},

{L
"CLSID\{586CDC7B-09F1-4f44-A110-F0E604AED81E}\InprocServer32", L"ThreadingModel", L"Both"}

};



STDAPI DllUnregisterServer()

{

HRESULT hr
=S_OK;



intregCount=sizeof(RegTable)/sizeof(RegTable);



for(inti=regCount-1; i>=0; i--)

{

LSTATUS error
=RegDeleteKey(HKEY_CLASSES_ROOT, RegTable[i][0]);



if(error!=ERROR_SUCCESS)

{

hr
=S_FALSE;

}

}



returnhr;

}



STDAPI DllRegisterServer(
void)

{

HRESULT hr
=S_OK;

TCHAR szFileName[MAX_PATH];



ZeroMemory(
&szFileName, MAX_PATH
sizeof(TCHAR));



GetModuleFileName(g_hModule, szFileName, MAX_PATH);



intregCount=sizeof(RegTable)/sizeof(RegTable);



for(inti=0; i<regCount; i++)

{

if(RegTable[i][2]==(LPCTSTR)-1)

{

RegTable[i][
2]=szFileName;

}



HKEY hKey;

LSTATUS error
=::RegCreateKey(HKEY_CLASSES_ROOT, RegTable[i][0],&hKey);



if(error==ERROR_SUCCESS)

{

error
=RegSetValueEx(hKey, RegTable[i][1],0, REG_SZ, (constBYTE
)RegTable[i][2], (lstrlen(RegTable[i][2])+1)sizeof(TCHAR));

RegCloseKey(hKey);

}



if(error!=ERROR_SUCCESS)

{

DllUnregisterServer();

}

}



returnhr;

}


现将需要修改的注册表内容放到一个二维数组中,第一维内容分别是注册表键,项,值。-1为占位符,标识该位置放置COM组件的路径,在写注册表之前将其替换。

删注册表时,应该先删子键,否则删除会失败。

同时需要在BeginningCOM.def中导出这两个函数:

LIBRARY"BeginningCOM"

EXPORTS

DllGetClassObject
private

DllRegisterServer
private

DllUnregisterServer
private

这样就可以用regsvr32.exe注册和反注册COM组件。



2. 自定义接口

可以用IDL文件定义,也可以在代码中直接继承IUnknown接口实现。用IDL定义接口的好处是,MIDL工具能自动生成GUID描述,接口存根/代理等。IDL全称为:Interface Definition Language,其语法类似于C语言,去掉了存在二义性的内容,并进行了扩展。作为示例,我们定义一个IBeginningCOM的接口,里面添加一个Sum函数和一个Num属性。

import"oaidl.idl";

import
"ocidl.idl";



[
object, uuid(93C3840F-AD5A-4020-AAAB-313C4B61B184)]

interfaceIBeginningCOM : IUnknown

{

HRESULT Sum([
in]inta, [in]intb, [out, retval]int
sum);

[propget] HRESULT Num([
out, retval]intpVal);

[propput] HRESULT Num([
in]intval);

}



[

uuid(D9161D4D
-66C0-4ae6-9264-C322BDE034C7),

version(
1.0),

helpstring(
"BeginningCOMLib")

]

library BEGINNINGCOMLib

{

importlib(
"stdole32.tlb");

importlib(
"stdole2.tlb");



[

uuid(586CDC7B
-09F1-4f44-A110-F0E604AED81E),

helpstring(
"BeginningCOM Lib")

]

coclass BeginningCOM

{

[
default]interfaceIBeginningCOM;

};

};


引入的oaidl.idl等包含了系统标准接口声明等,直接引入即可。接口的object和接口的名字这两个特性是必须的,为防止接口重名,用GUID来表示接口的名字。propget和proppub表示方法映射到属性,如VB中,在不支持属性的语言,如C++,映射为get_XXX,put_XXX。每个IDL文件只能有一个library标识,内部可以有多个coclass。

IDL文件经MIDL工具编译后,生成四个文件:

_h.h 接口说明文件

_i.c GUID描述文件

_p.c 接口存根/代理实现文件

dlldata.c 包含存根/代理相关内容



//BeginningCOM继承自IBeginningCOM接口

//...

classBeginningCOM :publicIBeginningCOM

//...



//实现IBeginningCOM接口成员,属性需要分别实现set/put。

//...

STDMETHODIMP BeginningCOM::Sum(inta,intb,intsum)

{

sum=a+b;

returnS_OK;

}



STDMETHODIMP BeginningCOM::get_Num(
intpVal)

{

pVal=m_Num;

returnS_OK;

}



STDMETHODIMP BeginningCOM::put_Num(
intval)

{

m_Num
=val;

returnS_OK;

}


3. 实现IClassFactory接口

IClassFactory是系统定义的对象创建的标准接口。IClassFactory有两个方法:LockServer和CreateInstance。LockServer用于进程外激活时,COM内部对其进行调用,目前我们都是进程内激活,可先不实现;CreateInstance用于创建请求的类对象,该方法的第一个参数在聚合时使用。

实现IClassFactory:

#pragmaonce

#include
"unknwn.h"



classBeginningCOMClassFactory :

publicIClassFactory

{

public:

BeginningCOMClassFactory(VOID);
//IUknown

STDMETHODIMP QueryInterface(REFIID riid, VOIDppv);

STDMETHODIMP_(ULONG) AddRef(VOID);

STDMETHODIMP_(ULONG) Release(VOID);



//IClassFactory

STDMETHODIMP CreateInstance(LPUNKNOWN punkOuter, REFIID riid, VOID
ppv);

STDMETHODIMP LockServer(BOOL fLock);



protected:

ULONG m_ulRefCount;

};



//...

STDMETHODIMP BeginningCOMClassFactory::CreateInstance(LPUNKNOWN punkOuter, REFIID riid, VOIDppv)

{

BeginningCOM
*pbc;

HRESULT hr;



//暂不支持聚合

if(punkOuter!=NULL)

{

returnCLASS_E_NOAGGREGATION;

}



pbc
=newBeginningCOM;



if(pbc==NULL)

{

returnE_OUTOFMEMORY;

}



pbc
->AddRef();

hr
=pbc->QueryInterface(riid, ppv);

pbc
->Release();



returnhr;

}



STDMETHODIMP BeginningCOMClassFactory::LockServer(BOOL fLock)

{

returnE_FAIL;

}



DllGetClassObject返回是否实现IClassFactory即可。

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid,
void
ppv)

{

if(rclsid==CLSID_BeginningCOM)

{

BeginningCOMClassFactory
pbc=newBeginningCOMClassFactory;



if(pbc==NULL)

{

returnE_OUTOFMEMORY;

}



returnpbc->QueryInterface(riid, ppv);

}



ppv=0;



returnCLASS_E_CLASSNOTAVAILABLE;

}


实现了IClassFactory接口,创建COM对象的代码可以通过查询IClassFactory接口,然后调用该接口的CreateInstance方法:

HRESULT hr=NULL;

IClassFactory
pcf;

IBeginningCOM
pbc;



hr
=CoGetClassObject(CLSID_BeginningCOM, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void)&pcf);



if(SUCCEEDED(hr))

{

hr
=pcf->CreateInstance(NULL, IID_IBeginningCOM, (void
)&pbc);

//...

也可以用CoCreateInstance创建,该函数包装了上面的两布,在分布式环境下能减少一次客户端与服务器之间的通信。

//...

hr=CoCreateInstance(CLSID_BeginningCOM, NULL, CLSCTX_INPROC_SERVER, IID_IBeginningCOM, (void**)&pbc);

//...

源程序下载:BeginningCOM2.zip

原文链接: https://www.cnblogs.com/zxjay/archive/2010/08/28/1811655.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/14437

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午2:04
下一篇 2023年2月7日 下午2:04

相关推荐