summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAaron Robinson <arobins@microsoft.com>2018-09-10 17:24:49 -0700
committerGitHub <noreply@github.com>2018-09-10 17:24:49 -0700
commitfc3378095f04946815e627a5ab70b528a898abe6 (patch)
tree44a1e2cd85c2a5c369bf6f754a0379a0751ccb66 /src
parentefd7220234aacef4af25a747797984d43ba5b961 (diff)
downloadcoreclr-fc3378095f04946815e627a5ab70b528a898abe6.tar.gz
coreclr-fc3378095f04946815e627a5ab70b528a898abe6.tar.bz2
coreclr-fc3378095f04946815e627a5ab70b528a898abe6.zip
Basic implementation for testing of COM activation of a .NET class (#19760)
* Rough outline of managed implementation for COM activation in SPCL * Add property for finding interop common Add property to exclude default assertion file Display exe ExeLaunchProgram class is going to launch * Add a native client for the NETServer Consume the ExeLauncherProgram.cs file as a wrapper for the native test * Update COM Server contracts to use 'int' instead of 'long' * Complete symmetric testing coverage for .NET server and native client. * Block EXE launch from running on non-Windows machines * Disable COM testing in helix since it has issues on Windows Nano and there is no way to determine that is the platform. * Update tests based on CLSID mapping manifest approach.
Diffstat (limited to 'src')
-rw-r--r--src/System.Private.CoreLib/System.Private.CoreLib.csproj1
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs261
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs7
-rw-r--r--src/coreclr/hosts/CMakeLists.txt1
-rw-r--r--src/coreclr/hosts/coreshim/CMakeLists.txt25
-rw-r--r--src/coreclr/hosts/coreshim/ComActivation.cpp91
-rw-r--r--src/coreclr/hosts/coreshim/CoreShim.cpp293
-rw-r--r--src/coreclr/hosts/coreshim/CoreShim.h179
-rw-r--r--src/coreclr/hosts/coreshim/Exports.def3
9 files changed, 858 insertions, 3 deletions
diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index 30b5b2bead..74f04d80d5 100644
--- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -158,6 +158,7 @@
<Compile Condition="'$(FeatureCominterop)' != 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\NonPortable.cs" />
<Compile Condition="'$(FeatureCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\DispatchWrapper.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ICustomFactory.cs" />
+ <Compile Condition="'$(FeatureCominteropUnmanagedActivation)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComActivator.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Expando\IExpando.cs" />
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs
new file mode 100644
index 0000000000..785e9ace62
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs
@@ -0,0 +1,261 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+namespace System.Runtime.InteropServices
+{
+ [ComImport]
+ [ComVisible(false)]
+ [Guid("00000001-0000-0000-C000-000000000046")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IClassFactory
+ {
+ void CreateInstance(
+ [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+ ref Guid riid,
+ [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+
+ void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct LICINFO
+ {
+ public int cbLicInfo;
+
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool fRuntimeKeyAvail;
+
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool fLicVerified;
+ }
+
+ [ComImport]
+ [ComVisible(false)]
+ [Guid("B196B28F-BAB4-101A-B69C-00AA00341D07")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IClassFactory2 : IClassFactory
+ {
+ new void CreateInstance(
+ [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+ ref Guid riid,
+ [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+
+ new void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
+
+ void GetLicInfo(ref LICINFO pLicInfo);
+
+ void RequestLicKey(
+ int dwReserved,
+ [MarshalAs(UnmanagedType.BStr)] out string pBstrKey);
+
+ void CreateInstanceLic(
+ [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+ [MarshalAs(UnmanagedType.Interface)] object pUnkReserved,
+ ref Guid riid,
+ [MarshalAs(UnmanagedType.BStr)] string bstrKey,
+ [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ComActivationContext
+ {
+ public Guid ClassId;
+ public Guid InterfaceId;
+ public string AssemblyName;
+ public string TypeName;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ComActivationContextInternal
+ {
+ public Guid ClassId;
+ public Guid InterfaceId;
+ public IntPtr AssemblyNameBuffer;
+ public IntPtr TypeNameBuffer;
+ public IntPtr ClassFactoryDest;
+ }
+
+ public static class ComActivator
+ {
+ /// <summary>
+ /// Entry point for unmanaged COM activation API from managed code
+ /// </summary>
+ /// <param name="cxt">Reference to a <see cref="ComActivationContext"/> instance</param>
+ public static object GetClassFactoryForType(ComActivationContext cxt)
+ {
+ if (cxt.InterfaceId != typeof(IClassFactory).GUID
+ && cxt.InterfaceId != typeof(IClassFactory2).GUID)
+ {
+ throw new NotSupportedException();
+ }
+
+ Type classType = FindClassType(cxt.ClassId, cxt.AssemblyName, cxt.TypeName);
+ return new BasicClassFactory(cxt.ClassId, classType);
+ }
+
+ /// <summary>
+ /// Internal entry point for unmanaged COM activation API from native code
+ /// </summary>
+ /// <param name="cxtInt">Reference to a <see cref="ComActivationContextInternal"/> instance</param>
+ public static int GetClassFactoryForTypeInternal(ref ComActivationContextInternal cxtInt)
+ {
+ if (IsLoggingEnabled())
+ {
+ Log(
+$@"{nameof(GetClassFactoryForTypeInternal)} arguments:
+ {cxtInt.ClassId}
+ {cxtInt.InterfaceId}
+ 0x{cxtInt.AssemblyNameBuffer.ToInt64():x}
+ 0x{cxtInt.TypeNameBuffer.ToInt64():x}
+ 0x{cxtInt.ClassFactoryDest.ToInt64():x}");
+ }
+
+ try
+ {
+ var cxt = new ComActivationContext()
+ {
+ ClassId = cxtInt.ClassId,
+ InterfaceId = cxtInt.InterfaceId,
+ AssemblyName = Marshal.PtrToStringUTF8(cxtInt.AssemblyNameBuffer),
+ TypeName = Marshal.PtrToStringUTF8(cxtInt.TypeNameBuffer)
+ };
+
+ object cf = GetClassFactoryForType(cxt);
+ IntPtr nativeIUnknown = Marshal.GetIUnknownForObject(cf);
+ Marshal.WriteIntPtr(cxtInt.ClassFactoryDest, nativeIUnknown);
+ }
+ catch (Exception e)
+ {
+ return e.HResult;
+ }
+
+ return 0;
+ }
+
+ private static bool IsLoggingEnabled()
+ {
+#if COM_ACTIVATOR_DEBUG
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ private static void Log(string fmt, params object[] args)
+ {
+ // [TODO] Use FrameworkEventSource in release builds
+
+ Debug.WriteLine(fmt, args);
+ }
+
+ private static Type FindClassType(Guid clsid, string assemblyName, string typeName)
+ {
+ try
+ {
+ Assembly assem = Assembly.LoadFrom(assemblyName);
+ Type t = assem.GetType(typeName);
+ if (t != null)
+ {
+ return t;
+ }
+ }
+ catch (Exception e)
+ {
+ if (IsLoggingEnabled())
+ {
+ Log($"COM Activation of {clsid} failed. {e}");
+ }
+ }
+
+ const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)0x80040111);
+ throw new COMException(string.Empty, CLASS_E_CLASSNOTAVAILABLE);
+ }
+
+ [ComVisible(true)]
+ internal class BasicClassFactory : IClassFactory2
+ {
+ private readonly Guid classId;
+ private readonly Type classType;
+
+ public BasicClassFactory(Guid clsid, Type classType)
+ {
+ this.classId = clsid;
+ this.classType = classType;
+ }
+
+ public void CreateInstance(
+ [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+ ref Guid riid,
+ [MarshalAs(UnmanagedType.Interface)] out object ppvObject)
+ {
+ if (riid != Marshal.IID_IUnknown)
+ {
+ bool found = false;
+
+ // Verify the class implements the desired interface
+ foreach (Type i in this.classType.GetInterfaces())
+ {
+ if (i.GUID == riid)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ // E_NOINTERFACE
+ throw new InvalidCastException();
+ }
+ }
+
+ ppvObject = Activator.CreateInstance(this.classType);
+ if (pUnkOuter != null)
+ {
+ try
+ {
+ IntPtr outerPtr = Marshal.GetIUnknownForObject(pUnkOuter);
+ IntPtr innerPtr = Marshal.CreateAggregatedObject(outerPtr, ppvObject);
+ ppvObject = Marshal.GetObjectForIUnknown(innerPtr);
+ }
+ finally
+ {
+ // Decrement the above 'Marshal.GetIUnknownForObject()'
+ Marshal.ReleaseComObject(pUnkOuter);
+ }
+ }
+ }
+
+ public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock)
+ {
+ // nop
+ }
+
+ public void GetLicInfo(ref LICINFO pLicInfo)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RequestLicKey(int dwReserved, [MarshalAs(UnmanagedType.BStr)] out string pBstrKey)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void CreateInstanceLic(
+ [MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
+ [MarshalAs(UnmanagedType.Interface)] object pUnkReserved,
+ ref Guid riid,
+ [MarshalAs(UnmanagedType.BStr)] string bstrKey,
+ [MarshalAs(UnmanagedType.Interface)] out object ppvObject)
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
index e214f1e731..c4c65da540 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
@@ -28,14 +28,15 @@ namespace System.Runtime.InteropServices
/// </summary>
public static partial class Marshal
{
+#if FEATURE_COMINTEROP
+ internal static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
+#endif //FEATURE_COMINTEROP
+
private const int LMEM_FIXED = 0;
private const int LMEM_MOVEABLE = 2;
#if !FEATURE_PAL
private const long HiWordMask = unchecked((long)0xffffffffffff0000L);
#endif //!FEATURE_PAL
-#if FEATURE_COMINTEROP
- private static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
-#endif //FEATURE_COMINTEROP
// Win32 has the concept of Atoms, where a pointer can either be a pointer
// or an int. If it's less than 64K, this is guaranteed to NOT be a
diff --git a/src/coreclr/hosts/CMakeLists.txt b/src/coreclr/hosts/CMakeLists.txt
index bb425b908a..c27ba16c56 100644
--- a/src/coreclr/hosts/CMakeLists.txt
+++ b/src/coreclr/hosts/CMakeLists.txt
@@ -3,6 +3,7 @@ include_directories(inc)
if(WIN32)
add_subdirectory(corerun)
add_subdirectory(coreconsole)
+ add_subdirectory(coreshim)
else(WIN32)
add_subdirectory(unixcoreruncommon)
add_subdirectory(unixcorerun)
diff --git a/src/coreclr/hosts/coreshim/CMakeLists.txt b/src/coreclr/hosts/coreshim/CMakeLists.txt
new file mode 100644
index 0000000000..828b91ce5b
--- /dev/null
+++ b/src/coreclr/hosts/coreshim/CMakeLists.txt
@@ -0,0 +1,25 @@
+project (CoreShim)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CoreShim_SOURCES
+ CoreShim.cpp
+ ComActivation.cpp
+ Exports.def)
+
+add_library_clr(CoreShim
+ SHARED
+ ${CoreShim_SOURCES}
+)
+
+target_link_libraries(CoreShim
+ utilcodestaticnohost
+ advapi32.lib
+ oleaut32.lib
+ uuid.lib
+ user32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_VCRT_LIB}
+)
+
+install_clr(CoreShim) \ No newline at end of file
diff --git a/src/coreclr/hosts/coreshim/ComActivation.cpp b/src/coreclr/hosts/coreshim/ComActivation.cpp
new file mode 100644
index 0000000000..5df1d000c1
--- /dev/null
+++ b/src/coreclr/hosts/coreshim/ComActivation.cpp
@@ -0,0 +1,91 @@
+// 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.
+
+#include "CoreShim.h"
+
+#include <vector>
+
+namespace
+{
+ HRESULT InitializeCoreClr(_In_ coreclr* inst)
+ {
+ assert(inst != nullptr);
+
+ HRESULT hr;
+
+ std::string tpaList;
+ RETURN_IF_FAILED(coreclr::CreateTpaList(tpaList));
+
+ const char *keys[] =
+ {
+ "APP_PATHS",
+ "TRUSTED_PLATFORM_ASSEMBLIES",
+ };
+
+ // [TODO] Support UNICODE app path
+ char wd[MAX_PATH];
+ (void)::GetCurrentDirectoryA(ARRAYSIZE(wd), wd);
+
+ const char *values[] =
+ {
+ wd,
+ tpaList.c_str(),
+ };
+
+ static_assert(ARRAYSIZE(keys) == ARRAYSIZE(values), "key/values pairs should match in length");
+
+ return inst->Initialize(ARRAYSIZE(keys), keys, values, "COMAct");
+ }
+}
+
+STDAPI DllGetClassObject(
+ _In_ REFCLSID rclsid,
+ _In_ REFIID riid,
+ _Outptr_ LPVOID FAR* ppv)
+{
+ HRESULT hr;
+
+ coreclr *inst;
+ RETURN_IF_FAILED(coreclr::GetCoreClrInstance(&inst));
+
+ if (hr == S_OK)
+ RETURN_IF_FAILED(InitializeCoreClr(inst));
+
+ using GetClassFactoryForTypeInternal_ptr = HRESULT(*)(void *);
+ GetClassFactoryForTypeInternal_ptr GetClassFactoryForTypeInternal;
+ RETURN_IF_FAILED(inst->CreateDelegate(
+ "System.Private.CoreLib",
+ "System.Runtime.InteropServices.ComActivator",
+ "GetClassFactoryForTypeInternal", (void**)&GetClassFactoryForTypeInternal));
+
+ // Get assembly and type for activation
+ std::string assemblyName;
+ RETURN_IF_FAILED(Utility::TryGetEnvVar(COMACT_ASSEMBLYNAME_ENVVAR, assemblyName));
+
+ std::string typeName;
+ RETURN_IF_FAILED(Utility::TryGetEnvVar(COMACT_TYPENAME_ENVVAR, typeName));
+
+ IUnknown *ccw = nullptr;
+
+ struct ComActivationContext
+ {
+ GUID ClassId;
+ GUID InterfaceId;
+ const void *AssemblyName;
+ const void *TypeName;
+ void **ClassFactoryDest;
+ } comCxt{ rclsid, riid, assemblyName.data(), typeName.data(), (void**)&ccw };
+
+ RETURN_IF_FAILED(GetClassFactoryForTypeInternal(&comCxt));
+ assert(ccw != nullptr);
+
+ hr = ccw->QueryInterface(riid, ppv);
+ ccw->Release();
+ return hr;
+}
+
+STDAPI DllCanUnloadNow(void)
+{
+ return S_FALSE;
+}
diff --git a/src/coreclr/hosts/coreshim/CoreShim.cpp b/src/coreclr/hosts/coreshim/CoreShim.cpp
new file mode 100644
index 0000000000..497c10e5d2
--- /dev/null
+++ b/src/coreclr/hosts/coreshim/CoreShim.cpp
@@ -0,0 +1,293 @@
+// 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.
+
+#include "CoreShim.h"
+
+#include <set>
+#include <sstream>
+#include <vector>
+#include <mutex>
+
+namespace
+{
+ struct PathBuffer
+ {
+ PathBuffer()
+ : DefBuffer{}
+ , Buf{ DefBuffer }
+ , Len{ ARRAYSIZE(DefBuffer) }
+ { }
+
+ void SetLength(_In_ DWORD len)
+ {
+ if (len > Len)
+ {
+ Buf = BigBuffer.data();
+ Len = static_cast<DWORD>(BigBuffer.size());
+ }
+ }
+
+ void ExpandBuffer(_In_ DWORD factor = 2)
+ {
+ SetLength(Len * factor);
+ }
+
+ operator DWORD()
+ {
+ return Len;
+ }
+
+ operator WCHAR *()
+ {
+ return Buf;
+ }
+
+ WCHAR DefBuffer[MAX_PATH];
+ std::vector<WCHAR> BigBuffer;
+
+ WCHAR *Buf;
+ DWORD Len;
+ };
+
+ std::string GetExePath()
+ {
+ PathBuffer buffer;
+ DWORD len = ::GetModuleFileNameW(nullptr, buffer, buffer);
+ while (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ buffer.ExpandBuffer();
+ len = ::GetModuleFileNameW(nullptr, buffer, buffer);
+ }
+
+ return std::string{ buffer.Buf, buffer.Buf + len };
+ }
+
+ std::wstring GetEnvVar(_In_z_ const WCHAR *env)
+ {
+ DWORD len = ::GetEnvironmentVariableW(env, nullptr, 0);
+ if (len == 0)
+ throw __HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND);
+
+ PathBuffer buffer;
+ buffer.SetLength(len);
+ (void)::GetEnvironmentVariableW(env, buffer, buffer);
+
+ return static_cast<WCHAR *>(buffer.Buf);
+ }
+
+ coreclr *s_CoreClrInstance;
+}
+
+namespace Utility
+{
+ HRESULT TryGetEnvVar(_In_z_ const WCHAR *env, _Inout_ std::string &envVar)
+ {
+ try
+ {
+ std::wstring envVarLocal = GetEnvVar(env);
+ envVar = { std::begin(envVarLocal), std::end(envVarLocal) };
+ }
+ catch (HRESULT hr)
+ {
+ return hr;
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT coreclr::GetCoreClrInstance(_Outptr_ coreclr **instance, _In_opt_z_ const WCHAR *path)
+{
+ if (s_CoreClrInstance != nullptr)
+ {
+ *instance = s_CoreClrInstance;
+ return S_FALSE;
+ }
+
+ try
+ {
+ std::wstring pathLocal;
+ if (path == nullptr)
+ {
+ pathLocal = GetEnvVar(W("CORE_ROOT"));
+ }
+ else
+ {
+ pathLocal = { path };
+ }
+
+ pathLocal.append(W("\\coreclr.dll"));
+
+ AutoModule hmod = ::LoadLibraryExW(pathLocal.c_str() , nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+ if (hmod == nullptr)
+ return HRESULT_FROM_WIN32(::GetLastError());
+
+ s_CoreClrInstance = new coreclr{ std::move(hmod) };
+ }
+ catch (HRESULT hr)
+ {
+ return hr;
+ }
+ catch (const std::bad_alloc&)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ *instance = s_CoreClrInstance;
+ return S_OK;
+}
+
+HRESULT coreclr::CreateTpaList(_Inout_ std::string &tpaList, _In_opt_z_ const WCHAR *dir)
+{
+ assert(tpaList.empty());
+
+ // Represents priority order
+ static const WCHAR * const tpaExtensions[] =
+ {
+ W(".ni.dll"),
+ W(".dll"),
+ W(".ni.exe"),
+ W(".exe"),
+ };
+
+ try
+ {
+ std::wstring w_dirLocal;
+ if (dir == nullptr)
+ {
+ w_dirLocal = GetEnvVar(W("CORE_ROOT"));
+ }
+ else
+ {
+ w_dirLocal = { dir };
+ }
+
+ std::string dirLocal{ std::begin(w_dirLocal), std::end(w_dirLocal) };
+ w_dirLocal.append(W("\\*"));
+
+ std::set<std::wstring> addedAssemblies;
+ std::stringstream tpaStream;
+
+ // Walk the directory for each extension separately so assembly types
+ // are discovered in priority order - see above.
+ for (int extIndex = 0; extIndex < ARRAYSIZE(tpaExtensions); extIndex++)
+ {
+ const WCHAR* ext = tpaExtensions[extIndex];
+ size_t extLength = ::wcslen(ext);
+
+ WIN32_FIND_DATAW ffd;
+ AutoFindFile sh = ::FindFirstFileW(w_dirLocal.c_str(), &ffd);
+ if (sh == nullptr)
+ break;
+
+ // For all entries in the directory
+ do
+ {
+ // Only examine non-directory entries
+ if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ std::wstring filename{ ffd.cFileName };
+
+ // Check if the extension matches
+ int extPos = static_cast<int>(filename.length() - extLength);
+ if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
+ {
+ continue;
+ }
+
+ std::wstring filenameWithoutExt{ filename.substr(0, extPos) };
+
+ // Only one type of a particular assembly instance should be inserted
+ // See extension list above.
+ if (addedAssemblies.find(filenameWithoutExt) == std::end(addedAssemblies))
+ {
+ addedAssemblies.insert(std::move(filenameWithoutExt));
+
+ // [TODO] Properly convert to UTF-8
+ std::string filename_utf8{ std::begin(filename), std::end(filename) };
+ tpaStream << dirLocal << "\\" << filename_utf8 << ";";
+ }
+ }
+ } while (::FindNextFileW(sh, &ffd) != FALSE);
+ }
+
+ tpaList = tpaStream.str();
+ }
+ catch (HRESULT hr)
+ {
+ return hr;
+ }
+ catch (const std::bad_alloc&)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
+coreclr::coreclr(_Inout_ AutoModule hmod)
+ : _hmod{ std::move(hmod) }
+ , _clrInst{ nullptr }
+ , _appDomainId{ std::numeric_limits<uint32_t>::max() }
+{
+ _initialize = (decltype(_initialize))::GetProcAddress(_hmod, "coreclr_initialize");
+ assert(_initialize != nullptr);
+
+ _create_delegate = (decltype(_create_delegate))::GetProcAddress(_hmod, "coreclr_create_delegate");
+ assert(_create_delegate != nullptr);
+
+ _shutdown = (decltype(_shutdown))::GetProcAddress(_hmod, "coreclr_shutdown");
+ assert(_shutdown != nullptr);
+}
+
+coreclr::~coreclr()
+{
+ if (_clrInst != nullptr)
+ {
+ HRESULT hr = _shutdown(_clrInst, _appDomainId);
+ assert(SUCCEEDED(hr));
+ (void)hr;
+ }
+}
+
+HRESULT coreclr::Initialize(
+ _In_ int propertyCount,
+ _In_reads_(propertCount) const char **keys,
+ _In_reads_(propertCount) const char **values,
+ _In_opt_z_ const char *appDomainName)
+{
+ if (_clrInst != nullptr)
+ return __HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+
+ if (appDomainName == nullptr)
+ appDomainName = "CoreShim";
+
+ HRESULT hr;
+ try
+ {
+ const std::string exePath = GetExePath();
+ RETURN_IF_FAILED(_initialize(exePath.c_str(), appDomainName, propertyCount, keys, values, &_clrInst, &_appDomainId));
+ }
+ catch (const std::bad_alloc&)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
+HRESULT coreclr::CreateDelegate(
+ _In_z_ const char *assembly,
+ _In_z_ const char *type,
+ _In_z_ const char *method,
+ _Out_ void **del)
+{
+ if (_clrInst == nullptr)
+ return E_NOT_VALID_STATE;
+
+ HRESULT hr;
+ RETURN_IF_FAILED(_create_delegate(_clrInst, _appDomainId, assembly, type, method, del));
+
+ return S_OK;
+}
diff --git a/src/coreclr/hosts/coreshim/CoreShim.h b/src/coreclr/hosts/coreshim/CoreShim.h
new file mode 100644
index 0000000000..dd5e9d1297
--- /dev/null
+++ b/src/coreclr/hosts/coreshim/CoreShim.h
@@ -0,0 +1,179 @@
+// 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.
+
+#ifndef _CORESHIM_H_
+#define _CORESHIM_H_
+
+// Platform
+#define NOMINMAX
+#include <Windows.h>
+#include <combaseapi.h>
+
+// Standard library
+#include <utility>
+#include <string>
+#include <cstdint>
+#include <cassert>
+
+// CoreCLR
+#include <palclr.h>
+#include <coreclrhost.h>
+
+#define WCHAR wchar_t
+
+#define RETURN_IF_FAILED(exp) { hr = (exp); if (FAILED(hr)) { assert(false && #exp); return hr; } }
+
+template
+<
+ typename T,
+ T DEFAULT,
+ void(*RELEASE)(T)
+>
+struct AutoClass
+{
+ T c;
+
+ AutoClass() : c{ DEFAULT }
+ { }
+
+ AutoClass(_Inout_ T t) : c{ t }
+ { }
+
+ AutoClass(_In_ const AutoClass&) = delete;
+ AutoClass& operator=(_In_ const AutoClass&) = delete;
+
+ AutoClass(_Inout_ AutoClass &&other)
+ : c{ other.Detach() }
+ { }
+
+ AutoClass& operator=(_Inout_ AutoClass &&other)
+ {
+ Attach(other.Detach());
+ }
+
+ ~AutoClass()
+ {
+ Attach(DEFAULT);
+ }
+
+ operator T()
+ {
+ return c;
+ }
+
+ T* operator &()
+ {
+ return &c;
+ }
+
+ void Attach(_In_opt_ T cm)
+ {
+ RELEASE(c);
+ c = cm;
+ }
+
+ T Detach()
+ {
+ T tmp = c;
+ c = DEFAULT;
+ return tmp;
+ }
+};
+
+inline void ReleaseHandle(_In_ HANDLE h)
+{
+ if (h != nullptr && h != INVALID_HANDLE_VALUE)
+ ::CloseHandle(h);
+}
+
+using AutoHandle = AutoClass<HANDLE, nullptr, &ReleaseHandle>;
+
+inline void ReleaseFindFile(_In_ HANDLE h)
+{
+ if (h != nullptr)
+ ::FindClose(h);
+}
+
+using AutoFindFile = AutoClass<HANDLE, nullptr, &ReleaseFindFile>;
+
+inline void ReleaseModule(_In_ HMODULE m)
+{
+ if (m != nullptr)
+ ::FreeLibrary(m);
+}
+
+using AutoModule = AutoClass<HMODULE, nullptr, &ReleaseModule>;
+
+namespace Utility
+{
+ /// <summary>
+ /// Get the supplied environment variable.
+ /// </summary>
+ HRESULT TryGetEnvVar(_In_z_ const WCHAR *env, _Inout_ std::string &envVar);
+}
+
+// CoreShim environment variables used to indicate what assembly/type tuple
+// to load during COM activation.
+#define COMACT_ASSEMBLYNAME_ENVVAR W("CORESHIM_COMACT_ASSEMBLYNAME")
+#define COMACT_TYPENAME_ENVVAR W("CORESHIM_COMACT_TYPENAME")
+
+// CoreCLR class to handle lifetime and provide a simpler API surface
+class coreclr
+{
+public: // static
+ /// <summary>
+ /// Get a CoreCLR instance
+ /// </summary>
+ /// <returns>S_OK if newly created and needs initialization, S_FALSE if already exists and no initialization needed, otherwise an error code</returns>
+ /// <remarks>
+ /// If a CoreCLR instance has already been created, the existing instance is returned.
+ /// If the <paramref name="path"/> is not supplied, the 'CORE_ROOT' environment variable is used.
+ /// </remarks>
+ static HRESULT GetCoreClrInstance(_Outptr_ coreclr **instance, _In_opt_z_ const WCHAR *path = nullptr);
+
+ /// <summary>
+ /// Populate the supplied string with a delimited string of TPA assemblies in from the supplied directory path.
+ /// </summary>
+ /// <remarks>
+ /// If <paramref name="dir"/> is not supplied, the 'CORE_ROOT' environment variable is used.
+ /// </remarks>
+ static HRESULT CreateTpaList(_Inout_ std::string &tpaList, _In_opt_z_ const WCHAR *dir = nullptr);
+
+public:
+ coreclr(_Inout_ AutoModule hmod);
+
+ coreclr(_In_ const coreclr &) = delete;
+ coreclr& operator=(_In_ const coreclr &) = delete;
+
+ coreclr(_Inout_ coreclr &&) = delete;
+ coreclr& operator=(_Inout_ coreclr &&) = delete;
+
+ ~coreclr();
+
+ // See exported function 'coreclr_initialize' from coreclr library
+ HRESULT Initialize(
+ _In_ int propertyCount,
+ _In_reads_(propertyCount) const char **keys,
+ _In_reads_(propertyCount) const char **values,
+ _In_opt_z_ const char *appDomainName = nullptr);
+
+ // See exported function 'coreclr_create_delegate' from coreclr library
+ HRESULT CreateDelegate(
+ _In_z_ const char *assembly,
+ _In_z_ const char *type,
+ _In_z_ const char *method,
+ _Out_ void **del);
+
+private:
+ AutoModule _hmod;
+
+ void *_clrInst;
+ uint32_t _appDomainId;
+
+ coreclr_initialize_ptr _initialize;
+ coreclr_create_delegate_ptr _create_delegate;
+ coreclr_shutdown_ptr _shutdown;
+};
+
+#endif /* _CORESHIM_H_ */
diff --git a/src/coreclr/hosts/coreshim/Exports.def b/src/coreclr/hosts/coreshim/Exports.def
new file mode 100644
index 0000000000..fbdded0f69
--- /dev/null
+++ b/src/coreclr/hosts/coreshim/Exports.def
@@ -0,0 +1,3 @@
+EXPORTS
+ DllGetClassObject PRIVATE
+ DllCanUnloadNow PRIVATE \ No newline at end of file