COM ---- Inside COM Note

本文深入探讨COM(组件对象模型)及其与OLE的关系,强调COM作为动态链接库(DLL)和C++类接口的增强。COM通过纯抽象基类实现接口,允许多重接口继承,并使用__stdcall调用约定。文章详细介绍了COM接口的实现,包括QueryInterface、AddRef和Release等方法,以及如何在组件中实现多个接口。此外,还讨论了COM组件的生命周期管理、类型库、错误处理(HRESULT)、GUID、注册表配置、CoCreateInstance函数以及工厂模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

COM 和 OLE 的关系非常类似于微积分和物理的关系.
COM 是用来解决诸如如何将一个电子表格嵌入到字处理程序之类的问题. 此类问题的解决方案正是OLE.

运行时动态链接(动态改变组件)  +  信息封装

一种开发与语言无关的组件库的方法.

DLL 的接口是它所输出的那些函数.
C++类的接口则是该类的一个成员函数集。
COM接口是一个包含一个函数指针数组的内存结构。

COM接口在C++中是用纯抽象基类实现的。
可以提供多个接口, 组件用类来实现,实现接口的所有成员。

__stdcall 标准调用约定, 函数将在返回到调用者之前将参数从栈中删除, 也就是Pascal调用。VB . All COM interface use __stdcall.

__cdecl 常规C/C++调用约定, 栈的清理工作由调用者完成.带变参函数.

定义一个纯抽象基类就是定义了相应的内存结构.但此结构只是在派生类中实现此抽象基类时才会被分配.派生类继承一个抽象基类时, 它将继承此内存结构.

不同实例共享同一 vtbl, 其中的元素指向虚拟成员函数的同一实现。各对象有不同的实例数据。 < 指向接口的指针指向vtbl>

interface IUnknown
{
    
virtual HRESULT _stdcall QueryInterface(const IID& iid, void** ppv) =0;
    
virtual ULONG _stdcall AddRef() = 0;
    
virtual ULONG _stdcall Release() = 0;
}


IUnknow
* pI ...
IX
* pIX = NULL;
HRESULT hr 
=pI->QueryInterface(IID_IX,(void**)&pIX);
if (SUCCEEDED(hr))
    pIX
->DoSomthing();

非虚拟方式继承IUnknown(
?)

HRESULT _stdcall QueryInterface(cosnt IID
& iid, void** ppv)
{
    
if (iid == IID_IUnknown)
        
*ppv = static_cast<IX*>(this);  // Not IUnknown*, multi-inheritance
    else if(iid == IID_IX)
        
*ppv = static_cast<IX*>(this);
    
else if (iid == IID_IY)
        
*ppy = static_cast<IY*>(this);
    
else
         
{*ppv = nullreturn E_NONINTERFACE;}

    static_cast
<IUnknow*>(*ppv)->AddRef();
    
return S_OK;
}


继承两个接口将有两个对应的vtbl指针.

1. Initialize pointer pIX = NULL is encouraged.

2.  Code Sample:

interface IX: IUnKnown {
    
virtual void _stdcall Fx() = 0;
}


interface IY: IUnKnown {
    
virtual void _stdcall Fy() = 0;
}


class CA: public IX, public IY {
    
//IUnKnown implementation
    virtual HRESULT _stdcall QueryInterface(const IID& iid, void** ppv);
    
virtual ULONG _stdcall AddRef() {return 0;}
    
virtual ULONG _stdcall Release() {return 0;}

    
// IX implementation
    vitual void _stdcall Fx() {cout << "FX" << endl;}

    
// IY implementation
    vitual void _stdcall Fy() {cout << "FY" << endl;}
}


IUnKnown
* CreateInstance(){
    IUnKnown
* pI = static_cast<IX*> (new CA);
    pI
->AddRef();
    
return pI;
}


static const IID IID_IX = { 0x??, ....}
static const IID IID_IY = { 0x??, ....}

int main()
{
    HRESULT hr;

    IUnknown
* pIUnknown = CreateInstance();

    IX
* pIX = NULL;
    hr 
= pIUnknown->QueryInterface(IID_IX, (void**)&pIX);
    
if (SUCCEEDED(hr)){
        pIX
->Fx();
    }


    IY
* pIY = NULL;
    hr 
= pIUnknown->QueryInterface(IID_IY, (void**)&pIY);
    
if (SUCCEEDED(hr)){
        pIY
->Fy();
    }


}


3. The samilar Interface ---> IUnknown

// To determine whether the two interface point the same component.
BOOL IsSameComponent(IX* pIX; IY* pIY){
    IUnknown
* px1 = NULL;
    IUnknown
* py1 = NULL;

    pIX
->QueryInterface(IID_IUnknown, (void**)&px1);
    pIY
->QueryInterface(IID_IUnknown, (void**)&py1);

    
return px1 == py1 ;
}



