2012-12-05 63 views

回答

0

我懷疑這是一種完全符合你想要的工具。但它也不應該太難以推出自己的轉換器。使用類型庫有點麻煩。我編寫了一個工具,用於從.idl文件或.tlb文件中爲COM接口生成C++包裝類。讀出接口相關信息的代碼大約有250行代碼,所以如果你添加一些代碼來生成一個.idl文件,你最終應該得到大約1000行代碼。

下面的代碼是我的C++代碼的簡化版本,應該給你一個總體思路。但是,由於目前我無法訪問編譯器,所以無法檢查它是否編譯(不太可能)或工作(當然不是)。

#include <afxwin.h> 
    #include <comdef.h> 
    #include <atlconv.h> 
    #include <atlbase.h> 

    char* paTypeNames[] = 
    { 
     "VT_EMPTY",   // = 0, 
     "VT_NULL",   // = 1, 
     "short", //"VT_I2", //    = 2, 
     "long", //"VT_I4", //    = 3, 
     "real", // "VT_R4", //    = 4, 
     "double", // "VT_R8", //    = 5, 
     "VT_CY", //    = 6, 
     "VT_DATE", //   = 7, 
     "BSTR", //"VT_BSTR", //   = 8, 
     "IDispatch*", // "VT_DISPATCH", //  = 9, 
     "VT_ERROR", //   = 10, 
     "VARIANT_BOOL", //"VT_BOOL", //   = 11, 
     "VARIANT", //"VT_VARIANT", //   = 12, 
     "IUnknown*", // VT_UNKNOWN  = 13, 
     "VT_DECIMAL", //   = 14, 
     "VBA reserves 15 for future use", 
     "VT_I1", //    = 16, 
     "VT_UI1", //    = 17, 
     "VT_UI2", //    = 18, 
     "VT_UI4", //    = 19, 
     "VT_I8", //    = 20, 
     "VT_UI8", //    = 21, 
     "int", //"VT_INT", //    = 22, 
     "UINT", //   = 23, 
     "VOID", //   = 24, 
     "HRESULT", //   = 25, 
     "VT_PTR", //    = 26, 
     "VT_SAFEARRAY", //  = 27, 
     "VT_CARRAY", //   = 28, 
     "VT_USERDEFINED", //  = 29, 
     "VT_LPSTR", //   = 30, 
     "VT_LPWSTR", //   = 31, 
     "VBA reserves 32 for future use", 
     "VBA reserves 33 for future use", 
     "VBA reserves 34 for future use", 
     "VBA reserves 35 for future use", 
     "VT_RECORD", //   = 36, 
     "VT_INT_PTR", //   = 37, 
     "VT_UINT_PTR", //  = 38, 
     "Unknown Type number above 39" 
    }; 

    CString GetType (const TYPEDESC& p_TYPEDESC, ITypeInfo* p_pTypeInfo) 
    { 
     // Look up user defined types in the type library. 
     if (p_TYPEDESC.vt == VT_USERDEFINED) 
     { 
      ITypeInfoPtr spInnerType; 
      VERIFY (SUCCEEDED (p_pTypeInfo->GetRefTypeInfo (p_TYPEDESC.hreftype, &spInnerType))); 
      BSTR CurrTypeName; 
      VERIFY (SUCCEEDED (spInnerType->GetDocumentation (MEMBERID_NIL, &CurrTypeName, NULL, NULL, NULL))); 
      return CString (CurrTypeName); 
     } 
     else if (p_TYPEDESC.vt == VT_PTR) 
      return GetType (*p_TYPEDESC.lptdesc, p_pTypeInfo) + CString (_T("*")); 
     else 
     { 
      return CString (paTypeNames[min (p_TYPEDESC.vt & VT_TYPEMASK, 39)]); 
     } 
    } 


    bool ParseTypeLib (char* p_strTypeLibName) 
    { 
     USES_CONVERSION; 

     // Load the type library. 
     ITypeLibPtr spTypeLib; 
     HRESULT hr = LoadTypeLibEx (A2OLE (p_strTypeLibName), REGKIND_DEFAULT, &spTypeLib); 
     if (!(bool)spTypeLib) 
      return false; 

     UINT uiNumberOfTypes = spTypeLib->GetTypeInfoCount(); 
     for (int i = 0; i < uiNumberOfTypes; i++) 
     { 
      ITypeInfoPtr spCurrTypeInfo; 
      spTypeLib->GetTypeInfo (i, &spCurrTypeInfo); 
      if (!(bool) spCurrTypeInfo) 
       return false; 

      // We only want to process interface definitions, so if we encounter anything 
      // else (for example enums), we skip the rest of the loop. 
      TYPEATTR* pCurrentTypeAttr; 
      VERIFY (SUCCEEDED (spCurrTypeInfo->GetTypeAttr (&pCurrentTypeAttr))); 
      if (pCurrentTypeAttr->typekind != TKIND_DISPATCH && 
       pCurrentTypeAttr->typekind != TKIND_INTERFACE) 
       continue; 

      // Retrieve the current interface name. 
      CComBSTR CurrInterfaceName; 
      hr = spTypeLib->GetDocumentation (i, &CurrInterfaceName, NULL, NULL, NULL); 

      std::cout << "interface " << CurrInterfaceName; 

      // Retrieve the name of the base class. According to MSDN 
      // (http://msdn.microsoft.com/en-us/library/aa909031.aspx), we must first retrieve 
      // the TKIND_INTERFACE type description for our class, and then we can retrieve 
      // the base class information. We also need the TKIND_INTERFACE type description 
      // because TKIND_DISPATCH type descriptions contain both the methods of the current 
      // interface as well as the methods of all base interfaces. 
      ITypeInfoPtr spBaseType; 
      if (pCurrentTypeAttr->typekind == TKIND_DISPATCH) 
      { 
       HREFTYPE TempHREF; 
       VERIFY (SUCCEEDED (spCurrTypeInfo->GetRefTypeOfImplType (-1, &TempHREF))); 
       ITypeInfoPtr spTempInfo; 
       VERIFY (SUCCEEDED (spCurrTypeInfo->GetRefTypeInfo (TempHREF, &spTempInfo))); 
       spCurrTypeInfo = spTempInfo; 
      } 
      HREFTYPE BaseClassHREF; 
      VERIFY (SUCCEEDED (spCurrTypeInfo->GetRefTypeOfImplType (0, &BaseClassHREF))); 
      VERIFY (SUCCEEDED (spCurrTypeInfo->GetRefTypeInfo (BaseClassHREF, &spBaseType))); 
      CComBSTR CurrBaseTypeName; 
      VERIFY (SUCCEEDED (spBaseType->GetDocumentation (MEMBERID_NIL, &CurrBaseTypeName, NULL, NULL, NULL))); 

      std::cout << " : " << CurrBaseTypeName << "{\n"; 

      // Process the methods of the current interface. 
      FUNCDESC* pCurrFUNCDESC; 
      int j = 0; 
      while (SUCCEEDED (spCurrTypeInfo->GetFuncDesc (j++, &pCurrFUNCDESC))) 
      { 
       // Retrieve the return type of the COM method (this does not necessarily have to be 
       // an HRESULT). 
       std::cout << paTypeNames[pCurrFUNCDESC->elemdescFunc.tdesc.vt] << " "; 

       // Ask for the function descriptor for the current function. 
       unsigned int cNames; 
       BSTR* CurrNames = DEBUG_NEW BSTR[pCurrFUNCDESC->cParams + 1]; 
       hr = spCurrTypeInfo->GetNames (pCurrFUNCDESC->memid, CurrNames, pCurrFUNCDESC->cParams + 1, &cNames); 

       // The first element contains the name of the function. 
       std::cout << CurrNames[0] << " ("; 

       // Process the parameters of the current function. 
       for (int k = 0; k < pCurrFUNCDESC->cParams; k++) 
       { 
        std::cout << "["; 

        // Determine the type of the parameter (in, out, retval). 
        bool needComma = false; 
        if (pCurrFUNCDESC->lprgelemdescParam[k].paramdesc.wParamFlags & PARAMFLAG_FIN) 
        { 
         std::cout << "in"; 
         needComma = true; 
        } 
        if (pCurrFUNCDESC->lprgelemdescParam[k].paramdesc.wParamFlags & PARAMFLAG_FOUT) 
        { 
         if (needComma) 
          std::cout << ", "; 
         std::cout << "out"; 
         needComma = true; 
        } 
        if (pCurrFUNCDESC->lprgelemdescParam[k].paramdesc.wParamFlags & PARAMFLAG_FRETVAL) 
        { 
         if (needComma) 
          std::cout << ", "; 
         std::cout << "retval"; 
        } 

        std::cout << "] "; 
        std::cout << GetType (pCurrFUNCDESC->lprgelemdescParam[k].tdesc, spCurrTypeInfo); 

        // If we didn't get a name for the parameter, it must be the parameter of a property put 
        // method. In this case we call the parameter simply "RHS" 
        if (k + 1 >= cNames) 
         std::cout << "RHS"; 
        else 
         std::cout << CurrNames[k + 1]; 
       } 
       delete[] CurrNames; 
      } 
     } 

     // If we have reached this line, all of the above operations must have succeeded. 
     return true; 
    } 

問候, 斯圖爾特

0

如果是這種存儲(第三方?)TLB文件,而不是在源控制IDL文件的副作用,一種可能性可以在IDL代替和自動進行檢查生成TLB作爲構建過程的一部分。

使用IDL而不是TLB還有一個額外的好處,即使用diff工具進行簡單比較,可以對COM接口進行版本更改。

相關問題