From 06b6b9ccf6980a18839a05c8bc7b4d49ca122312 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Tue, 7 Feb 2023 08:35:31 -0500 Subject: [PATCH] Added new registry modification code as suggested in solution 2 of #115. --- src/shellextension/CMakeLists.txt | 2 + src/shellextension/reg.cpp | 349 ++++++++++++++++++++++++++++++ src/shellextension/reg.h | 119 ++++++++++ 3 files changed, 470 insertions(+) create mode 100644 src/shellextension/reg.cpp create mode 100644 src/shellextension/reg.h diff --git a/src/shellextension/CMakeLists.txt b/src/shellextension/CMakeLists.txt index 01dc06a0..1dd9bf99 100644 --- a/src/shellextension/CMakeLists.txt +++ b/src/shellextension/CMakeLists.txt @@ -7,6 +7,8 @@ add_library(sa.shellextension SHARED ${CMAKE_SOURCE_DIR}/src/resource.rc.in ${CMAKE_SOURCE_DIR}/src/version.rc.in logger_stub.h + reg.cpp + reg.h shellext.cpp shellext.h shellext.def diff --git a/src/shellextension/reg.cpp b/src/shellextension/reg.cpp new file mode 100644 index 00000000..99db0acc --- /dev/null +++ b/src/shellextension/reg.cpp @@ -0,0 +1,349 @@ +/****************************** Module Header ******************************\ +Module Name: Reg.cpp +Project: CppShellExtContextMenuHandler +Copyright (c) Microsoft Corporation. + +The file implements the reusable helper functions to register and unregister +in-process COM components and shell context menu handlers in the registry. + +RegisterInprocServer - register the in-process component in the registry. +UnregisterInprocServer - unregister the in-process component in the registry. +RegisterShellExtContextMenuHandler - register the context menu handler. +UnregisterShellExtContextMenuHandler - unregister the context menu handler. + +This source is subject to the Microsoft Public License. +See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL. +All other rights reserved. + +THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#include "Reg.h" +#include + + +#pragma region Registry Helper Functions + +// +// FUNCTION: SetHKCRRegistryKeyAndValue +// +// PURPOSE: The function creates a HKCR registry key and sets the specified +// registry value. +// +// PARAMETERS: +// * pszSubKey - specifies the registry key under HKCR. If the key does not +// exist, the function will create the registry key. +// * pszValueName - specifies the registry value to be set. If pszValueName +// is NULL, the function will set the default value. +// * pszData - specifies the string data of the registry value. +// +// RETURN VALUE: +// If the function succeeds, it returns S_OK. Otherwise, it returns an +// HRESULT error code. +// +HRESULT SetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName, + PCWSTR pszData) +{ + HRESULT hr; + HKEY hKey = NULL; + + // Creates the specified registry key. If the key already exists, the + // function opens it. + hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_CLASSES_ROOT, pszSubKey, 0, + NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL)); + + if (SUCCEEDED(hr)) + { + if (pszData != NULL) + { + // Set the specified value of the key. + DWORD cbData = lstrlenW(pszData) * sizeof(*pszData); + hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, pszValueName, 0, + REG_SZ, reinterpret_cast(pszData), cbData)); + } + + RegCloseKey(hKey); + } + + return hr; +} + + +// +// FUNCTION: GetHKCRRegistryKeyAndValue +// +// PURPOSE: The function opens a HKCR registry key and gets the data for the +// specified registry value name. +// +// PARAMETERS: +// * pszSubKey - specifies the registry key under HKCR. If the key does not +// exist, the function returns an error. +// * pszValueName - specifies the registry value to be retrieved. If +// pszValueName is NULL, the function will get the default value. +// * pszData - a pointer to a buffer that receives the value's string data. +// * cbData - specifies the size of the buffer in bytes. +// +// RETURN VALUE: +// If the function succeeds, it returns S_OK. Otherwise, it returns an +// HRESULT error code. For example, if the specified registry key does not +// exist or the data for the specified value name was not set, the function +// returns COR_E_FILENOTFOUND (0x80070002). +// +HRESULT GetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName, + PWSTR pszData, DWORD cbData) +{ + HRESULT hr; + HKEY hKey = NULL; + + // Try to open the specified registry key. + hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_CLASSES_ROOT, pszSubKey, 0, + KEY_READ, &hKey)); + + if (SUCCEEDED(hr)) + { + // Get the data for the specified value name. + hr = HRESULT_FROM_WIN32(RegQueryValueExW(hKey, pszValueName, NULL, + NULL, reinterpret_cast(pszData), &cbData)); + + RegCloseKey(hKey); + } + + return hr; +} + +#pragma endregion + + +// +// FUNCTION: RegisterInprocServer +// +// PURPOSE: Register the in-process component in the registry. +// +// PARAMETERS: +// * pszModule - Path of the module that contains the component +// * clsid - Class ID of the component +// * pszFriendlyName - Friendly name +// * pszThreadModel - Threading model +// +// NOTE: The function creates the HKCR\CLSID\{} key in the registry. +// +// HKCR +// { +// NoRemove CLSID +// { +// ForceRemove {} = s '' +// { +// InprocServer32 = s '%MODULE%' +// { +// val ThreadingModel = s '' +// } +// } +// } +// } +// +HRESULT RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid, + PCWSTR pszFriendlyName, PCWSTR pszThreadModel) +{ + if (pszModule == NULL || pszThreadModel == NULL) + { + return E_INVALIDARG; + } + + HRESULT hr; + + wchar_t szCLSID[MAX_PATH]; + StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID)); + + wchar_t szSubkey[MAX_PATH]; + + // Create the HKCR\CLSID\{} key. + hr = StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID); + if (SUCCEEDED(hr)) + { + hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszFriendlyName); + + // Create the HKCR\CLSID\{}\InprocServer32 key. + if (SUCCEEDED(hr)) + { + hr = StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), + L"CLSID\\%s\\InprocServer32", szCLSID); + if (SUCCEEDED(hr)) + { + // Set the default value of the InprocServer32 key to the + // path of the COM module. + hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszModule); + if (SUCCEEDED(hr)) + { + // Set the threading model of the component. + hr = SetHKCRRegistryKeyAndValue(szSubkey, + L"ThreadingModel", pszThreadModel); + } + } + } + } + + return hr; +} + + +// +// FUNCTION: UnregisterInprocServer +// +// PURPOSE: Unegister the in-process component in the registry. +// +// PARAMETERS: +// * clsid - Class ID of the component +// +// NOTE: The function deletes the HKCR\CLSID\{} key in the registry. +// +HRESULT UnregisterInprocServer(const CLSID& clsid) +{ + HRESULT hr = S_OK; + + wchar_t szCLSID[MAX_PATH]; + StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID)); + + wchar_t szSubkey[MAX_PATH]; + + // Delete the HKCR\CLSID\{} key. + hr = StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID); + if (SUCCEEDED(hr)) + { + hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CLASSES_ROOT, szSubkey)); + } + + return hr; +} + + +// +// FUNCTION: RegisterShellExtContextMenuHandler +// +// PURPOSE: Register the context menu handler. +// +// PARAMETERS: +// * pszFileType - The file type that the context menu handler is +// associated with. For example, '*' means all file types; '.txt' means +// all .txt files. The parameter must not be NULL. +// * clsid - Class ID of the component +// * pszFriendlyName - Friendly name +// +// NOTE: The function creates the following key in the registry. +// +// HKCR +// { +// NoRemove +// { +// NoRemove shellex +// { +// NoRemove ContextMenuHandlers +// { +// {} = s '' +// } +// } +// } +// } +// +HRESULT RegisterShellExtContextMenuHandler( + PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName) +{ + if (pszFileType == NULL) + { + return E_INVALIDARG; + } + + HRESULT hr; + + wchar_t szCLSID[MAX_PATH]; + StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID)); + + wchar_t szSubkey[MAX_PATH]; + + // If pszFileType starts with '.', try to read the default value of the + // HKCR\ key which contains the ProgID to which the file type + // is linked. + if (*pszFileType == L'.') + { + wchar_t szDefaultVal[260]; + hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal, + sizeof(szDefaultVal)); + + // If the key exists and its default value is not empty, use the + // ProgID as the file type. + if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0') + { + pszFileType = szDefaultVal; + } + } + + // Create the key HKCR\\shellex\ContextMenuHandlers\{} + hr = StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), + L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, szCLSID); + if (SUCCEEDED(hr)) + { + // Set the default value of the key. + hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszFriendlyName); + } + + return hr; +} + + +// +// FUNCTION: UnregisterShellExtContextMenuHandler +// +// PURPOSE: Unregister the context menu handler. +// +// PARAMETERS: +// * pszFileType - The file type that the context menu handler is +// associated with. For example, '*' means all file types; '.txt' means +// all .txt files. The parameter must not be NULL. +// * clsid - Class ID of the component +// +// NOTE: The function removes the {} key under +// HKCR\\shellex\ContextMenuHandlers in the registry. +// +HRESULT UnregisterShellExtContextMenuHandler( + PCWSTR pszFileType, const CLSID& clsid) +{ + if (pszFileType == NULL) + { + return E_INVALIDARG; + } + + HRESULT hr; + + wchar_t szCLSID[MAX_PATH]; + StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID)); + + wchar_t szSubkey[MAX_PATH]; + + // If pszFileType starts with '.', try to read the default value of the + // HKCR\ key which contains the ProgID to which the file type + // is linked. + if (*pszFileType == L'.') + { + wchar_t szDefaultVal[260]; + hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal, + sizeof(szDefaultVal)); + + // If the key exists and its default value is not empty, use the + // ProgID as the file type. + if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0') + { + pszFileType = szDefaultVal; + } + } + + // Remove the HKCR\\shellex\ContextMenuHandlers\{} key. + hr = StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), + L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, szCLSID); + if (SUCCEEDED(hr)) + { + hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CLASSES_ROOT, szSubkey)); + } + + return hr; +} \ No newline at end of file diff --git a/src/shellextension/reg.h b/src/shellextension/reg.h new file mode 100644 index 00000000..e4d99815 --- /dev/null +++ b/src/shellextension/reg.h @@ -0,0 +1,119 @@ +/****************************** Module Header ******************************\ +Module Name: Reg.h +Project: CppShellExtContextMenuHandler +Copyright (c) Microsoft Corporation. + +The file declares reusable helper functions to register and unregister +in-process COM components and shell context menu handlers in the registry. + +RegisterInprocServer - register the in-process component in the registry. +UnregisterInprocServer - unregister the in-process component in the registry. +RegisterShellExtContextMenuHandler - register the context menu handler. +UnregisterShellExtContextMenuHandler - unregister the context menu handler. + +This source is subject to the Microsoft Public License. +See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL. +All other rights reserved. + +THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#pragma once + +#include + + +// +// FUNCTION: RegisterInprocServer +// +// PURPOSE: Register the in-process component in the registry. +// +// PARAMETERS: +// * pszModule - Path of the module that contains the component +// * clsid - Class ID of the component +// * pszFriendlyName - Friendly name +// * pszThreadModel - Threading model +// +// NOTE: The function creates the HKCR\CLSID\{} key in the registry. +// +// HKCR +// { +// NoRemove CLSID +// { +// ForceRemove {} = s '' +// { +// InprocServer32 = s '%MODULE%' +// { +// val ThreadingModel = s '' +// } +// } +// } +// } +// +HRESULT RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid, + PCWSTR pszFriendlyName, PCWSTR pszThreadModel); + + +// +// FUNCTION: UnregisterInprocServer +// +// PURPOSE: Unegister the in-process component in the registry. +// +// PARAMETERS: +// * clsid - Class ID of the component +// +// NOTE: The function deletes the HKCR\CLSID\{} key in the registry. +// +HRESULT UnregisterInprocServer(const CLSID& clsid); + + +// +// FUNCTION: RegisterShellExtContextMenuHandler +// +// PURPOSE: Register the context menu handler. +// +// PARAMETERS: +// * pszFileType - The file type that the context menu handler is +// associated with. For example, '*' means all file types; '.txt' means +// all .txt files. The parameter must not be NULL. +// * clsid - Class ID of the component +// * pszFriendlyName - Friendly name +// +// NOTE: The function creates the following key in the registry. +// +// HKCR +// { +// NoRemove +// { +// NoRemove shellex +// { +// NoRemove ContextMenuHandlers +// { +// {} = s '' +// } +// } +// } +// } +// +HRESULT RegisterShellExtContextMenuHandler( + PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName); + + +// +// FUNCTION: UnregisterShellExtContextMenuHandler +// +// PURPOSE: Unregister the context menu handler. +// +// PARAMETERS: +// * pszFileType - The file type that the context menu handler is +// associated with. For example, '*' means all file types; '.txt' means +// all .txt files. The parameter must not be NULL. +// * clsid - Class ID of the component +// +// NOTE: The function removes the {} key under +// HKCR\\shellex\ContextMenuHandlers in the registry. +// +HRESULT UnregisterShellExtContextMenuHandler( + PCWSTR pszFileType, const CLSID& clsid); \ No newline at end of file