4. Copy a pointer need to perform AddRef()

ULONG _stdcall AddRef() {
    
return ++m_cRef;
}


ULONG _stdcall Release() 
{
    
if (--m_cRef == 0){
        delete 
this;
        
return 0;
    }

    
return m_cRef;
}


Local interface pointer doesn't need AddRef()

5. Put the Component into DLL, DLL is simply a kind of style to publish Component, it is a component server.

6. Put extern "C" ahead of functions which are used to export can avoid adding type information by compiler.
?CreateInstance@@YAPAUUnknown@@XZ

dumpbin -exports Cmpt1.dll

DEF file
;
; Component1 module
-definition file.
;

LIBRARY           Compnt1.dll
DESCRIPTION    
'Paco'
Exports
                        CreateInstance @
1 PRIVATE


t
ypedef IUnknown* (*CREATEFUNCPTR)()

HINSTANCE hComponent 
= ::LoadLibrary(name);
if (hComponent == NULL ){
     ....
    
return NULL;
}

CREATEFUNCPTR CreateInstance 
= (CREATEFUNCPTR)::GetProcAddress(hComponent,"CreateInstance");

if (CreateInstance == NULL ){
     ....
    
return NULL;
}


return CreateInstance();


every exe application is performed in an individual process whch occupies 4GB address space.

6. HRESULT (Here's the RESULT)
    3 Fields
    32 bit
    S_OK       0
    S_FALSE   1
    E_FAIL
    ....
    if (hr == S_OK) ----****----- WRONG
    if (SUCCEED(hr))
    if (FAILED(hr))

7. GUID
    128bit (16 byte)
    UUIDGEN.EXE
    Class/Component ---- CLSID
    Interface            ---- IID
    Parameter --- Pass by Reference

8. Windows Registry
    Component CLSID register in Registry.
    HKEY_CLASSES_ROOT/CLSID/{0x..-0x..-.....}
    /InprocServer32  ---- data(filename)
    /ProgID              ---- Name to remember easily <Program>.<Component>.<Version>

    CLSIDFromProgID()
    ProgIDFromCLSID()

    Register itself  <#define STDAPI extern "c" HRESULT _stdcall>
    STDAPI DLLRegisterServer()
    STDAPI DLLUnregisterServer()

    Regsvr32.exe

9 COM library Functions <in EXE other than DLL>
   OLE32.dll
   HRESULT CoInitalize(void* reserved);
   void CoUninitialize();

10.
HRESULT _stdcall CoCreateInstance(
          
const CLSID& clsid,
          IUnknown
* pIUnknownOuter,         // Aggretion
          DWORD dwClsContext,                   // Server Context.. In-proc
          const IID& iid,
          
void** ppv
      )
     
        IX
* pIX = NULL;
        HRESULT hr 
= ::CoCreateInstance( CLSID_Component1,
                                                          NULL,
                                                          CLSCTX_INPROC_SERVER,
                                                          IID_IX,
                                                          (
void**)&pIX);
        
if (SUCCEEDED(hr)){
            pIX
->Fx();
            pIX
->Release();
        }


11.  Factory
     HRESULT _stdcall CoGetClassObject (
        
const CLSID& clsid,
        DWORD dwClsContext,
        COSERVER INFO
* pServerInfo,
        
const IID& iid,   // IID_IClassFactory
        void** ppv      // Pointer in a factory other than in component in CoCreateInstance()
      )

     CoGetClassObject() will call DLLGetClassObject() which should be implemented 
in DLL


      
interface IClassFactory: IUnknown
      ...
{
            HRESULT _stdcall CreateInstance (IUnknown
* pUnknownOuter, const IID& iid, void** ppv);
            HRESULT _stdcall LockServer(BOOL bLock);
      }


     So 
in fact, CoCreateInstance() is implemented by CoGetClassObject()

     
class CFactory : public IClassFactory
    ...
{
       CreateInstance () ...
{ .... new CA;}
    }


    
// Exports
    STDAPI DLLGetClassObject() ...{... new CFactory...}
    DLLMain () ...
{  dwReason == DLL_PROCESS_ATTACH  g_hModule = hModule;}

    
//***/**
    DLLCanUnloadNow()

1.  COM doesn't support implementation inheritance but support Interface inheritance.

2. Outer component consits the pointer of inner component.

3. 外部组件将调用转发给内部组件。--- 包容
   聚合 , 包容的一个特例。
   Boss (外部组件)
  Veteran employee (内部组件)  聚合 --- 实现类似于c++的--> 操作符重载 class CA : IX{m_pIUnknown; } 返回接口指针 <代理IUnknown>
   Novice employee (内部组件)      包容  --- 实现类似于c++的包含.class CA : IX, IY {m_pIY; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值