diff options
author | Aaron Robinson <arobins@microsoft.com> | 2018-09-10 17:24:49 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-10 17:24:49 -0700 |
commit | fc3378095f04946815e627a5ab70b528a898abe6 (patch) | |
tree | 44a1e2cd85c2a5c369bf6f754a0379a0751ccb66 /src | |
parent | efd7220234aacef4af25a747797984d43ba5b961 (diff) | |
download | coreclr-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.csproj | 1 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComActivator.cs | 261 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs | 7 | ||||
-rw-r--r-- | src/coreclr/hosts/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/coreclr/hosts/coreshim/CMakeLists.txt | 25 | ||||
-rw-r--r-- | src/coreclr/hosts/coreshim/ComActivation.cpp | 91 | ||||
-rw-r--r-- | src/coreclr/hosts/coreshim/CoreShim.cpp | 293 | ||||
-rw-r--r-- | src/coreclr/hosts/coreshim/CoreShim.h | 179 | ||||
-rw-r--r-- | src/coreclr/hosts/coreshim/Exports.def | 3 |
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 |