Enable reflection load ComImport assembly and Type.IsComObjectType (#16943)
authorLuqun Lou <luqunl@users.noreply.github.com>
Tue, 27 Mar 2018 21:20:09 +0000 (14:20 -0700)
committerJan Kotas <jkotas@microsoft.com>
Tue, 27 Mar 2018 21:20:09 +0000 (14:20 -0700)
* Enable reflection load ComImport assembly and Type.IsComObjectType

* Update Enable reflection load ComImport assembly

19 files changed:
src/dlls/mscorrc/mscorrc.rc
src/dlls/mscorrc/resource.h
src/vm/ecall.cpp
src/vm/interoputil.cpp
src/vm/interoputil.h
src/vm/methodtable.cpp
src/vm/methodtable.h
src/vm/methodtablebuilder.cpp
src/vm/methodtablebuilder.h
src/vm/runtimehandles.cpp
tests/src/Interop/CMakeLists.txt
tests/src/Interop/ClassicCOM/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/ClassicCOM/COMLib.cs [new file with mode: 0644]
tests/src/Interop/ClassicCOM/COMLib.csproj [new file with mode: 0644]
tests/src/Interop/ClassicCOM/COMLib2.cs [new file with mode: 0644]
tests/src/Interop/ClassicCOM/COMLib2.csproj [new file with mode: 0644]
tests/src/Interop/ClassicCOM/ClassicCOMNative.cpp [new file with mode: 0644]
tests/src/Interop/ClassicCOM/ClassicCOMUnitTest.cs [new file with mode: 0644]
tests/src/Interop/ClassicCOM/ClassicCOMUnitTest.csproj [new file with mode: 0644]

index 00a068c..61f5c9a 100644 (file)
@@ -2065,6 +2065,7 @@ BEGIN
     IDS_EE_BADMARSHAL_TYPE_ASANYA                     "Marshalling arbitrary types is not supported"
     IDS_EE_BADMARSHAL_TYPE_IDISPATCH                  "Marshalling as IDispatch is not supported"
     IDS_EE_ERROR_IDISPATCH                            "IDispatch and IDispatchEx are not supported"
+    IDS_EE_ERROR_COM                                  "COM is not supported"
 END
 
 STRINGTABLE DISCARDABLE 
index 0fbbf1c..b8e1b2b 100644 (file)
 #define IDS_EE_NDIRECT_LOADLIB_LINUX               0x263e
 #define IDS_EE_NDIRECT_LOADLIB_MAC                 0x263f
 #define IDS_EE_NDIRECT_GETPROCADDRESS_UNIX         0x2640
+#define IDS_EE_ERROR_COM                           0x2641
index dacec45..3812ff1 100644 (file)
@@ -320,10 +320,14 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /*
         return GetFCallImpl(MscorlibBinder::GetMethod(METHOD__DELEGATE__CONSTRUCT_DELEGATE));
     }
 
-#ifdef FEATURE_COMINTEROP
     // COM imported classes have special constructors
-    if (pMT->IsComObjectType() && pMT != g_pBaseCOMObject && pMT != g_pBaseRuntimeClass)
+    if (pMT->IsComObjectType() 
+#ifdef FEATURE_COMINTEROP
+        && pMT != g_pBaseCOMObject && pMT != g_pBaseRuntimeClass
+#endif // FEATURE_COMINTEROP
+    )
     {
+#ifdef FEATURE_COMINTEROP
         if (pfSharedOrDynamicFCallImpl)
             *pfSharedOrDynamicFCallImpl = TRUE;
 
@@ -333,8 +337,10 @@ PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /*
 
         // FCComCtor does not need to be in the fcall hashtable since it does not erect frame.
         return GetEEFuncEntryPoint(FCComCtor);
-    }
+#else
+        COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_COM);
 #endif // FEATURE_COMINTEROP
+    }
 
     if (!pMD->GetModule()->IsSystem())
         COMPlusThrow(kSecurityException, BFA_ECALLS_MUST_BE_IN_SYS_MOD);
index d48163c..630706b 100644 (file)
@@ -999,6 +999,7 @@ void GetCultureInfoForLCID(LCID lcid, OBJECTREF *pCultureObj)
     COMPlusThrow(kNotSupportedException);
 #endif
 }
+
 #endif // CROSSGEN_COMPILE
 
 //---------------------------------------------------------------------------
@@ -1618,6 +1619,55 @@ BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT)
     }
 }
 
+// Returns TRUE iff the argument represents the "__ComObject" type or
+// any type derived from it (i.e. typelib-imported RCWs).
+BOOL IsComWrapperClass(TypeHandle type)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+    
+    MethodTable* pMT = type.GetMethodTable();
+    if (pMT == NULL)
+        return FALSE;
+        
+    return pMT->IsComObjectType();
+}
+
+// Returns TRUE iff the argument represents the "__ComObject" type.
+BOOL IsComObjectClass(TypeHandle type)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        SO_TOLERANT;
+    }
+    CONTRACTL_END;
+
+#ifdef FEATURE_COMINTEROP
+    if (!type.IsTypeDesc())
+    {
+        MethodTable *pMT = type.AsMethodTable();
+
+        if (pMT->IsComObjectType())
+        {
+            // May be __ComObject or typed RCW. __ComObject must have already been loaded
+            // if we see an MT marked like this so calling the *NoInit method is sufficient.
+
+            return pMT == g_pBaseCOMObject;
+        }
+    }
+#endif
+
+    return FALSE;
+}
+
 VOID
 ReadBestFitCustomAttribute(MethodDesc* pMD, BOOL* BestFit, BOOL* ThrowOnUnmappableChar)
 {
@@ -6204,53 +6254,6 @@ MethodTable *WinRTDelegateRedirector::GetWinRTTypeForRedirectedDelegateIndex(Win
     }
 }
 
-// Returns TRUE iff the argument represents the "__ComObject" type or
-// any type derived from it (i.e. typelib-imported RCWs).
-BOOL IsComWrapperClass(TypeHandle type)
-{
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_ANY;
-    }
-    CONTRACTL_END;
-    
-    MethodTable* pMT = type.GetMethodTable();
-    if (pMT == NULL)
-        return FALSE;
-        
-    return pMT->IsComObjectType();
-}
-
-// Returns TRUE iff the argument represents the "__ComObject" type.
-BOOL IsComObjectClass(TypeHandle type)
-{
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_ANY;
-        SO_TOLERANT;
-    }
-    CONTRACTL_END;
-
-    if (!type.IsTypeDesc())
-    {
-        MethodTable *pMT = type.AsMethodTable();
-
-        if (pMT->IsComObjectType())
-        {
-            // May be __ComObject or typed RCW. __ComObject must have already been loaded
-            // if we see an MT marked like this so calling the *NoInit method is sufficient.
-            return (pMT == g_pBaseCOMObject);
-        }
-    }
-
-    return FALSE;
-}
-
-
 #ifndef CROSSGEN_COMPILE
 
 #ifdef _DEBUG
index 6762c80..a689624 100644 (file)
@@ -106,6 +106,13 @@ ULONG SafeReleasePreemp(IUnknown* pUnk, RCW* pRCW = NULL);
 // Determines if a COM object can be cast to the specified type.
 BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT);
 
+// includes Types which hold a "ComObject" class
+// and types which are imported through typelib
+BOOL IsComWrapperClass(TypeHandle type);
+
+// includes Type which hold a "__ComObject" class
+BOOL IsComObjectClass(TypeHandle type);
+
 //---------------------------------------------------------
 // Read the BestFit custom attribute info from 
 // both assembly level and interface level
@@ -491,14 +498,6 @@ public:
     static void ComputeGuidForGenericType(MethodTable *pMT, GUID *pGuid);
 };  // class WinRTGuidGenerator
 
-
-// includes Types which hold a "ComObject" class
-// and types which are imported through typelib
-BOOL IsComWrapperClass(TypeHandle type);
-
-// includes Type which hold a "__ComObject" class
-BOOL IsComObjectClass(TypeHandle type);
-
 IUnknown* MarshalObjectToInterface(OBJECTREF* ppObject, MethodTable* pItfMT, MethodTable* pClassMT, DWORD dwFlags);
 void UnmarshalObjectFromInterface(OBJECTREF *ppObjectDest, IUnknown **ppUnkSrc, MethodTable *pItfMT, MethodTable *pClassMT, DWORD dwFlags);
 
@@ -508,7 +507,6 @@ class ICOMInterfaceMarshalerCallback;
 void GetNativeWinRTFactoryObject(MethodTable *pMT, Thread *pThread, MethodTable *pFactoryIntfMT, BOOL bNeedUniqueRCW, ICOMInterfaceMarshalerCallback *pCallback, OBJECTREF *prefFactory);
 
 #else // FEATURE_COMINTEROP
-
 inline HRESULT EnsureComStartedNoThrow()
 {
     LIMITED_METHOD_CONTRACT;
index 710a81a..6765623 100644 (file)
@@ -602,8 +602,6 @@ void MethodTable::SetIsRestored()
 #endif
 }
 
-#ifdef FEATURE_COMINTEROP
-
 //==========================================================================================
 // mark as COM object type (System.__ComObject and types deriving from it)
 void MethodTable::SetComObjectType()
@@ -612,8 +610,6 @@ void MethodTable::SetComObjectType()
     SetFlag(enum_flag_ComObject);
 }
 
-#endif // FEATURE_COMINTEROP
-
 #if defined(FEATURE_TYPEEQUIVALENCE)
 void MethodTable::SetHasTypeEquivalence()
 {
index 7bc2a83..e88fe16 100644 (file)
@@ -882,9 +882,6 @@ public:
 
     BOOL            IsExtensibleRCW();
 
-    // mark the class type as COM object class
-    void SetComObjectType();
-
 #if defined(FEATURE_TYPEEQUIVALENCE)
     // mark the type as opted into type equivalence
     void SetHasTypeEquivalence();
@@ -894,12 +891,6 @@ public:
     // the hierarchy
     MethodTable* GetComPlusParentMethodTable();
 
-    // class is a com object class
-    BOOL IsComObjectType()
-    {
-        LIMITED_METHOD_DAC_CONTRACT;
-        return GetFlag(enum_flag_ComObject);
-    }
     // class is a WinRT object class (is itself or derives from a ProjectedFromWinRT class)
     BOOL IsWinRTObjectType();
 
@@ -943,11 +934,6 @@ public:
     InteropMethodTableData *GetComInteropData();
 
 #else // !FEATURE_COMINTEROP
-    BOOL IsComObjectType()
-    {
-        SUPPORTS_DAC;
-        return FALSE;
-    }
     BOOL IsWinRTObjectType()
     {
         LIMITED_METHOD_CONTRACT;
@@ -955,6 +941,16 @@ public:
     }
 #endif // !FEATURE_COMINTEROP
 
+    // class is a com object class
+    BOOL IsComObjectType()
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        return GetFlag(enum_flag_ComObject);
+    }
+
+    // mark the class type as COM object class
+    void SetComObjectType();
+
 #ifdef FEATURE_ICASTABLE
     void SetICastable();
 #endif  
index 6464e58..c57677b 100644 (file)
@@ -1536,12 +1536,11 @@ MethodTableBuilder::BuildMethodTableThrowing(
         }
     }
 
-#ifdef FEATURE_COMINTEROP 
-
     // Com Import classes are special. These types must derive from System.Object,
     // and we then substitute the parent with System._ComObject.
     if (IsComImport() && !IsEnum() && !IsInterface() && !IsValueClass() && !IsDelegate())
     {
+#ifdef FEATURE_COMINTEROP        
         // ComImport classes must either extend from Object or be a WinRT class
         // that extends from another WinRT class (and so form a chain of WinRT classes
         // that ultimately extend from object).
@@ -1579,11 +1578,12 @@ MethodTableBuilder::BuildMethodTableThrowing(
             bmtInternal->pType->SetParentType(CreateTypeChain(pCOMMT, Substitution()));
             bmtInternal->pParentMT = pCOMMT;
         }
-
+#endif
         // if the current class is imported
         bmtProp->fIsComObjectType = true;
     }
 
+#ifdef FEATURE_COMINTEROP
     if (GetHalfBakedClass()->IsProjectedFromWinRT() && IsValueClass() && !IsEnum())
     {
         // WinRT structures must have sequential layout
@@ -2865,12 +2865,10 @@ MethodTableBuilder::EnumerateClassMethods()
         // RVA : 0
         if (dwMethodRVA != 0)
         {
-#ifdef FEATURE_COMINTEROP 
             if(fIsClassComImport)
             {
                 BuildMethodTableThrowException(BFA_METHOD_WITH_NONZERO_RVA);
             }
-#endif // FEATURE_COMINTEROP
             if(IsMdAbstract(dwMemberAttrs))
             {
                 BuildMethodTableThrowException(BFA_ABSTRACT_METHOD_WITH_RVA);
@@ -3066,14 +3064,12 @@ MethodTableBuilder::EnumerateClassMethods()
             // The attribute is not present
             if (hr == S_FALSE)
             {
+#ifdef FEATURE_COMINTEROP
                 if (fIsClassComImport
-#ifdef FEATURE_COMINTEROP 
                     || GetHalfBakedClass()->IsProjectedFromWinRT()
                     || bmtProp->fComEventItfType
-#endif //FEATURE_COMINTEROP
                     )
                 {
-#ifdef FEATURE_COMINTEROP
                     // ComImport classes have methods which are just used
                     // for implementing all interfaces the class supports
                     type = METHOD_TYPE_COMINTEROP;
@@ -3090,13 +3086,10 @@ MethodTableBuilder::EnumerateClassMethods()
                             type = METHOD_TYPE_FCALL;
                         }
                     }
-#else
-                    //If we don't support com interop, refuse to load interop methods.  Otherwise we fail to
-                    //jit calls to them since the constuctor has no intrinsic ID.
-                    BuildMethodTableThrowException(hr, IDS_CLASSLOAD_GENERAL, tok);
-#endif // FEATURE_COMINTEROP
                 }
-                else if (dwMethodRVA == 0)
+                else 
+#endif //FEATURE_COMINTEROP
+                if (dwMethodRVA == 0)
                 {
                     type = METHOD_TYPE_FCALL;
                 }
@@ -10322,16 +10315,17 @@ MethodTableBuilder::SetupMethodTable2(
 
         GetHalfBakedClass()->SetBaseSizePadding(baseSize - bmtFP->NumInstanceFieldBytes);
 
-#ifdef FEATURE_COMINTEROP 
         if (bmtProp->fIsComObjectType)
         {   // Propagate the com specific info
             pMT->SetComObjectType();
-
+#ifdef FEATURE_COMINTEROP 
             // COM objects need an optional field on the EEClass, so ensure this class instance has allocated
             // the optional field descriptor.
             EnsureOptionalFieldsAreAllocated(pClass, m_pAllocMemTracker, GetLoaderAllocator()->GetLowFrequencyHeap());
+#endif // FEATURE_COMINTEROP
         }
 
+#ifdef FEATURE_COMINTEROP 
         if (pMT->GetAssembly()->IsManagedWinMD())
         {
             // We need to mark classes that are implementations of managed WinRT runtime classes with
index e64b72b..3e267a2 100644 (file)
@@ -1316,10 +1316,9 @@ private:
         bool fNoSanityChecks;
         bool fSparse;                           // Set to true if a sparse interface is being used.
 
-#ifdef FEATURE_COMINTEROP
         // Com Interop, ComWrapper classes extend from ComObject
         bool fIsComObjectType;                  // whether this class is an instance of ComObject class
-
+#ifdef FEATURE_COMINTEROP
         bool fIsMngStandardItf;                 // Set to true if the interface is a manages standard interface.
         bool fComEventItfType;                  // Set to true if the class is a special COM event interface.
         bool fIsRedirectedInterface;            // Set to true if the class is an interface redirected for WinRT
index 723222e..de9505a 100644 (file)
@@ -1012,7 +1012,6 @@ RuntimeTypeHandle::IsVisible(
 } // RuntimeTypeHandle::IsVisible
 
 FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsComObject, ReflectClassBaseObject *pTypeUNSAFE, CLR_BOOL isGenericCOM) {
-#ifdef FEATURE_COMINTEROP
     CONTRACTL {
         FCALL_CHECK;
     }
@@ -1037,17 +1036,6 @@ FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsComObject, ReflectClassBaseObject *pTy
     HELPER_METHOD_FRAME_END();
 
     FC_RETURN_BOOL(ret);
-#else
-    CONTRACTL {
-        DISABLED(NOTHROW);
-        GC_NOTRIGGER;
-        MODE_COOPERATIVE;
-        PRECONDITION(CheckPointer(pTypeUNSAFE));
-    }
-    CONTRACTL_END;
-    FCUnique(0x37);
-    FC_RETURN_BOOL(FALSE);
-#endif
 }
 FCIMPLEND
 
index fc6ee5d..9c21706 100644 (file)
@@ -26,3 +26,4 @@ add_subdirectory(StringMarshalling/UTF8)
 add_subdirectory(MarshalAPI/FunctionPointer)
 add_subdirectory(MarshalAPI/IUnknown)
 add_subdirectory(SizeConst)
+add_subdirectory(ClassicCOM)
\ No newline at end of file
diff --git a/tests/src/Interop/ClassicCOM/CMakeLists.txt b/tests/src/Interop/ClassicCOM/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d3416dd
--- /dev/null
@@ -0,0 +1,13 @@
+cmake_minimum_required (VERSION 2.6)
+project (ClassicCOMNative)
+include_directories(${INC_PLATFORM_DIR})
+set(SOURCES ClassicCOMNative.cpp)
+
+# add the executable
+add_library (ClassicCOMNative SHARED ${SOURCES})
+target_link_libraries(ClassicCOMNative ${LINK_LIBRARIES_ADDITIONAL}) 
+
+# add the install targets
+install (TARGETS ClassicCOMNative DESTINATION bin)
+
+
diff --git a/tests/src/Interop/ClassicCOM/COMLib.cs b/tests/src/Interop/ClassicCOM/COMLib.cs
new file mode 100644 (file)
index 0000000..fba866c
--- /dev/null
@@ -0,0 +1,34 @@
+// 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.
+
+using System;
+using System.Text;
+using System.Security;
+using System.Runtime.InteropServices;
+
+public class COMLib
+{
+    [Guid("00020404-0000-0000-C000-000000000046")]
+    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+    [ComImport]
+    public interface IEnumVARIANT
+    {
+        [PreserveSig]
+        int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] object[] rgVar, IntPtr pceltFetched);
+
+        [PreserveSig]
+        int Skip(int celt);
+
+        [PreserveSig]
+        int Reset();
+
+        IEnumVARIANT Clone();
+    }
+
+    [ComImport]
+    [Guid("78A51822-51F4-11D0-8F20-00805F2CD064")]
+    public class ProcessDebugManager
+    {
+    }
+}
diff --git a/tests/src/Interop/ClassicCOM/COMLib.csproj b/tests/src/Interop/ClassicCOM/COMLib.csproj
new file mode 100644 (file)
index 0000000..1f289d6
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>COMLib</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{5FEE5C46-8DD9-49FA-BDC1-AF22867A0704}</ProjectGuid>
+    <OutputType>library</OutputType>
+    <ProjectTypeGuids>{CDC3DF7E-04B4-4464-9A02-7E2B0FAB586A};{68EC03EE-C9EE-47FD-AA08-A954EB2D9816}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="COMLib.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/ClassicCOM/COMLib2.cs b/tests/src/Interop/ClassicCOM/COMLib2.cs
new file mode 100644 (file)
index 0000000..8f0ec78
--- /dev/null
@@ -0,0 +1,34 @@
+// 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.
+
+using System;
+using System.Text;
+using System.Security;
+using System.Runtime.InteropServices;
+
+namespace COMLib2
+{
+    [Guid("00020404-0000-0000-C000-000000000046")]
+    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+    [ComImport]
+    public interface IEnumVARIANT
+    {
+        [PreserveSig]
+        int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] object[] rgVar, IntPtr pceltFetched);
+
+        [PreserveSig]
+        int Skip(int celt);
+
+        [PreserveSig]
+        int Reset();
+
+        IEnumVARIANT Clone();
+    }
+    
+    [ComImport]
+    [Guid("09799AFB-AD67-11d1-ABCD-00C04FC30936")]
+    public class ContextMenu
+    {
+    }
+}
diff --git a/tests/src/Interop/ClassicCOM/COMLib2.csproj b/tests/src/Interop/ClassicCOM/COMLib2.csproj
new file mode 100644 (file)
index 0000000..601b94e
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>COMLib2</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{C04AB564-CC61-499D-9F4C-AA1A9FDE42C9}</ProjectGuid>
+    <OutputType>library</OutputType>
+    <ProjectTypeGuids>{4948E98A-ECFC-4988-851E-68E1ADD2DD5A};{B850CC46-E8FB-4569-A28D-423F81E8A861}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="COMLib2.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/ClassicCOM/ClassicCOMNative.cpp b/tests/src/Interop/ClassicCOM/ClassicCOMNative.cpp
new file mode 100644 (file)
index 0000000..962313c
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include <xplatform.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C" DLL_EXPORT void PassObjectToNative(void * ptr)
+{
+    // TODO: Add check
+}
+
+extern "C" DLL_EXPORT void PassObjectArrayToNative(void ** pptr)
+{
+    // TODO: Add check
+}
+
+extern "C" DLL_EXPORT void GetObjectFromNative(void ** pptr)
+{
+    *pptr = NULL;
+    // TODO: Add check
+}
+
+extern "C" DLL_EXPORT void GetObjectFromNativeAsRef(void ** pptr)
+{
+    // TODO: Add check
+}
\ No newline at end of file
diff --git a/tests/src/Interop/ClassicCOM/ClassicCOMUnitTest.cs b/tests/src/Interop/ClassicCOM/ClassicCOMUnitTest.cs
new file mode 100644 (file)
index 0000000..23bac89
--- /dev/null
@@ -0,0 +1,245 @@
+// 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.
+//
+
+//
+//  Adding tests for Classic COM code coverage
+//
+
+using System;
+using System.Text;
+using System.Security;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+public class ClassicCOMUnitTest
+{
+    /// <summary>
+    /// Try to reflect load ComImport Types by enumerate
+    /// </summary>
+    /// <returns></returns>
+    static bool RelectionLoad()
+    {
+        try
+        {
+            Console.WriteLine("Scenario: RelectionLoad");
+            var asm = Assembly.LoadFrom("COMLib.dll");
+            foreach (Type t in asm.GetTypes())
+            {
+                Console.WriteLine(t.Name);
+            }
+
+            return true;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine("Caught unexpected exception: " + e);
+            return false;
+        }
+    }
+
+    /// <summary>
+    /// Try to test Type.IsCOMObject
+    /// </summary>
+    /// <returns></returns>
+    static bool TypeIsComObject()
+    {
+        try
+        {
+            Console.WriteLine("Scenario: TypeIsComObject");
+            Type classType = typeof(COMLib2.ContextMenu);
+            if (!classType.IsCOMObject)
+            {
+                Console.WriteLine("ComImport Class's IsCOMObject should return true");
+                return false;
+            }
+
+            Type interfaceType = typeof(COMLib2.IEnumVARIANT);
+            if (interfaceType.IsCOMObject)
+            {
+                Console.WriteLine("ComImport interface's IsCOMObject should return false");
+                return false;
+            }
+
+            return true;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine("Caught unexpected exception: " + e);
+            return false;
+        }
+    }
+
+    /// <summary>
+    /// Try to create COM instance
+    /// </summary>
+    /// <returns></returns>
+    static bool AcivateCOMType()
+    {
+        try
+        {
+            Console.WriteLine("Scenario: AcivateCOMType");
+            COMLib2.ContextMenu contextMenu = (COMLib2.ContextMenu)Activator.CreateInstance(typeof(COMLib2.ContextMenu));
+            
+            // Linux should throw PlatformNotSupportedException
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                return false;
+            }
+            
+            if (contextMenu == null)
+            {
+                Console.WriteLine("AcivateCOMType failed");
+                return false;
+            }
+
+            return true;
+        }
+        catch (System.Reflection.TargetInvocationException e)
+        {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && e.InnerException is PlatformNotSupportedException)
+            {
+                return true;
+            }
+            
+            Console.WriteLine("Caught unexpected PlatformNotSupportedException: " + e);
+            return false;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine("Caught unexpected exception: " + e);
+            return false;
+        }
+    }
+
+    [DllImport("ClassicCOMNative.dll")]
+    extern static void PassObjectToNative([In, MarshalAs( UnmanagedType.Interface)] object o);
+    
+    [DllImport("ClassicCOMNative.dll")]
+    extern static void PassObjectArrayToNative([In,Out] object[] o);
+    
+    [DllImport("ClassicCOMNative.dll")]
+    extern static void GetObjectFromNative(out object o);
+    
+    [DllImport("ClassicCOMNative.dll")]
+    extern static void GetObjectFromNativeAsRef(ref object o);
+    
+    /// <summary>
+    /// Try to Marshal COM Type across managed-native boundary
+    /// </summary>
+    /// <returns></returns>
+    static bool MarshalCOMType()
+    {
+        Console.WriteLine("Scenario: MarshalCOMType");
+        try
+        {
+            object o = new object();
+            PassObjectToNative(o);
+        }
+        catch (System.Runtime.InteropServices.MarshalDirectiveException e) 
+        { 
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 
+            { 
+                return true; 
+            } 
+            Console.WriteLine("Caught unexpected MarshalDirectiveException: " + e); 
+            return false; 
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine("Caught unexpected exception in PassObjectToNative: " + e);
+            return false;
+        }
+        
+        try
+        {
+            object [] oa = new object[2];
+            PassObjectArrayToNative(oa);
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine("Caught unexpected exception in GetObjectFromNative: " + e);
+            return false;
+        }
+        
+        
+        try
+        {
+            object o; 
+            GetObjectFromNative(out o);
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine("Caught unexpected exception in GetObjectFromNative: " + e);
+            return false;
+        }
+        
+        try
+        {
+            object o = new object(); 
+            GetObjectFromNativeAsRef(ref o);
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine("Caught unexpected exception in GetObjectFromNativeAsRef: " + e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /// <summary>
+    /// Try to call Marshal API for COM Types
+    /// </summary>
+    /// <returns></returns>
+    static bool MarshalAPI()
+    {
+        Console.WriteLine("Scenario: MarshalAPI");
+        // MarshalAPI
+        if (Marshal.AreComObjectsAvailableForCleanup())
+        {
+            Console.WriteLine("AreComObjectsAvailableForCleanup should return false");
+            return false;
+        }
+        return true;
+    }
+
+    [System.Security.SecuritySafeCritical]
+    static int Main()
+    {
+        int failures = 0;
+        if (!RelectionLoad())
+        {
+            Console.WriteLine("RelectionLoad Failed");
+            failures++;
+        }
+
+        if (!TypeIsComObject())
+        {
+            Console.WriteLine("TypeIsComObject Failed");
+            failures++;
+        }
+
+        if (!AcivateCOMType())
+        {
+            Console.WriteLine("AcivateCOMType Failed");
+            failures++;
+        }
+
+        if (!MarshalCOMType())
+        {
+            Console.WriteLine("MarshalCOMType Failed");
+            failures++;
+        }
+
+        if (!MarshalAPI())
+        {
+            Console.WriteLine("MarshalAPI Failed");
+            failures++;
+        }
+
+        return failures > 0 ? 101 : 100;
+    }
+}
diff --git a/tests/src/Interop/ClassicCOM/ClassicCOMUnitTest.csproj b/tests/src/Interop/ClassicCOM/ClassicCOMUnitTest.csproj
new file mode 100644 (file)
index 0000000..673e216
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>ClassicCOMUnitTest</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{85C57688-DA98-4DE3-AC9B-526E4747434C}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{209912F9-0DA1-4184-9CC1-8D583BAF4A28};{87799F5D-CEBD-499D-BDBA-B2C6105CD766}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ClassicCOMUnitTest.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj">
+      <Project>{c8c0dc74-fac4-45b1-81fe-70c4808366e0}</Project>
+      <Name>CoreCLRTestLibrary</Name>
+    </ProjectReference>
+    <ProjectReference Include="COMLib.csproj">
+      <Project>{5FEE5C46-8DD9-49FA-BDC1-AF22867A0704}</Project>
+      <Name>COMLib</Name>
+    </ProjectReference>
+    <ProjectReference Include="COMLib2.csproj">
+      <Project>{C04AB564-CC61-499D-9F4C-AA1A9FDE42C9}</Project>
+      <Name>COMLib</Name>
+    </ProjectReference>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>