当前位置:编程学习 > VC++ >>

VC调用COM的方法总结

在文章<<采用ATL模型代替lib dll 的调用 >>中我介绍了VC中通过ProgID用度CLSID调用COM的一种方法,现在介绍其它方法。

一、COM端的设计
1、COM接口的设计
设计方法原理可参考我的文章<<采用ATL模型代替lib dll 的调用 >>

我们决定导出四个接口:

[cpp]
public: 
    STDMETHOD(get_CurrentAccount)(/*[out, retval]*/ double *pVal); 
    STDMETHOD(put_CurrentAccount)(/*[in]*/ double newVal); 
    STDMETHOD(Withdraw)(/*[in]*/double x); 
    STDMETHOD(Deposit)(/*[in]*/double x); 

public:
 STDMETHOD(get_CurrentAccount)(/*[out, retval]*/ double *pVal);
 STDMETHOD(put_CurrentAccount)(/*[in]*/ double newVal);
 STDMETHOD(Withdraw)(/*[in]*/double x);
 STDMETHOD(Deposit)(/*[in]*/double x);
类设计如下:

[cpp] view plaincopyprint?class ATL_NO_VTABLE CAccount :  
    public CComObjectRootEx<CComSingleThreadModel>, 
    public CComCoClass<CAccount, &CLSID_Account>, 
    public IDispatchImpl<IAccount, &IID_IAccount, &LIBID_BANKLib> 

public: 
    CAccount() 
    { 
        CurrentValue = 0; 
    } 
 
DECLARE_REGISTRY_RESOURCEID(IDR_ACCOUNT) 
 
DECLARE_PROTECT_FINAL_CONSTRUCT() 
 
BEGIN_COM_MAP(CAccount) 
    COM_INTERFACE_ENTRY(IAccount) 
    COM_INTERFACE_ENTRY(IDispatch) 
END_COM_MAP() 
 
// IAccount  
public: 
    STDMETHOD(get_CurrentAccount)(/*[out, retval]*/ double *pVal); 
    STDMETHOD(put_CurrentAccount)(/*[in]*/ double newVal); 
    STDMETHOD(Withdraw)(/*[in]*/double x); 
    STDMETHOD(Deposit)(/*[in]*/double x); 
 
protected: 
    double CurrentValue; 
}; 

class ATL_NO_VTABLE CAccount :
 public CComObjectRootEx<CComSingleThreadModel>,
 public CComCoClass<CAccount, &CLSID_Account>,
 public IDispatchImpl<IAccount, &IID_IAccount, &LIBID_BANKLib>
{
public:
 CAccount()
 {
  CurrentValue = 0;
 }

DECLARE_REGISTRY_RESOURCEID(IDR_ACCOUNT)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CAccount)
 COM_INTERFACE_ENTRY(IAccount)
 COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IAccount
public:
 STDMETHOD(get_CurrentAccount)(/*[out, retval]*/ double *pVal);
 STDMETHOD(put_CurrentAccount)(/*[in]*/ double newVal);
 STDMETHOD(Withdraw)(/*[in]*/double x);
 STDMETHOD(Deposit)(/*[in]*/double x);

protected:
 double CurrentValue;
};
在设计完成COM接口后,我们编写注册脚本文件Account.rgs(参考文章<<ATL产生的RGS文件介绍 >>):

[cpp]
HKCR 

    NoRemove AppID 
    { 
        {2F2CD476-6C09-4CFD-A819-7BE66BBE2CAF} = s 'Bank' 
        'Bank.EXE' 
        { 
            val AppID = s {2F2CD476-6C09-4CFD-A819-7BE66BBE2CAF} 
        } 
    } 

HKCR
{
 NoRemove AppID
 {
  {2F2CD476-6C09-4CFD-A819-7BE66BBE2CAF} = s 'Bank'
  'Bank.EXE'
  {
   val AppID = s {2F2CD476-6C09-4CFD-A819-7BE66BBE2CAF}
  }
 }
}
并把得到的.rgs文件导入到Res资源文件中。(目的将会在下面看到)

 

