summaryrefslogtreecommitdiff
path: root/src/vm/tlbexport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/tlbexport.cpp')
-rw-r--r--src/vm/tlbexport.cpp6344
1 files changed, 6344 insertions, 0 deletions
diff --git a/src/vm/tlbexport.cpp b/src/vm/tlbexport.cpp
new file mode 100644
index 0000000000..01f5239620
--- /dev/null
+++ b/src/vm/tlbexport.cpp
@@ -0,0 +1,6344 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//===========================================================================
+// File: TlbExport.CPP
+//
+
+//
+// Notes: Create a TypeLib from COM+ metadata.
+//---------------------------------------------------------------------------
+
+#include "common.h"
+
+#include "comcallablewrapper.h"
+#include "field.h"
+#include "dllimport.h"
+#include "fieldmarshaler.h"
+#include "eeconfig.h"
+#include "comdelegate.h"
+#include <nsutilpriv.h>
+#include <tlbimpexp.h>
+#include <mlang.h>
+#include "tlbexport.h"
+#include "commtmemberinfomap.h"
+#include <corerror.h>
+#include "posterror.h"
+#include "typeparse.h"
+
+#if defined(VALUE_MASK)
+#undef VALUE_MASK
+#endif
+
+#include <guidfromname.h>
+#include <stgpool.h>
+#include <siginfo.hpp>
+#include <typestring.h>
+#include "perfcounters.h"
+#include "comtypelibconverter.h"
+#include "caparser.h"
+
+// Define to export an empty dispinterface for an AutoDispatch IClassX
+#define EMPTY_DISPINTERFACE_ICLASSX
+#ifndef S_USEIUNKNOWN
+#define S_USEIUNKNOWN (HRESULT)2
+#endif
+
+#if defined(_DEBUG) && defined(_TRACE)
+#define TRACE printf
+#else
+#define TRACE NullFn
+inline void NullFn(const char *pf,...) {}
+#endif
+
+#if defined(_DEBUG)
+#define IfFailReport(expr) \
+ do { if(FAILED(hr = (expr))) { DebBreakHr(hr); ReportError(hr); } } while (0)
+#else
+#define IfFailReport(expr) \
+ do { if(FAILED(hr = (expr))) { ReportError(hr); } } while (0)
+#endif
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// This value determines whether, by default, we add the TYPEFLAG_FPROXY bit
+// to exported interfaces. If the value is true, Automation proxy is the
+// default, and we do not set the bit. If the value is false, no Automation
+// proxy is the default and we DO set the bit.
+#define DEFAULT_AUTOMATION_PROXY_VALUE TRUE
+//-----------------------------------------------------------------------------
+
+//*****************************************************************************
+// Constants.
+//*****************************************************************************
+static const WCHAR szRetVal[] = W("pRetVal");
+static const WCHAR szTypeLibExt[] = W(".TLB");
+
+static const WCHAR szTypeLibKeyName[] = W("TypeLib");
+static const WCHAR szClsidKeyName[] = W("CLSID");
+
+static const WCHAR szIClassX[] = W("_%ls");
+static const int cbIClassX = 1;
+static const WCHAR cIClassX = W('_');
+
+static const WCHAR szAlias[] = W("_MIDL_COMPAT_%ls");
+static const int cbAlias = lengthof(szAlias) - 1;
+static const WCHAR szParamName[] = W("p%d");
+
+static const WCHAR szGuidName[] = W("GUID");
+
+static const CHAR szObjectClass[] = "Object";
+static const CHAR szArrayClass[] = "Array";
+static const CHAR szDateTimeClass[] = "DateTime";
+static const CHAR szDecimalClass[] = "Decimal";
+static const CHAR szGuidClass[] = "Guid";
+static const CHAR szStringClass[] = g_StringName;
+static const CHAR szStringBufferClass[] = g_StringBufferName;
+static const CHAR szIEnumeratorClass[] = "IEnumerator";
+static const CHAR szColor[] = "Color";
+
+static const char szRuntime[] = {"System."};
+static const size_t cbRuntime = (lengthof(szRuntime)-1);
+
+static const char szText[] = {"System.Text."};
+static const size_t cbText = (lengthof(szText)-1);
+
+static const char szCollections[] = {"System.Collections."};
+static const size_t cbCollections = (lengthof(szCollections)-1);
+
+static const char szDrawing[] = {"System.Drawing."};
+static const size_t cbDrawing = (lengthof(szDrawing)-1);
+
+// The length of the following string(w/o the terminator): "HKEY_CLASSES_ROOT\\CLSID\\{00000000-0000-0000-0000-000000000000}".
+static const int cCOMCLSIDRegKeyLength = 62;
+
+// The length of the following string(w/o the terminator): "{00000000-0000-0000-0000-000000000000}".
+static const int cCLSIDStrLength = 38;
+
+// {17093CC8-9BD2-11cf-AA4F-304BF89C0001}
+static const GUID GUID_TRANS_SUPPORTED = {0x17093CC8,0x9BD2,0x11cf,{0xAA,0x4F,0x30,0x4B,0xF8,0x9C,0x00,0x01}};
+
+// {00020430-0000-0000-C000-000000000046}
+static const GUID LIBID_STDOLE2 = { 0x00020430, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
+
+// {66504301-BE0F-101A-8BBB-00AA00300CAB}
+static const GUID GUID_OleColor = { 0x66504301, 0xBE0F, 0x101A, { 0x8B, 0xBB, 0x00, 0xAA, 0x00, 0x30, 0x0C, 0xAB } };
+
+// LIBID mscoree
+static const GUID LIBID_MSCOREE = {0x5477469e,0x83b1,0x11d2,{0x8b,0x49,0x00,0xa0,0xc9,0xb7,0xc9,0xc4}};
+
+static const char XXX_DESCRIPTION_TYPE[] = {"System.ComponentModel.DescriptionAttribute"};
+static const char XXX_ASSEMBLY_DESCRIPTION_TYPE[] = {"System.Reflection.AssemblyDescriptionAttribute"};
+
+//*****************************************************************************
+// Table to map COM+ calling conventions to TypeLib calling conventions.
+//*****************************************************************************
+CALLCONV Clr2TlbCallConv[] =
+{
+ CC_STDCALL, // IMAGE_CEE_CS_CALLCONV_DEFAULT = 0x0,
+ CC_CDECL, // IMAGE_CEE_CS_CALLCONV_C = 0x1,
+ CC_STDCALL, // IMAGE_CEE_CS_CALLCONV_STDCALL = 0x2,
+ CC_STDCALL, // IMAGE_CEE_CS_CALLCONV_THISCALL = 0x3,
+ CC_FASTCALL, // IMAGE_CEE_CS_CALLCONV_FASTCALL = 0x4,
+ CC_CDECL, // IMAGE_CEE_CS_CALLCONV_VARARG = 0x5,
+ CC_MAX // IMAGE_CEE_CS_CALLCONV_FIELD = 0x6,
+ // IMAGE_CEE_CS_CALLCONV_MAX = 0x7
+};
+
+
+
+// Forward declarations.
+extern HRESULT _FillVariant(MDDefaultValue *pMDDefaultValue, VARIANT *pvar);
+extern HRESULT _FillMDDefaultValue(BYTE bType, void const *pValue, MDDefaultValue *pMDDefaultValue);
+
+//*****************************************************************************
+// Stolen from classlib.
+//*****************************************************************************
+double _TicksToDoubleDate(const __int64 ticks)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ const INT64 MillisPerSecond = 1000;
+ const INT64 MillisPerDay = MillisPerSecond * 60 * 60 * 24;
+ const INT64 TicksPerMillisecond = 10000;
+ const INT64 TicksPerSecond = TicksPerMillisecond * 1000;
+ const INT64 TicksPerMinute = TicksPerSecond * 60;
+ const INT64 TicksPerHour = TicksPerMinute * 60;
+ const INT64 TicksPerDay = TicksPerHour * 24;
+ const int DaysPer4Years = 365 * 4 + 1;
+ const int DaysPer100Years = DaysPer4Years * 25 - 1;
+ const int DaysPer400Years = DaysPer100Years * 4 + 1;
+ const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367;
+ const INT64 DoubleDateOffset = DaysTo1899 * TicksPerDay;
+ const int DaysTo10000 = DaysPer400Years * 25 - 366;
+ const INT64 MaxMillis = DaysTo10000 * MillisPerDay;
+ const int DaysPerYear = 365; // non-leap year
+ const INT64 OADateMinAsTicks = (DaysPer100Years - DaysPerYear) * TicksPerDay;
+
+ // Returns OleAut's zero'ed date ticks.
+ if (ticks == 0)
+ return 0.0;
+
+ if (ticks < OADateMinAsTicks)
+ return 0.0;
+
+ // Currently, our max date == OA's max date (12/31/9999), so we don't
+ // need an overflow check in that direction.
+ __int64 millis = (ticks - DoubleDateOffset) / TicksPerMillisecond;
+ if (millis < 0)
+ {
+ __int64 frac = millis % MillisPerDay;
+ if (frac != 0) millis -= (MillisPerDay + frac) * 2;
+ }
+
+ return (double)millis / MillisPerDay;
+} // double _TicksToDoubleDate()
+
+
+//*****************************************************************************
+// Get the name of a typelib or typeinfo, add it to error text.
+//*****************************************************************************
+void PostTypeLibError(
+ IUnknown *pUnk, // An interface on the typeinfo.
+ HRESULT hrT, // The TypeInfo error.
+ HRESULT hrX) // The Exporter error.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pUnk));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr; // A result.
+ WCHAR rcErr[1024]; // Buffer for error message.
+
+ SafeComHolder<ITypeInfo> pITI=0; // The ITypeInfo * on the typeinfo.
+ SafeComHolder<ITypeLib> pITLB=0; // The ITypeLib *.
+ BSTRHolder name=0; // The name of the TypeInfo.
+
+ // Try to get a name.
+ hr = SafeQueryInterface(pUnk, IID_ITypeInfo, (IUnknown**)&pITI);
+ if (SUCCEEDED(hr))
+ {
+ IfFailThrow(pITI->GetDocumentation(MEMBERID_NIL, &name, 0,0,0));
+ }
+ else
+ {
+ hr = SafeQueryInterface(pUnk, IID_ITypeLib, (IUnknown**)&pITLB);
+ if (SUCCEEDED(hr))
+ IfFailThrow(pITLB->GetDocumentation(MEMBERID_NIL, &name, 0,0,0));
+ }
+
+ if (name == NULL)
+ {
+ name = SysAllocString(W("???"));
+ if (name == NULL)
+ COMPlusThrowHR(E_OUTOFMEMORY);
+ }
+
+ // Format the typelib error.
+ FormatRuntimeError(rcErr, lengthof(rcErr), hrT);
+
+ SString strHRHex;
+ strHRHex.Printf("%.8x", hrX);
+
+ COMPlusThrowHR(hrX, hrX, strHRHex, name, rcErr);
+} // void PostTypeLibError()
+
+
+
+
+void ExportTypeLibFromLoadedAssembly(
+ Assembly *pAssembly, // The assembly.
+ LPCWSTR szTlb, // The typelib name.
+ ITypeLib **ppTlb, // If not null, also return ITypeLib here.
+ ITypeLibExporterNotifySink *pINotify,// Notification callback.
+ int flags) // Export flags.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pAssembly));
+ PRECONDITION(CheckPointer(szTlb, NULL_OK));
+ PRECONDITION(CheckPointer(ppTlb));
+ PRECONDITION(CheckPointer(pINotify, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ TypeLibExporter exporter; // Exporter object.
+ LPCWSTR szModule=0; // Module filename.
+ StackSString ssDrive;
+ StackSString ssDir;
+ StackSString ssFile;
+ size_t cchDrive;
+ size_t cchDir;
+ size_t cchFile;
+ CQuickWSTR rcTlb; // Buffer for the tlb filename.
+ int bDynamic=0; // If true, dynamic module.
+ Module *pModule; // The Assembly's SecurityModule.
+
+ pModule = pAssembly->GetManifestModule();
+ _ASSERTE(pModule);
+
+ // Retrieve the module filename.
+ szModule = pModule->GetPath();
+ PREFIX_ASSUME(szModule != NULL);
+
+ // Make sure the assembly has not been imported from COM.
+ if (pAssembly->IsImportedFromTypeLib())
+ COMPlusThrowHR(TLBX_E_CIRCULAR_EXPORT, (UINT)TLBX_E_CIRCULAR_EXPORT, W(""), szModule, NULL);
+
+ // If the module is dynamic then it will not have a file name. We
+ // assign a dummy name for typelib name (if the scope does not have
+ // a name), but won't create a typelib on disk.
+ if (*szModule == 0)
+ {
+ bDynamic = TRUE;
+ szModule = W("Dynamic");
+ }
+
+ // Create the typelib name, if none provided. Don't create one for Dynamic modules.
+ if (!szTlb || !*szTlb)
+ {
+ if (bDynamic)
+ szTlb = W("");
+ else
+ {
+ SplitPath(szModule, &ssDrive, &ssDir, &ssFile, nullptr);
+ MakePath(rcTlb, ssDrive.GetUnicode(), ssDir.GetUnicode(), ssFile.GetUnicode(), szTypeLibExt);
+ szTlb = rcTlb.Ptr();
+ }
+ }
+
+ // Do the conversion.
+ exporter.Convert(pAssembly, szTlb, pINotify, flags);
+
+ // Get a copy of the ITypeLib*
+ IfFailThrow(exporter.GetTypeLib(IID_ITypeLib, (IUnknown**)ppTlb));
+} // void ExportTypeLibFromLoadedAssemblyInternal()
+
+
+HRESULT ExportTypeLibFromLoadedAssemblyNoThrow(
+ Assembly *pAssembly, // The assembly.
+ LPCWSTR szTlb, // The typelib name.
+ ITypeLib **ppTlb, // If not null, also return ITypeLib here.
+ ITypeLibExporterNotifySink *pINotify,// Notification callback.
+ int flags) // Export flags.
+{
+ CONTRACTL
+ {
+ DISABLED(NOTHROW);
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ ExportTypeLibFromLoadedAssembly(pAssembly,
+ szTlb,
+ ppTlb,
+ pINotify,
+ flags);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+}
+
+//*****************************************************************************
+// Default notification class.
+//*****************************************************************************
+class CDefaultNotify : public ITypeLibExporterNotifySink
+{
+public:
+ virtual HRESULT __stdcall ReportEvent(
+ ImporterEventKind EventKind, // Type of event.
+ long EventCode, // HR of event.
+ BSTR EventMsg) // Text message for event.
+ {
+ LIMITED_METHOD_CONTRACT;
+ return S_OK;
+ } // virtual HRESULT __stdcall ReportEvent()
+
+ //-------------------------------------------------------------------------
+ virtual HRESULT __stdcall ResolveRef(
+ IUnknown *Asm,
+ IUnknown **pRetVal)
+ {
+ CONTRACTL
+ {
+ DISABLED(NOTHROW);
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(Asm));
+ PRECONDITION(CheckPointer(pRetVal));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ Assembly *pAssembly=0; // The referenced Assembly.
+ ITypeLib *pTLB=0; // The created TypeLib.
+ MethodTable *pAssemblyClass = NULL; //@todo -- get this.
+ LPVOID RetObj = NULL; // The object to return.
+
+ BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+ {
+ {
+ GCX_COOP_THREAD_EXISTS(GET_THREAD());
+ // Get the Referenced Assembly from the IUnknown.
+ ASSEMBLYREF asmRef = NULL;
+ GCPROTECT_BEGIN(asmRef);
+ GetObjectRefFromComIP((OBJECTREF*)&asmRef, Asm, pAssemblyClass);
+ pAssembly = asmRef->GetAssembly();
+ GCPROTECT_END();
+ }
+
+ // Default resolution provides no notification, flags are 0.
+ ExportTypeLibFromLoadedAssembly(pAssembly, 0, &pTLB, 0 /*pINotify*/, 0 /* flags*/);
+ }
+ END_EXTERNAL_ENTRYPOINT;
+
+ *pRetVal = pTLB;
+
+ return hr;
+ } // virtual HRESULT __stdcall ResolveRef()
+
+ //-------------------------------------------------------------------------
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(// S_OK or E_NOINTERFACE
+ REFIID riid, // Desired interface.
+ void **ppvObject) // Put interface pointer here.
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(ppvObject));
+ }
+ CONTRACTL_END;
+
+ *ppvObject = 0;
+ if (riid == IID_IUnknown || riid == IID_ITypeLibExporterNotifySink)
+ {
+ *ppvObject = this;
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ } // virtual HRESULT QueryInterface()
+
+ //-------------------------------------------------------------------------
+ virtual ULONG STDMETHODCALLTYPE AddRef(void)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return 1;
+ } // virtual ULONG STDMETHODCALLTYPE AddRef()
+
+ //-------------------------------------------------------------------------
+ virtual ULONG STDMETHODCALLTYPE Release(void)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return 1;
+ } // virtual ULONG STDMETHODCALLTYPE Release()
+};
+
+static CDefaultNotify g_Notify;
+
+//*****************************************************************************
+// CTOR/DTOR.
+//*****************************************************************************
+TypeLibExporter::TypeLibExporter()
+ : m_pICreateTLB(0),
+ m_pIUnknown(0),
+ m_pIDispatch(0),
+ m_pGuid(0),
+ m_hIUnknown(-1)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if defined(_DEBUG)
+ static int i;
+ ++i; // So a breakpoint can be set.
+#endif
+} // TypeLibExporter::TypeLibExporter()
+
+TypeLibExporter::~TypeLibExporter()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ ReleaseResources();
+} // TypeLibExporter::~TypeLibExporter()
+
+//*****************************************************************************
+// Get an interface pointer from the ICreateTypeLib interface.
+//*****************************************************************************
+HRESULT TypeLibExporter::GetTypeLib(
+ REFGUID iid,
+ IUnknown **ppITypeLib)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(ppITypeLib));
+ }
+ CONTRACTL_END;
+
+ return SafeQueryInterface(m_pICreateTLB, iid, (IUnknown**)ppITypeLib);
+} // HRESULT TypeLibExporter::GetTypeLib()
+
+//*****************************************************************************
+// LayOut a TypeLib. Call LayOut on all ICreateTypeInfo2s first.
+//*****************************************************************************
+void TypeLibExporter::LayOut() // S_OK or error.
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = S_OK; // A result.
+ int cTypes; // Count of exported types.
+ int ix; // Loop control.
+ CExportedTypesInfo *pData; // For iterating the entries.
+
+ cTypes = m_Exports.Count();
+
+ // Call LayOut on all ICreateTypeInfo2*s.
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_Exports[ix];
+ if (pData->pCTI && FAILED(hr = pData->pCTI->LayOut()))
+ PostTypeLibError(pData->pCTI, hr, TLBX_E_LAYOUT_ERROR);
+ }
+
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_Exports[ix];
+ if (pData->pCTIClassItf && FAILED(hr = pData->pCTIClassItf->LayOut()))
+ PostTypeLibError(pData->pCTIClassItf, hr, TLBX_E_LAYOUT_ERROR);
+ }
+
+ // Repeat for injected types.
+ cTypes = m_InjectedExports.Count();
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_InjectedExports[ix];
+ if (pData->pCTI && FAILED(hr = pData->pCTI->LayOut()))
+ PostTypeLibError(pData->pCTI, hr, TLBX_E_LAYOUT_ERROR);
+ }
+
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_InjectedExports[ix];
+ if (pData->pCTIClassItf && FAILED(hr = pData->pCTIClassItf->LayOut()))
+ PostTypeLibError(pData->pCTIClassItf, hr, TLBX_E_LAYOUT_ERROR);
+ }
+} // HRESULT TypeLibExporter::LayOut()
+
+//*****************************************************************************
+// Release all pointers.
+//*****************************************************************************
+void TypeLibExporter::ReleaseResources()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Release the ITypeInfo* pointers.
+ m_Exports.Clear();
+ m_InjectedExports.Clear();
+
+ // Clean up the created TLB.
+ SafeRelease(m_pICreateTLB);
+ m_pICreateTLB = 0;
+
+ // Clean up the ITypeInfo*s for well-known interfaces.
+ SafeRelease(m_pIUnknown);
+ m_pIUnknown = 0;
+
+ SafeRelease(m_pIDispatch);
+ m_pIDispatch = 0;
+
+ SafeRelease(m_pGuid);
+ m_pGuid = 0;
+} // void TypeLibExporter::ReleaseResources()
+
+//*****************************************************************************
+// Enumerate the Types in a Module, add to the list.
+//*****************************************************************************
+void TypeLibExporter::AddModuleTypes(
+ Module *pModule) // The module to convert.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pModule));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+ ULONG cTD; // Count of typedefs.
+ mdTypeDef td; // A TypeDef.
+ MethodTable *pClass; // A MethodTable for a TypeDef.
+ ULONG ix; // Loop control.
+ CExportedTypesInfo *pExported; // For adding classes to the exported types cache.
+ CExportedTypesInfo sExported; // For adding classes to the exported types cache.
+
+
+ // Convert all the types visible to COM.
+ // Get an enumerator on TypeDefs in the scope.
+ HENUMInternalHolder eTD(pModule->GetMDImport());
+ eTD.EnumTypeDefInit();
+ cTD = pModule->GetMDImport()->EnumTypeDefGetCount(&eTD);
+
+ // Add all the classes to the hash.
+ for (ix=0; ix<cTD; ++ix)
+ {
+ ZeroHolder zhType = &m_ErrorContext.m_pScope; // Clear error reporting info.
+
+ // Get the TypeDef.
+ if (!pModule->GetMDImport()->EnumTypeDefNext(&eTD, &td))
+ IfFailReport(E_UNEXPECTED);
+
+ IMDInternalImport* pInternalImport = pModule->GetMDImport();
+
+ // Error reporting info.
+ m_ErrorContext.m_tkType = td;
+ m_ErrorContext.m_pScope = pModule->GetMDImport();
+
+ // Get the class, perform the step.
+ pClass = LoadClass(pModule, td);
+
+ // Enumerate the formal type parameters
+ HENUMInternal hEnumGenericPars;
+ hr = pInternalImport->EnumInit(mdtGenericParam, td, &hEnumGenericPars);
+ if (SUCCEEDED(hr))
+ {
+ DWORD numGenericArgs = pInternalImport->EnumGetCount(&hEnumGenericPars);
+ // skip generic classes
+ if( numGenericArgs > 0 )
+ {
+ // We'll only warn if the type is marked ComVisible.
+ if (SpecialIsGenericTypeVisibleFromCom(TypeHandle(pClass)))
+ ReportWarning(TLBX_I_GENERIC_TYPE, TLBX_I_GENERIC_TYPE);
+
+ continue;
+ }
+ }
+
+ // If the flag to not ignore non COM visible types in name decoration is set, then
+ // add the ComVisible(false) types to our list of exported types by skipping this check.
+ if ((m_flags & TlbExporter_OldNames) == 0)
+ {
+ // If the type isn't visible from COM, don't add it to the list of exports.
+ if (!IsTypeVisibleFromCom(TypeHandle(pClass)))
+ continue;
+ }
+
+ // See if this class is already in the list.
+ sExported.pClass = pClass;
+ pExported = m_Exports.Find(&sExported);
+ if (pExported != 0)
+ continue;
+
+ // New class, add to list.
+ pExported = m_Exports.Add(&sExported);
+ if (!pExported)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // Prefix can't tell that IfFailReport will actually throw an exception if pExported is NULL so
+ // let's tell it explicitly that if we reach this point pExported will not be NULL.
+ PREFIX_ASSUME(pExported != NULL);
+ pExported->pClass = pClass;
+ pExported->pCTI = 0;
+ pExported->pCTIClassItf = 0;
+ }
+} // HRESULT TypeLibExporter::AddModuleTypes()
+
+//*****************************************************************************
+// Enumerate the Modules in an assembly, add the types to the list.
+//*****************************************************************************
+void TypeLibExporter::AddAssemblyTypes(
+ Assembly *pAssembly) // The assembly to convert.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pAssembly));
+ }
+ CONTRACTL_END;
+
+ Module *pManifestModule; // A module in the assembly.
+ mdFile mf; // A file token.
+
+ if (pAssembly->GetManifestImport())
+ {
+ // Enumerator over the modules of the assembly.
+ HENUMInternalHolder phEnum(pAssembly->GetManifestImport());
+ phEnum.EnumInit(mdtFile, mdTokenNil);
+
+ // Get the module for the assembly.
+ pManifestModule = pAssembly->GetManifestModule();
+ AddModuleTypes(pManifestModule);
+
+ while (pAssembly->GetManifestImport()->EnumNext(&phEnum, &mf))
+ {
+ DomainFile *pDomainFile = pAssembly->GetManifestModule()->LoadModule(GetAppDomain(), mf, FALSE);
+
+ if (pDomainFile != NULL && !pDomainFile->GetFile()->IsResource())
+ AddModuleTypes(pDomainFile->GetModule());
+ }
+ }
+} // HRESULT TypeLibExporter::AddAssemblyTypes()
+
+//*****************************************************************************
+// Convert COM+ metadata to a typelib.
+//*****************************************************************************
+void TypeLibExporter::Convert(
+ Assembly *pAssembly, // The Assembly to convert
+ LPCWSTR szTlbName, // Name of resulting TLB
+ ITypeLibExporterNotifySink *pNotify,// Notification callback.
+ int flags) // Conversion flags
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pAssembly));
+ PRECONDITION(CheckPointer(szTlbName));
+ PRECONDITION(CheckPointer(pNotify, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ ULONG i; // Loop control.
+ SString sName; // Library name.
+ GUID guid; // Library guid.
+ VARIANT vt = {0}; // Variant for ExportedFromComPlus.
+ CQuickArray<WCHAR> qLocale; // Wide string for locale.
+ LCID lcid; // LCID for typelib, default 0.
+
+ // Set PerfCounters
+ COUNTER_ONLY(GetPerfCounters().m_Interop.cTLBExports++);
+
+ SafeComHolder<IMultiLanguage> pIML=0; // For locale->lcid conversion.
+ SafeComHolder<ITypeLib> pITLB=0; // TypeLib for IUnknown, IDispatch.
+ BSTRHolder szTIName=0; // Name of a TypeInfo.
+ BSTRHolder szDescription=0; // Assembly Description.
+
+ // Error reporting information.
+ m_ErrorContext.m_szAssembly = pAssembly->GetSimpleName();
+
+ m_flags = flags;
+
+ // Set the callback.
+ m_pNotify = pNotify ? pNotify : &g_Notify;
+
+ // If we haven't set 32-bit or 64-bit export yet, set it now with defaults.
+ UpdateBitness(pAssembly);
+
+ // Check the bitness of the assembly against our output bitness
+ IfFailReport(CheckBitness(pAssembly));
+
+ // Get some well known TypeInfos.
+ GCX_PREEMP();
+
+ BSTR wzPath;// = SysAllocStringLen(NULL, _MAX_PATH);
+ IfFailReport(QueryPathOfRegTypeLib(LIBID_STDOLE2, -1, -1, 0, &wzPath));
+
+ if (IsExportingAs64Bit())
+ {
+ hr = LoadTypeLibEx(wzPath, (REGKIND)(REGKIND_NONE | LOAD_TLB_AS_64BIT), &pITLB);
+ }
+ else
+ {
+ hr = LoadTypeLibEx(wzPath, (REGKIND)(REGKIND_NONE | LOAD_TLB_AS_32BIT), &pITLB);
+ }
+
+ // If we failed to load StdOle2.tlb, then we're probably on a downlevel platform (< XP)
+ // so we'll just load whatever we have...note that at this point, cross-compile is not an option.
+ if (FAILED(hr))
+ {
+ IfFailReport(LoadRegTypeLib(LIBID_STDOLE2, -1, -1, 0, &pITLB));
+ }
+
+ IfFailReport(pITLB->GetTypeInfoOfGuid(IID_IUnknown, &m_pIUnknown));
+ IfFailReport(pITLB->GetTypeInfoOfGuid(IID_IDispatch, &m_pIDispatch));
+
+ // Look for GUID (which unfortunately has no GUID).
+ for (i=0; i<pITLB->GetTypeInfoCount() && !m_pGuid; ++i)
+ {
+ IfFailReport(pITLB->GetDocumentation(i, &szTIName, 0, 0, 0));
+ if (SString::_wcsicmp(szTIName, szGuidName) == 0)
+ IfFailReport(pITLB->GetTypeInfo(i, &m_pGuid));
+ }
+
+ // Create the output typelib.
+
+ // Win2K: passing in too long a filename triggers a nasty buffer overrun bug
+ // when the SaveAll() method is called. We'll avoid triggering this here.
+ //
+ if (wcslen(szTlbName) > MAX_PATH_FNAME)
+ IfFailReport(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE));
+
+ // Reverting to old behavior here until we can fix up the vtable offsets as well.
+ // Set the SYSKIND based on the 64bit/32bit switches
+ if (IsExportingAs64Bit())
+ {
+ IfFailReport(CreateTypeLib2(SYS_WIN64, szTlbName, &m_pICreateTLB));
+ }
+ else
+ {
+ IfFailReport(CreateTypeLib2(SYS_WIN32, szTlbName, &m_pICreateTLB));
+ }
+
+ // Set the typelib GUID.
+ IfFailReport(GetTypeLibGuidForAssembly(pAssembly, &guid));
+ IfFailReport(m_pICreateTLB->SetGuid(guid));
+
+ // Retrieve the type library's version number.
+ USHORT usMaj, usMin;
+ IfFailReport(GetTypeLibVersionForAssembly(pAssembly, &usMaj, &usMin));
+
+ // Set the TLB's version number.
+ IfFailReport(m_pICreateTLB->SetVersion(usMaj, usMin));
+
+ // Set the LCID. If no locale, set to 0, otherwise typelib defaults to 409.
+ lcid = 0;
+ LPCUTF8 pLocale = pAssembly->GetLocale();
+ if (pLocale && *pLocale)
+ {
+ // Have to build a BSTR, not just a unicode string (i.e. allocate a
+ // DWORD of length information at a negative offset from the string
+ // start).
+ _ASSERTE((sizeof(WCHAR) * 2) == sizeof(DWORD));
+ hr = qLocale.ReSizeNoThrow(sizeof(DWORD));
+ if (SUCCEEDED(hr))
+ hr = Utf2Quick(pLocale, qLocale, 2);
+ if (SUCCEEDED(hr))
+ {
+ *(DWORD*)qLocale.Ptr() = (DWORD)wcslen(&qLocale.Ptr()[2]);
+ hr = ::CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, (void**)&pIML);
+ }
+ if (SUCCEEDED(hr))
+ pIML->GetLcidFromRfc1766(&lcid, (BSTR)&qLocale.Ptr()[2]);
+ }
+ HRESULT hr2 = m_pICreateTLB->SetLcid(lcid);
+ if (hr2 == TYPE_E_UNKNOWNLCID)
+ {
+ ReportWarning(TYPE_E_UNKNOWNLCID, TYPE_E_UNKNOWNLCID);
+ hr2 = m_pICreateTLB->SetLcid(0);
+ }
+ IfFailReport(hr2);
+
+ // Get the list of types in the assembly.
+ AddAssemblyTypes(pAssembly);
+ m_Exports.InitArray();
+
+ // Get the assembly value for AutomationProxy.
+ m_bAutomationProxy = DEFAULT_AUTOMATION_PROXY_VALUE;
+ GetAutomationProxyAttribute(pAssembly->GetManifestImport(), TokenFromRid(1, mdtAssembly), &m_bAutomationProxy);
+
+ // Pre load any caller-specified names into the typelib namespace.
+ PreLoadNames();
+
+ // Convert all the types.
+ ConvertAllTypeDefs();
+
+ // Set library level properties.
+ sName.AppendUTF8(pAssembly->GetSimpleName());
+
+ // Make it a legal typelib name.
+ SString replaceChar = SL(W("_"));
+
+ SString::Iterator iter = sName.Begin();
+ while (sName.Find(iter, W(".")))
+ sName.Replace(iter, 1, replaceChar);
+
+ iter = sName.Begin();
+ while (sName.Find(iter, W(" ")))
+ sName.Replace(iter, 1, replaceChar);
+
+ IfFailReport(m_pICreateTLB->SetName((LPOLESTR)sName.GetUnicode()));
+
+ // If the assembly has a description CA, set that as the library Doc string.
+ if (GetStringCustomAttribute(pAssembly->GetManifestImport(), XXX_ASSEMBLY_DESCRIPTION_TYPE, TokenFromRid(mdtAssembly, 1), (BSTR &)szDescription))
+ m_pICreateTLB->SetDocString((LPWSTR)szDescription);
+
+ // Mark this typelib as exported.
+ LPCWSTR pszFullName;
+ {
+ //@todo: exceptions?
+ StackSString name;
+ pAssembly->GetDisplayName(name);
+ pszFullName = name.GetUnicode();
+
+ vt.vt = VT_BSTR;
+ vt.bstrVal = SysAllocString(pszFullName);
+ if (vt.bstrVal == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+ }
+
+ //WszMultiByteToWideChar(CP_ACP,0, (char*)rBuf.Ptr(), (DWORD)rBuf.Size(), vt.bstrVal, (DWORD)rBuf.Size());
+ IfFailReport(m_pICreateTLB->SetCustData(GUID_ExportedFromComPlus, &vt));
+
+ // Lay out the TypeInfos.
+ LayOut();
+
+ if(vt.bstrVal)
+ {
+ SysFreeString(vt.bstrVal);
+ vt.bstrVal = NULL;
+ }
+
+} // HRESULT TypeLibExporter::Convert()
+
+
+void TypeLibExporter::UpdateBitness(Assembly* pAssembly)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If one has already been set, just return.
+ if ((TlbExportAs64Bit(m_flags)) || (TlbExportAs32Bit(m_flags)))
+ return;
+
+ // If we are exporting a dynamic assembly, just go with the machine type we're running on.
+ if (pAssembly->IsDynamic())
+ {
+#ifdef _WIN64
+ m_flags |= TlbExporter_ExportAs64Bit;
+#else
+ m_flags |= TlbExporter_ExportAs32Bit;
+#endif
+ return;
+ }
+
+ // Get the assembly info
+ PEFile* pPEFile = pAssembly->GetDomainAssembly()->GetFile();
+ _ASSERTE(pPEFile);
+
+ DWORD PEKind, MachineKind;
+ pPEFile->GetPEKindAndMachine(&PEKind, &MachineKind);
+
+ // Based on the assembly flags, determine a bitness to export with.
+ // Algorithm base copied from ComputeProcArchFlags() in bcl\system\reflection\assembly.cs
+ if ((PEKind & pe32Plus) == pe32Plus)
+ {
+ switch (MachineKind)
+ {
+ case IMAGE_FILE_MACHINE_IA64:
+ case IMAGE_FILE_MACHINE_AMD64:
+ m_flags |= TlbExporter_ExportAs64Bit;
+ break;
+
+ case IMAGE_FILE_MACHINE_I386:
+ if ((PEKind & peILonly) == peILonly)
+ {
+#ifdef _WIN64
+ m_flags |= TlbExporter_ExportAs64Bit;
+#else
+ m_flags |= TlbExporter_ExportAs32Bit;
+#endif
+ }
+ else
+ {
+ _ASSERTE(!"Invalid MachineKind / PEKind pair on the assembly!");
+ }
+ break;
+
+ default:
+ _ASSERTE(!"Unknown MachineKind!");
+ }
+ }
+ else if (MachineKind == IMAGE_FILE_MACHINE_I386)
+ {
+ if ((PEKind & pe32BitRequired) == pe32BitRequired)
+ {
+ m_flags |= TlbExporter_ExportAs32Bit;
+ }
+ else if ((PEKind & peILonly) == peILonly)
+ {
+#ifdef _WIN64
+ m_flags |= TlbExporter_ExportAs64Bit;
+#else
+ m_flags |= TlbExporter_ExportAs32Bit;
+#endif
+ }
+ else
+ {
+ m_flags |= TlbExporter_ExportAs32Bit;
+ }
+ }
+ else if (MachineKind == IMAGE_FILE_MACHINE_ARMNT)
+ {
+ m_flags |= TlbExporter_ExportAs32Bit;
+ }
+ else
+ {
+#ifdef _WIN64
+ m_flags |= TlbExporter_ExportAs64Bit;
+#else
+ m_flags |= TlbExporter_ExportAs32Bit;
+#endif
+ }
+}
+
+
+// Find out if our assembly / bitness combination is valid.
+HRESULT TypeLibExporter::CheckBitness(Assembly* pAssembly)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (pAssembly->IsDynamic())
+ return S_OK;
+
+ PEFile* pPEFile = pAssembly->GetDomainAssembly()->GetFile();
+ if (pPEFile == NULL)
+ return TLBX_E_BITNESS_MISMATCH;
+
+ DWORD PEKind, MachineKind;
+ pPEFile->GetPEKindAndMachine(&PEKind, &MachineKind);
+
+ // Neutral assembly?
+ if ((PEKind & peILonly) == peILonly)
+ return S_OK;
+
+ if (IsExportingAs64Bit())
+ {
+ if ((MachineKind == IMAGE_FILE_MACHINE_IA64) || (MachineKind == IMAGE_FILE_MACHINE_AMD64))
+ return S_OK;
+ }
+ else
+ {
+ if ((MachineKind == IMAGE_FILE_MACHINE_I386) || (MachineKind == IMAGE_FILE_MACHINE_ARMNT))
+ return S_OK;
+ }
+
+ return TLBX_E_BITNESS_MISMATCH;
+}
+
+
+//*****************************************************************************
+//*****************************************************************************
+void TypeLibExporter::PreLoadNames()
+{
+ STANDARD_VM_CONTRACT;
+
+ SafeComHolder<ITypeLibExporterNameProvider> pINames = 0;
+ HRESULT hr = S_OK; // A result.
+ SafeArrayHolder pNames = 0; // Names provided by caller.
+ VARTYPE vt; // Type of data.
+ int lBound, uBound, ix; // Loop control.
+ BSTR name;
+
+ // Look for names provider, but don't require it.
+ hr = SafeQueryInterface(m_pNotify, IID_ITypeLibExporterNameProvider, (IUnknown**)&pINames);
+ if (FAILED(hr))
+ return;
+
+ // There is a provider, so get the list of names.
+ IfFailReport(pINames->GetNames(&pNames));
+
+ // Better have a single dimension array of strings.
+ if (pNames == 0)
+ IfFailReport(TLBX_E_BAD_NAMES);
+
+ if (SafeArrayGetDim(pNames) != 1)
+ IfFailReport(TLBX_E_BAD_NAMES);
+
+ IfFailReport(SafeArrayGetVartype(pNames, &vt));
+ if (vt != VT_BSTR)
+ IfFailReport(TLBX_E_BAD_NAMES);
+
+ // Get names bounds.
+ IfFailReport(SafeArrayGetLBound(pNames, 1, (LONG*)&lBound));
+ IfFailReport(SafeArrayGetUBound(pNames, 1, (LONG*)&uBound));
+
+ // Enumerate the names.
+ for (ix=lBound; ix<=uBound; ++ix)
+ {
+ IfFailReport(SafeArrayGetElement(pNames, (LONG*)&ix, (void*)&name));
+ m_pICreateTLB->SetName(name);
+ }
+}
+
+//*****************************************************************************
+//*****************************************************************************
+void TypeLibExporter::FormatErrorContextString(
+ CErrorContext *pContext, // The context to format.
+ SString *pOut) // Buffer to format into.
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ PRECONDITION(CheckPointer(pOut));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+ SString *pBuf;
+ SString ssInternal;
+
+ // Nested contexts?
+ if (pContext->m_prev == 0)
+ { // No, just convert into caller's buffer.
+ pBuf = pOut;
+ }
+ else
+ { // Yes, convert locally, then concatenate.
+ pBuf = &ssInternal;
+ }
+
+ // More?
+ if (pContext->m_pScope)
+ {
+ // Check whether type is nested (which requires more formatting).
+ DWORD dwFlags;
+ IfFailReport(pContext->m_pScope->GetTypeDefProps(pContext->m_tkType, &dwFlags, 0));
+
+ if (IsTdNested(dwFlags))
+ {
+ TypeNameBuilder tnb(pBuf, TypeNameBuilder::ParseStateNAME);
+ TypeString::AppendNestedTypeDef(tnb, pContext->m_pScope, pContext->m_tkType);
+ }
+ else
+ TypeString::AppendTypeDef(*pBuf, pContext->m_pScope, pContext->m_tkType);
+
+ // Member?
+ if (pContext->m_szMember)
+ {
+ pBuf->Append(NAMESPACE_SEPARATOR_WSTR);
+
+ pBuf->AppendUTF8(pContext->m_szMember);
+
+ // Param?
+ if (pContext->m_szParam)
+ {
+ pBuf->Append(W("("));
+ pBuf->AppendUTF8(pContext->m_szParam);
+ pBuf->Append(W(")"));
+ }
+ else if (pContext->m_ixParam > -1)
+ {
+ pBuf->AppendPrintf(W("(#%d)"), pContext->m_ixParam);
+ }
+ } // member
+
+ pBuf->Append(ASSEMBLY_SEPARATOR_WSTR);
+ } // Type name
+
+ pBuf->AppendUTF8(pContext->m_szAssembly);
+
+ // If there is a nested context, put it all together.
+ if (pContext->m_prev)
+ {
+ // Format the context this one was nested inside.
+ SString ssOuter;
+ FormatErrorContextString(pContext->m_prev, &ssOuter);
+
+ // Put them together with text.
+ LPWSTR pUnicodeBuffer = pOut->OpenUnicodeBuffer(1024);
+ FormatRuntimeError(pUnicodeBuffer, 1024, TLBX_E_CTX_NESTED, pBuf->GetUnicode(), ssOuter.GetUnicode());
+ pOut->CloseBuffer((COUNT_T)wcslen(pUnicodeBuffer));
+ }
+} // HRESULT TypeLibExporter::FormatErrorContextString()
+
+//*****************************************************************************
+//*****************************************************************************
+void TypeLibExporter::FormatErrorContextString(
+ SString *pBuf) // Buffer to format into.
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pBuf));
+ }
+ CONTRACTL_END;
+
+ FormatErrorContextString(&m_ErrorContext, pBuf);
+} // HRESULT TypeLibExporter::FormatErrorContextString()
+
+//*****************************************************************************
+// Event reporting helper.
+//*****************************************************************************
+void TypeLibExporter::ReportError(HRESULT hrRpt)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcErr[1024];
+ SString ssName;
+ SafeComHolder<IErrorInfo> pErrorInfo;
+ BSTRHolder bstrDescription = NULL;
+
+ // Format the error message.
+ if (SafeGetErrorInfo(&pErrorInfo) != S_OK)
+ pErrorInfo = NULL;
+
+ // If we retrieved and IErrorInfo then retrieve the description.
+ if (pErrorInfo)
+ {
+ if (FAILED(pErrorInfo->GetDescription(&bstrDescription)))
+ bstrDescription = NULL;
+ }
+
+ if (bstrDescription)
+ {
+ // Use the description as the error message.
+ wcsncpy_s(rcErr, COUNTOF(rcErr), bstrDescription, _TRUNCATE);
+ }
+ else
+ {
+ // Format the error message.
+ FormatRuntimeError(rcErr, lengthof(rcErr), hrRpt);
+ }
+
+ // Format the context.
+ FormatErrorContextString(&ssName);
+
+ // Post the error to the errorinfo object.
+ VMPostError(TLBX_E_ERROR_MESSAGE, ssName.GetUnicode(), rcErr);
+
+ // Throw the exception, including context info.
+ COMPlusThrowHR(TLBX_E_ERROR_MESSAGE, kGetErrorInfo);
+} // void TypeLibExporter::ReportError()
+
+//*****************************************************************************
+// Event reporting helper.
+//*****************************************************************************
+void TypeLibExporter::ReportEvent( // Returns the original HR.
+ int ev, // The event kind.
+ int hr, // HR.
+ ...) // Variable args.
+{
+ STANDARD_VM_CONTRACT;
+
+ WCHAR rcMsg[1024]; // Buffer for message.
+ va_list marker; // User text.
+ BSTRHolder bstrMsg=0; // BSTR for message.
+
+ // Format the message.
+ va_start(marker, hr);
+ hr = FormatRuntimeErrorVa(rcMsg, lengthof(rcMsg), hr, marker);
+ va_end(marker);
+
+ // Convert to a BSTR.
+ bstrMsg = SysAllocString(rcMsg);
+
+ // Display it, and clean up.
+ if (bstrMsg != NULL)
+ m_pNotify->ReportEvent(static_cast<ImporterEventKind>(ev), hr, bstrMsg);
+
+} // HRESULT CImportTlb::ReportEvent()
+
+//*****************************************************************************
+// Warning reporting helper.
+//*****************************************************************************
+void TypeLibExporter::ReportWarning( // Original error code.
+ HRESULT hrReturn, // HR to return.
+ HRESULT hrRpt, // Error code.
+ ...) // Args to message.
+{
+ STANDARD_VM_CONTRACT;
+
+ WCHAR rcErr[1024]; // Buffer for error message.
+ SString ssName; // Buffer for context.
+ va_list marker; // User text.
+ BSTRHolder bstrMsg=0; // BSTR for message.
+ BSTRHolder bstrBuf=0; // Buffer for message.
+ UINT iLen; // Length of allocated buffer.
+
+ // Format the message.
+ va_start(marker, hrRpt);
+ FormatRuntimeErrorVa(rcErr, lengthof(rcErr), hrRpt, marker);
+ va_end(marker);
+
+ // Format the context.
+ FormatErrorContextString(&ssName);
+
+ // Put them together.
+ iLen = (UINT)(wcslen(rcErr) + ssName.GetCount() + 200);
+ bstrBuf = SysAllocStringLen(0, iLen);
+
+ if (bstrBuf != NULL)
+ {
+ FormatRuntimeError(bstrBuf, iLen, TLBX_W_WARNING_MESSAGE, ssName.GetUnicode(), rcErr);
+
+ // Have to copy to another BSTR, because the runtime will also print the trash after the
+ // terminating nul.
+ bstrMsg = SysAllocString(bstrBuf);
+
+ if (bstrMsg != NULL)
+ m_pNotify->ReportEvent(NOTIF_CONVERTWARNING, hrRpt, bstrMsg);
+ }
+
+} // void TypeLibExporter::ReportWarning()
+
+// Throws exceptions encountered during type exportation.
+// Wrapped with ThrowHRWithContext.
+void TypeLibExporter::InternalThrowHRWithContext(HRESULT hrRpt, ...)
+{
+ STANDARD_VM_CONTRACT;
+
+ WCHAR rcErr[2048];
+ SString ssName;
+ va_list marker;
+
+ // Format the error message.
+ va_start(marker, hrRpt);
+ FormatRuntimeErrorVa(rcErr, lengthof(rcErr), hrRpt, marker);
+ va_end(marker);
+
+ // Format the context.
+ FormatErrorContextString(&ssName);
+
+ // Post the error to the errorinfo object.
+ VMPostError(TLBX_E_ERROR_MESSAGE, ssName.GetUnicode(), rcErr);
+
+ // Throw the exception, including context info.
+ COMPlusThrowHR(TLBX_E_ERROR_MESSAGE, kGetErrorInfo);
+} // void TypeLibExporter::InternalThrowHRWithContext()
+
+//*****************************************************************************
+// Post a class load error on failure.
+//*****************************************************************************
+void TypeLibExporter::PostClassLoadError(
+ LPCUTF8 pszName, // Name of the class.
+ SString& message) // Exception message of class load failure.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pszName));
+ }
+ CONTRACTL_END;
+
+ // See if we got anything back.
+ if (!message.IsEmpty())
+ InternalThrowHRWithContext(TLBX_E_CLASS_LOAD_EXCEPTION, pszName, message.GetUnicode());
+ else
+ InternalThrowHRWithContext(TLBX_E_CANT_LOAD_CLASS, pszName);
+} // HRESULT TypeLibExporter::PostClassLoadError()
+
+//*****************************************************************************
+// Determine the type, if any, of auto-interface for a class.
+// May be none, dispatch, or dual.
+//*****************************************************************************
+void TypeLibExporter::ClassHasIClassX(
+ MethodTable *pClass, // The class.
+ CorClassIfaceAttr *pClassItfType) // None, dual, dispatch
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pClass));
+ PRECONDITION(!pClass->IsInterface());
+ PRECONDITION(CheckPointer(pClassItfType));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ ComMethodTable *pClassComMT = NULL;
+
+ *pClassItfType = clsIfNone;
+
+ // If the class is a COM import or if it isn't COM visible, then from the
+ // exporter's perspective, it doens't have an IClassX.
+ if (!pClass->IsComImport())
+ {
+ ComCallWrapperTemplate *pTemplate = ComCallWrapperTemplate::GetTemplate(pClass);
+ if (pTemplate->SupportsIClassX())
+ {
+ pClassComMT = ComCallWrapperTemplate::SetupComMethodTableForClass(pClass, FALSE);
+ _ASSERTE(pClassComMT);
+
+ if (pClassComMT->IsComVisible())
+ *pClassItfType = pClassComMT->GetClassInterfaceType();
+ }
+ }
+} // HRESULT TypeLibExporter::ClassHasIClassX()
+
+//*****************************************************************************
+// Load a class by token, post an error on failure.
+//*****************************************************************************
+MethodTable * TypeLibExporter::LoadClass(
+ Module *pModule, // Module with Loader to use to load the class.
+ mdToken tk) // The token to load.
+{
+ CONTRACT(MethodTable *)
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pModule));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ // Get the MethodTable for the token.
+ TypeHandle th;
+ SString exceptionMessage;
+
+ EX_TRY
+ {
+ th = ClassLoader::LoadTypeDefOrRefThrowing(pModule, tk,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::PermitUninstDefOrRef);
+ }
+ EX_CATCH
+ {
+ GET_EXCEPTION()->GetMessage(exceptionMessage);
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (th.IsNull())
+ {
+ // Format a hopefully useful error message.
+ LPCUTF8 pNS, pName;
+ SString sName;
+
+ if (TypeFromToken(tk) == mdtTypeDef)
+ {
+ if (FAILED(pModule->GetMDImport()->GetNameOfTypeDef(tk, &pName, &pNS)))
+ {
+ pName = pNS = "Invalid TypeDef record";
+ }
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tk) == mdtTypeRef);
+ if (FAILED(pModule->GetMDImport()->GetNameOfTypeRef(tk, &pNS, &pName)))
+ {
+ pNS = pName = "Invalid TypeRef record";
+ }
+ }
+
+ if (pNS && *pNS)
+ {
+ sName.AppendUTF8(pNS);
+ sName.AppendUTF8(NAMESPACE_SEPARATOR_STR);
+ }
+
+ sName.AppendUTF8(pName);
+
+ StackScratchBuffer scratch;
+ PostClassLoadError(sName.GetUTF8(scratch), exceptionMessage);
+ }
+
+ RETURN (th.AsMethodTable());
+
+} // void TypeLibExporter::LoadClass()
+
+//*****************************************************************************
+// Load a class by name, post an error on failure.
+//*****************************************************************************
+TypeHandle TypeLibExporter::LoadClass(
+ Module *pModule, // Module with Loader to use to load the class.
+ LPCUTF8 pszName) // Name of class to load.
+{
+ CONTRACT(TypeHandle)
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(CheckPointer(pszName));
+ POSTCONDITION(!RETVAL.IsNull());
+ }
+ CONTRACT_END;
+
+ TypeHandle th;
+ SString exceptionMessage;
+
+ EX_TRY
+ {
+ th = TypeName::GetTypeUsingCASearchRules(pszName, pModule->GetAssembly());
+ _ASSERTE(!th.IsNull());
+ }
+ EX_CATCH
+ {
+ GET_EXCEPTION()->GetMessage(exceptionMessage);
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (th.IsNull())
+ {
+ PostClassLoadError(pszName, exceptionMessage);
+ }
+
+ RETURN th;
+
+} // void TypeLibExporter::LoadClass()
+
+
+//*****************************************************************************
+// Enumerate the TypeDefs and convert them to TypeInfos.
+//*****************************************************************************
+void TypeLibExporter::ConvertAllTypeDefs()
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = S_OK; // A result.
+ CExportedTypesInfo *pData; // For iterating the entries.
+ int cTypes; // Count of types.
+ int ix; // Loop control.
+
+ LPCSTR pName1, pNS1; // Names of a type.
+ LPCSTR pName2, pNS2; // Names of another type.
+ MethodTable *pc1; // A Type.
+ MethodTable *pc2; // Another type.
+ CQuickArray<BYTE> bNamespace; // Array of flags for namespace decoration.
+
+ cTypes = m_Exports.Count();
+
+ // If there are no types in the assembly, then we are done.
+ if (cTypes <= 0)
+ return;
+
+ // Order by name, then look for duplicates.
+ m_Exports.SortByName();
+
+ // Resize the array for namespace flags now, but use the ICreateTypeInfo*, so that
+ // the flags will be sorted.
+ bNamespace.ReSizeThrows(cTypes);
+
+ // Get names of first type.
+ pc1 = m_Exports[0]->pClass;
+ IfFailReport(pc1->GetMDImport()->GetNameOfTypeDef(pc1->GetCl(), &pName1, &pNS1));
+
+ // Iterate through the types, looking for duplicate type names.
+ for (ix=0; ix<cTypes-1; ++ix)
+ {
+ // Get the Type pointers and the types' names.
+ pc2 = m_Exports[ix+1]->pClass;
+ IfFailReport(pc2->GetMDImport()->GetNameOfTypeDef(pc2->GetCl(), &pName2, &pNS2));
+
+ // If the types match (case insensitive). mark both types for namespace
+ // decoration.
+ if (stricmpUTF8(pName1, pName2) == 0)
+ {
+ m_Exports[ix]->pCTI = reinterpret_cast<ICreateTypeInfo2*>(1);
+ m_Exports[ix+1]->pCTI = reinterpret_cast<ICreateTypeInfo2*>(1);
+ }
+ else
+ { // Didn't match, so advance "class 1" pointer.
+ pc1 = pc2;
+ pName1 = pName2;
+ pNS1 = pNS2;
+ }
+ }
+
+ // Put into token order for actual creation.
+ m_Exports.SortByToken();
+
+ // Fill the flag array, from the ICreateTypeInfo* pointers.
+ memset(bNamespace.Ptr(), 0, bNamespace.Size()*sizeof(BYTE));
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ if (m_Exports[ix]->pCTI)
+ bNamespace[ix] = 1, m_Exports[ix]->pCTI = 0;
+ }
+
+ // Pass 1. Create the TypeInfos.
+ // There are four steps in the process:
+ // a) Creates the TypeInfos for the types themselves. When a duplicate
+ // is encountered, skip the type until later, so that we don't create
+ // a decorated name that will conflict with a subsequent non-decorated
+ // name. We want to preserve a type's given name as much as possible.
+ // b) Create the TypeInfos for the types that were duplicates in step a.
+ // Perform decoration of the names as necessary to eliminate duplicates.
+ // c) Create the TypeInfos for the IClassXs. When there is a duplicate,
+ // skip, as in step a.
+ // d) Create the remaining TypeInfos for IClassXs. Perform decoration of
+ // the names as necessary to eliminate duplicates.
+
+ // Step a, Create the TypeInfos for the TypeDefs, no decoration.
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ int bAutoProxy = m_bAutomationProxy;
+ pData = m_Exports[ix];
+ pData->tkind = TKindFromClass(pData->pClass);
+ GetAutomationProxyAttribute(pData->pClass->GetMDImport(), pData->pClass->GetCl(), &bAutoProxy);
+ pData->bAutoProxy = (bAutoProxy != 0);
+
+ CreateITypeInfo(pData, (bNamespace[ix]!=0), false);
+ }
+ // Step b, Create the TypeInfos for the TypeDefs, decoration as needed.
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_Exports[ix];
+ if (pData->pCTI == 0)
+ CreateITypeInfo(pData, (bNamespace[ix]!=0), true);
+ }
+
+ // Step c, Create the TypeInfos for the IClassX interfaces. No decoration.
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_Exports[ix];
+ CreateIClassXITypeInfo(pData, (bNamespace[ix]!=0), false);
+ }
+ // Step d, Create the TypeInfos for the IClassX interfaces. Decoration as required.
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_Exports[ix];
+ if (pData->pCTIClassItf == 0)
+ CreateIClassXITypeInfo(pData, (bNamespace[ix]!=0), true);
+ }
+
+ // Pass 2, add the ImplTypes to the CoClasses.
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_Exports[ix];
+ ConvertImplTypes(pData);
+ }
+
+ // Pass 3, fill in the TypeInfo details...
+ for (ix=0; ix<cTypes; ++ix)
+ {
+ pData = m_Exports[ix];
+ ConvertDetails(pData);
+ }
+
+ hr = S_OK;
+} // void TypeLibExporter::ConvertAllTypeDefs()
+
+//*****************************************************************************
+// Convert one TypeDef. Useful for one-off TypeDefs in other scopes where
+// that other scope's typelib doesn't contain a TypeInfo. This happens
+// for the event information with imported typelibs.
+//*****************************************************************************
+HRESULT TypeLibExporter::ConvertOneTypeDef(
+ MethodTable *pClass) // The one class to convert.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pClass));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ ICreateTypeInfo2 *pCTI=0; // The TypeInfo to create.
+ ICreateTypeInfo2 *pDefault=0; // A possible IClassX TypeInfo.
+ CErrorContext SavedContext; // Previous error context.
+ CExportedTypesInfo *pExported; // For adding classes to the exported types cache.
+ CExportedTypesInfo sExported; // For adding classes to the exported types cache.
+
+ // Save error reporting context.
+ SavedContext = m_ErrorContext;
+ m_ErrorContext.m_szAssembly = pClass->GetAssembly()->GetSimpleName();
+ m_ErrorContext.m_tkType = mdTypeDefNil;
+ m_ErrorContext.m_pScope = 0;
+ m_ErrorContext.m_szMember = 0;
+ m_ErrorContext.m_szParam = 0;
+ m_ErrorContext.m_ixParam = -1;
+ m_ErrorContext.m_prev = &SavedContext;
+
+ // See if this class is already in the list.
+ sExported.pClass = pClass;
+ pExported = m_InjectedExports.Find(&sExported);
+ if (pExported == 0)
+ {
+ // Get the AutoProxy value for an isolated class.
+ int bAutoProxy = DEFAULT_AUTOMATION_PROXY_VALUE;
+ if (FALSE == GetAutomationProxyAttribute(pClass->GetMDImport(), pClass->GetCl(), &bAutoProxy))
+ GetAutomationProxyAttribute(pClass->GetAssembly()->GetManifestImport(), TokenFromRid(1, mdtAssembly), &bAutoProxy);
+
+ // New class, add to list.
+ if (NULL == (pExported = m_InjectedExports.Add(&sExported)))
+ IfFailReport(E_OUTOFMEMORY);
+ m_InjectedExports.UpdateArray();
+
+ // Prefix can't tell that IfFailReport will actually throw an exception if pExported is NULL so
+ // let's tell it explicitly that if we reach this point pExported will not be NULL.
+ PREFIX_ASSUME(pExported != NULL);
+ pExported->pClass = pClass;
+ pExported->pCTI = 0;
+ pExported->pCTIClassItf = 0;
+ pExported->tkind = TKindFromClass(pClass);
+ pExported->bAutoProxy = (bAutoProxy != 0);
+
+ // Step 1, Create the TypeInfos for the TypeDefs.
+ CreateITypeInfo(pExported);
+
+ // Step 1a, Create the TypeInfos for the IClassX interfaces.
+ CreateIClassXITypeInfo(pExported);
+
+ // Step 2, add the ImplTypes to the CoClasses.
+ ConvertImplTypes(pExported);
+
+ // Step 3, fill in the TypeInfo details...
+ ConvertDetails(pExported);
+ }
+
+ // Restore error reporting context.
+ m_ErrorContext = SavedContext;
+
+ return (hr);
+} // HRESULT TypeLibExporter::ConvertOneTypeDef()
+
+
+//*****************************************************************************
+// Create the ITypeInfo for a type. Well, sort of. This function will create
+// the first of possibly two typeinfos for the type. If the type is a class
+// we will create a COCLASS typeinfo now, and an INTERFACE typeinfo later,
+// which typeinfo will be the default interface for the coclass. If this
+// typeinfo needs to be aliased, we will create the ALIAS now (with the
+// real name) and the aliased typeinfo later, with the real attributes, but
+// with a mangled name.
+//*****************************************************************************
+void TypeLibExporter::CreateITypeInfo(
+ CExportedTypesInfo *pData, // Conversion data.
+ bool bNamespace, // If true, use namespace + name
+ bool bResolveDup) // If true, decorate name to resolve dups.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pData));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ LPCUTF8 pName; // Name in UTF8.
+ LPCUTF8 pNS; // Namespace in UTF8.
+ SString sName; // Name of the TypeDef.
+ TYPEKIND tkind; // The TYPEKIND of a TypeDef.
+ GUID clsid; // A TypeDef's clsid.
+ DWORD dwFlags; // A TypeDef's flags.
+ int iSuffix = 0; // Counter for suffix.
+ mdTypeDef td; // Token for the class.
+
+ VariantHolder vt; // For defining custom attribute.
+ SafeComHolder<ICreateTypeInfo> pCTITemp=0; // For creating a typeinfo.
+ SafeComHolder<ICreateTypeInfo2> pCTI2=0; // For creating the typeinfo.
+ SafeComHolder<ITypeLib> pITLB=0; // For dup IID reporting.
+ SafeComHolder<ITypeInfo> pITIDup=0; // For dup IID reporting.
+ BSTRHolder bstrDup=0; // For dup IID reporting.
+ BSTRHolder bstrDescr=0; // For description.
+ ZeroHolder zhType = &m_ErrorContext.m_pScope; // Clear error reporting info.
+ CorClassIfaceAttr classItfType = clsIfNone; // For class interface type.
+ TypeHandle thClass = TypeHandle(pData->pClass); // TypeHandle representing the class.
+
+ DefineFullyQualifiedNameForClassW();
+
+ // Get the TypeDef and some info about it.
+ td = pData->pClass->GetCl();
+ IfFailReport(pData->pClass->GetMDImport()->GetTypeDefProps(td, &dwFlags, 0));
+ tkind = pData->tkind;
+
+ // Error reporting info.
+ m_ErrorContext.m_tkType = td;
+ m_ErrorContext.m_pScope = pData->pClass->GetMDImport();
+
+ pData->pCTI = 0;
+ pData->pCTIClassItf = 0;
+
+ // If it is ComImport or WindowsRuntimeImport, do not export it.
+ if (IsTdImport(dwFlags) || pData->pClass->IsProjectedFromWinRT())
+ return;
+
+ // Check to see if the type is supposed to be visible from COM. If it
+ // is not then we go to the next type.
+ if (!IsTypeVisibleFromCom(TypeHandle(pData->pClass)))
+ return;
+
+ // Get the GUID for the class. Will generate from name if no defined GUID,
+ // will also use signatures if interface.
+ pData->pClass->GetGuid(&clsid, TRUE);
+
+ // Get the name.
+ IfFailReport(pData->pClass->GetMDImport()->GetNameOfTypeDef(td, &pName, &pNS));
+
+ // Warn about exporting AutoLayout valueclasses
+ if ( (pData->pClass->IsValueType()) && (!pData->pClass->IsEnum()) && (IsTdAutoLayout(pData->pClass->GetAttrClass())))
+ ReportWarning(TLBX_W_EXPORTING_AUTO_LAYOUT, TLBX_W_EXPORTING_AUTO_LAYOUT, pName);
+
+ // Warn about exporting generic classes.
+ if (pData->pClass->GetNumGenericArgs() != 0)
+ ReportWarning(TLBX_I_GENERIC_TYPE, TLBX_I_GENERIC_TYPE);
+
+ // Classes that derive from generic classes can be COM visible, however we don't
+ // expose a class interface for them. Give a warning to the user about this.
+ if (pData->pClass->HasGenericClassInstantiationInHierarchy())
+ {
+ if (!pData->pClass->IsComImport() && IsTypeVisibleFromCom(thClass))
+ {
+ // Note that we can't call ClassHasIClassX here since it would return
+ // classIfNone if the type has generic parents in it's hierarchy.
+ if (ReadClassInterfaceTypeCustomAttribute(thClass) != clsIfNone)
+ ReportWarning(TLBX_I_GENERIC_BASE_TYPE, TLBX_I_GENERIC_BASE_TYPE);
+ }
+ }
+
+ // Warn about exporting reference types as structs.
+ if ((pData->tkind == TKIND_RECORD || pData->tkind == TKIND_UNION) && !pData->pClass->IsValueType())
+ ReportWarning(TLBX_I_REF_TYPE_AS_STRUCT, TLBX_I_REF_TYPE_AS_STRUCT);
+
+ // workaround for microsoft.wfc.interop.dll -- skip their IDispatch.
+ if (clsid == IID_IDispatch || clsid == IID_IUnknown)
+ {
+ ReportEvent(NOTIF_CONVERTWARNING, TLBX_S_NOSTDINTERFACE, pName);
+ return;
+ }
+
+ if (bNamespace)
+ {
+ sName.MakeFullNamespacePath(SString(SString::Utf8, pNS), SString(SString::Utf8, pName));
+
+ SString replaceChar = SL(W("_"));
+
+ SString::Iterator iter = sName.Begin();
+ while (sName.Find(iter, W(".")))
+ sName.Replace(iter, 1, replaceChar);
+ }
+ else
+ { // Convert name to wide chars.
+ sName.AppendUTF8(pName);
+ }
+
+ // Create the typeinfo for this typedef.
+ for (;;)
+ {
+ // Attempt to create the TypeDef.
+ hr = m_pICreateTLB->CreateTypeInfo((LPOLESTR)sName.GetUnicode(), tkind, &pCTITemp);
+
+ // If a name conflict, decorate, otherwise, done.
+ if (hr != TYPE_E_NAMECONFLICT)
+ break;
+
+ if (!bResolveDup)
+ {
+ hr = S_FALSE;
+ return;
+ }
+
+ if (iSuffix == 0)
+ {
+ iSuffix = 2;
+ }
+ else
+ {
+ sName.Delete(sName.End()-=2, 2);
+ }
+
+ SString sDup;
+ sDup.Printf(szDuplicateDecoration, iSuffix++);
+
+ sName.Append(sDup);
+ }
+
+ IfFailReport(hr);
+ IfFailReport(SafeQueryInterface(pCTITemp, IID_ICreateTypeInfo2, (IUnknown**)&pCTI2));
+
+ // Set the guid.
+ _ASSERTE(clsid != GUID_NULL);
+ hr = pCTI2->SetGuid(clsid);
+ if (FAILED(hr))
+ {
+ if (hr == TYPE_E_DUPLICATEID)
+ {
+ IfFailReport(SafeQueryInterface(m_pICreateTLB, IID_ITypeLib, (IUnknown**)&pITLB));
+ IfFailReport(pITLB->GetTypeInfoOfGuid(clsid, &pITIDup));
+ IfFailReport(pITIDup->GetDocumentation(MEMBERID_NIL, &bstrDup, 0,0,0));
+ InternalThrowHRWithContext(TLBX_E_DUPLICATE_IID, sName.GetUnicode(), (BSTR)bstrDup);
+ }
+ return;
+ }
+ TRACE("TypeInfo %x: %ls, {%08x-%04x-%04x-%04x-%02x%02x%02x%02x}\n", pCTI2, sName,
+ clsid.Data1, clsid.Data2, clsid.Data3, clsid.Data4[0]<<8|clsid.Data4[1], clsid.Data4[2], clsid.Data4[3], clsid.Data4[4], clsid.Data4[5]);
+
+ IfFailReport(pCTI2->SetVersion(1, 0));
+
+ // Record the fully qualified type name in a custom attribute.
+ // If the TypelibImportClassAttribute exists, use that instead.
+ SString sName2;
+ hr = GetTypeLibImportClassName(pData->pClass, sName2);
+ if (hr == S_OK)
+ {
+ V_BSTR(&vt) = ::SysAllocString(sName2.GetUnicode());
+ if (V_BSTR(&vt) == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ V_VT(&vt) = VT_BSTR;
+ }
+ else
+ {
+ // Default to the real name.
+ LPCWSTR pszName = GetFullyQualifiedNameForClassNestedAwareW(pData->pClass);
+
+ V_BSTR(&vt) = ::SysAllocString(pszName);
+ if (V_BSTR(&vt) == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ V_VT(&vt) = VT_BSTR;
+ }
+
+ IfFailReport(pCTI2->SetCustData(GUID_ManagedName, &vt));
+
+ // If the class is decorated with a description, apply it to the typelib.
+ if (GetDescriptionString(pData->pClass, td, (BSTR &)bstrDescr))
+ IfFailReport(pCTI2->SetDocString(bstrDescr));
+
+ // Transfer ownership of the pointer.
+ pData->pCTI = pCTI2;
+ pCTI2.SuppressRelease();
+ pCTI2 = 0;
+} // void TypeLibExporter::CreateITypeInfo()
+
+HRESULT TypeLibExporter::GetTypeLibImportClassName(
+ MethodTable*pClass,
+ SString& szName)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ _ASSERTE(NULL != pClass);
+
+ HRESULT hr = S_OK;
+
+ // Check for the presence of the TypelibImportClassAttribute.
+ const char* pvData; // Pointer to a custom attribute data.
+ ULONG cbData; // Size of custom attribute data.
+
+ hr = pClass->GetMDImport()->GetCustomAttributeByName(pClass->GetCl(),
+ INTEROP_TYPELIBIMPORTCLASS_TYPE,
+ reinterpret_cast<const void**>(&pvData),
+ &cbData);
+
+ if (hr == S_OK && cbData > 5 && pvData[0] == 1 && pvData[1] == 0)
+ {
+ CustomAttributeParser cap(pvData, cbData);
+ VERIFY(SUCCEEDED(cap.ValidateProlog())); // Validated above, just ensure consistency.
+
+ LPCUTF8 szString;
+ ULONG cbString;
+ if (SUCCEEDED(cap.GetNonNullString(&szString, &cbString)))
+ {
+ // Set the string and null terminate it.
+ szName.SetUTF8(szString, cbString);
+ szName.AppendASCII("\0");
+
+ // We successfully retrieved the string.
+ return S_OK;
+ }
+ }
+
+ return S_FALSE;
+}
+
+
+
+//*****************************************************************************
+// See if an object has a Description, and get it as a BSTR.
+//*****************************************************************************
+BOOL TypeLibExporter::GetDescriptionString(
+ MethodTable *pClass, // Class containing the token.
+ mdToken tk, // Token of the object.
+ BSTR &bstrDescr) // Put description here.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pClass));
+ }
+ CONTRACTL_END;
+
+ // Check for a description custom attribute.
+ return GetStringCustomAttribute(pClass->GetMDImport(), XXX_DESCRIPTION_TYPE, tk, bstrDescr);
+
+} // HRESULT TypeLibExporter::GetDescriptionString()
+
+//*****************************************************************************
+// See if an object has a custom attribute, and get it as a BSTR.
+//*****************************************************************************
+BOOL TypeLibExporter::GetStringCustomAttribute(
+ IMDInternalImport *pImport,
+ LPCSTR szName,
+ mdToken tk,
+ BSTR &bstrDescr)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pImport));
+ PRECONDITION(CheckPointer(szName));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr; // A result.
+ const void *pvData; // Pointer to a custom attribute data.
+ ULONG cbData; // Size of custom attribute data.
+
+ // Look for the desired custom attribute.
+ IfFailReport(pImport->GetCustomAttributeByName(tk, szName, &pvData,&cbData));
+ if (hr == S_OK && cbData > 2)
+ {
+ CustomAttributeParser cap(pvData, cbData);
+ IfFailReport(cap.SkipProlog());
+
+ LPCUTF8 szString;
+ ULONG cbString;
+ IfFailReport(cap.GetString(&szString, &cbString));
+
+ bstrDescr = SysAllocStringLen(0, cbString); // allocates cbString+1 characters (appends '\0')
+ if (bstrDescr == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ if (cbString > 0)
+ {
+ ULONG cch = WszMultiByteToWideChar(CP_UTF8, 0, szString, cbString, bstrDescr, cbString);
+ bstrDescr[cch] = W('\0');
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+} // HRESULT GetStringCustomAttribute()
+
+//*****************************************************************************
+// Get the value for AutomationProxy for an object. Return the default
+// if there is no attribute.
+//*****************************************************************************
+BOOL TypeLibExporter::GetAutomationProxyAttribute(
+ IMDInternalImport *pImport,
+ mdToken tk,
+ int *bValue)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pImport));
+ PRECONDITION(CheckPointer(bValue));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr; // A result.
+ const void *pvData; // Pointer to a custom attribute data.
+ ULONG cbData; // Size of custom attribute data.
+
+ IfFailReport(pImport->GetCustomAttributeByName(tk, INTEROP_AUTOPROXY_TYPE, &pvData, &cbData));
+ if (hr == S_OK && cbData > 2)
+ {
+ CustomAttributeParser cap(pvData, cbData);
+ if (FAILED(cap.SkipProlog()))
+ return FALSE;
+
+ UINT8 u1;
+ if (FAILED(cap.GetU1(&u1)))
+ return FALSE;
+
+ *bValue = u1 != 0;
+ }
+
+ if (hr == S_OK)
+ return TRUE;
+
+ return FALSE;
+} // void TypeLibExporter::GetAutomationProxyAttribute()
+
+//*****************************************************************************
+// Create the IClassX ITypeInfo.
+//*****************************************************************************
+void TypeLibExporter::CreateIClassXITypeInfo(
+ CExportedTypesInfo *pData, // Conversion data.
+ bool bNamespace, // If true, use namespace + name
+ bool bResolveDup) // If true, decorate name to resolve dups.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pData));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ LPCUTF8 pName; // Name in UTF8.
+ LPCUTF8 pNS; // Namespace in UTF8.
+ SString sName; // Name of the TypeDef.
+ SString sNameTypeInfo; // Name of the IClassX.
+ TYPEKIND tkind; // The TYPEKIND of a TypeDef.
+ GUID clsid; // A TypeDef's clsid.
+ DWORD dwFlags; // A TypeDef's flags.
+ LPWSTR pSuffix; // Pointer into the name.
+ int iSuffix = 0; // Counter for suffix.
+ GUID guid = {0}; // A default interface's IID.
+ HREFTYPE href; // href of base interface of IClassX.
+ mdTypeDef td; // Token for the class.
+ CorClassIfaceAttr classItfType = clsIfNone; // For class interface type.
+
+ VariantHolder vt; // For defining custom attribute.
+ SafeComHolder<ICreateTypeInfo> pCTITemp=0; // For creating a typeinfo.
+ SafeComHolder<ITypeInfo> pITemp=0; // An ITypeInfo to get a name.
+ SafeComHolder<ITypeLib> pITLB=0; // For dup IID reporting.
+ SafeComHolder<ITypeInfo> pITIDup=0; // For dup IID reporting.
+ SafeComHolder<ICreateTypeInfo2> pCTI2=0; // For creating the typeinfo.
+ BSTRHolder bstrName=0; // An ITypeInfo's name.
+ BSTRHolder bstrDescr=0; // For description.
+ BSTRHolder bstrDup=0; // For dup IID reporting.
+ ZeroHolder zhType = &m_ErrorContext.m_pScope; // Clear error reporting info.
+
+ MethodTable* pClassOuter = pData->pClass;
+
+ DefineFullyQualifiedNameForClassW();
+
+ // Get the TypeDef and some info about it.
+ td = pData->pClass->GetCl();
+ IfFailReport(pData->pClass->GetMDImport()->GetTypeDefProps(td, &dwFlags, 0));
+ tkind = pData->tkind;
+
+ // Error reporting info.
+ m_ErrorContext.m_tkType = td;
+ m_ErrorContext.m_pScope = pData->pClass->GetMDImport();
+
+ // A CoClass needs an IClassX, and an alias kind needs an alias.
+ if (tkind != TKIND_COCLASS)
+ return;
+
+ // Check to see if the type is supposed to be visible from COM. If it
+ // is not then we go to the next type.
+ if (!IsTypeVisibleFromCom(TypeHandle(pClassOuter)))
+ return;
+
+ // Imported types don't need an IClassX.
+ if (IsTdImport(dwFlags))
+ return;
+
+ // Check to see if we need to set up an IClassX for the class.
+ ClassHasIClassX(pData->pClass, &classItfType);
+ if (classItfType == clsIfNone)
+ return;
+
+ // Get full name from metadata.
+ IfFailReport(pData->pClass->GetMDImport()->GetNameOfTypeDef(td, &pName, &pNS));
+
+ // Get the GUID for the class. Used to generate IClassX guid.
+ pData->pClass->GetGuid(&clsid, TRUE);
+
+ // Get the name of the class. Use the ITypeInfo if there is one, except don't
+ // use the typeinfo for types which are Aliased.
+ if (pData->pCTI)
+ {
+ IfFailReport(SafeQueryInterface(pData->pCTI, IID_ITypeInfo, (IUnknown**)&pITemp));
+ IfFailReport(pITemp->GetDocumentation(MEMBERID_NIL, &bstrName, 0,0,0));
+ sName.Append(bstrName);
+ }
+ else
+ {
+ sName.AppendUTF8(pName);
+ }
+
+ // Create the typeinfo name for the IClassX
+ sNameTypeInfo.Set(cIClassX);
+ sNameTypeInfo.Append(sName);
+
+ tkind = TKIND_INTERFACE;
+ pSuffix = 0;
+ for (;;)
+ {
+ // Try to create the TypeInfo.
+ hr = m_pICreateTLB->CreateTypeInfo((LPOLESTR)sNameTypeInfo.GetUnicode(), tkind, &pCTITemp);
+
+ // If a name conflict, decorate, otherwise, done.
+ if (hr != TYPE_E_NAMECONFLICT)
+ break;
+
+ if (!bResolveDup)
+ {
+ hr = S_FALSE;
+ return;
+ }
+
+ if (iSuffix == 0)
+ {
+ iSuffix = 2;
+ }
+ else
+ {
+ sNameTypeInfo.Delete(sNameTypeInfo.End()-=2, 2);
+ }
+
+ SString sDup;
+ sDup.Printf(szDuplicateDecoration, iSuffix++);
+
+ sNameTypeInfo.Append(sDup);
+ }
+
+ IfFailReport(hr);
+ IfFailReport(SafeQueryInterface(pCTITemp, IID_ICreateTypeInfo2, (IUnknown**)&pCTI2));
+
+ // Generate the "IClassX" UUID and set it.
+ GenerateClassItfGuid(TypeHandle(pData->pClass), &guid);
+ hr = pCTI2->SetGuid(guid);
+ if (FAILED(hr))
+ {
+ if (hr == TYPE_E_DUPLICATEID)
+ {
+ IfFailReport(SafeQueryInterface(m_pICreateTLB, IID_ITypeLib, (IUnknown**)&pITLB));
+ IfFailReport(pITLB->GetTypeInfoOfGuid(guid, &pITIDup));
+ IfFailReport(pITIDup->GetDocumentation(MEMBERID_NIL, &bstrDup, 0,0,0));
+ InternalThrowHRWithContext(TLBX_E_DUPLICATE_IID, sNameTypeInfo.GetUnicode(), (BSTR)bstrDup);
+ }
+ return;
+ }
+
+ // Adding methods may cause an href to this typeinfo, which will cause it to be layed out.
+ // Set the inheritance, so that nesting will be correct when that layout happens.
+ // Add IDispatch as impltype 0.
+ GetRefTypeInfo(pCTI2, m_pIDispatch, &href);
+ IfFailReport(pCTI2->AddImplType(0, href));
+
+ // Record the fully qualified type name in a custom attribute.
+ LPCWSTR szName = GetFullyQualifiedNameForClassNestedAwareW(pData->pClass);
+ V_VT(&vt) = VT_BSTR;
+ V_BSTR(&vt) = SysAllocString(szName);
+ if (V_BSTR(&vt) == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ IfFailReport(pCTI2->SetCustData(GUID_ManagedName, &vt));
+
+ TRACE("IClassX %x: %ls, {%08x-%04x-%04x-%04x-%02x%02x%02x%02x}\n", pCTI2, sName,
+ guid.Data1, guid.Data2, guid.Data3, guid.Data4[0]<<8|guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5]);
+
+ // If the class is decorated with a description, apply it to the typelib.
+ if(GetDescriptionString(pData->pClass, td, (BSTR &)bstrDescr))
+ IfFailReport(pCTI2->SetDocString(bstrDescr));
+
+ // Transfer ownership of the pointer.
+ _ASSERTE(pData->pCTIClassItf == 0);
+ pData->pCTIClassItf = pCTI2;
+ pCTI2.SuppressRelease();
+ pCTI2 = 0;
+} // HRESULT TypeLibExporter::CreateIClassXITypeInfo()
+
+//*****************************************************************************
+// Add the impltypes to an ITypeInfo.
+//*****************************************************************************
+void TypeLibExporter::ConvertImplTypes(
+ CExportedTypesInfo *pData) // Conversion data.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pData));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ DWORD dwFlags; // A TypeDef's flags.
+ mdTypeDef td; // Token for the class.
+ ZeroHolder zhType = &m_ErrorContext.m_pScope; // Clear error reporting info.
+
+ // Get the TypeDef and some info about it.
+ td = pData->pClass->GetCl();
+ IfFailReport(pData->pClass->GetMDImport()->GetTypeDefProps(td, &dwFlags, 0));
+
+ // Error reporting info.
+ m_ErrorContext.m_tkType = td;
+ m_ErrorContext.m_pScope = pData->pClass->GetMDImport();
+
+ // If there is no ITypeInfo, skip it.
+ if (pData->pCTI == 0)
+ return;
+
+ // Check to see if the type is supposed to be visible from COM. If it
+ // is not then we go to the next type.
+ if (!IsTypeVisibleFromCom(TypeHandle(pData->pClass)))
+ return;
+
+ // Add the ImplTypes to the CoClass.
+ switch (pData->tkind)
+ {
+ case TKIND_INTERFACE:
+ case TKIND_DISPATCH:
+ // Add the base type to the interface.
+ ConvertInterfaceImplTypes(pData->pCTI, pData->pClass);
+ break;
+
+ case TKIND_RECORD:
+ case TKIND_UNION:
+ case TKIND_ENUM:
+ // Nothing to do at this step.
+ break;
+
+ case TKIND_COCLASS:
+ // Add the ImplTypes to the CoClass.
+ ConvertClassImplTypes(pData->pCTI, pData->pCTIClassItf, pData->pClass);
+ break;
+
+ default:
+ _ASSERTE(!"Unknown TYPEKIND");
+ IfFailReport(E_INVALIDARG);
+ break;
+ }
+} // HRESULT TypeLibExporter::ConvertImplTypes()
+
+//*****************************************************************************
+// Convert the details (members) of an ITypeInfo.
+//*****************************************************************************
+void TypeLibExporter::ConvertDetails(
+ CExportedTypesInfo *pData) // Conversion data.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pData));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ DWORD dwFlags; // A TypeDef's flags.
+ mdTypeDef td; // Token for the class.
+ ZeroHolder zhType = &m_ErrorContext.m_pScope; // Clear error reporting info.
+
+ // Get the TypeDef and some info about it.
+ td = pData->pClass->GetCl();
+ IfFailReport(pData->pClass->GetMDImport()->GetTypeDefProps(td, &dwFlags, 0));
+
+ // Error reporting info.
+ m_ErrorContext.m_tkType = td;
+ m_ErrorContext.m_pScope = pData->pClass->GetMDImport();
+
+ // If there is no TypeInfo, skip it, but for CoClass need to populate IClassX.
+ if (pData->pCTI == 0 && pData->tkind != TKIND_COCLASS)
+ return;
+
+ // Check to see if the type is supposed to be visible from COM. If it
+ // is not then we go to the next type.
+ if (!IsTypeVisibleFromCom(TypeHandle(pData->pClass)))
+ return;
+
+ // Fill in the rest of the typeinfo for this typedef.
+ switch (pData->tkind)
+ {
+ case TKIND_INTERFACE:
+ case TKIND_DISPATCH:
+ ConvertInterfaceDetails(pData->pCTI, pData->pClass, pData->bAutoProxy);
+ break;
+
+ case TKIND_RECORD:
+ case TKIND_UNION:
+ ConvertRecord(pData);
+ break;
+
+ case TKIND_ENUM:
+ ConvertEnum(pData->pCTI, pData->pClass);
+ break;
+
+ case TKIND_COCLASS:
+ // Populate the methods on the IClassX interface.
+ ConvertClassDetails(pData->pCTI, pData->pCTIClassItf, pData->pClass, pData->bAutoProxy);
+ break;
+
+ default:
+ _ASSERTE(!"Unknown TYPEKIND");
+ IfFailReport(E_INVALIDARG);
+ break;
+ } // Switch (tkind)
+
+ // Report that this type has been converted.
+ SString ssType;
+ if (IsTdNested(dwFlags))
+ {
+ TypeNameBuilder tnb(&ssType, TypeNameBuilder::ParseStateNAME);
+ TypeString::AppendNestedTypeDef(tnb, m_ErrorContext.m_pScope, m_ErrorContext.m_tkType);
+ }
+ else
+ TypeString::AppendTypeDef(ssType, m_ErrorContext.m_pScope, m_ErrorContext.m_tkType);
+ ReportEvent(NOTIF_TYPECONVERTED, TLBX_I_TYPE_EXPORTED, ssType.GetUnicode());
+} // void TypeLibExporter::ConvertDetails()
+
+//*****************************************************************************
+// Add the ImplTypes to the TypeInfo.
+//*****************************************************************************
+void TypeLibExporter::ConvertInterfaceImplTypes(
+ ICreateTypeInfo2 *pThisTypeInfo, // The typeinfo being created.
+ MethodTable *pClass) // MethodTable for the TypeInfo.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pThisTypeInfo));
+ PRECONDITION(CheckPointer(pClass));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ ULONG ulIface; // Is this interface [dual]?
+ HREFTYPE href; // href of base interface.
+
+ // IDispatch or IUnknown derived?
+ IfFailReport(pClass->GetMDImport()->GetIfaceTypeOfTypeDef(pClass->GetCl(), &ulIface));
+
+ // Parent interface.
+ if (IsDispatchBasedItf((CorIfaceAttr)ulIface))
+ {
+ // Get the HREFTYPE for IDispatch.
+ GetRefTypeInfo(pThisTypeInfo, m_pIDispatch, &href);
+ }
+ else
+ {
+ // Get the HREFTYPE for IUnknown.
+ GetRefTypeInfo(pThisTypeInfo, m_pIUnknown, &href);
+ }
+
+ // Add the HREF as an interface.
+ IfFailReport(pThisTypeInfo->AddImplType(0, href));
+} // void TypeLibExporter::ConvertInterfaceImplTypes()
+
+
+//*****************************************************************************
+// Create the TypeInfo for an interface by iterating over functions.
+//*****************************************************************************
+void TypeLibExporter::ConvertInterfaceDetails (
+ ICreateTypeInfo2 *pThisTypeInfo, // The typeinfo being created.
+ MethodTable *pMT, // MethodTable for the TypeInfo.
+ int bAutoProxy) // If true, oleaut32 is the interface's marshaller.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pThisTypeInfo));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ ULONG ulIface; // Is this interface [dual]?
+ DWORD dwTIFlags=0; // TypeLib flags.
+ int cVisibleMembers = 0; // The count of methods that are visible to COM.
+
+ // Retrieve the map of members.
+ ComMTMemberInfoMap MemberMap(pMT);
+
+ // IDispatch or IUnknown derived?
+ IfFailReport(pMT->GetMDImport()->GetIfaceTypeOfTypeDef(pMT->GetCl(), &ulIface));
+
+ if (IsDispatchBasedItf((CorIfaceAttr)ulIface))
+ {
+ // IDispatch derived.
+ dwTIFlags |= TYPEFLAG_FDISPATCHABLE;
+
+ if (ulIface == ifDual)
+ dwTIFlags |= TYPEFLAG_FDUAL | TYPEFLAG_FOLEAUTOMATION;
+ else
+ _ASSERTE(ulIface == ifDispatch);
+ }
+ else
+ {
+ // IUnknown derived.
+ dwTIFlags |= TYPEFLAG_FOLEAUTOMATION;
+ }
+
+ if (!bAutoProxy)
+ dwTIFlags |= TYPEFLAG_FPROXY;
+
+ // Set appropriate flags.
+ IfFailReport(pThisTypeInfo->SetTypeFlags(dwTIFlags));
+
+ // Retrieve the method properties.
+ size_t sizeOfPtr = IsExportingAs64Bit() ? 8 : 4;
+
+ MemberMap.Init(sizeOfPtr);
+ if (MemberMap.HadDuplicateDispIds())
+ ReportWarning(TLBX_I_DUPLICATE_DISPID, TLBX_I_DUPLICATE_DISPID);
+
+ // We need a scope to bypass the inialization skipped by goto ErrExit
+ // compiler error.
+ {
+ CQuickArray<ComMTMethodProps> &rProps = MemberMap.GetMethods();
+
+ // Now add the methods to the TypeInfo.
+ MethodTable::MethodIterator it(pMT);
+ for (; it.IsValid(); it.Next())
+ {
+ if (it.IsVirtual())
+ {
+ // Only convert the method if it is visible from COM.
+ if (rProps[it.GetSlotNumber()].bMemberVisible)
+ {
+ if (ConvertMethod(pThisTypeInfo, &rProps[it.GetSlotNumber()], cVisibleMembers, ulIface))
+ cVisibleMembers++;
+ }
+ }
+ }
+ }
+} // void TypeLibExporter::ConvertInterfaceDetails()
+
+//*****************************************************************************
+// Export a Record to a TypeLib.
+//*****************************************************************************
+void TypeLibExporter::ConvertRecordBaseClass(
+ CExportedTypesInfo *pData, // Conversion data.
+ MethodTable *pSubMT, // The base class.
+ ULONG &ixVar) // Variable index in the typelib.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pData));
+ PRECONDITION(CheckPointer(pSubMT));
+ }
+ CONTRACTL_END;
+
+ // The typeinfo being created.
+ ICreateTypeInfo2 *pThisTypeInfo = pData->pCTI;
+
+ HRESULT hr = S_OK; // A result.
+ mdFieldDef fd; // A Field def.
+ ULONG iFD; // Loop control.
+ ULONG cFD; // Count of total MemberDefs.
+ DWORD dwFlags; // Field flags.
+ LPCUTF8 szName; // Name in UTF8.
+ LPCUTF8 szNamespace; // A Namespace in UTF8.
+ SString sName; // Name
+
+ // To enum fields.
+ HENUMInternalHolder eFDi(pSubMT->GetMDImport());
+
+ // If there is no class here, or if the class is Object, don't add members.
+ if (pSubMT == 0 ||
+ pSubMT == g_pObjectClass)
+ return;
+
+ // If this class has a base class, export those members first.
+ ConvertRecordBaseClass(pData, pSubMT->GetParentMethodTable(), ixVar);
+
+ // Build the member name prefix.
+ IfFailReport(pSubMT->GetMDImport()->GetNameOfTypeDef(pSubMT->GetCl(), &szName, &szNamespace));
+
+ sName.SetUTF8(szName);
+ sName.Append(W("_"));
+
+ // Get an enumerator for the MemberDefs in the TypeDef.
+ eFDi.EnumInit(mdtFieldDef, pSubMT->GetCl());
+ cFD = pSubMT->GetMDImport()->EnumGetCount(&eFDi);
+
+ SString sNameMember;
+ // For each MemberDef...
+ for (iFD=0; iFD<cFD; ++iFD)
+ {
+ // Get the next field.
+ if (!pSubMT->GetMDImport()->EnumNext(&eFDi, &fd))
+ {
+ IfFailReport(E_UNEXPECTED);
+ }
+
+ IfFailReport(pSubMT->GetMDImport()->GetFieldDefProps(fd, &dwFlags));
+
+ // Only non-static fields.
+ if (!IsFdStatic(dwFlags))
+ {
+ IfFailReport(pSubMT->GetMDImport()->GetNameOfFieldDef(fd, &szName));
+
+ sNameMember.Set(sName);
+ sNameMember.AppendUTF8(szName);
+ if (ConvertVariable(pThisTypeInfo, pSubMT, fd, sNameMember, ixVar))
+ ixVar++;
+ }
+ }
+} // void TypeLibExporter::ConvertRecordBaseClass()
+
+void TypeLibExporter::ConvertRecord(
+ CExportedTypesInfo *pData) // Conversion data.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pData));
+ }
+ CONTRACTL_END;
+
+ ICreateTypeInfo2 *pThisTypeInfo=pData->pCTI; // The typeinfo being created.
+ MethodTable *pMT=pData->pClass; // MethodTable for the TypeInfo.
+
+ HRESULT hr = S_OK; // A result.
+ mdFieldDef fd; // A Field def.
+ ULONG iFD; // Loop control.
+ ULONG ixVar=0; // Index of current var converted.
+ ULONG cFD; // Count of total MemberDefs.
+ DWORD dwFlags; // Field flags.
+ DWORD dwPack; // Class pack size.
+ mdToken tkExtends; // A class's parent.
+ LPCUTF8 szName; // Name in UTF8.
+ SString sName; // Name.
+
+ // To enum fields.
+ HENUMInternalHolder eFDi(pMT->GetMDImport());
+
+ // If the type is a struct, but it has explicit layout, don't export the members,
+ // because we can't export them accurately (unless they're really sequential).
+ if (pData->tkind == TKIND_RECORD)
+ {
+ IfFailReport(pMT->GetMDImport()->GetTypeDefProps(pMT->GetCl(), &dwFlags, &tkExtends));
+
+ if (IsTdExplicitLayout(dwFlags))
+ {
+ ReportWarning(S_OK, TLBX_I_NONSEQUENTIALSTRUCT);
+ return;
+ }
+ }
+
+ // Set the packing size, if there is one.
+ dwPack = 0;
+ if (FAILED(pMT->GetMDImport()->GetClassPackSize(pMT->GetCl(), &dwPack)))
+ {
+ dwPack = 0;
+ }
+ if (dwPack == 0)
+ {
+ dwPack = DEFAULT_PACKING_SIZE;
+ }
+
+ IfFailReport(pThisTypeInfo->SetAlignment((USHORT)dwPack));
+
+ // Haven't seen any non-public members yet.
+ m_bWarnedOfNonPublic = FALSE;
+
+ // If this class has a base class, export those members first.
+ ConvertRecordBaseClass(pData, pMT->GetParentMethodTable(), ixVar);
+
+ // Get an enumerator for the MemberDefs in the TypeDef.
+ eFDi.EnumInit(mdtFieldDef, pMT->GetCl());
+ cFD = pMT->GetMDImport()->EnumGetCount(&eFDi);
+
+ // For each MemberDef...
+ for (iFD=0; iFD<cFD; ++iFD)
+ {
+ // Get the next field.
+ if (!pMT->GetMDImport()->EnumNext(&eFDi, &fd))
+ {
+ IfFailReport(E_UNEXPECTED);
+ }
+
+ IfFailReport(pMT->GetMDImport()->GetFieldDefProps(fd, &dwFlags));
+
+ // Skip static fields.
+ if (IsFdStatic(dwFlags) == 0)
+ {
+ IfFailReport(pMT->GetMDImport()->GetNameOfFieldDef(fd, &szName));
+
+ sName.SetUTF8(szName);
+ if (ConvertVariable(pThisTypeInfo, pMT, fd, sName, ixVar))
+ ixVar++;
+ }
+ }
+} // HRESULT TypeLibExporter::ConvertRecord()
+
+//*****************************************************************************
+// Export an Enum to a typelib.
+//*****************************************************************************
+void TypeLibExporter::ConvertEnum(
+ ICreateTypeInfo2 *pThisTypeInfo, // The typeinfo being created.
+ MethodTable *pMT) // MethodTable for the TypeInfo.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pThisTypeInfo));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ mdFieldDef fd; // A Field def.
+ DWORD dwTIFlags=0; // TypeLib flags.
+ ULONG dwFlags; // A field's flags.
+ ULONG iFD; // Loop control.
+ ULONG cFD; // Count of total MemberDefs.
+ ULONG iVar=0; // Count of vars actually converted.
+ LPCUTF8 szName; // Name in UTF8.
+ SString sName; // Name.
+ SafeComHolder<ITypeInfo> pThisTI=0; // TypeInfo for this ICreateITypeInfo.
+ BSTRHolder szThisTypeInfo=0; // Name of this ITypeInfo.
+
+ IMDInternalImport* pImport = pMT->GetMDImport();
+
+ // To enum fields.
+ HENUMInternalHolder eFDi(pImport);
+
+ // Explicitly set the flags.
+ IfFailReport(pThisTypeInfo->SetTypeFlags(dwTIFlags));
+
+ // Get an enumerator for the MemberDefs in the TypeDef.
+ eFDi.EnumInit(mdtFieldDef, pMT->GetCl());
+ cFD = pImport->EnumGetCount(&eFDi);
+
+ // Build the member name prefix. If generating an enum, get the real name from the default interface.
+ IfFailReport(SafeQueryInterface(pThisTypeInfo, IID_ITypeInfo, (IUnknown**)&pThisTI));
+ IfFailReport(pThisTI->GetDocumentation(MEMBERID_NIL, &szThisTypeInfo, 0,0,0));
+
+ sName.Set(szThisTypeInfo);
+ sName.Append(W("_"));
+
+ SString sNameMember;
+ // For each MemberDef...
+ for (iFD=0; iFD<cFD; ++iFD)
+ {
+ // Get the next field.
+ if (!pImport->EnumNext(&eFDi, &fd))
+ {
+ IfFailReport(E_UNEXPECTED);
+ }
+
+ // Only convert static fields.
+ IfFailReport(pImport->GetFieldDefProps(fd, &dwFlags));
+
+ if (IsFdStatic(dwFlags) == 0)
+ {
+ continue;
+ }
+
+ // Skip ComVisible(false) members
+ if (!IsMemberVisibleFromCom(pMT, fd, mdTokenNil))
+ {
+ continue;
+ }
+
+ sNameMember.Set(sName);
+ IfFailReport(pImport->GetNameOfFieldDef(fd, &szName));
+
+ sNameMember.AppendUTF8(szName);
+
+ if (ConvertEnumMember(pThisTypeInfo, pMT, fd, sNameMember, iVar))
+ {
+ iVar++;
+ }
+ }
+} // void TypeLibExporter::ConvertEnum()
+
+//*****************************************************************************
+// Does a class have a default ctor?
+//*****************************************************************************
+BOOL TypeLibExporter::HasDefaultCtor(
+ MethodTable *pMT) // The class in question.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr; // A result.
+ mdMethodDef md; // A method of the type.
+ DWORD dwFlags; // Method's flags.
+ ULONG cMD; // Count of returned tokens.
+ ULONG iMD; // Loop control.
+ PCCOR_SIGNATURE pSig; // The signature.
+ ULONG ixSig; // Index into signature.
+ ULONG cbSig; // Size of the signature.
+ ULONG callconv; // Method's calling convention.
+ ULONG cParams; // Method's count of parameters.
+ BOOL rslt=FALSE; // Was one found?
+ LPCUTF8 pName; // Method name.
+
+ IMDInternalImport* pImport = pMT->GetMDImport();
+
+ // To enum methods.
+ HENUMInternalHolder eMDi(pImport);
+
+ // Get an enumerator for the MemberDefs in the TypeDef.
+ eMDi.EnumInit(mdtMethodDef, pMT->GetCl());
+ cMD = pImport->EnumGetCount(&eMDi);
+
+ // For each MemberDef...
+ for (iMD=0; iMD<cMD; ++iMD)
+ {
+ // Get the next field.
+ if (!pImport->EnumNext(&eMDi, &md))
+ {
+ IfFailReport(E_UNEXPECTED);
+ }
+
+ // Is the name special? Is the method public?
+ IfFailReport(pImport->GetMethodDefProps(md, &dwFlags));
+
+ if (!IsMdRTSpecialName(dwFlags) || !IsMdPublic(dwFlags))
+ continue;
+
+ // Yes, is the name a ctor?
+ IfFailReport(pImport->GetNameOfMethodDef(md, &pName));
+
+ if (!IsMdInstanceInitializer(dwFlags, pName))
+ continue;
+
+ // It is a ctor. Is it a default ctor?
+ IfFailReport(pImport->GetSigOfMethodDef(md, &cbSig, &pSig));
+
+ // Skip the calling convention, and get the param count.
+ ixSig = CorSigUncompressData(pSig, &callconv);
+ CorSigUncompressData(&pSig[ixSig], &cParams);
+
+ // Default ctor has zero params.
+ if (cParams == 0)
+ {
+ rslt = TRUE;
+ break;
+ }
+ }
+
+ return rslt;
+} // BOOL TypeLibExporter::HasDefaultCtor()
+
+//*****************************************************************************
+// Export a class to a TypeLib.
+//*****************************************************************************
+void TypeLibExporter::ConvertClassImplTypes(
+ ICreateTypeInfo2 *pThisTypeInfo, // The typeinfo being created.
+ ICreateTypeInfo2 *pClassItfTypeInfo,// The ICLassX for the TypeInfo.
+ MethodTable *pMT) // MethodTable for the TypeInfo.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pThisTypeInfo, NULL_OK));
+ PRECONDITION(CheckPointer(pClassItfTypeInfo, NULL_OK));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HREFTYPE href; // HREF to a TypeInfo.
+ DWORD dwFlags; // Metadata flags.
+ int flags=0; // Flags for the interface impl or CoClass.
+ UINT iImpl=0; // Current Impl index.
+ MethodTable *pIDefault = 0; // Default interface, if any.
+ MethodTable *pDefItfMT = 0; // Default interface method table, if any.
+ CQuickArray<MethodTable *> SrcItfList; // List of event sources.
+ CorClassIfaceAttr classItfType = clsIfNone; // For class interface type.
+ DefaultInterfaceType DefItfType;
+ TypeHandle hndDefItfClass;
+
+ SafeComHolder<ITypeInfo> pTI=0; // TypeInfo for default dispinterface.
+ SafeComHolder<ICreateTypeInfo2> pCTI2 = NULL; // The ICreateTypeInfo2 interface used to define custom data.
+
+ // We should never be converting the class impl types of COM imported CoClasses.
+ _ASSERTE(!pMT->IsComImport());
+
+ if (pThisTypeInfo)
+ {
+ IfFailReport(pMT->GetMDImport()->GetTypeDefProps(pMT->GetCl(), &dwFlags, 0));
+
+ // If abstract class, or no default ctor, don't make it creatable.
+ if (!IsTdAbstract(dwFlags) && HasDefaultCtor(pMT))
+ flags |= TYPEFLAG_FCANCREATE;
+
+ // PreDeclid as appropriate.
+ IfFailReport(pThisTypeInfo->SetTypeFlags(flags));
+ }
+
+ // Retrieve the MethodTable that represents the default interface.
+ DefItfType = GetDefaultInterfaceForClassWrapper(TypeHandle(pMT), &hndDefItfClass);
+
+ // Remember the MethodTable of the default interface.
+ pIDefault = hndDefItfClass.GetMethodTable();
+
+ // For some classes we synthesize an IClassX. We don't do that for
+ // configured class, classes imported from COM,
+ // or for classes with an explicit default interface.
+ if (pClassItfTypeInfo)
+ {
+ // Set the interface as the default for the class.
+ IfFailReport(SafeQueryInterface(pClassItfTypeInfo, IID_ITypeInfo, (IUnknown**)&pTI));
+ GetRefTypeInfo(pThisTypeInfo, pTI, &href);
+ IfFailReport(pThisTypeInfo->AddImplType(iImpl, href));
+
+ // If the class interface is the default interface, mark it as such.
+ if (pMT == pIDefault)
+ IfFailReport(pThisTypeInfo->SetImplTypeFlags(iImpl, IMPLTYPEFLAG_FDEFAULT));
+
+ // Increment the impl count.
+ ++iImpl;
+ }
+
+ // Go up the class hierarchy and add the IClassX's of the parent classes
+ // as interfaces implemented by the COM component.
+ MethodTable *pParentClass = pMT->GetComPlusParentMethodTable();
+ while (pParentClass)
+ {
+ // If the parent class has an IClassX interface then add it.
+ ClassHasIClassX(pParentClass, &classItfType);
+ if (classItfType == clsIfAutoDual)
+ {
+ hr = EEClassToHref(pThisTypeInfo, pParentClass, FALSE, &href);
+
+ // If not IUnknown, add the HREF as an interface.
+ if (hr != S_USEIUNKNOWN)
+ {
+ IfFailReport(pThisTypeInfo->AddImplType(iImpl, href));
+ if (pParentClass == pIDefault)
+ IfFailReport(pThisTypeInfo->SetImplTypeFlags(iImpl, IMPLTYPEFLAG_FDEFAULT));
+
+ ++iImpl;
+ }
+ }
+
+ // Process the next class up the hierarchy.
+ pParentClass = pParentClass->GetComPlusParentMethodTable();
+ }
+
+ ComCallWrapperTemplate *pClassTemplate = ComCallWrapperTemplate::GetTemplate(TypeHandle(pMT));
+ MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap();
+ while (it.Next())
+ {
+ flags = 0;
+
+ // Get the MethodTable for an implemented interface.
+ MethodTable *pIClass = it.GetInterface();
+
+ // Retrieve the ComMethodTable for the interface.
+ ComMethodTable *pItfComMT = pClassTemplate->GetComMTForItf(pIClass);
+
+ // If the interface is visible from COM, add it.
+ if (IsTypeVisibleFromCom(TypeHandle(pIClass)) && !pItfComMT->IsComClassItf())
+ {
+#if defined(_DEBUG)
+ TRACE("Class %s implements %s\n", pMT->GetDebugClassName(), pIClass->GetDebugClassName());
+#endif
+ // Get an href for the managed class.
+ hr = EEClassToHref(pThisTypeInfo, pIClass, FALSE, &href);
+
+ // If not IUnknown, add the HREF as an interface.
+ if (hr != S_USEIUNKNOWN)
+ {
+ if (pIClass == pIDefault)
+ flags |= IMPLTYPEFLAG_FDEFAULT;
+
+ IfFailReport(pThisTypeInfo->AddImplType(iImpl, href));
+ IfFailReport(pThisTypeInfo->SetImplTypeFlags(iImpl, flags));
+ ++iImpl;
+ }
+ }
+ else if (!IsTypeVisibleFromCom(TypeHandle(pIClass)) && (pIClass == pIDefault))
+ {
+ // Report a warning if the default interface is not COM visible
+ ReportWarning(TLBX_W_DEFAULT_INTF_NOT_VISIBLE, TLBX_W_DEFAULT_INTF_NOT_VISIBLE);
+ }
+ }
+
+ // Retrieve the list of COM source interfaces for the managed class.
+ GetComSourceInterfacesForClass(pMT, SrcItfList);
+
+ // Add all the source interfaces to the CoClass.
+ flags = IMPLTYPEFLAG_FSOURCE | IMPLTYPEFLAG_FDEFAULT;
+ for (UINT i = 0; i < SrcItfList.Size(); i++)
+ {
+ hr = EEClassToHref(pThisTypeInfo, SrcItfList[i], FALSE, &href);
+
+ // If not IUnknown, add the HREF as an interface.
+ if (hr != S_USEIUNKNOWN)
+ {
+ IfFailReport(pThisTypeInfo->AddImplType(iImpl, href));
+ IfFailReport(pThisTypeInfo->SetImplTypeFlags(iImpl, flags));
+ ++iImpl;
+ flags = IMPLTYPEFLAG_FSOURCE;
+ }
+ }
+} // void TypeLibExporter::ConvertClassImplTypes()
+
+//*****************************************************************************
+// Export a class to a TypeLib.
+//*****************************************************************************
+void TypeLibExporter::ConvertClassDetails(
+ ICreateTypeInfo2 *pThisTypeInfo, // The typeinfo being created.
+ ICreateTypeInfo2 *pDefaultTypeInfo, // The ICLassX for the TypeInfo.
+ MethodTable *pMT, // MethodTable for the TypeInfo.
+ int bAutoProxy) // If true, oleaut32 is the proxy.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pThisTypeInfo, NULL_OK));
+ PRECONDITION(CheckPointer(pDefaultTypeInfo, NULL_OK));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ CorClassIfaceAttr classItfType = clsIfNone;
+
+ ClassHasIClassX(pMT, &classItfType);
+ if (classItfType == clsIfAutoDual)
+ {
+ // Set up the IClassX interface.
+ ConvertIClassX(pDefaultTypeInfo, pMT, bAutoProxy);
+ }
+ else if (pDefaultTypeInfo)
+ {
+ DWORD dwTIFlags = TYPEFLAG_FDUAL | TYPEFLAG_FOLEAUTOMATION | TYPEFLAG_FDISPATCHABLE | TYPEFLAG_FHIDDEN;
+ if (!bAutoProxy)
+ dwTIFlags |= TYPEFLAG_FPROXY;
+ IfFailReport(pDefaultTypeInfo->SetTypeFlags(dwTIFlags));
+ }
+} // void TypeLibExporter::ConvertClassDetails()
+
+//*****************************************************************************
+// Create the DispInterface for the vtable that describes an entire class.
+//*****************************************************************************
+void TypeLibExporter::ConvertIClassX(
+ ICreateTypeInfo2 *pThisTypeInfo, // The TypeInfo for the IClassX.
+ MethodTable *pMT, // The MethodTable object for the class.
+ int bAutoProxy) // If true, oleaut32 is the proxy.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pThisTypeInfo));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ DWORD dwTIFlags=0; // TypeLib flags.
+ DWORD nSlots; // Number of vtable slots.
+ UINT i; // Loop control.
+ int cVisibleMembers = 0; // The count of methods that are visible to COM.
+ ComMTMemberInfoMap MemberMap(pMT); // The map of members.
+
+ // Should be an actual class.
+ _ASSERTE(!pMT->IsInterface());
+
+ // Retrieve the method properties.
+ size_t sizeOfPtr = IsExportingAs64Bit() ? 8 : 4;
+
+ MemberMap.Init(sizeOfPtr);
+ if (MemberMap.HadDuplicateDispIds())
+ ReportWarning(TLBX_I_DUPLICATE_DISPID, TLBX_I_DUPLICATE_DISPID);
+
+ // We need a scope to bypass the inialization skipped by goto ErrExit
+ // compiler error.
+ {
+ CQuickArray<ComMTMethodProps> &rProps = MemberMap.GetMethods();
+ nSlots = (DWORD)rProps.Size();
+
+ dwTIFlags |= TYPEFLAG_FDUAL | TYPEFLAG_FOLEAUTOMATION | TYPEFLAG_FDISPATCHABLE | TYPEFLAG_FHIDDEN | TYPEFLAG_FNONEXTENSIBLE;
+ if (!bAutoProxy)
+ dwTIFlags |= TYPEFLAG_FPROXY;
+ IfFailReport(pThisTypeInfo->SetTypeFlags(dwTIFlags));
+
+ // Assign slot numbers.
+ for (i=0; i<nSlots; ++i)
+ rProps[i].oVft = (short)((7 + i) * sizeOfPtr);
+
+ // Now add the methods to the TypeInfo.
+ for (i=0; i<nSlots; ++i)
+ {
+ TRACE("[%d] %10ls pMeth:%08x, prop:%d, semantic:%d, dispid:0x%x, oVft:%d\n", i, rProps[i].pName, rProps[i].pMeth,
+ rProps[i].property, rProps[i].semantic, rProps[i].dispid, rProps[i].oVft);
+ if (rProps[i].bMemberVisible)
+ {
+ if (rProps[i].semantic < FieldSemanticOffset)
+ {
+ if (ConvertMethod(pThisTypeInfo, &rProps[i], cVisibleMembers, ifDual))
+ cVisibleMembers++;
+ }
+ else
+ {
+ if (ConvertFieldAsMethod(pThisTypeInfo, &rProps[i], cVisibleMembers))
+ cVisibleMembers++;
+ }
+ }
+ }
+ }
+} // void TypeLibExporter::ConvertIClassX()
+
+
+//*****************************************************************************
+// Export a Method's metadata to a typelib.
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+BOOL TypeLibExporter::ConvertMethod(
+ ICreateTypeInfo2 *pCTI, // ICreateTypeInfo2 to get the method.
+ ComMTMethodProps *pProps, // Some properties of the method.
+ ULONG iMD, // Index of the member
+ ULONG ulIface) // Is this interface : IUnknown, [dual], or DISPINTERFACE?
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pProps));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ HRESULT hrSignature = S_OK; // A failure HR;
+ LPCUTF8 pszName; // Name in UTF8.
+ SString sName; // Holds name.
+ ULONG dwImplFlags; // The function's impl flags.
+ PCCOR_SIGNATURE pbSig; // Pointer to Cor signature.
+ ULONG cbSig; // Size of Cor signature.
+ ULONG ixSig; // Index into signature.
+ ULONG cbElem; // Size of an element in the signature.
+ ULONG callconv; // A member's calling convention.
+ ULONG ret; // The return type.
+ ULONG elem; // A signature element.
+ TYPEDESC *pRetVal=0; // Return type's TYPEDESC.
+ ULONG cSrcParams; // Count of source params.
+ ULONG cDestParams = 0; // Count of dest parameters.
+ USHORT iSrcParam; // Loop control, over params.
+ USHORT iDestParam; // Loop control, over params.
+ USHORT iLCIDParam; // The index of the LCID param.
+ ULONG dwParamFlags; // A parameter's flags.
+ CDescPool sPool; // Pool of memory in which to build funcdesc.
+ CDescPool sVariants; // Pool of variants for default values.
+ PARAMDESCEX *pParamDesc; // Pointer to one param default value.
+ int bHrMunge=true; // Munge return type to HRESULT?
+ CQuickArray<BSTR> rNames; // Array of names to function and parameters.
+ ULONG cNames=0; // Count of function and parameter names.
+ FUNCDESC *pfunc = NULL; // A funcdesc.
+ MethodDesc *pMeth; // A MethodDesc.
+ IMDInternalImport *pInternalImport; // Internal interface containing the method.
+ MDDefaultValue defaultValue; // place holder for default value
+ PCCOR_SIGNATURE pvNativeType; // native parameter type
+ ULONG cbNativeType = 0; // native parameter type length
+ MethodTable *pMT; // Class containing the method.
+ int bHasOptorDefault=false; // If true, the method has optional params or default values -- no vararg
+ const void *pvData; // Pointer to a custom attribute.
+ ULONG cbData; // Size of custom attribute.
+ BOOL bByRef; // Is a parameter byref?
+ BSTRHolder bstrDescr=0; // Description of the method.
+ VariantHolder vtManagedName; // Variant used to set the managed name of the member.
+
+ ZeroHolder zhParam = &m_ErrorContext.m_szParam; // Clear error reporting info.
+ ZeroHolder zhMember = &m_ErrorContext.m_szMember; // Clear error reporting info.
+
+ // Get info about the method.
+ pMeth = pProps->pMeth;
+ pMeth->GetSig(&pbSig, &cbSig);
+ pInternalImport = pMeth->GetMDImport();
+ pMT = pMeth->GetMethodTable();
+ IfFailReport(pInternalImport->GetMethodImplProps(pMeth->GetMemberDef(), 0, &dwImplFlags));
+
+ // Error reporting info.
+ IfFailReport(pInternalImport->GetNameOfMethodDef(pMeth->GetMemberDef(), &m_ErrorContext.m_szMember));
+
+ // Allocate one variant.
+ pParamDesc = reinterpret_cast<PARAMDESCEX*>(sVariants.AllocZero(sizeof(PARAMDESCEX)));
+ if(NULL == pParamDesc)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // Prepare to parse signature and build the FUNCDESC.
+ pfunc = reinterpret_cast<FUNCDESC*>(sPool.AllocZero(sizeof(FUNCDESC)));
+ if (pfunc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ixSig = 0;
+
+ // Get the calling convention.
+ ixSig += CorSigUncompressData(&pbSig[ixSig], &callconv);
+ _ASSERTE((callconv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_FIELD);
+ pfunc->callconv = Clr2TlbCallConv[callconv & IMAGE_CEE_CS_CALLCONV_MASK];
+
+ // vtable offset.
+ pfunc->oVft = pProps->oVft;
+
+ // Get the argument count. Allow for an extra in case of [retval].
+ ixSig += CorSigUncompressData(&pbSig[ixSig], &cSrcParams);
+ cDestParams = cSrcParams;
+ rNames.ReSizeThrows(cDestParams+3);
+ memset(rNames.Ptr(), 0, (cDestParams+3) * sizeof(BSTR));
+
+ // Set some method properties.
+ pfunc->memid = pProps->dispid;
+ if (pfunc->memid == -11111) //@todo: fix for msvbalib.dll
+ pfunc->memid = -1;
+ pfunc->funckind = FUNC_PUREVIRTUAL;
+
+ // Set the invkind based on whether the function is an accessor.
+ if (pProps->semantic == 0)
+ pfunc->invkind = INVOKE_FUNC;
+ else if (pProps->semantic == msGetter)
+ pfunc->invkind = INVOKE_PROPERTYGET;
+ else if (pProps->semantic == msSetter)
+ pfunc->invkind = INVOKE_PROPERTYPUTREF;
+ else if (pProps->semantic == msOther)
+ pfunc->invkind = INVOKE_PROPERTYPUT;
+ else
+ pfunc->invkind = INVOKE_FUNC; // non-accessor property function.
+
+ rNames[0] = pProps->pName;
+ cNames = 1;
+
+ // Convert return type to elemdesc. If we are doing HRESULT munging, we need to
+ // examine the return type, and if it is not VOID, create an additional final
+ // parameter as a pointer to the type.
+
+ // Get the return type.
+ cbElem = CorSigUncompressData(&pbSig[ixSig], &ret);
+
+ // Error reporting info.
+ m_ErrorContext.m_ixParam = 0;
+
+ // Get native type of return if available
+ mdParamDef pdParam;
+ pvNativeType = NULL;
+ hr = pInternalImport->FindParamOfMethod(pMeth->GetMemberDef(), 0, &pdParam);
+ if (hr == S_OK)
+ {
+ hr = pInternalImport->GetFieldMarshal(pdParam, &pvNativeType, &cbNativeType);
+ if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailReport(hr);
+ }
+ }
+
+ // Determine if we need to do HRESULT munging.
+ bHrMunge = !IsMiPreserveSig(dwImplFlags);
+
+ // Reset some properties for DISPINTERFACES.
+ if (ulIface == ifDispatch)
+ {
+ pfunc->callconv = CC_STDCALL;
+ pfunc->funckind = FUNC_DISPATCH;
+
+ // Never munge a dispinterface.
+ bHrMunge = false;
+ }
+
+ if (bHrMunge)
+ {
+ // Munge the return type into a new last param, set return type to HRESULT.
+ pfunc->elemdescFunc.tdesc.vt = VT_HRESULT;
+
+ // Does the function actually return anything?
+ if (ret == ELEMENT_TYPE_VOID)
+ {
+ // Skip over the return value, no [retval].
+ pRetVal = 0;
+ ixSig += cbElem;
+ }
+ else
+ {
+ // Allocate a TYPEDESC to be pointed to, convert type into it.
+ pRetVal = reinterpret_cast<TYPEDESC*>(sPool.AllocZero(sizeof(TYPEDESC)));
+ if (pRetVal == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ hr = CorSigToTypeDesc(pCTI, pMT, &pbSig[ixSig], pvNativeType, cbNativeType, &cbElem, pRetVal, &sPool, TRUE);
+ if (FAILED(hr))
+ return FALSE;
+
+ ixSig += cbElem;
+
+ ++cDestParams;
+ // It is pretty weird for a property putter to return something, but apparenly legal.
+ //_ASSERTE(pfunc->invkind != INVOKE_PROPERTYPUT && pfunc->invkind != INVOKE_PROPERTYPUTREF);
+
+ // Todo: When the C compiler tries to import a typelib with a C
+ // array return type (even if it's a retval),
+ // it generates a wrapper method with a signature like "int [] foo()",
+ // which isn't valid C, so it barfs. So, we'll change the return type
+ // to a pointer by hand.
+ if (pRetVal->vt == VT_CARRAY)
+ {
+ pRetVal->vt = VT_PTR;
+ pRetVal->lptdesc = &pRetVal->lpadesc->tdescElem;
+ }
+ }
+ }
+ else
+ {
+ // No munging, convert return type.
+ pRetVal = 0;
+ hr = CorSigToTypeDesc(pCTI, pMT, &pbSig[ixSig], pvNativeType, cbNativeType, &cbElem, &pfunc->elemdescFunc.tdesc, &sPool, TRUE);
+ if (FAILED(hr))
+ return FALSE;
+
+ ixSig += cbElem;
+ }
+
+ // Error reporting info.
+ m_ErrorContext.m_ixParam = -1;
+
+ // Check to see if there is an LCIDConversion attribute on the method.
+ iLCIDParam = (USHORT)GetLCIDParameterIndex(pMeth);
+ if (iLCIDParam != (USHORT)-1)
+ {
+ BOOL bValidLCID = TRUE;
+
+ // Make sure the parameter index is valid.
+ if (iLCIDParam > cSrcParams)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_INVALIDLCIDPARAM);
+ bValidLCID = FALSE;
+ }
+
+ // LCID's are not allowed on pure dispatch interfaces.
+ if (ulIface == ifDispatch)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_LCIDONDISPONLYITF);
+ bValidLCID = FALSE;
+ }
+
+ if (bValidLCID)
+ {
+ // Take the LCID parameter into account in the exported method.
+ ++cDestParams;
+ }
+ else
+ {
+ // The LCID is invalid so we will ignore it.
+ iLCIDParam = -1;
+ }
+ }
+
+ // for each parameter
+ pfunc->lprgelemdescParam = reinterpret_cast<ELEMDESC*>(sPool.AllocZero(cDestParams * sizeof(ELEMDESC)));
+ if (pfunc->lprgelemdescParam == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // Holds the allocated strings so we can deallocate on function exit.
+ // Only need +1 as we don't clean up the first and last names (function name and retval)
+ NewArrayHolder<BSTRHolder> namesHolder = new BSTRHolder[cDestParams+1];
+
+ // Variant array used to hold default value data
+ NewArrayHolder<VariantPtrHolder> vtDefaultValues = new VariantPtrHolder[cDestParams];
+
+ pfunc->cParams = static_cast<short>(cDestParams);
+ for (iSrcParam=1, iDestParam=0; iDestParam<cDestParams; ++iSrcParam, ++iDestParam)
+ {
+ // Check to see if we need to insert the LCID param before the current param.
+ if (iLCIDParam == iDestParam)
+ {
+ // Set the flags and the type of the parameter.
+ pfunc->lprgelemdescParam[iDestParam].paramdesc.wParamFlags = PARAMFLAG_FIN | PARAMFLAG_FLCID;
+ pfunc->lprgelemdescParam[iDestParam].tdesc.vt = VT_I4;
+
+ // Generate a parameter name.
+ sName.Printf(szParamName, iDestParam + 1);
+
+ rNames[iDestParam + 1] = SysAllocString(sName.GetUnicode());
+ if (rNames[iDestParam + 1] == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ namesHolder[iDestParam+1] = rNames[iDestParam + 1];
+
+ ++cNames;
+
+ // Increment the current destination parameter.
+ ++iDestParam;
+ }
+
+ // If we are past the end of the source parameters then we are done.
+ if (iSrcParam > cSrcParams)
+ break;
+
+ // Get additional parameter metadata.
+ dwParamFlags = 0;
+ sName.Clear();
+
+ // Error reporting info.
+ m_ErrorContext.m_ixParam = iSrcParam;
+
+ // See if there is a ParamDef for this param.
+ hr = pInternalImport->FindParamOfMethod(pMeth->GetMemberDef(), iSrcParam, &pdParam);
+
+ pvNativeType = NULL;
+ if (hr == S_OK)
+ {
+ // Get info about the param.
+ IfFailReport(pInternalImport->GetParamDefProps(pdParam, &iSrcParam, &dwParamFlags, &pszName));
+
+ // Error reporting info.
+ m_ErrorContext.m_szParam = pszName;
+
+ // Turn off reserved (internal use) bits.
+ dwParamFlags &= ~pdReservedMask;
+
+ // Convert name from UTF8 to unicode.
+ sName.SetUTF8(pszName);
+
+ // Param default value, if any.
+ IfFailReport(pInternalImport->GetDefaultValue(pdParam, &defaultValue));
+ IfFailReport(_FillVariant(&defaultValue, &pParamDesc->varDefaultValue));
+
+ // If no default value, check for decimal custom attribute.
+ if (pParamDesc->varDefaultValue.vt == VT_EMPTY)
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(pdParam, INTEROP_DECIMALVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK && cbData >= (2 + sizeof(BYTE)+sizeof(BYTE)+sizeof(UINT)+sizeof(UINT)+sizeof(UINT)))
+ {
+ const BYTE *pbData = (const BYTE *)pvData;
+ pParamDesc->varDefaultValue.vt = VT_DECIMAL;
+ pParamDesc->varDefaultValue.decVal.scale = *(BYTE*)(pbData+2);
+ pParamDesc->varDefaultValue.decVal.sign= *(BYTE*)(pbData+3);
+ pParamDesc->varDefaultValue.decVal.Hi32= GET_UNALIGNED_32(pbData+4);
+ pParamDesc->varDefaultValue.decVal.Mid32= GET_UNALIGNED_32(pbData+8);
+ pParamDesc->varDefaultValue.decVal.Lo32= GET_UNALIGNED_32(pbData+12);
+ }
+ }
+ // If still no default value, check for date time custom attribute.
+ if (pParamDesc->varDefaultValue.vt == VT_EMPTY)
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(pdParam, INTEROP_DATETIMEVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK && cbData >= (2 + sizeof(__int64)))
+ {
+ const BYTE *pbData = (const BYTE *)pvData;
+ pParamDesc->varDefaultValue.vt = VT_DATE;
+ pParamDesc->varDefaultValue.date = _TicksToDoubleDate(GET_UNALIGNED_64(pbData+2));
+ }
+ }
+ // If still no default value, check for IDispatch custom attribute.
+ if (pParamDesc->varDefaultValue.vt == VT_EMPTY)
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(pdParam, INTEROP_IDISPATCHVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK)
+ {
+ pParamDesc->varDefaultValue.vt = VT_DISPATCH;
+ pParamDesc->varDefaultValue.pdispVal = 0;
+ }
+ }
+ // If still no default value, check for IUnknown custom attribute.
+ if (pParamDesc->varDefaultValue.vt == VT_EMPTY)
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(pdParam, INTEROP_IUNKNOWNVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK)
+ {
+ pParamDesc->varDefaultValue.vt = VT_UNKNOWN;
+ pParamDesc->varDefaultValue.punkVal = 0;
+ }
+ }
+
+ if (pParamDesc->varDefaultValue.vt != VT_EMPTY)
+ {
+ // Copy the variant into the holder object so we release on function exit.
+ vtDefaultValues[iDestParam] = (VARIANT*)&pParamDesc->varDefaultValue;
+
+ pfunc->lprgelemdescParam[iDestParam].paramdesc.pparamdescex = pParamDesc;
+ dwParamFlags |= PARAMFLAG_FHASDEFAULT;
+
+ // Allocate another paramdesc.
+ pParamDesc = reinterpret_cast<PARAMDESCEX*>(sVariants.AllocZero(sizeof(PARAMDESCEX)));
+ if (pParamDesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ bHasOptorDefault = true;
+ }
+
+ // native marshal type, if any.
+ hr = pInternalImport->GetFieldMarshal(pdParam, &pvNativeType, &cbNativeType);
+ if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailReport(hr);
+ }
+
+ // Remember if there are optional params.
+ if (dwParamFlags & PARAMFLAG_FOPT)
+ bHasOptorDefault = true;
+ }
+ else
+ {
+ pdParam = 0, m_ErrorContext.m_szParam = 0;
+ }
+
+ // Do we need a name for this parameter?
+ if ((pfunc->invkind & (INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF)) == 0 ||
+ iSrcParam < cSrcParams)
+ {
+ // Yes, so make one up if we don't have one.
+ if (sName.GetCount() == 0)
+ {
+ sName.Printf(szParamName, iDestParam + 1);
+ }
+
+ rNames[iDestParam + 1] = SysAllocString(sName.GetUnicode());
+ if (rNames[iDestParam + 1] == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ namesHolder[iDestParam+1] = rNames[iDestParam + 1];
+
+ ++cNames;
+ }
+
+ // Save the element type.
+ CorSigUncompressData(&pbSig[ixSig], &elem);
+
+ // Convert the param info to elemdesc.
+ bByRef = FALSE;
+ hr = CorSigToTypeDesc(pCTI, pMT, &pbSig[ixSig], pvNativeType, cbNativeType, &cbElem,
+ &pfunc->lprgelemdescParam[iDestParam].tdesc, &sPool, TRUE, &bByRef);
+ if (FAILED(hr))
+ return FALSE;
+
+ ixSig += cbElem;
+
+ // If there is no [in,out], set one, based on the parameter.
+ if ((dwParamFlags & (PARAMFLAG_FOUT | PARAMFLAG_FIN)) == 0)
+ {
+ // If param is by reference, make in/out
+ if (bByRef)
+ dwParamFlags |= PARAMFLAG_FIN | PARAMFLAG_FOUT;
+ else
+ dwParamFlags |= PARAMFLAG_FIN;
+ }
+
+ // If this is the last param, and it an array of objects, and has a ParamArrayAttribute,
+ // the function is varargs.
+ if ((iSrcParam == cSrcParams) && !IsNilToken(pdParam) && !bHasOptorDefault)
+ {
+ if (pfunc->lprgelemdescParam[iDestParam].tdesc.vt == VT_SAFEARRAY &&
+ pfunc->lprgelemdescParam[iDestParam].tdesc.lpadesc->tdescElem.vt == VT_VARIANT)
+ {
+ if (pInternalImport->GetCustomAttributeByName(pdParam, INTEROP_PARAMARRAY_TYPE, 0,0) == S_OK)
+ pfunc->cParamsOpt = -1;
+ }
+ }
+
+ pfunc->lprgelemdescParam[iDestParam].paramdesc.wParamFlags = static_cast<USHORT>(dwParamFlags);
+ }
+
+ // Is there a [retval]?
+ if (pRetVal)
+ {
+ // Error reporting info.
+ m_ErrorContext.m_ixParam = 0;
+ m_ErrorContext.m_szParam = 0;
+
+ _ASSERTE(bHrMunge);
+ _ASSERTE(cDestParams > cSrcParams);
+ pfunc->lprgelemdescParam[cDestParams-1].tdesc.vt = VT_PTR;
+ pfunc->lprgelemdescParam[cDestParams-1].tdesc.lptdesc = pRetVal;
+ pfunc->lprgelemdescParam[cDestParams-1].paramdesc.wParamFlags = PARAMFLAG_FOUT | PARAMFLAG_FRETVAL;
+
+ // no need to allocate a new string for this. rather use the constant szRetVal
+ rNames[cDestParams] = (LPWSTR)szRetVal;
+
+ ++cNames;
+ }
+
+ // Error reporting info.
+ m_ErrorContext.m_ixParam = -1;
+
+ // Was there a signature error? If so, exit now that all sigs have been reported.
+ IfFailReport(hrSignature);
+
+ IfFailReport(pCTI->AddFuncDesc(iMD, pfunc));
+
+ IfFailReport(pCTI->SetFuncAndParamNames(iMD, rNames.Ptr(), cNames));
+
+ if (pProps->bFunction2Getter)
+ {
+ VARIANT vtOne;
+ vtOne.vt = VT_I4;
+ vtOne.lVal = 1;
+ IfFailReport(pCTI->SetFuncCustData(iMD, GUID_Function2Getter, &vtOne));
+ }
+
+ // If the managed name of the method is different from the unmanaged name, then
+ // we need to capture the managed name in a custom value. We only apply this
+ // attribute for methods since properties cannot be overloaded.
+ if (pProps->semantic == 0)
+ {
+ sName.SetUTF8(pMeth->GetName());
+ if (sName.Compare(SString(pProps->pName)) != 0)
+ {
+ V_VT(&vtManagedName) = VT_BSTR;
+
+ if (NULL == (V_BSTR(&vtManagedName) = SysAllocString(sName.GetUnicode())))
+ IfFailReport(E_OUTOFMEMORY);
+
+ IfFailReport(pCTI->SetFuncCustData(iMD, GUID_ManagedName, &vtManagedName));
+ }
+ }
+
+ // Check for a description.
+ if(GetDescriptionString(pMT, pMeth->GetMemberDef(), (BSTR &)bstrDescr))
+ IfFailReport(pCTI->SetFuncDocString(iMD, bstrDescr));
+
+
+ // Error reporting info.
+ m_ErrorContext.m_szMember = 0;
+ m_ErrorContext.m_szParam = 0;
+ m_ErrorContext.m_ixParam = -1;
+
+ return TRUE;
+} // void TypeLibExporter::ConvertMethod()
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Export a Field as getter/setter method's to a typelib.
+//*****************************************************************************
+BOOL TypeLibExporter::ConvertFieldAsMethod(
+ ICreateTypeInfo2 *pCTI, // ICreateTypeInfo2 to get the method.
+ ComMTMethodProps *pProps, // Some properties of the method.
+ ULONG iMD) // Index of the member
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pProps));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ PCCOR_SIGNATURE pbSig; // Pointer to Cor signature.
+ ULONG cbSig; // Size of Cor signature.
+ ULONG ixSig; // Index into signature.
+ ULONG cbElem; // Size of an element in the signature.
+
+ ULONG callconv; // A member's calling convention.
+ TYPEDESC *pType; // TYPEDESC for the field type.
+ CDescPool sPool; // Pool of memory in which to build funcdesc.
+ BSTR rNames[2]; // Array of names to function and parameters.
+ ULONG cNames; // Count of function and parameter names.
+ FUNCDESC *pfunc; // A funcdesc.
+ ComCallMethodDesc *pFieldMeth; // A MethodDesc for a field call.
+ FieldDesc *pField; // A FieldDesc.
+ IMDInternalImport *pInternalImport; // Internal interface containing the field.
+ PCCOR_SIGNATURE pvNativeType; // native field type
+ ULONG cbNativeType; // native field type length
+ MethodTable *pMT; // Class containing the field.
+ BSTRHolder bstrDescr=0; // Description of the method.
+
+ // Get info about the method.
+ pFieldMeth = reinterpret_cast<ComCallMethodDesc*>(pProps->pMeth);
+ pField = pFieldMeth->GetFieldDesc();
+ pField->GetSig(&pbSig, &cbSig);
+ pInternalImport = pField->GetMDImport();
+ pMT = pField->GetEnclosingMethodTable();
+
+ // Error reporting info.
+ IfFailReport(pMT->GetMDImport()->GetNameOfFieldDef(pField->GetMemberDef(), &m_ErrorContext.m_szMember));
+
+ // Prepare to parse signature and build the FUNCDESC.
+ pfunc = reinterpret_cast<FUNCDESC*>(sPool.AllocZero(sizeof(FUNCDESC)));
+ if (NULL == pfunc)
+ IfFailReport(E_OUTOFMEMORY);
+ ixSig = 0;
+
+ // Get the calling convention.
+ ixSig += CorSigUncompressData(&pbSig[ixSig], &callconv);
+ _ASSERTE(callconv == IMAGE_CEE_CS_CALLCONV_FIELD);
+ pfunc->callconv = CC_STDCALL;
+
+ // vtable offset.
+ pfunc->oVft = pProps->oVft;
+
+ // Set some method properties.
+ pfunc->memid = pProps->dispid;
+ pfunc->funckind = FUNC_PUREVIRTUAL;
+
+ // Set the invkind based on whether the function is an accessor.
+ if ((pProps->semantic - FieldSemanticOffset) == msGetter)
+ pfunc->invkind = INVOKE_PROPERTYGET;
+ else if ((pProps->semantic - FieldSemanticOffset) == msSetter)
+ {
+ if (IsVbRefType(&pbSig[ixSig], pInternalImport))
+ pfunc->invkind = INVOKE_PROPERTYPUTREF;
+ else
+ pfunc->invkind = INVOKE_PROPERTYPUT;
+ }
+ else
+ _ASSERTE(!"Incorrect semantic in ConvertFieldAsMethod");
+
+ // Name of the function.
+ rNames[0] = pProps->pName;
+ cNames = 1;
+
+ // Return type is HRESULT.
+ pfunc->elemdescFunc.tdesc.vt = VT_HRESULT;
+
+ // Set up the one and only parameter.
+ pfunc->lprgelemdescParam = reinterpret_cast<ELEMDESC*>(sPool.AllocZero(sizeof(ELEMDESC)));
+ if (NULL == pfunc->lprgelemdescParam)
+ IfFailReport(E_OUTOFMEMORY);
+ pfunc->cParams = 1;
+
+ // Do we need a name for the parameter? If PROPERTYGET, we do.
+ if (pfunc->invkind == INVOKE_PROPERTYGET)
+ {
+ // Yes, so make one up.
+ rNames[1] = (WCHAR*)szRetVal;
+ ++cNames;
+ }
+
+ // If Getter, convert param as ptr, otherwise convert directly.
+ if (pfunc->invkind == INVOKE_PROPERTYGET)
+ {
+ pType = reinterpret_cast<TYPEDESC*>(sPool.AllocZero(sizeof(TYPEDESC)));
+ if (NULL == pType)
+ IfFailReport(E_OUTOFMEMORY);
+
+ pfunc->lprgelemdescParam[0].tdesc.vt = VT_PTR;
+ pfunc->lprgelemdescParam[0].tdesc.lptdesc = pType;
+ pfunc->lprgelemdescParam[0].paramdesc.wParamFlags = PARAMFLAG_FOUT | PARAMFLAG_FRETVAL;
+ }
+ else
+ {
+ pType = &pfunc->lprgelemdescParam[0].tdesc;
+ pfunc->lprgelemdescParam[0].paramdesc.wParamFlags = PARAMFLAG_FIN;
+ }
+
+ // Get native field type
+ pvNativeType = NULL;
+ hr = pInternalImport->GetFieldMarshal(
+ pField->GetMemberDef(),
+ &pvNativeType,
+ &cbNativeType);
+ if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailReport(hr);
+ }
+
+ // Convert the field type to elemdesc.
+ hr = CorSigToTypeDesc(pCTI, pMT, &pbSig[ixSig], pvNativeType, cbNativeType, &cbElem, pType, &sPool, TRUE);
+ if (FAILED(hr))
+ return FALSE;
+
+ ixSig += cbElem;
+
+ // It is unfortunate that we can not handle this better. Fortunately
+ // this should be very rare.
+ // This is a weird case - if we're getting a CARRAY, we cannot add
+ // a VT_PTR in the sig, as it will cause the C getter to return an
+ // array, which is bad. So we omit the extra pointer, which at least
+ // makes the compiler happy.
+ if (pfunc->invkind == INVOKE_PROPERTYGET
+ && pType->vt == VT_CARRAY)
+ {
+ pfunc->lprgelemdescParam[0].tdesc.vt = pType->vt;
+ pfunc->lprgelemdescParam[0].tdesc.lptdesc = pType->lptdesc;
+ }
+
+ // A property put of an object should be a propertyputref
+ if (pfunc->invkind == INVOKE_PROPERTYPUT &&
+ (pType->vt == VT_UNKNOWN || pType->vt == VT_DISPATCH))
+ {
+ pfunc->invkind = INVOKE_PROPERTYPUTREF;
+ }
+
+ IfFailReport(pCTI->AddFuncDesc(iMD, pfunc));
+
+ IfFailReport(pCTI->SetFuncAndParamNames(iMD, rNames, cNames));
+
+ // Check for a description.
+ if(GetDescriptionString(pMT, pField->GetMemberDef(), (BSTR &)bstrDescr))
+ IfFailReport(pCTI->SetFuncDocString(iMD, bstrDescr));
+
+ // Error reporting info.
+ m_ErrorContext.m_szMember = 0;
+
+ return TRUE;
+} // void TypeLibExporter::ConvertFieldAsMethod()
+
+//*****************************************************************************
+// Export a variable's metadata to a typelib.
+//*****************************************************************************
+BOOL TypeLibExporter::ConvertVariable(
+ ICreateTypeInfo2 *pCTI, // ICreateTypeInfo2 to get the variable.
+ MethodTable *pMT, // The class containing the variable.
+ mdFieldDef md, // The member definition.
+ SString& sName, // Name of the member.
+ ULONG iMD) // Index of the member
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ PCCOR_SIGNATURE pbSig; // Pointer to Cor signature.
+ ULONG cbSig; // Size of Cor signature.
+ ULONG ixSig; // Index into signature.
+ ULONG cbElem; // Size of an element in the signature.
+ DWORD dwFlags; // A member's flags.
+ ULONG callconv; // A member's calling convention.
+ MDDefaultValue defaultValue; // default value
+ ULONG dispid=DISPID_UNKNOWN; // The variable's dispid.
+ CDescPool sPool; // Pool of memory in which to build vardesc.
+ VARDESC *pvar; // A vardesc.
+ PCCOR_SIGNATURE pvNativeType; // native field type
+ ULONG cbNativeType; // native field type length
+ const void *pvData; // Pointer to a custom attribute.
+ ULONG cbData; // Size of custom attribute.
+ LPWSTR pSuffix; // Pointer into the name.
+ int iSuffix = 0; // Counter for suffix.
+ BSTRHolder bstrDescr=0; // Description of the method.
+
+ VARIANT vtTemp;
+ VariantPtrHolder vtVariant = &vtTemp;
+
+ SafeVariantInit(vtVariant);
+
+ // Error reporting info.
+ IfFailReport(pMT->GetMDImport()->GetNameOfFieldDef(md, &m_ErrorContext.m_szMember));
+
+ // Get info about the field.
+ IfFailReport(pMT->GetMDImport()->GetDispIdOfMemberDef(md, &dispid));
+ IfFailReport(pMT->GetMDImport()->GetFieldDefProps(md, &dwFlags));
+ if (IsFdHasDefault(dwFlags))
+ {
+ IfFailReport(pMT->GetMDImport()->GetDefaultValue(md, &defaultValue));
+ IfFailReport( _FillVariant(&defaultValue, vtVariant) );
+ }
+
+ // If exporting a non-public member of a struct, warn the user.
+ if (!IsFdPublic(dwFlags) && !m_bWarnedOfNonPublic)
+ {
+ m_bWarnedOfNonPublic = TRUE;
+ ReportWarning(TLBX_E_NONPUBLIC_FIELD, TLBX_E_NONPUBLIC_FIELD);
+ }
+
+ IfFailReport(pMT->GetMDImport()->GetSigOfFieldDef(md, &cbSig, &pbSig));
+
+ // Prepare to parse signature and build the VARDESC.
+ pvar = reinterpret_cast<VARDESC*>(sPool.AllocZero(sizeof(VARDESC)));
+ if(pvar == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+ ixSig = 0;
+
+ // Get the calling convention.
+ ixSig += CorSigUncompressData(&pbSig[ixSig], &callconv);
+ _ASSERTE(callconv == IMAGE_CEE_CS_CALLCONV_FIELD);
+
+ // Get native field type
+ pvNativeType = NULL;
+ hr = pMT->GetMDImport()->GetFieldMarshal(md, &pvNativeType, &cbNativeType);
+ if (hr != CLDB_E_RECORD_NOTFOUND)
+ {
+ IfFailReport(hr);
+ }
+
+ // Convert the type to elemdesc.
+ hr = CorSigToTypeDesc(pCTI, pMT, &pbSig[ixSig], pvNativeType, cbNativeType, &cbElem, &pvar->elemdescVar.tdesc, &sPool, FALSE);
+ if (FAILED(hr))
+ return FALSE;
+
+ ixSig += cbElem;
+
+ pvar->wVarFlags = 0;
+ pvar->varkind = VAR_PERINSTANCE;
+ pvar->memid = dispid;
+
+ // Constant value.
+ if (vtVariant->vt != VT_EMPTY)
+ pvar->lpvarValue = vtVariant;
+ else
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(md, INTEROP_DECIMALVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK && cbData >= (2 + sizeof(BYTE)+sizeof(BYTE)+sizeof(UINT)+sizeof(UINT)+sizeof(UINT)))
+ {
+ const BYTE *pbData = (const BYTE *)pvData;
+ vtVariant->vt = VT_DECIMAL;
+ vtVariant->decVal.scale = *(BYTE*)(pbData+2);
+ vtVariant->decVal.sign= *(BYTE*)(pbData+3);
+ vtVariant->decVal.Hi32= GET_UNALIGNED_32(pbData+4);
+ vtVariant->decVal.Mid32= GET_UNALIGNED_32(pbData+8);
+ vtVariant->decVal.Lo32= GET_UNALIGNED_32(pbData+12);
+ pvar->lpvarValue = vtVariant;
+ }
+ // If still no default value, check for date time custom attribute.
+ if (vtVariant->vt == VT_EMPTY)
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(md, INTEROP_DATETIMEVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK && cbData >= (2 + sizeof(__int64)))
+ {
+ const BYTE *pbData = (const BYTE *)pvData;
+ vtVariant->vt = VT_DATE;
+ vtVariant->date = _TicksToDoubleDate(GET_UNALIGNED_64(pbData+2));
+ }
+ }
+ // If still no default value, check for IDispatch custom attribute.
+ if (vtVariant->vt == VT_EMPTY)
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(md, INTEROP_IDISPATCHVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK)
+ {
+ vtVariant->vt = VT_DISPATCH;
+ vtVariant->pdispVal = 0;
+ }
+ }
+ // If still no default value, check for IUnknown custom attribute.
+ if (vtVariant->vt == VT_EMPTY)
+ {
+ IfFailReport(pMT->GetMDImport()->GetCustomAttributeByName(md, INTEROP_IUNKNOWNVALUE_TYPE, &pvData,&cbData));
+ if (hr == S_OK)
+ {
+ vtVariant->vt = VT_UNKNOWN;
+ vtVariant->punkVal = 0;
+ }
+ }
+ }
+
+ IfFailReport(pCTI->AddVarDesc(iMD, pvar));
+
+ // Set the name for the member; decorate if necessary.
+ pSuffix = 0;
+ for (;;)
+ {
+ // Attempt to set the name.
+ hr = pCTI->SetVarName(iMD, (LPOLESTR)sName.GetUnicode());
+
+ // If a name conflict, decorate, otherwise, done.
+ if (hr != TYPE_E_AMBIGUOUSNAME)
+ break;
+
+ if (iSuffix == 0)
+ {
+ iSuffix = 2;
+ }
+ else
+ {
+ sName.Delete(sName.End()-=2, 2);
+ }
+
+ SString sDup;
+ sDup.Printf(szDuplicateDecoration, iSuffix++);
+
+ sName.Append(sDup);
+ }
+ IfFailReport(hr);
+
+ // Check for a description.
+ if(GetDescriptionString(pMT, md, (BSTR &)bstrDescr))
+ IfFailReport(pCTI->SetVarDocString(iMD, bstrDescr));
+
+ // Error reporting info.
+ m_ErrorContext.m_szMember = 0;
+
+ return TRUE;
+} // HRESULT TypeLibExporter::ConvertVariable()
+
+//*****************************************************************************
+// Export a variable's metadata to a typelib.
+//*****************************************************************************
+BOOL TypeLibExporter::ConvertEnumMember(
+ ICreateTypeInfo2 *pCTI, // ICreateTypeInfo2 to get the variable.
+ MethodTable *pMT, // The Class containing the member.
+ mdFieldDef md, // The member definition.
+ SString& sName, // Name of the member.
+ ULONG iMD) // Index of the member
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ LPCUTF8 pName, pNS; // To format name.
+ DWORD dwFlags; // A member's flags.
+ VARIANT vtVariant; // A Variant.
+ MDDefaultValue defaultValue; // default value
+ ULONG dispid=DISPID_UNKNOWN; // The variable's dispid.
+ CDescPool sPool; // Pool of memory in which to build vardesc.
+ VARDESC *pvar; // A vardesc.
+ BSTRHolder bstrDescr=0; // Description of the method.
+
+ vtVariant.vt = VT_EMPTY;
+
+ // Error reporting info.
+ IfFailReport(pMT->GetMDImport()->GetNameOfFieldDef(md, &m_ErrorContext.m_szMember));
+
+ // Get info about the field.
+ IfFailReport(pMT->GetMDImport()->GetDispIdOfMemberDef(md, &dispid));
+ IfFailReport(pMT->GetMDImport()->GetFieldDefProps(md, &dwFlags));
+
+ // We do not need to handle decimal's here since enum's can only be integral types.
+ IfFailReport(pMT->GetMDImport()->GetDefaultValue(md, &defaultValue));
+
+ // Prepare to parse signature and build the VARDESC.
+ pvar = reinterpret_cast<VARDESC*>(sPool.AllocZero(sizeof(VARDESC)));
+ if (NULL == pvar)
+ IfFailReport(E_OUTOFMEMORY);
+
+ IfFailReport( _FillVariant(&defaultValue, &vtVariant) );
+
+ // Don't care what the metadata says the type is -- the type is I4 in the typelib.
+ pvar->elemdescVar.tdesc.vt = VT_I4;
+
+ pvar->wVarFlags = 0;
+ pvar->varkind = VAR_CONST;
+ pvar->memid = dispid;
+
+ // Constant value.
+ if (vtVariant.vt != VT_EMPTY)
+ {
+ pvar->lpvarValue = &vtVariant;
+
+ // If this is an I8 or UI8, do the conversion manually, because some
+ // systems' oleaut32 don't support 64-bit integers.
+ if (vtVariant.vt == VT_I8)
+ {
+ // If withing range of 32-bit signed number, OK.
+ if (vtVariant.llVal <= LONG_MAX && vtVariant.llVal >= LONG_MIN)
+ vtVariant.vt = VT_I4, hr = S_OK;
+ else
+ hr = E_FAIL;
+ }
+ else if (vtVariant.vt == VT_UI8)
+ {
+ // If withing range of 32-bit unsigned number, OK.
+ if (vtVariant.ullVal <= ULONG_MAX)
+ vtVariant.vt = VT_UI4, hr = S_OK;
+ else
+ hr = E_FAIL;
+ }
+ else
+ {
+ hr = SafeVariantChangeTypeEx(&vtVariant, &vtVariant, 0, 0, VT_I4);
+ }
+
+ if (FAILED(hr))
+ {
+ if (FAILED(pMT->GetMDImport()->GetNameOfTypeDef(pMT->GetCl(), &pName, &pNS)))
+ {
+ pName = pNS = "Invalid TypeDef record";
+ }
+ ReportWarning(TLBX_W_ENUM_VALUE_TOOBIG, TLBX_W_ENUM_VALUE_TOOBIG, pName, sName.GetUnicode());
+ return FALSE;
+ }
+ }
+ else
+ { // No value assigned, use 0.
+ pvar->lpvarValue = &vtVariant;
+ vtVariant.vt = VT_I4;
+ vtVariant.lVal = 0;
+ }
+
+ IfFailReport(pCTI->AddVarDesc(iMD, pvar));
+ IfFailReport(pCTI->SetVarName(iMD, (LPOLESTR)sName.GetUnicode()));
+
+ // Check for a description.
+ if(GetDescriptionString(pMT, md, (BSTR &)bstrDescr))
+ IfFailReport(pCTI->SetVarDocString(iMD, bstrDescr));
+
+ // Error reporting info.
+ m_ErrorContext.m_szMember = 0;
+
+ return TRUE;
+} // void TypeLibExporter::ConvertEnumMember()
+
+//*****************************************************************************
+// Given a COM+ signature of a field or property, determine if it should
+// be a PROPERTYPUT or PROPERTYPUTREF.
+//*****************************************************************************
+BOOL TypeLibExporter::IsVbRefType(
+ PCCOR_SIGNATURE pbSig,
+ IMDInternalImport *pInternalImport)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pInternalImport));
+ }
+ CONTRACTL_END;
+
+ ULONG elem=0; // An element from a COM+ signature.
+ ULONG cbElem=0;
+
+ cbElem = CorSigUncompressData(pbSig, &elem);
+ if (elem == ELEMENT_TYPE_PTR || elem == ELEMENT_TYPE_BYREF)
+ {
+ return IsVbRefType(&pbSig[cbElem], pInternalImport);
+ }
+ else
+ {
+ switch (elem)
+ {
+ // For documentation -- arrays are NOT ref types here.
+ //case ELEMENT_TYPE_SDARRAY:
+ //case ELEMENT_TYPE_ARRAY:
+ //case ELEMENT_TYPE_SZARRAY:
+ // Look for variant.
+ case ELEMENT_TYPE_VALUETYPE:
+ return FALSE;
+
+ case ELEMENT_TYPE_CLASS:
+ return TRUE;
+
+ case ELEMENT_TYPE_OBJECT:
+ return FALSE;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+} // BOOL TypeLibExporter::IsVbRefType()
+
+BOOL TypeLibExporter::IsExportingAs64Bit()
+{
+ LIMITED_METHOD_CONTRACT;
+ if (TlbExportAs64Bit(m_flags))
+ {
+ return TRUE;
+ }
+ else if (TlbExportAs32Bit(m_flags))
+ {
+ return FALSE;
+ }
+ else
+ {
+#ifdef _WIN64
+ return TRUE;
+#else
+ return FALSE;
+#endif
+ }
+} // BOOL TypeLibExporter::IsExportingAs64Bit()
+
+void TypeLibExporter::ArrayToTypeDesc(ICreateTypeInfo2 *pCTI, CDescPool *ppool, ArrayMarshalInfo *pArrayMarshalInfo, TYPEDESC *ptdesc)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(ppool));
+ PRECONDITION(CheckPointer(pArrayMarshalInfo));
+ PRECONDITION(CheckPointer(ptdesc));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_FAIL;
+ VARTYPE vtElement = pArrayMarshalInfo->GetElementVT();
+ TypeHandle thElement = pArrayMarshalInfo->GetElementTypeHandle();
+
+ if (vtElement == VT_RECORD)
+ {
+ // We are dealing with an array of embedded structures.
+ ptdesc->vt = VT_USERDEFINED;
+ EEClassToHref(pCTI, thElement.GetMethodTable(), FALSE, &ptdesc->hreftype);
+ }
+ else if ((vtElement == VT_UNKNOWN || vtElement == VT_DISPATCH) && !thElement.IsObjectType())
+ {
+ if (!thElement.IsValueType() && !pArrayMarshalInfo->IsSafeArraySubTypeExplicitlySpecified())
+ {
+ // We are dealing with an array of user defined interfaces.
+ ptdesc->vt = VT_PTR;
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ptdesc->lptdesc->vt = VT_USERDEFINED;
+ EEClassToHref(pCTI, thElement.GetMethodTable(), FALSE, &ptdesc->lptdesc->hreftype);
+ }
+ else
+ {
+ // The user specified that the array of value classes be converted to an
+ // array of IUnknown or IDispatch pointers.
+ ptdesc->vt = vtElement;
+ }
+ }
+ else if (pArrayMarshalInfo->IsPtr())
+ {
+ ptdesc->vt = VT_PTR;
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ptdesc->lptdesc->vt = vtElement;
+ }
+ else
+ {
+ // We are dealing with an array of primitive types.
+ ptdesc->vt = vtElement;
+ }
+}
+// HRESULT ArrayToTypeDesc(ArrayMarshalInfo *pArrayMarshalInfo, TYPEDESC *pElementTypeDesc)
+
+VARTYPE TypeLibExporter::GetVtForIntPtr()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return static_cast<VARTYPE>(IsExportingAs64Bit() ? VT_I8 : VT_I4);
+} // VARTYPE TypeLibExporter::GetVtForIntPtr()
+
+VARTYPE TypeLibExporter::GetVtForUIntPtr()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return static_cast<VARTYPE>(IsExportingAs64Bit() ? VT_UI8 : VT_UI4);
+} // VARTYPE TypeLibExporter::GetVtForUIntPtr()
+
+/*
+BOOL TypeLibExporter::ValidateSafeArrayElemVT(VARTYPE vt)
+{
+ switch(vt)
+ {
+ case VT_I2:
+ case VT_I4:
+ case VT_R4:
+ case VT_R8:
+ case VT_CY:
+ case VT_DATE:
+ case VT_BSTR:
+ case VT_DISPATCH:
+ case VT_ERROR:
+ case VT_BOOL:
+ case VT_VARIANT:
+ case VT_UNKNOWN:
+ case VT_DECIMAL:
+ case VT_RECORD:
+ case VT_I1:
+ case VT_UI1:
+ case VT_UI2:
+ case VT_UI4:
+ case VT_INT:
+ case VT_UINT:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+*/
+
+//*****************************************************************************
+// Read a COM+ signature element and create a TYPEDESC that corresponds
+// to it.
+//*****************************************************************************
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+HRESULT TypeLibExporter::CorSigToTypeDesc(
+ ICreateTypeInfo2 *pCTI, // Typeinfo being created.
+ MethodTable *pMT, // MethodTable with the token.
+ PCCOR_SIGNATURE pbSig, // Pointer to the Cor Signature.
+ PCCOR_SIGNATURE pbNativeSig, // Pointer to the native sig, if any
+ ULONG cbNativeSig, // Count of bytes in native sig.
+ ULONG *pcbElem, // Put # bytes consumed here.
+ TYPEDESC *ptdesc, // Build the typedesc here.
+ CDescPool *ppool, // Pool for additional storage as required.
+ BOOL bMethodSig, // TRUE if the sig is for a method, FALSE for a field.
+ BOOL *pbByRef) // If not null, and the type is byref, set to true.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pMT));
+ PRECONDITION(CheckPointer(pcbElem));
+ PRECONDITION(CheckPointer(ptdesc));
+ PRECONDITION(CheckPointer(ppool));
+ PRECONDITION(CheckPointer(pbByRef, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr=S_OK;
+ ULONG elem = 0; // The element type.
+ ULONG cbElem = 0; // Bytes in the element.
+ ULONG cb; // Bytes in a sub-element.
+ ULONG cbNativeElem = 0; // # of bytes parsed off of native type.
+ ULONG nativeElem = 0; // The native element type
+ ULONG nativeCount; // The native element size
+ mdToken tkTypeRef; // Token for a TypeRef/TypeDef
+ SString sName; // Buffer to build a name from NS/Name.
+ LPCUTF8 pclsname; // Class name for ELEMENT_TYPE_CLASS.
+ HREFTYPE hRef = 0; // HREF to some type.
+ IMDInternalImport *pInternalImport; // Internal interface containing the signature.
+ Module* pModule = NULL; // Module containing the signature.
+ int i; // Loop control.
+ SigTypeContext emptyTypeContext; // an empty type context is sufficient: all methods should be non-generic
+ ULONG dwTypeFlags = 0; // The type flags.
+ BOOL fAnsi = FALSE; // Is the structure marked as CharSet=Ansi.
+ BOOL fIsStringBuilder = FALSE;
+ LPCUTF8 pNS;
+
+
+ pInternalImport = pMT->GetMDImport();
+ pModule = pMT->GetModule();
+
+ // Just be sure the count is zero if the pointer is.
+ if (pbNativeSig == NULL)
+ cbNativeSig = 0;
+
+ // Grab the native marshaling type.
+ if (cbNativeSig > 0)
+ {
+ cbNativeElem = CorSigUncompressData(pbNativeSig, &nativeElem);
+ pbNativeSig += cbNativeElem;
+ cbNativeSig -= cbNativeElem;
+
+ // AsAny makes no sense for COM Interop. Ignore it.
+ if (nativeElem == NATIVE_TYPE_ASANY)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_ASANY);
+ nativeElem = 0;
+ }
+ }
+
+ // If we are dealing with a struct, determine if it is marked as CharSet=Ansi.
+ if (!bMethodSig)
+ {
+ // Make sure one of Auto, Ansi or Unicode is specified.
+ if (!IsTdAnsiClass(dwTypeFlags) && !IsTdAutoClass(dwTypeFlags) && !IsTdUnicodeClass(dwTypeFlags))
+ {
+ _ASSERTE(!"Bad stringformat value in wrapper class.");
+ ReportWarning(TLBX_E_BAD_SIGNATURE, E_FAIL); // bad metadata
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ if (FAILED(pInternalImport->GetTypeDefProps(pMT->GetCl(), &dwTypeFlags, NULL)))
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, E_FAIL);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ fAnsi = IsTdAnsiClass(dwTypeFlags);
+ }
+
+ // Get the element type.
+TryAgain:
+ cbElem += CorSigUncompressData(pbSig+cbElem, &elem);
+
+ // Handle the custom marshaler native type separately.
+ if (elem != ELEMENT_TYPE_BYREF && nativeElem == NATIVE_TYPE_CUSTOMMARSHALER)
+ {
+ switch(elem)
+ {
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_OBJECT:
+ // @TODO(DM): Ask the custom marshaler for the ITypeInfo to use for the unmanaged type.
+ ptdesc->vt = VT_UNKNOWN;
+ break;
+
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ ptdesc->vt = GetVtForIntPtr();
+ break;
+
+ default:
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ return(TLBX_E_BAD_SIGNATURE);
+ break;
+ }
+
+ // Eat the rest of the signature. The extra -1's are to account
+ // for the byte parsed off above.
+ SigPointer p(&pbSig[cbElem-1]);
+ IfFailThrow(p.SkipExactlyOne());
+ cbElem += (ULONG)(p.GetPtr() - &pbSig[cbElem]); // Note I didn't use -1 here.
+ goto ExitFunc;
+ }
+
+// This label is used to try again with a new element type, but without consuming more signature.
+// Usage is to set 'elem' to a new value, goto this label.
+TryWithElemType:
+ switch (elem)
+ {
+ case ELEMENT_TYPE_END: // 0x0,
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_UNKNOWN_SIGNATURE);
+ return(TLBX_E_BAD_SIGNATURE);
+ break;
+
+ case ELEMENT_TYPE_VOID: // 0x1,
+ ptdesc->vt = VT_VOID;
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN: // 0x2,
+ switch (nativeElem)
+ {
+ case 0:
+ ptdesc->vt = static_cast<VARTYPE>(bMethodSig ? VT_BOOL : VT_I4);
+ break;
+
+ case NATIVE_TYPE_VARIANTBOOL:
+ ptdesc->vt = VT_BOOL;
+ break;
+
+ case NATIVE_TYPE_BOOLEAN:
+ ptdesc->vt = VT_I4;
+ break;
+
+ case NATIVE_TYPE_U1:
+ case NATIVE_TYPE_I1:
+ ptdesc->vt = VT_UI1;
+ break;
+
+ default:
+ DEBUG_STMT(DbgWriteEx(W("Bad Native COM attribute specified!\n")));
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ return(TLBX_E_BAD_SIGNATURE);
+ }
+ break;
+
+ case ELEMENT_TYPE_CHAR: // 0x3,
+ if (nativeElem == 0)
+ {
+ if (!bMethodSig && IsTdAutoClass(dwTypeFlags))
+ {
+ // Types with a char set of auto and that would be represented differently
+ // on different platforms are not allowed to be exported to COM.
+ DefineFullyQualifiedNameForClassW();
+ LPCWSTR szName = GetFullyQualifiedNameForClassW(pMT);
+ _ASSERTE(szName);
+
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_AUTO_CS_NOT_ALLOWED, szName);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ ptdesc->vt = static_cast<VARTYPE>(fAnsi ? VT_UI1 : VT_UI2);
+ }
+ else
+ {
+ switch (nativeElem)
+ {
+ case 0:
+ case NATIVE_TYPE_U2:
+ case NATIVE_TYPE_I2:
+ ptdesc->vt = VT_UI2;
+ break;
+
+ case NATIVE_TYPE_U1:
+ case NATIVE_TYPE_I1:
+ ptdesc->vt = VT_UI1;
+ break;
+
+ default:
+ DEBUG_STMT(DbgWriteEx(W("Bad Native COM attribute specified!\n")));
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_I1: // 0x4,
+ ptdesc->vt = VT_I1;
+ break;
+
+ case ELEMENT_TYPE_U1: // 0x5,
+ ptdesc->vt = VT_UI1;
+ break;
+
+ case ELEMENT_TYPE_I2: // 0x6,
+ ptdesc->vt = VT_I2;
+ break;
+
+ case ELEMENT_TYPE_U2: // 0x7,
+ ptdesc->vt = VT_UI2;
+ break;
+
+ case ELEMENT_TYPE_I4: // 0x8,
+ switch (nativeElem)
+ {
+ case 0:
+ case NATIVE_TYPE_I4:
+ case NATIVE_TYPE_U4: case NATIVE_TYPE_INTF: //@todo: Fix Microsoft.Win32.Interop.dll and remove this line.
+ ptdesc->vt = VT_I4;
+ break;
+
+ case NATIVE_TYPE_ERROR:
+ ptdesc->vt = VT_HRESULT;
+ break;
+
+ default:
+ DEBUG_STMT(DbgWriteEx(W("Bad Native COM attribute specified!\n")));
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ break;
+
+ case ELEMENT_TYPE_U4: // 0x9,
+ switch (nativeElem)
+ {
+ case 0:
+ case NATIVE_TYPE_U4:
+ ptdesc->vt = VT_UI4;
+ break;
+
+ case NATIVE_TYPE_ERROR:
+ ptdesc->vt = VT_HRESULT;
+ break;
+
+ default:
+ DEBUG_STMT(DbgWriteEx(W("Bad Native COM attribute specified!\n")));
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ break;
+
+ case ELEMENT_TYPE_I8: // 0xa,
+ ptdesc->vt = VT_I8;
+ break;
+
+ case ELEMENT_TYPE_U8: // 0xb,
+ ptdesc->vt = VT_UI8;
+ break;
+
+ case ELEMENT_TYPE_R4: // 0xc,
+ ptdesc->vt = VT_R4;
+ break;
+
+ case ELEMENT_TYPE_R8: // 0xd,
+ ptdesc->vt = VT_R8;
+ break;
+
+ case ELEMENT_TYPE_OBJECT:
+ goto IsObject;
+
+ case ELEMENT_TYPE_STRING: // 0xe,
+ IsString:
+ if (nativeElem == 0)
+ {
+ if (bMethodSig)
+ {
+ ptdesc->vt = VT_BSTR;
+ }
+ else
+ {
+ if (IsTdAutoClass(dwTypeFlags))
+ {
+ // Types with a char set of auto and that would be represented differently
+ // on different platforms are not allowed to be exported to COM.
+ DefineFullyQualifiedNameForClassW();
+ LPCWSTR szName = GetFullyQualifiedNameForClassW(pMT);
+ _ASSERTE(szName);
+
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_AUTO_CS_NOT_ALLOWED, szName);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ ptdesc->vt = static_cast<VARTYPE>(fAnsi ? VT_LPSTR : VT_LPWSTR);
+ }
+ }
+ else
+ {
+ switch (nativeElem)
+ {
+ case NATIVE_TYPE_BSTR:
+ if (fIsStringBuilder)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ ptdesc->vt = VT_BSTR;
+ break;
+
+ case NATIVE_TYPE_LPSTR:
+ ptdesc->vt = VT_LPSTR;
+ break;
+
+ case NATIVE_TYPE_LPWSTR:
+ ptdesc->vt = VT_LPWSTR;
+ break;
+
+ case NATIVE_TYPE_LPTSTR:
+ {
+ // NATIVE_TYPE_LPTSTR is not allowed to be exported to COM.
+ DefineFullyQualifiedNameForClassW();
+ LPCWSTR szName = GetFullyQualifiedNameForClassW(pMT);
+ _ASSERTE(szName);
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_LPTSTR_NOT_ALLOWED, szName);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ case NATIVE_TYPE_FIXEDSYSSTRING:
+ // NATIVE_TYPE_FIXEDSYSSTRING is only allowed on fields.
+ if (bMethodSig)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Retrieve the count of characters.
+ if (cbNativeSig != 0)
+ {
+ cb = CorSigUncompressData(pbNativeSig, &nativeCount);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+ }
+ else
+ {
+ nativeCount = 0;
+ }
+
+ // Fixed strings become embedded array's of characters.
+ ptdesc->vt = VT_CARRAY;
+ ptdesc->lpadesc = reinterpret_cast<ARRAYDESC*>(ppool->AllocZero(sizeof(ARRAYDESC)));
+ if (ptdesc->lpadesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // Set the count of characters.
+ ptdesc->lpadesc->cDims = 1;
+ ptdesc->lpadesc->rgbounds[0].cElements = nativeCount;
+ ptdesc->lpadesc->rgbounds[0].lLbound = 0;
+
+ if (IsTdAutoClass(dwTypeFlags))
+ {
+ // Types with a char set of auto and that would be represented differently
+ // on different platforms are not allowed to be exported to COM.
+ DefineFullyQualifiedNameForClassW();
+ LPCWSTR szName = GetFullyQualifiedNameForClassW(pMT);
+ _ASSERTE(szName);
+
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_AUTO_CS_NOT_ALLOWED, szName);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ ptdesc->lpadesc->tdescElem.vt = static_cast<VARTYPE>(fAnsi ? VT_UI1 : VT_UI2);
+ break;
+
+ default:
+ DEBUG_STMT(DbgWriteEx(W("Bad Native COM attribute specified!\n")));
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ }
+ break;
+
+ // every type above PTR will be simple type
+ case ELEMENT_TYPE_PTR: // 0xf,
+ case ELEMENT_TYPE_BYREF: // 0x10,
+ // TYPEDESC is a pointer.
+ ptdesc->vt = VT_PTR;
+ if (pbByRef)
+ *pbByRef = TRUE;
+
+ // Pointer to what?
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ hr = CorSigToTypeDesc(pCTI, pMT, &pbSig[cbElem], pbNativeSig-cbNativeElem,
+ cbNativeSig+cbNativeElem, &cb, ptdesc->lptdesc, ppool, bMethodSig);
+ cbElem += cb;
+
+ if (FAILED(hr))
+ goto ExitFunc;
+
+ break;
+
+ case ELEMENT_TYPE_CLASS: // 0x12,
+ case ELEMENT_TYPE_VALUETYPE:
+ // Get the TD/TR.
+ cb = CorSigUncompressToken(&pbSig[cbElem], &tkTypeRef);
+ cbElem += cb;
+
+ if (TypeFromToken(tkTypeRef) == mdtTypeDef)
+ {
+ // Get the name of the TypeDef.
+ if (FAILED(pInternalImport->GetNameOfTypeDef(tkTypeRef, &pclsname, &pNS)))
+ {
+ IfFailReport(COR_E_BADIMAGEFORMAT);
+ }
+ }
+ else
+ {
+ // Get the name of the TypeRef.
+ _ASSERTE(TypeFromToken(tkTypeRef) == mdtTypeRef);
+ IfFailReport(pInternalImport->GetNameOfTypeRef(tkTypeRef, &pNS, &pclsname));
+ }
+
+ if (pNS)
+ {
+ sName.MakeFullNamespacePath(SString(SString::Utf8, pNS), SString(SString::Utf8, pclsname));
+ StackScratchBuffer scratch;
+ pclsname = sName.GetUTF8(scratch);
+ }
+
+ _ASSERTE(strlen(szRuntime) == cbRuntime); // If you rename System, fix this invariant.
+ _ASSERTE(strlen(szText) == cbText); // If you rename System.Text, fix this invariant.
+
+ // Is it System.something?
+ if (SString::_strnicmp(pclsname, szRuntime, cbRuntime) == 0)
+ {
+ // Which one?
+ LPCUTF8 pcls; pcls = pclsname + cbRuntime;
+ if (stricmpUTF8(pcls, szStringClass) == 0)
+ {
+ goto IsString;
+ }
+ else if (stricmpUTF8(pcls, szDateTimeClass) == 0)
+ {
+ ptdesc->vt = VT_DATE;
+ goto ExitFunc;
+ }
+ else if (stricmpUTF8(pcls, szDecimalClass) == 0)
+ {
+ switch (nativeElem)
+ {
+ case NATIVE_TYPE_CURRENCY:
+ // Make this a currency.
+ ptdesc->vt = VT_CY;
+ break;
+
+ case 0:
+ // Make this a decimal
+ ptdesc->vt = VT_DECIMAL;
+ break;
+
+ default:
+ DEBUG_STMT(DbgWriteEx(W("Bad Native COM attribute specified!\n")));
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ goto ExitFunc;
+ }
+ else if (stricmpUTF8(pcls, szGuidClass) == 0)
+ {
+ switch (nativeElem)
+ {
+ case NATIVE_TYPE_LPSTRUCT:
+ // Make this a pointer to . . .
+ ptdesc->vt = VT_PTR;
+ if (pbByRef)
+ *pbByRef = TRUE;
+
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // . . . a user defined type for GUID
+ ptdesc->lptdesc->vt = VT_USERDEFINED;
+ GetRefTypeInfo(pCTI, m_pGuid, &ptdesc->lptdesc->hreftype);
+ break;
+
+ case 0:
+ case NATIVE_TYPE_STRUCT:
+ // a user defined type for GUID
+ ptdesc->vt = VT_USERDEFINED;
+ GetRefTypeInfo(pCTI, m_pGuid, &ptdesc->hreftype);
+ break;
+
+ default:
+ DEBUG_STMT(DbgWriteEx(W("Bad Native COM attribute specified!\n")));
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ goto ExitFunc;
+ }
+ else if (stricmpUTF8(pcls, szArrayClass) == 0)
+ {
+ // If no native type is specified then assume its a NATIVE_TYPE_INTF.
+ if (nativeElem == 0)
+ nativeElem = NATIVE_TYPE_INTF;
+
+ if (nativeElem == NATIVE_TYPE_SAFEARRAY)
+ {
+ // Compat: If no safe array used def subtype was specified we will map it to a SAFEARRAY of VARIANTs.
+ ULONG vtElement = VT_VARIANT;
+ TypeHandle thElement = TypeHandle(g_pObjectClass);
+
+ if (cbNativeSig > 0)
+ {
+ // Retrieve the safe array sub type.
+ cb = CorSigUncompressData(pbNativeSig, &vtElement);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+
+ // Get the type name if specified.
+ if (cbNativeSig > 0)
+ {
+ ULONG cbClass = 0;
+
+ cb = CorSigUncompressData(pbNativeSig, &cbClass);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+
+ if (cbClass > 0)
+ {
+ // Load the type. Use an SString for the string since we need to NULL terminate the string
+ // that comes from the metadata.
+ StackScratchBuffer utf8Name;
+ SString safeArrayUserDefTypeName(SString::Utf8, (LPUTF8)pbNativeSig, cbClass);
+ thElement = LoadClass(pMT->GetModule(), safeArrayUserDefTypeName.GetUTF8(utf8Name));
+ }
+ }
+ }
+ else
+ {
+ if (!bMethodSig)
+ {
+ // The field marshaller converts these to SAFEARRAYs of the type specified
+ // at runtime by the array. This isn't expressible in a type library
+ // so provide a warning.
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_W_BAD_SAFEARRAYFIELD_NO_ELEMENTVT);
+ }
+ }
+
+ ArrayMarshalInfo arrayMarshalInfo(IsExportingAs64Bit() ? amiExport64Bit : amiExport32Bit);
+ MarshalInfo::MarshalScenario ms = bMethodSig ? MarshalInfo::MARSHAL_SCENARIO_COMINTEROP : MarshalInfo::MARSHAL_SCENARIO_FIELD;
+ arrayMarshalInfo.InitForSafeArray(ms, thElement, (VARTYPE)vtElement, fAnsi);
+
+ if (!arrayMarshalInfo.IsValid())
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, arrayMarshalInfo.GetErrorResourceId());
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // TYPEDESC is an array.
+ ptdesc->vt = VT_SAFEARRAY;
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ArrayToTypeDesc(pCTI, ppool, &arrayMarshalInfo, ptdesc->lptdesc);
+
+ goto ExitFunc;
+ }
+ else if (nativeElem == NATIVE_TYPE_FIXEDARRAY)
+ {
+ // NATIVE_TYPE_FIXEDARRAY is only allowed on fields.
+ if (bMethodSig)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Retrieve the size of the fixed array. This is required.
+ if (cbNativeSig == 0)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, IDS_EE_BADMARSHALFIELD_FIXEDARRAY_NOSIZE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ cb = CorSigUncompressData(pbNativeSig, &nativeCount);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+
+ // A size const of 0 isn't supported.
+ if (nativeCount == 0)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, IDS_EE_BADMARSHALFIELD_FIXEDARRAY_ZEROSIZE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Since these always export to arrays of BSTRs, we don't need to fetch the native type.
+
+ // Set the data
+ ptdesc->vt = VT_CARRAY;
+ ptdesc->lpadesc = NULL;
+ ptdesc->lpadesc = reinterpret_cast<ARRAYDESC*>(ppool->AllocZero(sizeof(ARRAYDESC)));
+ if (ptdesc->lpadesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // Compat: FixedArrays of System.Arrays map to fixed arrays of BSTRs.
+ ptdesc->lpadesc->tdescElem.vt = VT_BSTR;
+ ptdesc->lpadesc->cDims = 1;
+ ptdesc->lpadesc->rgbounds->cElements = nativeCount;
+ ptdesc->lpadesc->rgbounds->lLbound = 0;
+
+ goto ExitFunc;
+ }
+ else if (nativeElem != NATIVE_TYPE_INTF)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // If the native type is NATIVE_TYPE_INTF then we fall through and convert
+ // System.Array to its IClassX interface.
+ }
+ else if (stricmpUTF8(pcls, szObjectClass) == 0)
+ {
+ IsObject:
+ // This next statement is to work around a "feature" that marshals an object inside
+ // a struct as an interface, instead of as a variant. fieldmarshal metadata
+ // can override that.
+ if (nativeElem == 0 && !bMethodSig)
+ nativeElem = NATIVE_TYPE_IUNKNOWN;
+
+ switch (nativeElem)
+ {
+ case NATIVE_TYPE_INTF:
+ case NATIVE_TYPE_IUNKNOWN:
+ // an IUnknown based interface.
+ ptdesc->vt = VT_UNKNOWN;
+ break;
+
+ case NATIVE_TYPE_IDISPATCH:
+ // an IDispatch based interface.
+ ptdesc->vt = VT_DISPATCH;
+ break;
+
+ case 0:
+ case NATIVE_TYPE_STRUCT:
+ // a VARIANT
+ ptdesc->vt = VT_VARIANT;
+ break;
+
+ default:
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ goto ExitFunc;
+ }
+ } // System
+
+ if (SString::_strnicmp(pclsname, szText, cbText) == 0)
+ {
+ LPCUTF8 pcls; pcls = pclsname + cbText;
+ if (stricmpUTF8(pcls, szStringBufferClass) == 0)
+ {
+ fIsStringBuilder = TRUE;
+
+ // If there is no fieldmarshal information, marshal as a LPWSTR
+ if (nativeElem == 0)
+ nativeElem = NATIVE_TYPE_LPWSTR;
+
+ // Marshaller treats stringbuilders as [in, out] by default.
+ if (pbByRef)
+ *pbByRef = TRUE;
+
+ goto IsString;
+ }
+ } // System.Text
+
+ if (SString::_strnicmp(pclsname, szCollections, cbCollections) == 0)
+ {
+ LPCUTF8 pcls; pcls = pclsname + cbCollections;
+ if (stricmpUTF8(pcls, szIEnumeratorClass) == 0)
+ {
+ StdOleTypeToHRef(pCTI, IID_IEnumVARIANT, &hRef);
+ ptdesc->vt = VT_PTR;
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ptdesc->lptdesc->vt = VT_USERDEFINED;
+ ptdesc->lptdesc->hreftype = hRef;
+ goto ExitFunc;
+ }
+ } // System.Collections
+
+ if (SString::_strnicmp(pclsname, szDrawing, cbDrawing) == 0)
+ {
+ LPCUTF8 pcls; pcls = pclsname + cbDrawing;
+ if (stricmpUTF8(pcls, szColor) == 0)
+ {
+ StdOleTypeToHRef(pCTI, GUID_OleColor, &hRef);
+ ptdesc->vt = VT_USERDEFINED;
+ ptdesc->hreftype = hRef;
+ goto ExitFunc;
+ }
+ } // System.Drawing
+
+ // It is not a built-in VT type, so build the typedesc.
+
+ // Determine whether the type is a reference type (IUnknown derived) or a struct type.
+ // Get the MethodTable for the referenced class.
+ MethodTable *pRefdClass; // MethodTable object for referenced TypeDef.
+ pRefdClass = LoadClass(pMT->GetModule(), tkTypeRef);
+
+ // Is the type a ref type or a struct type. Note that a ref type that has layout
+ // is exported as a TKIND_RECORD but is referenced as a **Foo, whereas a
+ // value type is also exported as a TKIND_RECORD but is referenced as a *Foo.
+ if (elem == ELEMENT_TYPE_CLASS)
+ {
+ // Check if it is a delegate (which can be marshaled as a function pointer).
+ if (COMDelegate::IsDelegate(pRefdClass))
+ {
+ if (nativeElem == NATIVE_TYPE_FUNC)
+ {
+ ptdesc->vt = GetVtForIntPtr();
+ goto ExitFunc;
+ }
+ else if (nativeElem != 0 && nativeElem != NATIVE_TYPE_INTF)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ }
+ else if (TypeHandle(pRefdClass).CanCastTo(TypeHandle(MscorlibBinder::GetClass(CLASS__SAFE_HANDLE))))
+ {
+ ptdesc->vt = GetVtForIntPtr();
+ goto ExitFunc;
+ }
+ else if (TypeHandle(pRefdClass).CanCastTo(TypeHandle(MscorlibBinder::GetClass(CLASS__CRITICAL_HANDLE))))
+ {
+ ptdesc->vt = GetVtForIntPtr();
+ goto ExitFunc;
+ }
+
+ if (pRefdClass->HasLayout())
+ {
+ if (nativeElem == NATIVE_TYPE_INTF)
+ {
+ // Classes with layout are exported as structs. Because of this, we can't export field or
+ // parameters of these types marked with [MarshalAs(UnmanagedType.Interface)] as interface
+ // pointers of the actual type. The best we can do is make them IUnknown pointers and
+ // provide a warning.
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_W_LAYOUTCLASS_AS_INTERFACE);
+ ptdesc->vt = VT_UNKNOWN;
+ goto ExitFunc;
+ }
+ else if (!bMethodSig)
+ {
+ // Classes with layout inside structures must be either marked with [MarshalAs(UnmanagedType.Interface)],
+ // [MarshalAs(UnmanagedType.Struct)] or not have any MarshalAs information.
+ if ((nativeElem != 0) && (nativeElem != NATIVE_TYPE_STRUCT))
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // These types are embedded structures so we can treat them as value classes.
+ goto IsStructWithLayout;
+ }
+ else
+ {
+ // Classes with layout as parameters must be either marked with [MarshalAs(UnmanagedType.Interface)]
+ // [MarshalAs(UnmanagedType.LPStruct)] or not have any MarshalAs information.
+ if ((nativeElem != 0) && (nativeElem != NATIVE_TYPE_LPSTRUCT))
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ }
+ }
+
+ // A reference to some non-system-defined/non delegate derived type. Get the reference to the
+ // type, unless it is an imported COM type, in which case, we'll just use
+ // IUnknown.
+ // If the type is not visible from COM then we return S_USEIUNKNOWN.
+ if (!IsTypeVisibleFromCom(TypeHandle(pRefdClass)))
+ hr = S_USEIUNKNOWN;
+ else
+ hr = EEClassToHref(pCTI, pRefdClass, TRUE, &hRef);
+
+ if (hr == S_USEIUNKNOWN)
+ {
+ // Not a known type, so use IUnknown
+ ptdesc->vt = VT_UNKNOWN;
+ goto ExitFunc;
+ }
+
+ // Not a known class, so make this a pointer to . . .
+ ptdesc->vt = VT_PTR;
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // . . . a user defined type . . .
+ ptdesc->lptdesc->vt = VT_USERDEFINED;
+ // . . . based on the token.
+ ptdesc->lptdesc->hreftype = hRef;
+ }
+ else // It's a value type.
+ {
+IsStructWithLayout:
+ // If it is an enum, check the underlying type. All COM enums are 32 bits,
+ // so if the .Net enum is not a 32 bit enum, convert to the underlying type
+ // instead of the enum type.
+ if (pRefdClass->IsEnum())
+ {
+ // Get the element type of the underlying type.
+ CorElementType et = pRefdClass->GetInternalCorElementType();
+ // If it is not a 32-bit type or MarshalAs is specified, convert as the
+ // underlying type.
+ if ((et != ELEMENT_TYPE_I4 && et != ELEMENT_TYPE_U4) ||
+ (nativeElem != 0))
+ {
+ elem = et;
+ goto TryWithElemType;
+ }
+ // Fall through to convert as the enum type.
+ }
+ else
+ {
+ // Value classes must be either marked with [MarshalAs(UnmanagedType.Struct)]
+ // or not have any MarshalAs information.
+ if ((nativeElem != 0) && (nativeElem != NATIVE_TYPE_STRUCT))
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+ }
+
+ // A reference to some non-system-defined type. Get the reference to the
+ // type. Since this is a value class we must get a valid href. Otherwise
+ // we fail the conversion.
+ hr = TokenToHref(pCTI, pMT, tkTypeRef, FALSE, &hRef);
+ if (hr == S_USEIUNKNOWN)
+ {
+ SString sClsName;
+ sClsName.SetUTF8(pclsname);
+
+ LPCWSTR szVCName = sClsName.GetUnicode();
+ if (NAMESPACE_SEPARATOR_WCHAR == *szVCName)
+ szVCName++;
+
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_NONVISIBLEVALUECLASS, szVCName);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Value class is like other UserDefined types, except passed by value, ie
+ // on the stack, instead of by pointer.
+ // . . . a user defined type . . .
+ ptdesc->vt = VT_USERDEFINED;
+ // . . . based on the token.
+ ptdesc->hreftype = hRef;
+ }
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ {
+ SigPointer sig(&pbSig[cbElem]);
+
+ // Retrieve the type handle for the array elements.
+ TypeHandle thElement = sig.GetTypeHandleThrowing(pModule, &emptyTypeContext);
+ _ASSERTE(!thElement.IsNull());
+
+ // Update the index into the managed signature array.
+ IfFailThrow(sig.SkipExactlyOne());
+ cbElem += static_cast<ULONG>(sig.GetPtr() - &pbSig[cbElem]);
+
+ switch (nativeElem)
+ {
+ case 0:
+ case NATIVE_TYPE_SAFEARRAY:
+ {
+ ULONG vtElement = VT_EMPTY;
+
+ // Retrieve the safe array element type.
+ if (cbNativeSig != 0)
+ {
+ cb = CorSigUncompressData(pbNativeSig, &vtElement);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+ }
+
+ ArrayMarshalInfo arrayMarshalInfo(IsExportingAs64Bit() ? amiExport64Bit : amiExport32Bit);
+ MarshalInfo::MarshalScenario ms = bMethodSig ? MarshalInfo::MARSHAL_SCENARIO_COMINTEROP : MarshalInfo::MARSHAL_SCENARIO_FIELD;
+ arrayMarshalInfo.InitForSafeArray(ms, thElement, (VARTYPE)vtElement, fAnsi);
+
+ if (!arrayMarshalInfo.IsValid())
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, arrayMarshalInfo.GetErrorResourceId());
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // TYPEDESC is an array.
+ ptdesc->vt = VT_SAFEARRAY;
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if (ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ArrayToTypeDesc(pCTI, ppool, &arrayMarshalInfo, ptdesc->lptdesc);
+ }
+ break;
+
+ case NATIVE_TYPE_FIXEDARRAY:
+ {
+ ULONG ntElement = NATIVE_TYPE_DEFAULT;
+
+ // NATIVE_TYPE_FIXEDARRAY is only allowed on fields.
+ if (bMethodSig)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Retrieve the size of the fixed array. This is required.
+ if (cbNativeSig == 0)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, IDS_EE_BADMARSHALFIELD_FIXEDARRAY_NOSIZE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ cb = CorSigUncompressData(pbNativeSig, &nativeCount);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+
+ // A size const of 0 isn't supported.
+ if (nativeCount == 0)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, IDS_EE_BADMARSHALFIELD_FIXEDARRAY_ZEROSIZE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Read the optional array sub type if specified.
+ if (cbNativeSig != 0)
+ {
+ cb = CorSigUncompressData(pbNativeSig, &ntElement);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+ }
+
+ ArrayMarshalInfo arrayMarshalInfo(IsExportingAs64Bit() ? amiExport64Bit : amiExport32Bit);
+ arrayMarshalInfo.InitForFixedArray(thElement, (CorNativeType)ntElement, fAnsi);
+
+ if (!arrayMarshalInfo.IsValid())
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, arrayMarshalInfo.GetErrorResourceId());
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Set the data
+ ptdesc->vt = VT_CARRAY;
+ ptdesc->lpadesc = reinterpret_cast<ARRAYDESC*>(ppool->AllocZero(sizeof(ARRAYDESC)));
+ if (ptdesc->lpadesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ArrayToTypeDesc(pCTI, ppool, &arrayMarshalInfo, &ptdesc->lpadesc->tdescElem);
+
+ ptdesc->lpadesc->cDims = 1;
+ ptdesc->lpadesc->rgbounds->cElements = nativeCount;
+ ptdesc->lpadesc->rgbounds->lLbound = 0;
+ }
+ break;
+
+ case NATIVE_TYPE_ARRAY:
+ {
+ ULONG ntElement = NATIVE_TYPE_DEFAULT;
+
+ // NATIVE_TYPE_ARRAY is not allowed on fields.
+ if (!bMethodSig)
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_ARRAY_NEEDS_NT_FIXED);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // Read the optional array sub type if specified.
+ if (cbNativeSig != 0)
+ {
+ cb = CorSigUncompressData(pbNativeSig, &ntElement);
+ pbNativeSig += cb;
+ cbNativeSig -= cb;
+ }
+
+ ArrayMarshalInfo arrayMarshalInfo(IsExportingAs64Bit() ? amiExport64Bit : amiExport32Bit);
+ arrayMarshalInfo.InitForNativeArray(MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, thElement, (CorNativeType)ntElement, fAnsi);
+
+ if (!arrayMarshalInfo.IsValid())
+ {
+ ReportWarning(TLBX_E_BAD_SIGNATURE, arrayMarshalInfo.GetErrorResourceId());
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ ptdesc->vt = VT_PTR;
+ ptdesc->lptdesc = reinterpret_cast<TYPEDESC*>(ppool->AllocZero(sizeof(TYPEDESC)));
+ if(ptdesc->lptdesc == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ ArrayToTypeDesc(pCTI, ppool, &arrayMarshalInfo, ptdesc->lptdesc);
+ }
+ break;
+
+ default:
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_BAD_NATIVETYPE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ }
+
+ // If we are dealing with an ELEMENT_TYPE_ARRAY, we need to eat the array description.
+ if (elem == ELEMENT_TYPE_ARRAY)
+ {
+ // Eat the rank.
+ cbElem += CorSigUncompressData(pbSig+cbElem, &elem);
+
+ // Count of ubounds, ubounds.
+ cbElem += CorSigUncompressData(pbSig+cbElem, &elem);
+ for (i=elem; i>0; --i)
+ cbElem += CorSigUncompressData(pbSig+cbElem, &elem);
+
+ // Count of lbounds, lbounds.
+ cbElem += CorSigUncompressData(pbSig+cbElem, &elem);
+ for (i=elem; i>0; --i)
+ cbElem += CorSigUncompressData(pbSig+cbElem, &elem);
+ }
+
+ break;
+ }
+
+ case ELEMENT_TYPE_TYPEDBYREF: // 0x16
+ ptdesc->vt = VT_VARIANT;
+ break;
+
+ //------------------------------------------
+ // This really should be the commented out
+ // block following.
+ case ELEMENT_TYPE_I: // 0x18,
+ ptdesc->vt = GetVtForIntPtr();
+ break;
+
+ case ELEMENT_TYPE_U: // 0x19,
+ ptdesc->vt = GetVtForUIntPtr();
+ break;
+
+ case ELEMENT_TYPE_CMOD_REQD: // 0x1F // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_UNKNOWN_SIGNATURE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+
+ case ELEMENT_TYPE_SENTINEL:
+ goto TryAgain;
+
+ case ELEMENT_TYPE_CMOD_OPT: // 0x20 // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
+ cb = CorSigUncompressToken(&pbSig[cbElem], &tkTypeRef);
+ cbElem += cb;
+ goto TryAgain;
+
+ case ELEMENT_TYPE_FNPTR:
+ {
+ ptdesc->vt = GetVtForIntPtr();
+
+ // Eat the rest of the signature.
+ SigPointer p(&pbSig[cbElem-1]);
+ IfFailThrow(p.SkipExactlyOne());
+ cbElem += (ULONG)(p.GetPtr() - &pbSig[cbElem]); // Note I didn't use -1 here.
+ break;
+ }
+
+ case ELEMENT_TYPE_GENERICINST:
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_GENERICINST_SIGNATURE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_GENERICPAR_SIGNATURE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ break;
+
+ default:
+ ReportWarning(TLBX_E_BAD_SIGNATURE, TLBX_E_UNKNOWN_SIGNATURE);
+ hr = TLBX_E_BAD_SIGNATURE;
+ goto ExitFunc;
+ break;
+ }
+
+ExitFunc:
+ *pcbElem = cbElem;
+
+ if (hr == S_USEIUNKNOWN)
+ hr = S_OK;
+
+ return hr;
+} // TypeLibExporter::CorSigToTypeDesc
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Get an HREFTYPE for an ITypeInfo, in the context of a ICreateTypeInfo2.
+//*****************************************************************************
+HRESULT TypeLibExporter::TokenToHref(
+ ICreateTypeInfo2 *pCTI, // Typeinfo being created.
+ MethodTable *pMT, // MethodTable with the token.
+ mdToken tk, // The TypeRef to resolve.
+ BOOL bWarnOnUsingIUnknown, // A flag indicating if we should warn on substituting IUnknown.
+ HREFTYPE *pHref) // Put HREFTYPE here.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pMT));
+ PRECONDITION(CheckPointer(pHref));
+ }
+ CONTRACTL_END;
+
+ MethodTable *pRefdClass; // MethodTable object for referenced TypeDef.
+
+ // Get the MethodTable for the referenced class, and see if it is being converted.
+ pRefdClass = LoadClass(pMT->GetModule(), tk);
+
+ // If the type is not visible from COM then we return S_USEIUNKNOWN.
+ if (!IsTypeVisibleFromCom(TypeHandle(pRefdClass)))
+ return S_USEIUNKNOWN;
+
+ return EEClassToHref(pCTI, pRefdClass, bWarnOnUsingIUnknown, pHref);
+} // HRESULT TypeLibExporter::TokenToHref()
+
+//*****************************************************************************
+// Call the resolver to export the typelib for an assembly.
+//*****************************************************************************
+void TypeLibExporter::ExportReferencedAssembly(
+ Assembly *pAssembly)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pAssembly));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ ITypeLib *pTLB = 0; // Exported typelib.
+
+ // Assembly as IP.
+ SafeComHolder<IUnknown> pIAssembly = 0;
+
+ {
+ // Switch to cooperative to get an object ref.
+ GCX_COOP();
+
+ // Invoke the callback to resolve the reference.
+ OBJECTREF orAssembly=0;
+ GCPROTECT_BEGIN(orAssembly)
+ {
+ orAssembly = pAssembly->GetExposedObject();
+
+ pIAssembly = GetComIPFromObjectRef(&orAssembly, MscorlibBinder::GetClass(CLASS__IASSEMBLY));
+ }
+ GCPROTECT_END();
+ }
+
+ IfFailReport(m_pNotify->ResolveRef((IUnknown*)pIAssembly, (IUnknown**)&pTLB));
+
+ // If we got a typelib, store it on the assembly.
+ if (pTLB)
+ pAssembly->SetTypeLib(pTLB);
+} // void TypeLibExporter::ExportReferencedAssembly()
+
+//*****************************************************************************
+// Determine if a class represents a well-known interface, and return that
+// interface (from its real typelib) if it does.
+//*****************************************************************************
+void TypeLibExporter::GetWellKnownInterface(
+ MethodTable *pMT, // MethodTable to check.
+ ITypeInfo **ppTI) // Put ITypeInfo here, if found.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pMT));
+ PRECONDITION(CheckPointer(ppTI));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr; // A result.
+ GUID guid; // The MethodTable guid.
+ WCHAR wzGuid[40]; // Guid in string format.
+ LONG cbGuid; // Size of guid buffer.
+ GUID guidTlb; // The typelib guid.
+ DWORD dwError; // Note: HRESULT_FROM_WIN32 macro evaluates the argument 3x times
+
+
+ HKEYHolder hInterface; // Registry key HKCR/Interface
+ HKEYHolder hGuid; // Registry key of .../{xxx...xxx}
+ HKEYHolder hTlb; // Registry key of .../TypeLib
+
+ // The ITypeLib.
+ SafeComHolder<ITypeLib> pTLB=0;
+
+ // Get the GUID for the class. Will generate from name if no defined GUID,
+ // will also use signatures if interface.
+ pMT->GetGuid(&guid, TRUE);
+
+ GuidToLPWSTR(guid, wzGuid, lengthof(wzGuid));
+
+ // Look up that interface in the registry.
+ dwError = WszRegOpenKeyEx(HKEY_CLASSES_ROOT, W("Interface"),0,KEY_READ, &hInterface);
+ hr = HRESULT_FROM_WIN32(dwError);
+ if (FAILED(hr))
+ return;
+
+ dwError = WszRegOpenKeyEx((HKEY)hInterface, wzGuid, 0, KEY_READ, &hGuid);
+ hr = HRESULT_FROM_WIN32(dwError);
+ if (FAILED(hr))
+ return;
+
+ dwError = WszRegOpenKeyEx((HKEY)hGuid, W("TypeLib"), 0, KEY_READ, &hTlb);
+ hr = HRESULT_FROM_WIN32(dwError);
+ if (FAILED(hr))
+ return;
+
+ cbGuid = sizeof(wzGuid);
+ dwError = WszRegQueryValue((HKEY)hTlb, W(""), wzGuid, &cbGuid);
+ hr = HRESULT_FROM_WIN32(dwError);
+ if (FAILED(hr))
+ return;
+
+ CLSIDFromString(wzGuid, &guidTlb);
+
+ // Retrieve the major and minor version number.
+ USHORT wMajor;
+ USHORT wMinor;
+ Assembly *pAssembly = pMT->GetAssembly();
+
+ hr = GetTypeLibVersionForAssembly(pAssembly,&wMajor, &wMinor);
+ if (SUCCEEDED(hr))
+ {
+ hr = LoadRegTypeLib(guidTlb, wMajor, wMinor, 0, &pTLB);
+ }
+ if (FAILED(hr))
+ {
+ pAssembly->GetVersion(&wMajor, &wMinor, NULL, NULL);
+
+ hr = LoadRegTypeLib(guidTlb, wMajor, wMinor, 0, &pTLB);
+ if (FAILED(hr))
+ {
+ hr = LoadRegTypeLib(guidTlb, -1, -1, 0, &pTLB);
+ if (FAILED(hr))
+ {
+ return;
+ }
+ }
+ }
+
+
+ hr = pTLB->GetTypeInfoOfGuid(guid, ppTI);
+} // void TypeLibExporter::GetWellKnownInterface()
+
+//*****************************************************************************
+// Get an HREFTYPE for an ITypeInfo, in the context of a ICreateTypeInfo2.
+//*****************************************************************************
+HRESULT TypeLibExporter::EEClassToHref( // S_OK or error.
+ ICreateTypeInfo2 *pCTI, // Typeinfo being created.
+ MethodTable *pClass, // The MethodTable * to resolve.
+ BOOL bWarnOnUsingIUnknown, // A flag indicating if we should warn on substituting IUnknown.
+ HREFTYPE *pHref) // Put HREFTYPE here.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pClass));
+ PRECONDITION(CheckPointer(pHref));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr=S_OK; // A result.
+ int bUseIUnknown=false; // Use IUnknown (if so, don't release pTI)?
+ int bUseIUnknownWarned=false; // If true, used IUnknown, but already issued a more specific warning.
+ CExportedTypesInfo sExported; // Cached ICreateTypeInfo pointers.
+ CExportedTypesInfo *pExported; // Pointer to found or new cached pointers.
+ CHrefOfClassHashKey sLookup; // Hash structure to lookup.
+ CHrefOfClassHashKey *pFound; // Found structure.
+ bool bImportedAssembly; // The assembly containing pClass is imported.
+ bool bForceResolveCallback; // Type library resolution should always be handled by caller first.
+
+ // A different typeinfo; default for pTI.
+ SafeComHolder<ITypeInfo> pTIDef=0;
+
+ // A TypeInfo; maybe for TypeDef, maybe for TypeRef.
+ SafeComHolder<ITypeInfo> pTI=0;
+
+
+ // See if we already know this MethodTable' href.
+ sLookup.pClass = pClass;
+ if ((pFound=m_HrefOfClassHash.Find(&sLookup)) != NULL)
+ {
+ *pHref = pFound->href;
+ if (*pHref == m_hIUnknown)
+ return S_USEIUNKNOWN;
+ return S_OK;
+ }
+
+ // See if the class is in the export list.
+ sExported.pClass = pClass;
+ pExported = m_Exports.Find(&sExported);
+
+ // If not in the exported assembly, possibly it was injected?
+ if (pExported == 0)
+ {
+ pExported = m_InjectedExports.Find(&sExported);
+ }
+
+ // Is there an export for this class?
+ if (pExported)
+ {
+ // Yes, For interfaces and value types (and enums), just use the typeinfo.
+ if (pClass->IsValueType() || pClass->IsEnum() || pClass->HasLayout())
+ {
+ // No default interface, so use the class itself.
+ if (pExported->pCTI)
+ IfFailReport(SafeQueryInterface(pExported->pCTI, IID_ITypeInfo, (IUnknown**)&pTI));
+ }
+ else
+ if (!pClass->IsInterface())
+ {
+ // If there is an explicit default interface, get the class for it.
+ TypeHandle hndDefItfClass;
+ DefaultInterfaceType DefItfType;
+ DefItfType = GetDefaultInterfaceForClassWrapper(TypeHandle(pClass), &hndDefItfClass);
+ switch (DefItfType)
+ {
+ case DefaultInterfaceType_Explicit:
+ {
+ _ASSERTE(!hndDefItfClass.IsNull());
+
+ // Recurse to get the href for the default interface class.
+ hr = EEClassToHref(pCTI, hndDefItfClass.GetMethodTable(), bWarnOnUsingIUnknown, pHref);
+ // Done. Note that the previous call will have cached the href for
+ // the default interface class. As this function exits, it will
+ // also cache the SAME href for this class.
+ goto ErrExit;
+ }
+
+ case DefaultInterfaceType_AutoDispatch:
+ case DefaultInterfaceType_AutoDual:
+ {
+ _ASSERTE(!hndDefItfClass.IsNull());
+
+ if (hndDefItfClass.GetMethodTable() != pClass)
+ {
+ // Recurse to get the href for the default interface class.
+ hr = EEClassToHref(pCTI, hndDefItfClass.GetMethodTable(), bWarnOnUsingIUnknown, pHref);
+ // Done. Note that the previous call will have cached the href for
+ // the default interface class. As this function exits, it will
+ // also cache the SAME href for this class.
+ goto ErrExit;
+ }
+
+ // Return the class interface.
+ _ASSERTE(pExported->pCTIClassItf);
+ IfFailReport(SafeQueryInterface(pExported->pCTIClassItf, IID_ITypeInfo, (IUnknown**)&pTI));
+ break;
+ }
+
+ case DefaultInterfaceType_IUnknown:
+ case DefaultInterfaceType_BaseComClass:
+ {
+ pTI = m_pIUnknown;
+ bUseIUnknown=true;
+ SafeAddRef(pTI);
+ break;
+ }
+
+ default:
+ {
+ _ASSERTE(!"Invalid default interface type!");
+ hr = E_FAIL;
+ break;
+ }
+ }
+ }
+ else
+ { // This is an interface, so use the typeinfo for the interface, if there is one.
+ if (pExported->pCTI)
+ IfFailReport(SafeQueryInterface(pExported->pCTI, IID_ITypeInfo, (IUnknown**)&pTI));
+ }
+
+ if ((IUnknown*)pTI == 0)
+ {
+ // This is a class from the module/assembly, yet it is not being exported.
+
+ // Whatever happens, the result is OK.
+ hr = S_OK;
+
+ if (pClass->IsComImport())
+ {
+ // If it is an imported type, get an href to it.
+ GetWellKnownInterface(pClass, &pTI);
+ }
+
+ // If still didn't get a TypeInfo, use IUnknown.
+ if ((IUnknown*)pTI == 0)
+ {
+ pTI = m_pIUnknown;
+ bUseIUnknown=true;
+ SafeAddRef(pTI);
+ }
+ }
+ }
+ else
+ { // Not local. Try to get from the class' module's typelib.
+ // If the caller wants to get a chance to resolve type library references themselves (before we go probing the assembly),
+ // we'll skip the next step and go directly to the notify sink callback.
+ bForceResolveCallback = (m_flags & TlbExporter_CallerResolvedReferences) != 0;
+ if (!bForceResolveCallback)
+ hr = GetITypeInfoForEEClass(pClass, &pTI, false/* interface, not coclass */, false/* do not create */, m_flags);
+
+ // If getting the typeinfo from the class itself failed, there are
+ // several possibilities:
+ // - typelib didn't exist, and couldn't be created.
+ // - typelib did exist, but didn't contain the typeinfo.
+ // We can create a local (to the exported typelib) copy of the
+ // typeinfo, and get a reference to that.
+ // However, we don't want to export the whole tree into this typelib,
+ // so we only create the typeinfo if the typelib existed but the
+ // typeinfo wasn't found and the assembly is not an imported assembly.
+ bImportedAssembly = pClass->GetAssembly()->IsImportedFromTypeLib();
+
+ if (bForceResolveCallback || (FAILED(hr) && hr != TYPE_E_ELEMENTNOTFOUND && !bImportedAssembly))
+ {
+ // Invoke the callback to resolve the reference.
+
+ Assembly *pAssembly = pClass->GetAssembly();
+
+ ExportReferencedAssembly(pAssembly);
+
+ hr = GetITypeInfoForEEClass(pClass, &pTI, false/* interface, not coclass */, false/* do not create */, m_flags);
+ }
+
+ if (hr == TYPE_E_ELEMENTNOTFOUND)
+ {
+ if (pClass->IsComImport())
+ {
+ // If it is an imported type, get an href to it.
+
+ // Whatever happens, the result is OK.
+ hr = S_OK;
+
+ GetWellKnownInterface(pClass, &pTI);
+
+ // If still didn't get a TypeInfo, use IUnknown.
+ if ((IUnknown*)pTI == 0)
+ {
+ pTI = m_pIUnknown;
+ bUseIUnknown=true;
+ SafeAddRef(pTI);
+ }
+ }
+ else
+ {
+ // Convert the single typedef from the other scope.
+ ConvertOneTypeDef(pClass);
+
+ // Now that the type has been injected, recurse to let the default-interface code run.
+ hr = EEClassToHref(pCTI, pClass, bWarnOnUsingIUnknown, pHref);
+
+ // This class should already have been cached by the recursive call. Don't want to add
+ // it again.
+ goto ErrExit2;
+ }
+ }
+ else if (FAILED(hr))
+ {
+ DefineFullyQualifiedNameForClassWOnStack();
+ LPCWSTR szName = GetFullyQualifiedNameForClassNestedAwareW(pClass);
+ if (hr == TLBX_W_LIBNOTREGISTERED)
+ {
+ // The imported typelib is not registered on this machine. Give a warning, and substitute IUnknown.
+ ReportEvent(NOTIF_CONVERTWARNING, hr, szName, (LPCWSTR) pClass->GetAssembly()->GetManifestModule()->GetPath());
+ hr = S_OK;
+ pTI = m_pIUnknown;
+ bUseIUnknown = true;
+ SafeAddRef(pTI);
+ bUseIUnknownWarned = true;
+ }
+ else if (hr == TLBX_E_CANTLOADLIBRARY)
+ {
+ // The imported typelib is registered, but can't be loaded. Corrupt? Missing?
+ InternalThrowHRWithContext(TLBX_E_CANTLOADLIBRARY, szName, (LPCWSTR) pClass->GetAssembly()->GetManifestModule()->GetPath());
+ }
+ IfFailReport(hr);
+ }
+ }
+
+ // Make sure we could resolve the typeinfo.
+ if (!(IUnknown*)pTI)
+ IfFailReport(TYPE_E_ELEMENTNOTFOUND);
+
+ // Assert that the containing typelib for pContainer is the typelib being created.
+#if defined(_DEBUG)
+ {
+ SafeComHolder<ITypeInfo> pTI=0;
+ SafeComHolder<ITypeLib> pTL=0;
+ SafeComHolder<ITypeLib> pTLMe=0;
+ UINT ix;
+ SafeQueryInterface(pCTI, IID_ITypeInfo, (IUnknown**)&pTI);
+ SafeQueryInterface(m_pICreateTLB, IID_ITypeLib, (IUnknown**)&pTLMe);
+ pTI->GetContainingTypeLib(&pTL, &ix);
+ _ASSERTE(pTL == pTLMe);
+ }
+#endif
+
+ // If there is an ITypeInfo, convert to HREFTYPE.
+ if ((IUnknown*)pTI)
+ {
+ if ((IUnknown*)pTI != m_pIUnknown)
+ {
+ // Resolve to default.
+ if (pTIDef)
+ hr = S_OK; // Already have default.
+ else
+ {
+ // TypeLib API has a issue (sort of by design):
+ // Before a type (and its dependencies) is completely created (all members added),
+ // if you call Layout(), or anything that will lead to Layout(), such as ITypeInfo::GetTypeAttr
+ // it will give the type an incorrect size. Ideally TypeLib API should fail in this case.
+ // Anyway, we only need to avoid calling Layout() directly or indirectly until we have
+ // completely created all types.
+ // In this case, we are calling ITypeInfo::GetTypeAttr() in the function below, which is only
+ // needed for coclasses. Fortunately, coclass doesn't have a size problem, as it don't have any members
+ // So, we skip calling GetDefaultInterfaceForCoclass unless the class is an coclass.
+ if (TKindFromClass(pClass) == TKIND_COCLASS)
+ IfFailReport(GetDefaultInterfaceForCoclass(pTI, &pTIDef));
+ else
+ hr = S_FALSE;
+ }
+
+ if (hr == S_OK)
+ hr = pCTI->AddRefTypeInfo(pTIDef, pHref);
+ else
+ hr = pCTI->AddRefTypeInfo(pTI, pHref);
+ }
+ else
+ { // pTI == m_pIUnknown
+ if (m_hIUnknown == -1)
+ hr = pCTI->AddRefTypeInfo(pTI, &m_hIUnknown);
+ *pHref = m_hIUnknown;
+ }
+ }
+
+ErrExit:
+ // If we got the href...
+ if (hr == S_OK)
+ {
+ // Save for later use.
+ if ( NULL == (pFound=m_HrefOfClassHash.Add(&sLookup)))
+ IfFailReport(E_OUTOFMEMORY);
+
+ pFound->pClass = pClass;
+ pFound->href = *pHref;
+ }
+
+ // If substituting IUnknown, give a warning.
+ if (hr == S_OK && bUseIUnknown && bWarnOnUsingIUnknown && !bUseIUnknownWarned)
+ {
+ DefineFullyQualifiedNameForClassWOnStack();
+ LPCWSTR szName = GetFullyQualifiedNameForClassNestedAwareW(pClass);
+ ReportWarning(S_OK, TLBX_I_USEIUNKNOWN, szName);
+ }
+
+ErrExit2:
+ if (hr == S_OK && bUseIUnknown)
+ hr = S_USEIUNKNOWN;
+
+ return hr;
+} // HRESULT TypeLibExporter::EEClassToHref()
+
+//*****************************************************************************
+// Retrieve an HRef to the a type defined in StdOle.
+//*****************************************************************************
+void TypeLibExporter::StdOleTypeToHRef(ICreateTypeInfo2 *pCTI, REFGUID rGuid, HREFTYPE *pHref)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pCTI));
+ PRECONDITION(CheckPointer(pHref));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ SafeComHolder<ITypeLib> pITLB = NULL;
+ SafeComHolder<ITypeInfo> pITI = NULL;
+ MEMBERID MemID = 0;
+ USHORT cFound = 0;
+
+ IfFailReport(LoadRegTypeLib(LIBID_STDOLE2, -1, -1, 0, &pITLB));
+ IfFailReport(pITLB->GetTypeInfoOfGuid(rGuid, &pITI));
+ IfFailReport(pCTI->AddRefTypeInfo(pITI, pHref));
+} // void TypeLibExporter::ColorToHRef()
+
+//*****************************************************************************
+// Given a TypeDef's flags, determine the proper TYPEKIND.
+//*****************************************************************************
+TYPEKIND TypeLibExporter::TKindFromClass(
+ MethodTable *pClass) // MethodTable.
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pClass));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+ ULONG ulIface = ifDual; // Is this interface [dual], IUnknown, or DISPINTERFACE.
+
+ if (pClass->IsInterface())
+ {
+ // IDispatch or IUnknown derived?
+ IfFailReport(pClass->GetMDImport()->GetIfaceTypeOfTypeDef(pClass->GetCl(), &ulIface));
+ if (ulIface == ifDispatch)
+ return TKIND_DISPATCH;
+
+ return TKIND_INTERFACE;
+ }
+
+ if (pClass->IsEnum())
+ return TKIND_ENUM;
+
+ if (pClass->IsValueType() || pClass->HasLayout())
+ {
+ TYPEKIND tkResult=TKIND_RECORD; // The resulting typekind.
+ mdFieldDef fd; // A Field def.
+ ULONG cFD; // Count of fields.
+ ULONG iFD=0; // Loop control.
+ ULONG ulOffset; // Field offset.
+ bool bNonZero=false; // Found any non-zero?
+ MD_CLASS_LAYOUT sLayout; // For enumerating layouts.
+
+ // To enum fields.
+ HENUMInternalHolder eFDi(pClass->GetMDImport());
+ eFDi.EnumInit(mdtFieldDef, pClass->GetCl());
+
+ // Get an enumerator for the FieldDefs in the TypeDef. Only need the counts.
+ cFD = pClass->GetMDImport()->EnumGetCount(&eFDi);
+
+ // Get an enumerator for the class layout.
+ IfFailReport(pClass->GetMDImport()->GetClassLayoutInit(pClass->GetCl(), &sLayout));
+
+ // Enumerate the layout.
+ while (pClass->GetMDImport()->GetClassLayoutNext(&sLayout, &fd, &ulOffset) == S_OK)
+ {
+ if (ulOffset != 0)
+ {
+ bNonZero = true;
+ break;
+ }
+ ++iFD;
+ }
+
+ // If there were fields, all had layout, and all layouts are zero, call it a union.
+ if (cFD > 0 && iFD == cFD && !bNonZero)
+ tkResult = TKIND_UNION;
+
+ return tkResult;
+ }
+
+ return TKIND_COCLASS;
+} // TYPEKIND TypeLibExporter::TKindFromClass()
+
+//*****************************************************************************
+// Generate a HREFTYPE in the output TypeLib for a TypeInfo.
+//*****************************************************************************
+void TypeLibExporter::GetRefTypeInfo(
+ ICreateTypeInfo2 *pContainer,
+ ITypeInfo *pReferenced,
+ HREFTYPE *pHref)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pContainer));
+ PRECONDITION(CheckPointer(pReferenced));
+ PRECONDITION(CheckPointer(pHref));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr; // A result.
+ CHrefOfTIHashKey sLookup; // Hash structure to lookup.
+ CHrefOfTIHashKey *pFound; // Found structure.
+
+ // See if we already know this TypeInfo.
+ sLookup.pITI = pReferenced;
+ if ((pFound=m_HrefHash.Find(&sLookup)) != NULL)
+ {
+ *pHref = pFound->href;
+ return;
+ }
+
+ // Assert that the containing typelib for pContainer is the typelib being created.
+#if defined(_DEBUG)
+ {
+ SafeComHolder<ITypeInfo> pTI=0;
+ SafeComHolder<ITypeLib> pTL=0;
+ SafeComHolder<ITypeLib> pTLMe=0;
+ UINT ix;
+
+ SafeQueryInterface(pContainer, IID_ITypeInfo, (IUnknown**)&pTI);
+ SafeQueryInterface(m_pICreateTLB, IID_ITypeLib, (IUnknown**)&pTLMe);
+ pTI->GetContainingTypeLib(&pTL, &ix);
+ _ASSERTE(pTL == pTLMe);
+ }
+#endif
+
+ // Haven't seen it -- add the href.
+ // NOTE: This code assumes that hreftypes are per-typelib.
+ IfFailReport(pContainer->AddRefTypeInfo(pReferenced, pHref));
+
+ // Save for later use.
+ pFound=m_HrefHash.Add(&sLookup);
+ if (pFound == NULL)
+ IfFailReport(E_OUTOFMEMORY);
+
+ // Prefix can't tell that IfFailReport will actually throw an exception if pFound is NULL so
+ // let's tell it explicitly that if we reach this point pFound will not be NULL.
+ PREFIX_ASSUME(pFound != NULL);
+ pFound->pITI = pReferenced;
+ pFound->href = *pHref;
+ pReferenced->AddRef();
+} // HRESULT TypeLibExporter::GetRefTypeInfo()
+
+//*****************************************************************************
+// Implementation of a hashed ITypeInfo to HREFTYPE association.
+//*****************************************************************************
+void TypeLibExporter::CHrefOfTIHash::Clear()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ CHrefOfTIHashKey *p;
+ for (p=GetFirst(); p; p=GetNext(p))
+ {
+ SafeRelease(p->pITI);
+ }
+
+ CClosedHash<class CHrefOfTIHashKey>::Clear();
+} // void TypeLibExporter::CHrefOfTIHash::Clear()
+
+unsigned int TypeLibExporter::CHrefOfTIHash::Hash(const CHrefOfTIHashKey *pData)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifndef _WIN64
+ // The pointers are at least 4-byte aligned, so ignore bottom two bits.
+ return (unsigned int) (((size_t)(pData->pITI))>>2);
+#else
+ // @TODO IA64: Is this a good hashing mechanism on IA64?
+ return (unsigned int) (((size_t)(pData->pITI))>>3);
+#endif
+} // unsigned long TypeLibExporter::CHrefOfTIHash::Hash()
+
+unsigned int TypeLibExporter::CHrefOfTIHash::Compare(const CHrefOfTIHashKey *p1, CHrefOfTIHashKey *p2)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (p1->pITI == p2->pITI)
+ return (0);
+ return (1);
+} // unsigned long TypeLibExporter::CHrefOfTIHash::Compare()
+
+TypeLibExporter::CHrefOfTIHash::ELEMENTSTATUS TypeLibExporter::CHrefOfTIHash::Status(CHrefOfTIHashKey *p)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (p->pITI == reinterpret_cast<ITypeInfo*>(FREE))
+ return (FREE);
+ if (p->pITI == reinterpret_cast<ITypeInfo*>(DELETED))
+ return (DELETED);
+ return (USED);
+} // TypeLibExporter::CHrefOfTIHash::ELEMENTSTATUS TypeLibExporter::CHrefOfTIHash::Status()
+
+void TypeLibExporter::CHrefOfTIHash::SetStatus(CHrefOfTIHashKey *p, ELEMENTSTATUS s)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ p->pITI = reinterpret_cast<ITypeInfo*>(s);
+} // void TypeLibExporter::CHrefOfTIHash::SetStatus()
+
+void *TypeLibExporter::CHrefOfTIHash::GetKey(CHrefOfTIHashKey *p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return &p->pITI;
+} // void *TypeLibExporter::CHrefOfTIHash::GetKey()
+
+
+//*****************************************************************************
+// Implementation of a hashed MethodTable* to HREFTYPE association.
+//*****************************************************************************
+void TypeLibExporter::CHrefOfClassHash::Clear()
+{
+ WRAPPER_NO_CONTRACT;
+ CClosedHash<class CHrefOfClassHashKey>::Clear();
+} // void TypeLibExporter::CHrefOfClassHash::Clear()
+
+unsigned int TypeLibExporter::CHrefOfClassHash::Hash(const CHrefOfClassHashKey *pData)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifndef _WIN64
+ // Tbe pointers are at least 4-byte aligned, so ignore bottom two bits.
+ return (unsigned int) (((size_t)(pData->pClass))>>2);
+#else
+ // @TODO IA64: Is this a good hashing mechanism on IA64?
+ return (unsigned int) (((size_t)(pData->pClass))>>3);
+#endif
+} // unsigned long TypeLibExporter::CHrefOfClassHash::Hash()
+
+unsigned int TypeLibExporter::CHrefOfClassHash::Compare(const CHrefOfClassHashKey *p1, CHrefOfClassHashKey *p2)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (p1->pClass == p2->pClass)
+ return (0);
+ return (1);
+} // unsigned long TypeLibExporter::CHrefOfClassHash::Compare()
+
+TypeLibExporter::CHrefOfClassHash::ELEMENTSTATUS TypeLibExporter::CHrefOfClassHash::Status(CHrefOfClassHashKey *p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (p->pClass == reinterpret_cast<MethodTable*>(FREE))
+ return (FREE);
+ if (p->pClass == reinterpret_cast<MethodTable*>(DELETED))
+ return (DELETED);
+ return (USED);
+} // TypeLibExporter::CHrefOfClassHash::ELEMENTSTATUS TypeLibExporter::CHrefOfClassHash::Status()
+
+void TypeLibExporter::CHrefOfClassHash::SetStatus(CHrefOfClassHashKey *p, ELEMENTSTATUS s)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ p->pClass = reinterpret_cast<MethodTable*>(s);
+} // void TypeLibExporter::CHrefOfClassHash::SetStatus()
+
+void *TypeLibExporter::CHrefOfClassHash::GetKey(CHrefOfClassHashKey *p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return &p->pClass;
+} // void *TypeLibExporter::CHrefOfClassHash::GetKey()
+
+
+//*****************************************************************************
+// Implementation of a hashed MethodTable* to conversion information association.
+//*****************************************************************************
+void TypeLibExporter::CExportedTypesHash::Clear()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Iterate over entries and free pointers.
+ CExportedTypesInfo *pData;
+ pData = GetFirst();
+ while (pData)
+ {
+ SetStatus(pData, DELETED);
+ pData = GetNext(pData);
+ }
+
+ CClosedHash<class CExportedTypesInfo>::Clear();
+} // void TypeLibExporter::CExportedTypesHash::Clear()
+
+unsigned int TypeLibExporter::CExportedTypesHash::Hash(const CExportedTypesInfo *pData)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifndef _WIN64
+ // Tbe pointers are at least 4-byte aligned, so ignore bottom two bits.
+ return (unsigned int) (((size_t)(pData->pClass))>>2);
+#else
+ // @TODO IA64: Is this a good hashing mechanism on IA64?
+ return (unsigned int) (((size_t)(pData->pClass))>>3);
+#endif
+} // unsigned long TypeLibExporter::CExportedTypesHash::Hash()
+
+unsigned int TypeLibExporter::CExportedTypesHash::Compare(const CExportedTypesInfo *p1, CExportedTypesInfo *p2)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (p1->pClass == p2->pClass)
+ return (0);
+ return (1);
+} // unsigned long TypeLibExporter::CExportedTypesHash::Compare()
+
+TypeLibExporter::CExportedTypesHash::ELEMENTSTATUS TypeLibExporter::CExportedTypesHash::Status(CExportedTypesInfo *p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (p->pClass == reinterpret_cast<MethodTable*>(FREE))
+ return (FREE);
+ if (p->pClass == reinterpret_cast<MethodTable*>(DELETED))
+ return (DELETED);
+ return (USED);
+} // TypeLibExporter::CExportedTypesHash::ELEMENTSTATUS TypeLibExporter::CExportedTypesHash::Status()
+
+void TypeLibExporter::CExportedTypesHash::SetStatus(CExportedTypesInfo *p, ELEMENTSTATUS s)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If deleting a used entry, free the pointers.
+ if (s == DELETED && Status(p) == USED)
+ {
+ if (p->pCTI) p->pCTI->Release(), p->pCTI=0;
+ if (p->pCTIClassItf) p->pCTIClassItf->Release(), p->pCTIClassItf=0;
+ }
+ p->pClass = reinterpret_cast<MethodTable*>(s);
+} // void TypeLibExporter::CExportedTypesHash::SetStatus()
+
+void *TypeLibExporter::CExportedTypesHash::GetKey(CExportedTypesInfo *p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return &p->pClass;
+} // void *TypeLibExporter::CExportedTypesHash::GetKey()
+
+void TypeLibExporter::CExportedTypesHash::InitArray()
+{
+ STANDARD_VM_CONTRACT;
+
+ // For iterating the entries.
+ CExportedTypesInfo *pData = 0;
+
+ // Make room for the data.
+ m_iCount = 0;
+ m_Array = new CExportedTypesInfo*[Base::Count()];
+
+ // Fill the array.
+ pData = GetFirst();
+ while (pData)
+ {
+ m_Array[m_iCount++] = pData;
+ pData = GetNext(pData);
+ }
+} // void TypeLibExporter::CExportedTypesHash::InitArray()
+
+void TypeLibExporter::CExportedTypesHash::UpdateArray()
+{
+ STANDARD_VM_CONTRACT;
+
+ // For iterating the entries.
+ CExportedTypesInfo *pData = 0;
+
+ // Clear the old data.
+ if (m_Array)
+ delete[] m_Array;
+
+ // Make room for the data.
+ m_iCount = 0;
+ m_Array = new CExportedTypesInfo*[Base::Count()];
+
+ // Fill the array.
+ pData = GetFirst();
+ while (pData)
+ {
+ m_Array[m_iCount++] = pData;
+ pData = GetNext(pData);
+ }
+} // void TypeLibExporter::CExportedTypesHash::UpdateArray()
+
+void TypeLibExporter::CExportedTypesHash::SortByName()
+{
+ WRAPPER_NO_CONTRACT;
+
+ CSortByName sorter(m_Array, (int)m_iCount);
+ sorter.Sort();
+} // void TypeLibExporter::CExportedTypesHash::SortByName()
+
+void TypeLibExporter::CExportedTypesHash::SortByToken()
+{
+ WRAPPER_NO_CONTRACT;
+
+ CSortByToken sorter(m_Array, (int)m_iCount);
+ sorter.Sort();
+} // void TypeLibExporter::CExportedTypesHash::SortByToken()
+
+int TypeLibExporter::CExportedTypesHash::CSortByToken::Compare(
+ CExportedTypesInfo **p1,
+ CExportedTypesInfo **p2)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ MethodTable *pC1 = (*p1)->pClass;
+ MethodTable *pC2 = (*p2)->pClass;
+ // Compare scopes.
+ if (pC1->GetMDImport() < pC2->GetMDImport())
+ return -1;
+ if (pC1->GetMDImport() > pC2->GetMDImport())
+ return 1;
+ // Same scopes, compare tokens.
+ if (pC1->GetTypeDefRid() < pC2->GetTypeDefRid())
+ return -1;
+ if (pC1->GetTypeDefRid() > pC2->GetTypeDefRid())
+ return 1;
+ // Hmmm. Same class.
+ return 0;
+} // int TypeLibExporter::CExportedTypesHash::CSortByToken::Compare()
+
+int TypeLibExporter::CExportedTypesHash::CSortByName::Compare(
+ CExportedTypesInfo **p1,
+ CExportedTypesInfo **p2)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(p1));
+ PRECONDITION(CheckPointer(p2));
+ PRECONDITION(CheckPointer(*p1));
+ PRECONDITION(CheckPointer(*p2));
+ }
+ CONTRACTL_END;
+
+ int iRslt; // A compare result.
+
+ MethodTable *pC1 = (*p1)->pClass;
+ MethodTable *pC2 = (*p2)->pClass;
+
+ // Ignore scopes. Need to see name collisions across scopes.
+ // Same scopes, compare names.
+ LPCSTR pName1, pNS1;
+ LPCSTR pName2, pNS2;
+ IfFailThrow(pC1->GetMDImport()->GetNameOfTypeDef(pC1->GetCl(), &pName1, &pNS1));
+ IfFailThrow(pC2->GetMDImport()->GetNameOfTypeDef(pC2->GetCl(), &pName2, &pNS2));
+
+ // Compare case-insensitive, because we want different capitalizations to sort together.
+ SString sName1(SString::Utf8, pName1);
+ SString sName2(SString::Utf8, pName2);
+
+ iRslt = sName1.CompareCaseInsensitive(sName2);
+ if (iRslt)
+ return iRslt;
+
+ // If names are spelled the same, ignoring capitalization, sort by namespace.
+ // We will attempt to use namespace for disambiguation.
+ SString sNS1(SString::Utf8, pNS1);
+ SString sNS2(SString::Utf8, pNS2);
+
+ iRslt = sNS1.CompareCaseInsensitive(sNS2);
+ return iRslt;
+} // int TypeLibExporter::CExportedTypesHash::CSortByName::Compare()
+