From 9b4bbffd96b7b028be7dd01a72e1a0b97b2807b3 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 2 Aug 2019 22:43:43 -0700 Subject: [PATCH 1/5] Add support for ProgID registration --- src/corehost/cli/comhost/clsidmap.cpp | 10 +- src/corehost/cli/comhost/comhost.cpp | 168 +++++++++++++++++++++++--- src/corehost/cli/comhost/comhost.h | 2 + 3 files changed, 162 insertions(+), 18 deletions(-) diff --git a/src/corehost/cli/comhost/clsidmap.cpp b/src/corehost/cli/comhost/clsidmap.cpp index d2d343f860..c39880618c 100644 --- a/src/corehost/cli/comhost/clsidmap.cpp +++ b/src/corehost/cli/comhost/clsidmap.cpp @@ -77,10 +77,17 @@ namespace clsid_map_entry e{}; + e.clsid = clsidMaybe; + json::object &val = prop.second.as_object(); e.assembly = val.at(_X("assembly")).as_string(); e.type = val.at(_X("type")).as_string(); + // Check if a ProgID was defined. + auto prodIdMaybe = val.find(_X("progid")); + if (prodIdMaybe != val.cend()) + e.progid = prodIdMaybe->second.as_string(); + mapping[clsidMaybe] = std::move(e); } @@ -210,7 +217,8 @@ clsid_map comhost::get_clsid_map() // { // "": { // "assembly": , - // "type": + // "type": , + // "progid": [Optional] // }, // ... // } diff --git a/src/corehost/cli/comhost/comhost.cpp b/src/corehost/cli/comhost/comhost.cpp index c3cf23a144..7a227ccc0e 100644 --- a/src/corehost/cli/comhost/comhost.cpp +++ b/src/corehost/cli/comhost/comhost.cpp @@ -152,7 +152,8 @@ COM_API HRESULT STDMETHODCALLTYPE DllCanUnloadNow(void) namespace { - const WCHAR EntryKeyFmt[] = _X("SOFTWARE\\Classes\\CLSID\\%s"); + const WCHAR CLSIDKeyFmt[] = _X("SOFTWARE\\Classes\\CLSID\\%s"); + const WCHAR ProgIDKeyFmt[] = _X("SOFTWARE\\Classes\\%s"); struct OleStr : public std::unique_ptr::type, decltype(&::CoTaskMemFree)> { @@ -168,17 +169,10 @@ namespace { } }; - HRESULT RemoveClsid(_In_ REFCLSID clsid) + // Removes the key and all sub-keys + HRESULT RemoveRegistryKey(_In_z_ LPCWSTR regKeyPath) { - HRESULT hr; - - LPOLESTR clsidAsStrRaw; - RETURN_IF_FAILED(::StringFromCLSID(clsid, &clsidAsStrRaw)); - - OleStr clsidAsStr{ clsidAsStrRaw }; - - WCHAR regKeyPath[1024]; - ::swprintf_s(regKeyPath, EntryKeyFmt, clsidAsStr.get()); + assert(regKeyPath != nullptr); LSTATUS res; @@ -208,20 +202,123 @@ namespace return S_OK; } - HRESULT RegisterClsid(_In_ REFCLSID clsid, _In_opt_z_ const WCHAR *threadingModel) + HRESULT RemoveProgId(_In_ const comhost::clsid_map_entry &entry) + { + if (entry.progid.empty()) + return S_OK; + + HRESULT hr; + + WCHAR regKeyPath[1024]; + ::swprintf_s(regKeyPath, ProgIDKeyFmt, entry.progid.c_str()); + + // Remove ProgID key + RETURN_IF_FAILED(RemoveRegistryKey(regKeyPath)); + + return S_OK; + } + + HRESULT RemoveClsid(_In_ const comhost::clsid_map_entry &entry) + { + HRESULT hr; + + LPOLESTR clsidAsStrRaw; + RETURN_IF_FAILED(::StringFromCLSID(entry.clsid, &clsidAsStrRaw)); + + OleStr clsidAsStr{ clsidAsStrRaw }; + + WCHAR regKeyPath[1024]; + ::swprintf_s(regKeyPath, CLSIDKeyFmt, clsidAsStr.get()); + + // Remove CLSID key + RETURN_IF_FAILED(RemoveRegistryKey(regKeyPath)); + RETURN_IF_FAILED(RemoveProgId(entry)); + + return S_OK; + } + + HRESULT RegisterProgId(_In_ const comhost::clsid_map_entry &entry, _In_z_ LPOLESTR clsidAsStr) + { + assert(!entry.progid.empty() && clsidAsStr != nullptr); + + WCHAR regKeyProgIdPath[1024]; + ::swprintf_s(regKeyProgIdPath, ProgIDKeyFmt, entry.progid.c_str()); + + HKEY regKeyRaw; + DWORD disp; + LSTATUS res = ::RegCreateKeyExW( + HKEY_LOCAL_MACHINE, + regKeyProgIdPath, + 0, + REG_NONE, + REG_OPTION_NON_VOLATILE, + (KEY_READ | KEY_WRITE), + nullptr, + ®KeyRaw, + &disp); + if (res != ERROR_SUCCESS) + return __HRESULT_FROM_WIN32(res); + + RegKey regKey{ regKeyRaw }; + + // Set the default value for the ProgID to be the type name + res = ::RegSetValueExW( + regKey.get(), + nullptr, + 0, + REG_SZ, + reinterpret_cast(entry.type.c_str()), + static_cast(sizeof(entry.type.size() + 1) * sizeof(entry.type[0]))); + if (res != ERROR_SUCCESS) + return __HRESULT_FROM_WIN32(res); + + WCHAR regKeyProgIdClsidPath[ARRAYSIZE(regKeyProgIdPath) * 2]; + ::swprintf_s(regKeyProgIdClsidPath, L"%s\\CLSID", regKeyProgIdPath); + + HKEY regProgIdClsidRaw; + res = ::RegCreateKeyExW( + HKEY_LOCAL_MACHINE, + regKeyProgIdClsidPath, + 0, + REG_NONE, + REG_OPTION_NON_VOLATILE, + (KEY_READ | KEY_WRITE), + nullptr, + ®ProgIdClsidRaw, + &disp); + if (res != ERROR_SUCCESS) + return __HRESULT_FROM_WIN32(res); + + regKey.reset(regProgIdClsidRaw); + + // The value for the key is the CLSID + res = ::RegSetValueExW( + regKey.get(), + nullptr, + 0, + REG_SZ, + reinterpret_cast(clsidAsStr), + static_cast(::wcslen(clsidAsStr) + 1) * sizeof(clsidAsStr[0])); + if (res != ERROR_SUCCESS) + return __HRESULT_FROM_WIN32(res); + + return S_OK; + } + + HRESULT RegisterClsid(_In_ const comhost::clsid_map_entry &entry, _In_opt_z_ const WCHAR *threadingModel) { HRESULT hr; // Remove the CLSID in case it exists and has undesirable settings - RETURN_IF_FAILED(RemoveClsid(clsid)); + RETURN_IF_FAILED(RemoveClsid(entry)); LPOLESTR clsidAsStrRaw; - RETURN_IF_FAILED(::StringFromCLSID(clsid, &clsidAsStrRaw)); + RETURN_IF_FAILED(::StringFromCLSID(entry.clsid, &clsidAsStrRaw)); OleStr clsidAsStr{ clsidAsStrRaw }; WCHAR regKeyClsidPath[1024]; - ::swprintf_s(regKeyClsidPath, EntryKeyFmt, clsidAsStr.get()); + ::swprintf_s(regKeyClsidPath, CLSIDKeyFmt, clsidAsStr.get()); HKEY regKeyRaw; DWORD disp; @@ -309,6 +406,43 @@ namespace return __HRESULT_FROM_WIN32(res); } + // Check if a Prog ID is defined + if (!entry.progid.empty()) + { + // Register the ProgID in the CLSID key + WCHAR regKeyProgIdPath[ARRAYSIZE(regKeyClsidPath) * 2]; + ::swprintf_s(regKeyProgIdPath, L"%s\\ProgId", regKeyClsidPath); + + HKEY regProgIdKeyRaw; + res = ::RegCreateKeyExW( + HKEY_LOCAL_MACHINE, + regKeyProgIdPath, + 0, + REG_NONE, + REG_OPTION_NON_VOLATILE, + (KEY_READ | KEY_WRITE), + nullptr, + ®ProgIdKeyRaw, + &disp); + if (res != ERROR_SUCCESS) + return __HRESULT_FROM_WIN32(res); + + regKey.reset(regProgIdKeyRaw); + + // The default value for the key is the ProgID + res = ::RegSetValueExW( + regKey.get(), + nullptr, + 0, + REG_SZ, + reinterpret_cast(entry.progid.c_str()), + static_cast(entry.progid.size() + 1) * sizeof(entry.progid[0])); + if (res != ERROR_SUCCESS) + return __HRESULT_FROM_WIN32(res); + + RETURN_IF_FAILED(RegisterProgId(entry, clsidAsStr.get())); + } + return S_OK; } } @@ -345,7 +479,7 @@ COM_API HRESULT STDMETHODCALLTYPE DllRegisterServer(void) for (clsid_map::const_reference p : map) { // Register the CLSID in registry - RETURN_IF_FAILED(RegisterClsid(p.first, _X("Both"))); + RETURN_IF_FAILED(RegisterClsid(p.second, _X("Both"))); // Call user-defined register function cxt.class_id = p.first; @@ -395,7 +529,7 @@ COM_API HRESULT STDMETHODCALLTYPE DllUnregisterServer(void) RETURN_IF_FAILED(unreg(&cxt)); // Unregister the CLSID from registry - RETURN_IF_FAILED(RemoveClsid(p.first)); + RETURN_IF_FAILED(RemoveClsid(p.second)); } return S_OK; diff --git a/src/corehost/cli/comhost/comhost.h b/src/corehost/cli/comhost/comhost.h index 0b194bb8f1..7a895651f5 100644 --- a/src/corehost/cli/comhost/comhost.h +++ b/src/corehost/cli/comhost/comhost.h @@ -38,8 +38,10 @@ namespace comhost { struct clsid_map_entry { + CLSID clsid; pal::string_t assembly; pal::string_t type; + pal::string_t progid; }; using clsid_map = std::map; From 7bd1f323bfc543ef4482b9628ce2a19f92de3265 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 2 Aug 2019 22:54:36 -0700 Subject: [PATCH 2/5] Update COM activation documentation --- Documentation/design-docs/COM-activation.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/design-docs/COM-activation.md b/Documentation/design-docs/COM-activation.md index a0c71f6be5..5ebd412efc 100644 --- a/Documentation/design-docs/COM-activation.md +++ b/Documentation/design-docs/COM-activation.md @@ -116,13 +116,14 @@ The `DllRegisterServer()` and `DllUnregisterServer()` functions adhere to the [C ##### CLSID map format -The `CLSID` mapping manifest is a JSON format (`.clsidmap` extension when on disk) that defines a mapping from `CLSID` to an assembly name and type name tuple. Each `CLSID` mapping is a key in the outer JSON object. +The `CLSID` mapping manifest is a JSON format (`.clsidmap` extension when on disk) that defines a mapping from `CLSID` to an assembly name and type name tuple as well as an optional [ProgID](https://docs.microsoft.com/windows/win32/com/-progid--key). Each `CLSID` mapping is a key in the outer JSON object. ``` json { "": { "assembly": "", - "type": "" + "type": "", + "progid": "" } } ``` @@ -132,9 +133,9 @@ The `CLSID` mapping manifest is a JSON format (`.clsidmap` extension when on dis 1) A new .NET Core class library project is created using [`dotnet.exe`][dotnet_link]. 1) A class is defined that has the [`GuidAttribute("")`][guid_link] and the [`ComVisibleAttribute(true)`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.comvisibleattribute). - In .NET Core, unlike .NET Framework, there is no generated class interface generation (i.e. `IClassX`). This means it is advantageous for users to have the class implement a marshalable interface. -1) The `UseComHost` property is added to the project file. - - i.e. `true` -1) During class project build, the following actions occur if the `UseComHost` property is `true`: +1) The `EnableComHosting` property is added to the project file. + - i.e. `true` +1) During class project build, the following actions occur if the `EnableComHosting` property is `true`: 1) A `.runtimeconfig.json` file is created for the assembly. 1) The resulting assembly is interrogated for classes with the attributes defined above and a `CLSID` map is created on disk (`.clsidmap`). 1) The target Framework's shim binary (i.e. `comhost.dll`) is copied to the local output directory. From 79cf648101304b447dd2e5a2dd15c781a61b5899 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 2 Aug 2019 22:57:27 -0700 Subject: [PATCH 3/5] Match casing in `Clsid` variables --- src/corehost/cli/comhost/comhost.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/corehost/cli/comhost/comhost.cpp b/src/corehost/cli/comhost/comhost.cpp index 7a227ccc0e..2784f265bf 100644 --- a/src/corehost/cli/comhost/comhost.cpp +++ b/src/corehost/cli/comhost/comhost.cpp @@ -152,7 +152,7 @@ COM_API HRESULT STDMETHODCALLTYPE DllCanUnloadNow(void) namespace { - const WCHAR CLSIDKeyFmt[] = _X("SOFTWARE\\Classes\\CLSID\\%s"); + const WCHAR ClsidKeyFmt[] = _X("SOFTWARE\\Classes\\CLSID\\%s"); const WCHAR ProgIDKeyFmt[] = _X("SOFTWARE\\Classes\\%s"); struct OleStr : public std::unique_ptr::type, decltype(&::CoTaskMemFree)> @@ -228,7 +228,7 @@ namespace OleStr clsidAsStr{ clsidAsStrRaw }; WCHAR regKeyPath[1024]; - ::swprintf_s(regKeyPath, CLSIDKeyFmt, clsidAsStr.get()); + ::swprintf_s(regKeyPath, ClsidKeyFmt, clsidAsStr.get()); // Remove CLSID key RETURN_IF_FAILED(RemoveRegistryKey(regKeyPath)); @@ -318,7 +318,7 @@ namespace OleStr clsidAsStr{ clsidAsStrRaw }; WCHAR regKeyClsidPath[1024]; - ::swprintf_s(regKeyClsidPath, CLSIDKeyFmt, clsidAsStr.get()); + ::swprintf_s(regKeyClsidPath, ClsidKeyFmt, clsidAsStr.get()); HKEY regKeyRaw; DWORD disp; From d9dedca0779e3e73509911f2ce1499f167d600f9 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 5 Aug 2019 11:16:16 -0700 Subject: [PATCH 4/5] Review feedback --- src/corehost/cli/comhost/comhost.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/corehost/cli/comhost/comhost.cpp b/src/corehost/cli/comhost/comhost.cpp index 2784f265bf..37b4e7a724 100644 --- a/src/corehost/cli/comhost/comhost.cpp +++ b/src/corehost/cli/comhost/comhost.cpp @@ -262,6 +262,8 @@ namespace RegKey regKey{ regKeyRaw }; // Set the default value for the ProgID to be the type name + // This valid is used purely for user consumption and has no + // functional impact. res = ::RegSetValueExW( regKey.get(), nullptr, @@ -411,7 +413,7 @@ namespace { // Register the ProgID in the CLSID key WCHAR regKeyProgIdPath[ARRAYSIZE(regKeyClsidPath) * 2]; - ::swprintf_s(regKeyProgIdPath, L"%s\\ProgId", regKeyClsidPath); + ::swprintf_s(regKeyProgIdPath, L"%s\\ProgID", regKeyClsidPath); HKEY regProgIdKeyRaw; res = ::RegCreateKeyExW( From 336c9afe0c7e46e5bf9f5f993e7b448a038edde4 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 5 Aug 2019 11:40:28 -0700 Subject: [PATCH 5/5] Update comhost.cpp --- src/corehost/cli/comhost/comhost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corehost/cli/comhost/comhost.cpp b/src/corehost/cli/comhost/comhost.cpp index 37b4e7a724..ab1f73429c 100644 --- a/src/corehost/cli/comhost/comhost.cpp +++ b/src/corehost/cli/comhost/comhost.cpp @@ -262,7 +262,7 @@ namespace RegKey regKey{ regKeyRaw }; // Set the default value for the ProgID to be the type name - // This valid is used purely for user consumption and has no + // This value is only used for user consumption and has no // functional impact. res = ::RegSetValueExW( regKey.get(),