none
《COM本质论》第一章的有些例子不太恰当 RRS feed

  • 常规讨论

  • 链接:COM本质论》第一章的有些例子不太恰当


        

    今天在公司的技术沙龙给同事讲解COM,使用了一些《COM本质论》的例子,发现《COM本质论》(英译《Essntial COM》)第一章的有些例子有不恰当的地方。比如1.7 运行时多态性

         原来的字符串类:

    IFastString  *CallCreateFastSTring(const  char  *psz){
       static  IFastString  *  (*pfn)(const  char  *)  =0;
       if(!pfn){
          const  TCHAR  szDLL[]=_TEXT( "FastString.DLL ");
          const  char  szFn[]= "CreateFastSTring ";
          HINSTANCWE  h=  LoadLibrary(szDll);
          if  (h)
             *(FARPROC*)&pfn  =  GetProcAddress(h,szFN);  
        }
        return  pfn?pfn(psz):0;
    }
    
    

        然后为了实现从右向左实现查找,加了个参数bLeftToRight

    IFastString  * 
    CallCreateFastString(const  char  *char  *psz, 
                         bool  bLeftToRight  =true){ 
      static  IFastSTring  *(*pfnlr)(const  char  *)  =0  ; 
      static  IFastSTring  *(*pfnrl)(const  char  *)  =0  ; 
    
      IFaastString  *(**ppfn)(const  char  *)=&pfnlr; 
      const  TCHAR  8pszDll=_TEXT( "FastSTring.Dll "); 
    
      if(!bLeftToRight)  { 
         pszDll  =_Text( "FastSTringRL.DLL "); 
         ppfn=&pfnrl; 
      } 
      
      if(!(*ppfn)  { 
         const  char  szFn[]  =  "CreateFastString "; 
         HINSTANCE  h  =LoadLibrary(pszDll); 
         if  (h) 
             *(FARPROC  *)ppfn  =GetProceAddress(h,szFn); 
       } 
      
      return  (*ppfn)?(*ppfn)(psz):0; 
    
    }
    
    

    今天有同事问:COM的功能扩展难道采用这种方式吗?我突然发觉1.7节比较奇怪,因为在我看来1.7节所说的运行时多态性根本不是COM的特点,而且这种功能通过设置参数进行扩展本身就非常别扭。COM组件进行功能扩展根本不是新建一个DLL(就是上面提的FastSTringRL.DLL),类似上面提的FastSTringRL.DLL,而是定义一个IFastSTring2: public IFastSTring,然后再从这个类中定义一个从右往左查找的纯虚方法。而且我没听说运行时多态性是COM的特点。

     

    下面一个例子举得更是有些离谱,就是使用C++模拟QueryInterface的实现(我都不太相信当初看这书为何没有疑问):

    Void *FastString::Dynamic_Cast(const char *pszType)
    {
      If(strcmp(pszType,”IFastString”)==0)
         return static_cast<IFastString*>(this);
      else If(strcmp(pszType,”IPersistentObject”)==0)
          return static_cast<IPersistentObject*>(this);
      else If(strcmp(pszType,”IExtensibleObject”)==0)
         return static_cast<IFastString*>(this);
      Return 0;
    }
    
    

        这时有同事质疑:记得有一本书说使用static_cast进行类型转换本身就不是很好的设计。我连忙认真看了一下,发觉搞笑了,居然使用字符串来查找用户所需的接口(重复的概率很大啊!)COM是通过32位的IID来查找接口的(这样才能确保接口不重复)。其实《COM技术内幕》上有一个QueryInterface的很好的实现:

       

    #include <iostream>
    using namespace std;
    #include <objbase.h>
    
    void trace(const char* msg) 
    { 
      cout << msg << endl;
    }
    
    
    // 接口定义
    interface IX : IUnknown
    {
      virtual void __stdcall Fx() = 0;
    };
    
    interface IY : IUnknown
    {
      virtual void __stdcall Fy() = 0;
    };
    
    interface IZ : IUnknown
    {
      virtual void __stdcall Fz() = 0;
    };
    
    // Forward references for GUIDs
    extern const IID IID_IX;
    extern const IID IID_IY;
    extern const IID IID_IZ;
    
    //
    // 实现接口 IX,IY(这里表示一个组件)
    //
    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;}
    
      // Interface IX implementation
      virtual void __stdcall Fx() { cout << "这里是Fx函数" << endl;}
    
      // Interface IY implementation
      virtual void __stdcall Fy() { cout << "这里是Fy函数" << endl;}
    };
    
    HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
    {   
      if (iid == IID_IUnknown)
      {
        trace("QueryInterface: Return pointer to IUnknown.");
        *ppv = static_cast<IX*>(this);
      }
      else if (iid == IID_IX)
      {
        trace("QueryInterface: Return pointer to IX.");
        *ppv = static_cast<IX*>(this);
      }
      else if (iid == IID_IY)
      {
        trace("QueryInterface: Return pointer to IY.");
        *ppv = static_cast<IY*>(this);
      }
      else
      {     
        trace("QueryInterface: Interface not supported.");
        *ppv = NULL;
        return E_NOINTERFACE;
      }
      reinterpret_cast<IUnknown*>(*ppv)->AddRef(); // 加计数
      return S_OK;
    }
    
    //
    // 创建类CA,并返回一个指向IUnknown的指针
    //
    IUnknown* CreateInstance()
    {
      IUnknown* pI = static_cast<IX*>(new CA);
      pI->AddRef();
      return pI ;
    }
    
    //
    // 下面是各接口的IID
    //
    // {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
    static const IID IID_IX = 
    {0x32bb8320, 0xb41b, 0x11cf,
    {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}};
    
    // {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
    static const IID IID_IY = 
    {0x32bb8321, 0xb41b, 0x11cf,
    {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}};
    
    // {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
    static const IID IID_IZ = 
    {0x32bb8322, 0xb41b, 0x11cf,
    {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}};
    
    //
    // 主函数(这里代表客户)
    //
    int main()
    {
      HRESULT hr;
    
      trace("Client:获取 IUnknown指针.");
      IUnknown* pIUnknown = CreateInstance();
    
      trace("Client:获取接口IX.");
    
      IX* pIX = NULL; 
      hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX);
      if (SUCCEEDED(hr))
      {
        trace("Client:获取接口IX成功.");
        pIX->Fx();     // 使用 IX.
      }
    
      trace("Client:获取接口IY.");
    
      IY* pIY = NULL;
      hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY);
      if (SUCCEEDED(hr))
      {
        trace("Client:     Succeeded getting IY.");
        pIY->Fy();     // 使用 IY.
      }
    
    
      trace("Client:是否支持接口IZ.");
    
      IZ* pIZ = NULL;
      hr = pIUnknown->QueryInterface(IID_IZ, (void**)&pIZ);
      if (SUCCEEDED(hr))
      {
        trace("Client:获取接口IZ成功.");
        pIZ->Fz();
      }
      else
      {
        trace("Client:获取接口IZ失败,不支持接口IZ.");
      }
    
    
      trace("Client:用接口IX查询接口IY.");
    
      IY* pIYfromIX = NULL;
      hr = pIX->QueryInterface(IID_IY, (void**)&pIYfromIX);
      if (SUCCEEDED(hr))
      {  
        trace("Client:获取接口IY成功.");
        pIYfromIX->Fy();
      }
    
    
      trace("Client:用接口IY查询接口IUnknown.");
    
      IUnknown* pIUnknownFromIY = NULL;
      hr = pIY->QueryInterface(IID_IUnknown, (void**)&pIUnknownFromIY);
      if (SUCCEEDED(hr))
      {
        cout << "IUnknown指针是否相等?";
        if (pIUnknownFromIY == pIUnknown)
        {
          cout << "Yes, pIUnknownFromIY == pIUnknown." << endl;
        }
        else
        {
          cout << "No, pIUnknownFromIY != pIUnknown." << endl;
        }
      }
    
      // Delete the component.
      delete pIUnknown;
    
      return 0;
    }
    
    
    
    
    


     

     

     

     

     

     

     

     

     

     

     




    前无古人,后无来者
    2011年8月4日 14:45

全部回复

  • 我觉得多个dll是为了演示多态吧。

    不同的dll都实现了CreateFastString这个接口,但是实现的代码不同。

    就像不同的类对于一个接口的实现。

     

    还有用字符串模拟iid。iid固定长度的,字符串可以任意长度的,所以重复率的话当然iid比较大。

    实际用的话,要在接口名字前面加命名空间,比如:"com.microsoft.IFastString"

    2011年8月5日 5:37
  • 我觉得多个dll是为了演示多态吧。

    不同的dll都实现了CreateFastString这个接口,但是实现的代码不同。

    就像不同的类对于一个接口的实现。

     

    还有用字符串模拟iid。iid固定长度的,字符串可以任意长度的,所以重复率的话当然iid比较大。

    实际用的话,要在接口名字前面加命名空间,比如:"com.microsoft.IFastString"

         1.7节所演示的多态和COM的特点没啥关系吧。

    前无古人,后无来者
    2011年8月7日 5:37