2、COM端提供服务有两种方式(测试时只要任选一种就可以了):
做为一个COM的服务器,为了实现服务器的调用逻辑,我们设计一个服务器的类,它继承自CComModule。

[cpp]
class CExeModule : public CComModule 

public: 
    LONG Unlock(); 
    DWORD dwThreadID; 
    HANDLE hEventShutdown; 
    void MonitorShutdown(); 
    bool StartMonitor(); 
    bool bActivity; 
}; 

class CExeModule : public CComModule
{
public:
 LONG Unlock();
 DWORD dwThreadID;
 HANDLE hEventShutdown;
 void MonitorShutdown();
 bool StartMonitor();
 bool bActivity;
};且我们得到服务器模块的对象:

[cpp]
CExeModule _Module; 

CExeModule _Module;
不管是采用哪种方式我们都要让服务器初始化,目的是将第1、部设计的COM接口和当前的服务器对象绑定:

[cpp]
_Module.Init(ObjectMap, hInstance, &LIBID_BANKLib); 

_Module.Init(ObjectMap, hInstance, &LIBID_BANKLib);
 

(1)注册COM服务器方式
由于在第1、步中我们把Account.rgs导入到RES文件中,所以注册服务器前得更新此资源,然后调用RegisterServer注册:

[cpp]
_Module.UpdateRegistryFromResource(IDR_Bank, TRUE); 
            nRet = _Module.RegisterServer(TRUE); 

_Module.UpdateRegistryFromResource(IDR_Bank, TRUE);
            nRet = _Module.RegisterServer(TRUE);

(2)运行COM服务EXE方式
在运行COM服务EXE前只需要注册类对象即可:

[cpp] view plaincopyprint?hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,  
           REGCLS_MULTIPLEUSE); 

 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
            REGCLS_MULTIPLEUSE);
注册的类对象通过之前Init已经实现了:

[cpp] view plaincopyprint?_Module.Init(ObjectMap, hInstance, &LIBID_BANKLib); 

_Module.Init(ObjectMap, hInstance, &LIBID_BANKLib);注意:由于采用的运行EXE方式,只要此EXE进程退出,客户端即会出现调用不到接口的现象,虽然不会报接口出错,但得到的数据都是0。

二、客户端的设计
客户端要能调用到服务端的接口,必须满足两个条件:

(1)服务器开启了服务;

(2)知道服务器端的IID与CLSID;

1、在客户端我们先定义IID与CLSID:
[cpp]
///////////////////////////////////////CLSID & IID   
const IID IID_IAccount = {0xEF327845,0x40A8,0x4AE0,{0x90,0x50,0xEA,0x7F,0xAD,0x02,0x75,0xEA}}; 
 
 
const IID LIBID_BANKLib = {0x98ECF356,0x06EF,0x4F72,{0xA3,0xA0,0x6E,0xCA,0x37,0x25,0x50,0x9E}}; 
 
 
const CLSID CLSID_Account = {0xEB914BA1,0x3C9C,0x4321,{0xBB,0x87,0xD2,0xF8,0xE3,0xEF,0xEB,0x09}}; 

///////////////////////////////////////CLSID & IID
const IID IID_IAccount = {0xEF327845,0x40A8,0x4AE0,{0x90,0x50,0xEA,0x7F,0xAD,0x02,0x75,0xEA}};


const IID LIBID_BANKLib = {0x98ECF356,0x06EF,0x4F72,{0xA3,0xA0,0x6E,0xCA,0x37,0x25,0x50,0x9E}};


const CLSID CLSID_Account = {0xEB914BA1,0x3C9C,0x4321,{0xBB,0x87,0xD2,0xF8,0xE3,0xEF,0xEB,0x09}};

2、获得服务器的接口IAccount *pAccount;
通过CoCreateInstance获得IUnknown指针,然后通过IUnknown指针返回IAccount指针:

[cpp]
//创建对象实例

补充:软件开发 , Vc ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,