summaryrefslogtreecommitdiff
path: root/src/coreclr
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/coreclr
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/coreclr')
-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
6 files changed, 592 insertions, 0 deletions
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