实现一个最最简单的COM组件

实现一个最最简单的COM组件
//BeginningCOM.h 
#ifndef __BEGINNINGCOM_H__ 
#define __BEGINNINGCOM_H__ 

#pragma once 
#include <windows.h> 

// {7BB69A25-68E4-427a-BE4B-B06ED17531AA} 
CLSID CLSID_BeginningCOM = 
0x7bb69a250x68e40x427a, { 0xbe0x4b0xb00x6e0xd10x750x310xaa } }; 

#endif //__BEGINNINGCOM_H__ 


//BeginningCOM.cpp 
#include "BeginningCOM.h" 

class BeginningCOM : public IUnknown 

public
    BeginningCOM(VOID); 
    STDMETHODIMP QueryInterface(REFIID riid, VOID** ppv); 
    STDMETHODIMP_(ULONG) AddRef(VOID); 
    STDMETHODIMP_(ULONG) Release(VOID); 

protected
    ULONG m_ulRefCount; 
}; 


BeginningCOM::BeginningCOM(VOID) : m_ulRefCount(0



STDMETHODIMP BeginningCOM::QueryInterface(REFIID riid, VOID** ppv) 

    if(riid == IID_IUnknown) 
    { 
        *ppv = static_cast<IUnknown*>(this); 
    } 
    else 
    { 
        *ppv = NULL; 

        return E_NOINTERFACE; 
    } 

    reinterpret_cast<IUnknown*>(*ppv) -> AddRef(); 
    return S_OK; 


STDMETHODIMP_(ULONG) BeginningCOM::AddRef(VOID) 

    return InterlockedIncrement(&m_ulRefCount); 


STDMETHODIMP_(ULONG) BeginningCOM::Release(VOID) 

    ULONG tmp = InterlockedDecrement(&m_ulRefCount); 

    if(tmp == 0
    { 
        delete this
    } 

    return tmp; 


// DLL entry point. 
BOOL APIENTRY DllMain( HMODULE hModule, 
                      DWORD  ul_reason_for_call, 
                      LPVOID lpReserved 
                      ) 

    switch (ul_reason_for_call) 
    { 
    case DLL_PROCESS_ATTACH: 
    case DLL_THREAD_ATTACH: 
    case DLL_THREAD_DETACH: 
    case DLL_PROCESS_DETACH: 
        break
    } 
    return TRUE; 



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

    if(rclsid == CLSID_BeginningCOM) 
    { 
        BeginningCOM *pbc = new BeginningCOM; 

        if(pbc == NULL) 
        { 
            return E_OUTOFMEMORY; 
        } 

        return pbc -> QueryInterface(riid, ppv); 
    } 

    *ppv = 0

    return CLASS_E_CLASSNOTAVAILABLE; 
}

BeginningCOM.h中,先定义一个GUID作为实现接口的class的ID。BeginningCOM.cpp中实现IUnknown接口。
DllGetClassObject是客户调用COM组件的入口,需要导出这个函数:
//BeginningCOM.def 
LIBRARY    "BeginningCOM" 
EXPORTS 
    DllGetClassObject private

4. 注册COM组件
在HKEY_CLASSES_ROOT\CLSID键下注册COM组件的class信息。创建一个reg文件,导入注册表即可。
//regsvr.reg 
Windows Registry Editor Version 5.00 

[HKEY_CLASSES_ROOT\CLSID\{7BB69A25-68E4-427a-BE4B-B06ED17531AA}] 
@="BeginningCOM" 

[HKEY_CLASSES_ROOT\CLSID\{7BB69A25-68E4-427a-BE4B-B06ED17531AA}\InprocServer32] 
@="E:\\Projects\\BeginningCOM\\Debug\\BeginningCOM.dll" 
"ThreadingModel"="Both"

@="BeginningCOM"是class的描述信息,可忽略。
"ThreadingModel"="Both"是COM组件的套件类型,后面介绍。

相应的卸载COM组件的reg文件内容应该为:
//unregsvr.reg 
Windows Registry Editor Version 5.00 

[-HKEY_CLASSES_ROOT\CLSID\{7BB69A25-68E4-427a-BE4B-B06ED17531AA}] 
@="BeginningCOM"
需要注意的是,在Win64中,如果COM组件编译选择的是X86,那么注册表会进行重定向,读取位置变为HKEY_CLASSES_ROOT\Wow6432Node\CLSID,因此,注册时,键值应该写在这个路径下。见《Win64 注册表重定机制向导致程序运行异常

5. 调用COM组件
要调用COM组件,必须向客户公布接口ID,class ID等,我们这里把需要公布的信息放到BeginningCOM.h中。
下面的代码用于调用刚创建的COM组件。
#include <Windows.h> 
#include <tchar.h> 
#include <iostream> 
#include "../BeginningCOM/BeginningCOM.h" 
using namespace std; 

int _tmain(int argc, _TCHAR* argv[]) 

    CoInitialize(NULL); 

    HRESULT hr = NULL; 
    IUnknown *puk; 

    hr = CoGetClassObject(CLSID_BeginningCOM, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown, (void**)&puk); 

    if(SUCCEEDED(hr)) 
    { 
        //do nothing 
        puk -> Release(); 
    } 
    else 
    { 
        cout << "Failed to create object"<<endl; 
    } 

    CoUninitialize(); 

    return 0
}

