【问题标题】:Convert TLB to IDL将 TLB 转换为 IDL
【发布时间】:2012-11-23 01:35:26
【问题描述】:

有人知道从类型库 (.tlb) 生成 idl 文件的命令行工具吗?这样做的原因是为了摆脱 regasm 生成的枚举值中的下划线。这个博客讨论了枚举中下划线的问题

http://blogs.artinsoft.net/mrojas/archive/2010/05/17/interop-remove-prefix-from-c-enums-for-com.aspx

我知道 OLEView 可以生成 IDL 文件,但我正在寻找适合自动构建的解决方案。

【问题讨论】:

    标签: .net com interop idl oleview


    【解决方案1】:

    我怀疑这是一个完全可以满足您需求的工具。但是,滚动您自己的转换器应该不会太难。使用类型库有点麻烦。我编写了一个工具,它可以从 .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;
        }
    

    问候, 斯图尔特

    【讨论】:

      【解决方案2】:

      如果这是在源代码控制中存储(第 3 方?)TLB 文件而不是 IDL 文件的副作用,则一种可能性可能是检查 IDL 并从中自动生成 TLB 作为构建过程的一部分。

      使用 IDL 代替 TLB 的另一个好处是,可以使用 diff 工具轻松比较 COM 接口的版本更改。

      【讨论】:

        猜你喜欢
        • 2011-02-19
        • 2023-03-13
        • 2010-11-21
        • 2023-02-10
        • 2010-09-11
        • 2011-03-27
        • 1970-01-01
        • 1970-01-01
        • 2013-08-25
        相关资源
        最近更新 更多