随笔 - 55  文章 - 15  trackbacks - 0
<2012年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用链接

留言簿

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

原文链接:http://www.codeproject.com/Articles/633/Introduction-to-COM-What-It-Is-and-How-to-Use-It 
COM是个啥?



是一个可以跨语言跨进成的二进制复用方法。不同于C++的模板和继承是源代码级别的复用,COM是二进制级别的。windows下服用二进制代码的例子就是dll文件。但是之前这些dll的接口都是用c写的,所以只有c语言能够明白这些接口,vb都不行。
COM通过定义二进制标准来解决这些问题。COM指定这些DLL或者EXE必须按特定的结构在内存中进行组织。并且,这些二进制代码与语言不相关。一旦这么做了,那么用不同的语言访问这些二进制代码将变成现实。
由于COM对象在内存中的结构碰巧同那些用c++虚函数编写的结构相同,所以,很多COM代码用C++来些,但是记住COM其实是语言无关的。






COM基本概念


1. 接口类: 一组函数或者方法。一般是抽象类,方法全部为纯虚函数。作为父类,提供服务。接口类一般以I开头,例如IShellLink。接口可以从其他接口继承,接口的继承是单根继承模式,即从一个IUnknown继承而来。不允许多重继承,有点像Java。
2. coclass类(component object class)包含在一个DLL或者一个EXE文件中,继
承一个或多个接口,具体实现这些接口。



3. COM对象:一个coclass类在内存中的实例。


4. COM服务器:提供coclass的二进制文件(DLL或EXE),包含一个或多个coclass类。


要想提供服务,该COM服务器必须注册到本地的注册表中,也就是说,这个COM服务器放在了哪里,可以在那个文件目录下找到。每个COM服务器都可以在注册表中找到。
那么整个的层次结构应该是: 


GUID(Globally Unique identifier)128位数,因为是个数值,所以是语言无关的。每个接口类和coclass都有一个GUID,而且全球唯一。
CLSID 是coclass 的GUID

IID是接口类的GUID

有两个原因使得GUID在COM中被广泛应用
1. 只是一个数值,每个编程语言都可以处理。
2. 每个被创建的GUID都是唯一的。因此,COM开发者可以穿件自己的GUID而不会同别人相同。









5. HRESULT: 一个整形,是返回值。

6. COM库是操作系统的一部分,当你在做COM相关的工作时,会和它交互。



开动:



先看看COM和C++的不同:





1. 创建新对象: C++ operator new 在堆上创建,或者在栈上创建
                      COM,调用COM库中的API函数创建



2. 删除对象:    C++ operator delete删除堆上对象,或者移动栈指针清楚栈上的临时变量
                      COM中调用COM库中的API函数删除
当你创建一个COM对象时,你要跟COM库的API函数说你需要哪个接口,如果对象创建成功,会返回指向那个接口的指针。
然后你就可以调用这个接口中的方法了。




从代码中看看怎么创建一个COM对象
COM库API

HRESULT CoCreateInstance (
    REFCLSID  rclsid,// coclass 的CLSID
    LPUNKNOWN pUnkOuter,// 用于聚合
    DWORD     dwClsContext,//COM server 的类型
    REFIID    riid, // 你请求的接口IID
    LPVOID*   ppv  // 请求的接口返回值);

  你可能会问,如果所有的COM库API的返回值都是HRESULT的话,那么有时候我要返回一些结果怎么办?COM中有个属性是OUT的参数来存放该返回结果,这里就是ppv。


当你调用上述的函数的时候,该函数会在注册表中搜索与CLSID相符合的ID,并且获取该COM服务所在的文件夹,把它放到内存中去,并且创建一个coclass的实力。

HRESULT     hr;
IShellLink* pISL;

hr = CoCreateInstance ( CLSID_ShellLink,         // CLSID of coclass
                        NULL,                    // not used - aggregation
                        CLSCTX_INPROC_SERVER,    // type of server
                        IID_IShellLink,          // IID of interface
                        (void**) &pISL );        // Pointer to our interface pointer

    if ( SUCCEEDED ( hr ) )
        {
        // Call methods using pISL here.
        }
    else
        {
        // Couldn't create the COM object.  hr holds the error code.
        }

删除一个COM对象:
我们不必释放掉一个COM对象,只要说我不用它就行了。每个COM对象都实现了IUnknown接口(因为所有的接口类都从这个接口继承),这个接口中有个release()方法。当你调用了这个方法,你就不能再用这个接口指针了









IUnknown接口类

不是不知道接口类,而是如果你有个一个IUnknown接口指针指向COM对象,你不需要知道这个对象是个啥,这就是向上类型转换的结果,IUnknown是所有类的父类,所有的类都可以向上转型为IUnknown类,这样的好处是所有的函数都可以接受IUnkonwn指针或者引用。
只有三个方法,非常重要
1. AddRef()--告诉COM对象增加自己的引用计数。如果你copy了一个接口指针,比如通过赋值操作,就的把引用计数加一。
2. Release()--减小引用计数。
3. QueryInterface()--请求一个COM对象的接口。

当你用CoCreateInstance()创建了一个COM对象之后,你将会得到一个接口指针。如果这个COM对象有多个接口,即从多个接口继承的话,你就要用QueryInterface()来获得这个接口的指针。

HRESULT IUnknown::QueryInterface (  REFIID iid,   void** ppv );
iid就是你要请求的接口ID
ppv就是你要请求的接口的指


一个简单的例子:
利用shell中的Active Desktop coclass获得当前壁纸的名字。
步骤:
1. 初始化COM库,因为我们要用到其中的函数
2. Create COM对象,获得一个IActiveDesktop借口
3. 调用GetWallpaper方法
4. 如果成功打印名字
5. Release()接口
6. 卸载COM库。
WCHAR   wszWallpaper [MAX_PATH];
CString strPath;
HRESULT hr;
IActiveDesktop
* pIAD;

    
// 1. Initialize the COM library (make Windows load the DLLs). Normally you would
    
// call this in your InitInstance() or other startup code.  In MFC apps, use
    
//  AfxOleInit() instead.</FONT>
    CoInitialize ( NULL );

    
<FONT COLOR="#009900">// 2. Create a COM object, using the Active Desktop coclass provided by the shell.
    
// The 4th parameter tells COM what interface we want (IActiveDesktop).</FONT>
    hr = CoCreateInstance ( CLSID_ActiveDesktop,
                            NULL,
                            CLSCTX_INPROC_SERVER,
                            IID_IActiveDesktop,
                            (
void**&pIAD );

    
if ( SUCCEEDED(hr) )
        {
        
<FONT COLOR="#009900">// 3. If the COM object was created, call its GetWallpaper() method.</FONT>
        hr = pIAD->GetWallpaper ( wszWallpaper, MAX_PATH, 0 );

        
if ( SUCCEEDED(hr) )
            {
            
// 4. If GetWallpaper() succeeded, print the filename it returned.
            
// Note that I'm using wcout to display the Unicode string wszWallpaper.
            
// wcout is the Unicode equivalent of cout.
            wcout << L"Wallpaper path is:\n    " << wszWallpaper << endl << endl;
            }
        
else
            {
            cout 
<< _T("GetWallpaper() failed."<< endl << endl;
            }

        
// 5. Release the interface.
        pIAD->Release();
        }
    
else
        {
        cout 
<< _T("CoCreateInstance() failed."<< endl << endl;
        }

    
// 6. Uninit the COM library.  In MFC apps, this is not necessary since MFC does
    
// it for us.
    CoUninitialize();





1





1
posted on 2012-04-18 17:17 Dino-Tech 阅读(296) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理