Convert TLB to IDL

1.9k Views Asked by At

Does anyone know of a commandline-tool that generates an idl file from a type library (.tlb)? The reason for doing this is to get rid of underscores in enum-values generated from regasm. The problem with underscores in enums is discussed in this blog

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

I know that OLEView can generate IDL files, but i'm looking for a solution that can fit into an automatic build.

2

There are 2 best solutions below

0
On

I doubt that the is a tool that does exactly what you want. But it should not be too hard too roll your own converter. Working with type libraries is a bit cumbersome. I wrote a tool that generated C++ wrapper classes for COM interfaces either from .idl files or from .tlb files. The code that reads out the relevant information of the interface is about 250 lines of code, so if you add some code to generate a .idl file you should end up with about 1000 lines of code.

The following code is a cut down version of my C++ code that should give you the general idea. However, since I don't have access to a compiler at the moment, I could not check whether it compiles (unlikely) or works (certainly not).

    #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;
    }

Regards, Stuart

0
On

If this is a side effect of storing (3rd party?) TLB files and not IDL files in source control, one possibility may be check in the IDL instead and auto-generate the TLB from that as part of the build process.

Using IDL instead of TLB has the added benefit of making version changes to the COM interfaces simple to compare using a diff tool.