在使用COM组件前,需要初始化COM调用环境,CoInitialize初始化单线程套间,只有一个保留的参数,CoInitializeEx除了一个保留的参数,还可以指定初始化的套件类型。

Author: Zhenxing Zhou
Blog: http://www.xianfen.net/
展开阅读全文

[如何简单的理解com组件]

06-09

[如何简单的理解com组件]rnrn 很多的初学者会对com组件设计感到很迷茫,其实可以把理解简单化些。当然,你深入的理解它的设计原理和调用机制,还是有些复杂的。rn 但这些都有人做了,一般来说,我们不必过度关注。rn 先来看看,我们一般是怎么设计程序的。rn 首先定义一个接口(在自己的exe程序中都不怎么定义接口了,一来就一个类,呵呵,除非模块化,而这个模块以后可能会有变化):rnrn//这个接口有一个唯一标识的号码:518(我要发财)rnclass IDownloadObjectrnrnpublic:rn bool StartNetwork(unsigned short usPort) = 0;rn bool Download(string & strUrl) = 0;rn;rnrn然后派生我们的下载类(.h):rn#include "IDownloadObject"rnclass CFileDownload : public IDownloadObjectrnrnpublic:rn bool StartNetwork(unsigned short usPort);rn bool Download(string & strUrl); rn;rn(.pp)rnbool CFileDownload::StartNetwork(unsigned short usPort)rnrn 实现.....rnrnbool CFileDownload:Download(string & strUrl)rnrn 实现.....rnrnrnok了,我们再加一个类专门new各类的对象rnclass CCreateObjctrnrnpublic:rn //创建一个类的对象rn static bool CreateInstance(int iNO,void** pp)rn rn switch(iNO)rn rn case 528:rn *p = new CFileDownload();rn break;rn case 529:rn *p = 其他的类rn break;rn defualt:rn break;rn rn rn return (*p == NULL);rn rnrnrn好啦,我们的类设计完了,我们要开始构造调用了rn#include "FileDownload.h"rn#include "MgrPtr"rnint main(int argc, char *a[])rnrn IDownload * pIDn = NULL; //定义接口指针rn CCtreaeObject::CreateInstance(518,&pIDn);rn if(pIDn)rn //ok,得到一个接口指针了,就是CFileDownload的指针,向上转换了rn if(pIDn->Network(8888))rn rn string strUrl = "bt://xxxx/xxxx";rn pIDn->Download(strUrl);rn rn rn return 0;rnrnrn====rn完了,呵呵.rn那对应到客户端程序和com组件中来,我们写的这些类应该各在什么位置呢?rnrn这个是我们客户端程序的代码:rn--------------------------------------rn#include "FileDownload.h" //这里相当于导入com的接口描述rn#include "MgrPtr" //这里相当于加载com库(CoInitialize),让我们可以方便的调用和管理com组件rnint main(int argc, char *a[])rnrn IDownload * pIDn = NULL; //定义接口指针rn CCtreaeObject::CreateInstance(518,&pIDn);rn if(pIDn)rn //ok,得到一个接口指针了,就是CFileDownload的指针,向上转换了rn if(pIDn->Network(8888))rn rn string strUrl = "bt://xxxx/xxxx";rn pIDn->Download(strUrl);rn rn rn return 0;rnrn---------------------------------------------rnrnrn这个相对于com库的api:CoCreateInstancern只是它实现了更复杂的东东,比如查询注册表,加载com组件,得到类厂接口....rn--------------------------------------------rnclass CCreateObjctrnrnpublic:rn //创建一个类的对象rn static bool CreateInstance(int iNO,void** pp)rn rn switch(iNO)rn rn case 528:rn *p = new CFileDownload();rn break;rn case 529:rn *p = 其他的类rn break;rn defualt:rn break;rn rn rn return (*p == NULL);rn rnrn-------------------------------------------rnrn这就是我们写的com组件了rn-----------------------------------------------------rn//这个类有一个唯一标识这个接口的号码:518rnclass IDownloadObjectrnrnpublic:rn bool StartNetwork(unsigned short usPort) = 0;rn bool Download(string & strUrl) = 0;rn;rnrn然后派生我们的下载类(.h):rn#include "IDownloadObject"rnclass CFileDownload : public IDownloadObjectrnrnpublic:rn bool StartNetwork(unsigned short usPort);rn bool Download(string & strUrl); rn;rn(.pp)rnbool CFileDownload::StartNetwork(unsigned short usPort)rnrn 实现.....rnrnbool CFileDownload:Download(string & strUrl)rnrn 实现.....rnrn------------------------------------------------------rnrn这样理解是不是简单些?rnrn以上代码是信手写来,没调试运行过.....也不知道写这些东东对一些人有没有用rn反正大家就把com组件当成完成某个功能的类的集合,我们要调用什么类的功能时,就new里面的一个类对象,rn然后得到这个类对象的指针(经过向基类转换过的),然后高用函数,与我们平时调用自己的类没有什么多大的区别.rnrn走了...嗯?谁扔的臭鸡蛋? 论坛

没有更多推荐了,返回首页