Skip to content

Commit 9c20b9e

Browse files
authored
Merge pull request #172 from end2endzone/feature-issue148 #148
Feature issue 148
2 parents 8ca8c57 + 56f1420 commit 9c20b9e

File tree

7 files changed

+782
-0
lines changed

7 files changed

+782
-0
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Changes for 0.10.0
55
* Fixed issue #31 : (twice) Error in logs for CContextMenu::GetCommandString()
66
* Fixed issue #109: Implement default and verbose logging.
77
* Fixed issue #110: Create a simple command line arguments debugging application.
8+
* Fixed issue #148: Can't uninstall.
89
* Fixed issue #150: ico icon (that do not specifically force index=0) are not working.
910
* Fixed issue #157: Compilation fails on Github Action: `fatal error C1083: Cannot open include file: 'atlbase.h': No such file or directory`.
1011
* Fixed issue #158: Compilation fails on Github Action: `CPack error : Problem running WiX.`.

src/shellextension/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ add_library(sa.shellextension SHARED
1111
CCriticalSection.cpp
1212
CCriticalSection.h
1313
dllmain.cpp
14+
Reg.cpp
15+
Reg.h
1416
resource.h
1517
shellext.def
1618
shellext.idl
1719
shellext.rgs
1820
stdafx.cpp
1921
stdafx.h
2022
targetver.h
23+
TypeLibHelper.cpp
24+
TypeLibHelper.h
2125
utils.cpp
2226
utils.h
2327
)
@@ -28,6 +32,9 @@ source_group("Source Files" FILES "shellext.rgs")
2832
# Force CMAKE_DEBUG_POSTFIX for executables
2933
set_target_properties(sa.shellextension PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
3034

35+
# Force UNICODE for target
36+
target_compile_definitions(sa.shellextension PRIVATE -D_UNICODE -DUNICODE)
37+
3138
# Define include directories for the library.
3239
target_include_directories(sa.shellextension
3340
PUBLIC

src/shellextension/Reg.cpp

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
/****************************** Module Header ******************************\
2+
Module Name: Reg.cpp
3+
Project: CppShellExtContextMenuHandler
4+
Copyright (c) Microsoft Corporation.
5+
6+
The file implements the reusable helper functions to register and unregister
7+
in-process COM components and shell context menu handlers in the registry.
8+
9+
RegisterInprocServer - register the in-process component in the registry.
10+
UnregisterInprocServer - unregister the in-process component in the registry.
11+
RegisterShellExtContextMenuHandler - register the context menu handler.
12+
UnregisterShellExtContextMenuHandler - unregister the context menu handler.
13+
14+
This source is subject to the Microsoft Public License.
15+
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
16+
All other rights reserved.
17+
18+
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
19+
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
20+
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
21+
\***************************************************************************/
22+
23+
#include "Reg.h"
24+
#include <strsafe.h>
25+
#include <Shlwapi.h>
26+
27+
28+
#pragma region Registry Helper Functions
29+
30+
//
31+
// FUNCTION: SetHKCRRegistryKeyAndValue
32+
//
33+
// PURPOSE: The function creates a HKCR registry key and sets the specified
34+
// registry value.
35+
//
36+
// PARAMETERS:
37+
// * pszSubKey - specifies the registry key under HKCR. If the key does not
38+
// exist, the function will create the registry key.
39+
// * pszValueName - specifies the registry value to be set. If pszValueName
40+
// is NULL, the function will set the default value.
41+
// * pszData - specifies the string data of the registry value.
42+
//
43+
// RETURN VALUE:
44+
// If the function succeeds, it returns S_OK. Otherwise, it returns an
45+
// HRESULT error code.
46+
//
47+
HRESULT SetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName,
48+
PCWSTR pszData)
49+
{
50+
HRESULT hr;
51+
HKEY hKey = NULL;
52+
53+
// Creates the specified registry key. If the key already exists, the
54+
// function opens it.
55+
hr = HRESULT_FROM_WIN32(RegCreateKeyEx(HKEY_CLASSES_ROOT, pszSubKey, 0,
56+
NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL));
57+
58+
if (SUCCEEDED(hr))
59+
{
60+
if (pszData != NULL)
61+
{
62+
// Set the specified value of the key.
63+
DWORD cbData = lstrlen(pszData) * sizeof(*pszData);
64+
hr = HRESULT_FROM_WIN32(RegSetValueEx(hKey, pszValueName, 0,
65+
REG_SZ, reinterpret_cast<const BYTE *>(pszData), cbData));
66+
}
67+
68+
RegCloseKey(hKey);
69+
}
70+
71+
return hr;
72+
}
73+
74+
75+
//
76+
// FUNCTION: GetHKCRRegistryKeyAndValue
77+
//
78+
// PURPOSE: The function opens a HKCR registry key and gets the data for the
79+
// specified registry value name.
80+
//
81+
// PARAMETERS:
82+
// * pszSubKey - specifies the registry key under HKCR. If the key does not
83+
// exist, the function returns an error.
84+
// * pszValueName - specifies the registry value to be retrieved. If
85+
// pszValueName is NULL, the function will get the default value.
86+
// * pszData - a pointer to a buffer that receives the value's string data.
87+
// * cbData - specifies the size of the buffer in bytes.
88+
//
89+
// RETURN VALUE:
90+
// If the function succeeds, it returns S_OK. Otherwise, it returns an
91+
// HRESULT error code. For example, if the specified registry key does not
92+
// exist or the data for the specified value name was not set, the function
93+
// returns COR_E_FILENOTFOUND (0x80070002).
94+
//
95+
HRESULT GetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName,
96+
PWSTR pszData, DWORD cbData)
97+
{
98+
HRESULT hr;
99+
HKEY hKey = NULL;
100+
101+
// Try to open the specified registry key.
102+
hr = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CLASSES_ROOT, pszSubKey, 0,
103+
KEY_READ, &hKey));
104+
105+
if (SUCCEEDED(hr))
106+
{
107+
// Get the data for the specified value name.
108+
hr = HRESULT_FROM_WIN32(RegQueryValueEx(hKey, pszValueName, NULL,
109+
NULL, reinterpret_cast<LPBYTE>(pszData), &cbData));
110+
111+
RegCloseKey(hKey);
112+
}
113+
114+
return hr;
115+
}
116+
117+
#pragma endregion
118+
119+
120+
//
121+
// FUNCTION: RegisterInprocServer
122+
//
123+
// PURPOSE: Register the in-process component in the registry.
124+
//
125+
// PARAMETERS:
126+
// * pszModule - Path of the module that contains the component
127+
// * clsid - Class ID of the component
128+
// * pszFriendlyName - Friendly name
129+
// * pszThreadModel - Threading model
130+
//
131+
// NOTE: The function creates the HKCR\CLSID\{<CLSID>} key in the registry.
132+
//
133+
// HKCR
134+
// {
135+
// NoRemove CLSID
136+
// {
137+
// ForceRemove {<CLSID>} = s '<Friendly Name>'
138+
// {
139+
// InprocServer32 = s '%MODULE%'
140+
// {
141+
// val ThreadingModel = s '<Thread Model>'
142+
// }
143+
// }
144+
// }
145+
// }
146+
//
147+
HRESULT RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid,
148+
PCWSTR pszFriendlyName, PCWSTR pszThreadModel)
149+
{
150+
if (pszModule == NULL || pszThreadModel == NULL)
151+
{
152+
return E_INVALIDARG;
153+
}
154+
155+
HRESULT hr;
156+
157+
wchar_t szCLSID[MAX_PATH];
158+
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
159+
160+
wchar_t szSubkey[MAX_PATH];
161+
162+
// Create the HKCR\CLSID\{<CLSID>} key.
163+
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
164+
if (SUCCEEDED(hr))
165+
{
166+
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszFriendlyName);
167+
168+
// Create the HKCR\CLSID\{<CLSID>}\InprocServer32 key.
169+
if (SUCCEEDED(hr))
170+
{
171+
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
172+
L"CLSID\\%s\\InprocServer32", szCLSID);
173+
if (SUCCEEDED(hr))
174+
{
175+
// Set the default value of the InprocServer32 key to the
176+
// path of the COM module.
177+
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszModule);
178+
if (SUCCEEDED(hr))
179+
{
180+
// Set the threading model of the component.
181+
hr = SetHKCRRegistryKeyAndValue(szSubkey,
182+
L"ThreadingModel", pszThreadModel);
183+
}
184+
}
185+
}
186+
}
187+
188+
return hr;
189+
}
190+
191+
192+
//
193+
// FUNCTION: UnregisterInprocServer
194+
//
195+
// PURPOSE: Unegister the in-process component in the registry.
196+
//
197+
// PARAMETERS:
198+
// * clsid - Class ID of the component
199+
//
200+
// NOTE: The function deletes the HKCR\CLSID\{<CLSID>} key in the registry.
201+
//
202+
HRESULT UnregisterInprocServer(const CLSID& clsid)
203+
{
204+
HRESULT hr = S_OK;
205+
206+
wchar_t szCLSID[MAX_PATH];
207+
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
208+
209+
wchar_t szSubkey[MAX_PATH];
210+
211+
// Delete the HKCR\CLSID\{<CLSID>} key.
212+
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
213+
if (SUCCEEDED(hr))
214+
{
215+
hr = HRESULT_FROM_WIN32(SHDeleteKey(HKEY_CLASSES_ROOT, szSubkey));
216+
}
217+
218+
return hr;
219+
}
220+
221+
222+
//
223+
// FUNCTION: RegisterShellExtContextMenuHandler
224+
//
225+
// PURPOSE: Register the context menu handler.
226+
//
227+
// PARAMETERS:
228+
// * pszFileType - The file type that the context menu handler is
229+
// associated with. For example, '*' means all file types; '.txt' means
230+
// all .txt files. The parameter must not be NULL.
231+
// * clsid - Class ID of the component
232+
// * pszFriendlyName - Friendly name
233+
//
234+
// NOTE: The function creates the following key in the registry.
235+
//
236+
// HKCR
237+
// {
238+
// NoRemove <File Type>
239+
// {
240+
// NoRemove shellex
241+
// {
242+
// NoRemove ContextMenuHandlers
243+
// {
244+
// {<CLSID>} = s '<Friendly Name>'
245+
// }
246+
// }
247+
// }
248+
// }
249+
//
250+
HRESULT RegisterShellExtContextMenuHandler(
251+
PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName)
252+
{
253+
if (pszFileType == NULL)
254+
{
255+
return E_INVALIDARG;
256+
}
257+
258+
HRESULT hr;
259+
260+
wchar_t szCLSID[MAX_PATH];
261+
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
262+
263+
wchar_t szSubkey[MAX_PATH];
264+
265+
// If pszFileType starts with '.', try to read the default value of the
266+
// HKCR\<File Type> key which contains the ProgID to which the file type
267+
// is linked.
268+
if (*pszFileType == L'.')
269+
{
270+
wchar_t szDefaultVal[260];
271+
hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal,
272+
sizeof(szDefaultVal));
273+
274+
// If the key exists and its default value is not empty, use the
275+
// ProgID as the file type.
276+
if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0')
277+
{
278+
pszFileType = szDefaultVal;
279+
}
280+
}
281+
282+
// Create the key HKCR\<File Type>\shellex\ContextMenuHandlers\{<CLSID>}
283+
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
284+
L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, szCLSID);
285+
if (SUCCEEDED(hr))
286+
{
287+
// Set the default value of the key.
288+
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszFriendlyName);
289+
}
290+
291+
return hr;
292+
}
293+
294+
295+
//
296+
// FUNCTION: UnregisterShellExtContextMenuHandler
297+
//
298+
// PURPOSE: Unregister the context menu handler.
299+
//
300+
// PARAMETERS:
301+
// * pszFileType - The file type that the context menu handler is
302+
// associated with. For example, '*' means all file types; '.txt' means
303+
// all .txt files. The parameter must not be NULL.
304+
// * clsid - Class ID of the component
305+
//
306+
// NOTE: The function removes the {<CLSID>} key under
307+
// HKCR\<File Type>\shellex\ContextMenuHandlers in the registry.
308+
//
309+
HRESULT UnregisterShellExtContextMenuHandler(
310+
PCWSTR pszFileType, const CLSID& clsid)
311+
{
312+
if (pszFileType == NULL)
313+
{
314+
return E_INVALIDARG;
315+
}
316+
317+
HRESULT hr;
318+
319+
wchar_t szCLSID[MAX_PATH];
320+
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
321+
322+
wchar_t szSubkey[MAX_PATH];
323+
324+
// If pszFileType starts with '.', try to read the default value of the
325+
// HKCR\<File Type> key which contains the ProgID to which the file type
326+
// is linked.
327+
if (*pszFileType == L'.')
328+
{
329+
wchar_t szDefaultVal[260];
330+
hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal,
331+
sizeof(szDefaultVal));
332+
333+
// If the key exists and its default value is not empty, use the
334+
// ProgID as the file type.
335+
if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0')
336+
{
337+
pszFileType = szDefaultVal;
338+
}
339+
}
340+
341+
// Remove the HKCR\<File Type>\shellex\ContextMenuHandlers\{<CLSID>} key.
342+
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
343+
L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, szCLSID);
344+
if (SUCCEEDED(hr))
345+
{
346+
hr = HRESULT_FROM_WIN32(SHDeleteKey(HKEY_CLASSES_ROOT, szSubkey));
347+
}
348+
349+
return hr;
350+
}

0 commit comments

Comments
 (0)