Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Windows_FileRegistration.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
7
8#include "Windows_FileRegistration.h"
9
10using namespace Stroika::Foundation;
11
12using namespace Stroika::Frameworks;
13using namespace Stroika::Frameworks::Led;
14using namespace Stroika::Frameworks::Led::Platform;
15
17
18#if qStroika_Foundation_Common_Platform_Windows
19
20inline void ThrowIfRegError (LONG e)
21{
22 if (e != ERROR_SUCCESS) {
23 throw e;
24 }
25}
26
27/*
28 ********************************************************************************
29 ************ Win32FileAssociationRegistrationHelper::KeyHolder *****************
30 ********************************************************************************
31 */
32inline Win32FileAssociationRegistrationHelper::KeyHolder::KeyHolder (HKEY hk)
33 : fKey (hk)
34{
35}
36inline Win32FileAssociationRegistrationHelper::KeyHolder::KeyHolder (HKEY baseKey, LPCTSTR lpSubKey)
37 : fKey (NULL)
38{
39 ThrowIfRegError (::RegOpenKey (baseKey, lpSubKey, &fKey));
40}
41inline Win32FileAssociationRegistrationHelper::KeyHolder::KeyHolder (HKEY baseKey, LPCTSTR lpSubKey, [[maybe_unused]] CreateIfNotThereFlag createIfNotThereFlag)
42 : fKey (NULL)
43{
44 Assert (createIfNotThereFlag == eCreateIfNotThere);
45 DWORD ignored = 0;
46 ThrowIfRegError (::RegCreateKeyEx (baseKey, lpSubKey, 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, &fKey, &ignored));
47}
48inline Win32FileAssociationRegistrationHelper::KeyHolder::~KeyHolder ()
49{
50 ::RegCloseKey (fKey);
51}
52inline Win32FileAssociationRegistrationHelper::KeyHolder::operator HKEY ()
53{
54 return fKey;
55}
56
57/*
58 ********************************************************************************
59 ***************** Win32FileAssociationRegistrationHelper ***********************
60 ********************************************************************************
61 */
62Win32FileAssociationRegistrationHelper::Win32FileAssociationRegistrationHelper (const SDKString& fileSuffix)
63 : fFileSuffix (fileSuffix)
64{
65}
66
67SDKString Win32FileAssociationRegistrationHelper::GetAssociatedProgID () const
68{
69 try {
70 LONG itemLen = 0;
71 ThrowIfRegError (::RegQueryValue (HKEY_CLASSES_ROOT, fFileSuffix.c_str (), NULL, &itemLen));
72 StackBuffer<Characters::SDKChar> buf{Memory::eUninitialized, static_cast<size_t> (itemLen + 1)};
73 ThrowIfRegError (::RegQueryValue (HKEY_CLASSES_ROOT, fFileSuffix.c_str (), buf.data (), &itemLen));
74 return SDKString{buf.data ()};
75 }
76 catch (...) {
77 return SDKString{};
78 }
79}
80
81SDKString Win32FileAssociationRegistrationHelper::GetAssociatedDefaultIcon () const
82{
83 try {
84 SDKString progID = GetAssociatedProgID ();
85 KeyHolder progIDKey (HKEY_CLASSES_ROOT, progID.c_str ());
86 LONG itemLen = 0;
87 ThrowIfRegError (::RegQueryValue (progIDKey, Led_SDK_TCHAROF ("DefaultIcon"), NULL, &itemLen));
88 StackBuffer<Characters::SDKChar> buf{Memory::eUninitialized, static_cast<size_t> (itemLen + 1)};
89 ThrowIfRegError (::RegQueryValue (progIDKey, Led_SDK_TCHAROF ("DefaultIcon"), buf.data (), &itemLen));
90 return SDKString{buf.data ()};
91 }
92 catch (...) {
93 return SDKString{};
94 }
95}
96
97SDKString Win32FileAssociationRegistrationHelper::GetAssociatedEditCommand () const
98{
99 try {
100 SDKString progID = GetAssociatedProgID ();
101 KeyHolder progIDKey (HKEY_CLASSES_ROOT, progID.c_str ());
102 KeyHolder shellKey (progIDKey, Led_SDK_TCHAROF ("shell"));
103 KeyHolder openKey (shellKey, Led_SDK_TCHAROF ("edit"));
104 LONG itemLen = 0;
105 ThrowIfRegError (::RegQueryValue (openKey, Led_SDK_TCHAROF ("command"), NULL, &itemLen));
106 StackBuffer<Characters::SDKChar> buf{Memory::eUninitialized, static_cast<size_t> (itemLen + 1)};
107 ThrowIfRegError (::RegQueryValue (openKey, Led_SDK_TCHAROF ("command"), buf.data (), &itemLen));
108 return SDKString{buf.data ()};
109 }
110 catch (...) {
111 return SDKString{};
112 }
113}
114
115SDKString Win32FileAssociationRegistrationHelper::GetAssociatedOpenCommand () const
116{
117 try {
118 SDKString progID = GetAssociatedProgID ();
119 KeyHolder progIDKey (HKEY_CLASSES_ROOT, progID.c_str ());
120 KeyHolder shellKey (progIDKey, Led_SDK_TCHAROF ("shell"));
121 KeyHolder openKey (shellKey, Led_SDK_TCHAROF ("open"));
122 LONG itemLen = 0;
123 ThrowIfRegError (::RegQueryValue (openKey, Led_SDK_TCHAROF ("command"), NULL, &itemLen));
124 StackBuffer<Characters::SDKChar> buf{Memory::eUninitialized, static_cast<size_t> (itemLen + 1)};
125 ThrowIfRegError (::RegQueryValue (openKey, Led_SDK_TCHAROF ("command"), buf.data (), &itemLen));
126 return SDKString{buf.data ()};
127 }
128 catch (...) {
129 return SDKString{};
130 }
131}
132
133void Win32FileAssociationRegistrationHelper::SetAssociatedProgIDAndOpenCommand (const SDKString& progID, const SDKString& progIDPrettyName,
134 const SDKString& defaultIcon, const SDKString& editCommandLine,
135 const SDKString& openCommandLine)
136{
137 /*
138 * Make HKCR/SUFFIX point to the progID
139 */
140 ThrowIfRegError (::RegSetValue (HKEY_CLASSES_ROOT, fFileSuffix.c_str (), REG_SZ, progID.c_str (), static_cast<DWORD> (progID.length ())));
141
142 /*
143 * The Create/Make the pointed to progID, with appropriate subkeys.
144 */
145 ThrowIfRegError (::RegSetValue (HKEY_CLASSES_ROOT, progID.c_str (), REG_SZ, progIDPrettyName.c_str (),
146 static_cast<DWORD> (progIDPrettyName.length ())));
147
148 KeyHolder progIDKey (HKEY_CLASSES_ROOT, progID.c_str (), KeyHolder::eCreateIfNotThere);
149 if (defaultIcon != Win32UIFileAssociationInfo::kNoChange) {
150 ThrowIfRegError (::RegSetValue (progIDKey, Led_SDK_TCHAROF ("DefaultIcon"), REG_SZ, defaultIcon.c_str (),
151 static_cast<DWORD> (defaultIcon.length ())));
152 }
153 if (editCommandLine != Win32UIFileAssociationInfo::kNoChange) {
154 KeyHolder shellKey (progIDKey, Led_SDK_TCHAROF ("shell"), KeyHolder::eCreateIfNotThere);
155 KeyHolder openKey (shellKey, Led_SDK_TCHAROF ("edit"), KeyHolder::eCreateIfNotThere);
156 ThrowIfRegError (::RegSetValue (openKey, Led_SDK_TCHAROF ("command"), REG_SZ, editCommandLine.c_str (),
157 static_cast<DWORD> (editCommandLine.length ())));
158 }
159 if (openCommandLine != Win32UIFileAssociationInfo::kNoChange) {
160 KeyHolder shellKey (progIDKey, Led_SDK_TCHAROF ("shell"), KeyHolder::eCreateIfNotThere);
161 KeyHolder openKey (shellKey, Led_SDK_TCHAROF ("open"), KeyHolder::eCreateIfNotThere);
162 ThrowIfRegError (::RegSetValue (openKey, Led_SDK_TCHAROF ("command"), REG_SZ, openCommandLine.c_str (),
163 static_cast<DWORD> (openCommandLine.length ())));
164 }
165}
166
167/*
168 ********************************************************************************
169 ************************ Win32UIFileAssociationInfo ****************************
170 ********************************************************************************
171 */
172SDKString Win32UIFileAssociationInfo::kNoChange;
173Win32UIFileAssociationInfo::Win32UIFileAssociationInfo (const SDKString& fileSuffix, const SDKString& fileProgID, const SDKString& fileProgIDPrettyName,
174 const SDKString& defaultIcon, const SDKString& shellEditNOpenCommandLine)
175 : fFileSuffix{fileSuffix}
176 , fFileProgID{fileProgID}
177 , fFileProgIDPrettyName{fileProgIDPrettyName}
178 , fDefaultIcon{defaultIcon}
179 , fShellEditCommandLine{shellEditNOpenCommandLine}
180 , fShellOpenCommandLine{shellEditNOpenCommandLine}
181{
182}
183
184Win32UIFileAssociationInfo::Win32UIFileAssociationInfo (const SDKString& fileSuffix, const SDKString& fileProgID,
185 const SDKString& fileProgIDPrettyName, const SDKString& defaultIcon,
186 const SDKString& shellEditCommandLine, const SDKString& shellOpenCommandLine)
187 : fFileSuffix (fileSuffix)
188 , fFileProgID (fileProgID)
189 , fFileProgIDPrettyName (fileProgIDPrettyName)
190 , fDefaultIcon (defaultIcon)
191 , fShellEditCommandLine (shellEditCommandLine)
192 , fShellOpenCommandLine (shellOpenCommandLine)
193{
194}
195
196/*
197 ********************************************************************************
198 *************** Win32UIFileAssociationRegistrationHelper ***********************
199 ********************************************************************************
200 */
201Win32UIFileAssociationRegistrationHelper::Win32UIFileAssociationRegistrationHelper (HINSTANCE hInstance)
202 : fHINSTANCE (hInstance)
203 , fInfoRecs ()
204{
205}
206
207Win32UIFileAssociationRegistrationHelper::Win32UIFileAssociationRegistrationHelper (HINSTANCE hInstance, const vector<Win32UIFileAssociationInfo>& infoRecs)
208 : fHINSTANCE (hInstance)
209 , fInfoRecs (infoRecs)
210{
211}
212
213void Win32UIFileAssociationRegistrationHelper::Add (const Win32UIFileAssociationInfo& infoRec)
214{
215 fInfoRecs.push_back (infoRec);
216}
217
218void Win32UIFileAssociationRegistrationHelper::DoIt () noexcept
219{
220 /*
221 * By default - if this fails - just ignore the failure.
222 */
223 try {
224 bool doRegister = true;
225 if (RegisteredToSomeoneElse () and not CheckUserSaysOKToUpdate ()) {
226 doRegister = false;
227 }
228 if (doRegister) {
229 ApplyChangesSilently ();
230 }
231 }
232 catch (...) {
233 }
234}
235
236bool Win32UIFileAssociationRegistrationHelper::RegisteredToSomeoneElse () const
237{
238 for (auto i = fInfoRecs.begin (); i != fInfoRecs.end (); ++i) {
239 /*
240 * NB: Only check if the progid and the open command are the same as us. Don't bother reporting to the user we need to
241 * update things if all that differs is the icon, or something like that.
242 *
243 * Also - if there are any exceptions - its probably cuz the keys aren't there to begin with, and so we assume
244 * we are the first to specify that key/suffix/etc.
245 */
246 try {
247 Win32FileAssociationRegistrationHelper registryAssoc ((*i).fFileSuffix);
248 SDKString progid = registryAssoc.GetAssociatedProgID ();
249 SDKString assocEditCmd = registryAssoc.GetAssociatedEditCommand ();
250 SDKString assocOpenCmd = registryAssoc.GetAssociatedOpenCommand ();
251 SDKString editCommandLine = (*i).fShellEditCommandLine;
252 SDKString openCommandLine = (*i).fShellOpenCommandLine;
253 ExpandVariables (&editCommandLine);
254 ExpandVariables (&openCommandLine);
255 if (progid != (*i).fFileProgID or (not editCommandLine.empty () and assocEditCmd != editCommandLine) or
256 (not openCommandLine.empty () and assocOpenCmd != openCommandLine)) {
257 return true;
258 }
259 }
260 catch (...) {
261 // ignore - check for conflicts on the rest...
262 }
263 }
264 return false;
265}
266
267void Win32UIFileAssociationRegistrationHelper::ApplyChangesSilently ()
268{
269 // for each info guy - check that ALL already pointed to our app
270 for (auto i = fInfoRecs.begin (); i != fInfoRecs.end (); ++i) {
271 Win32FileAssociationRegistrationHelper registryAssoc ((*i).fFileSuffix);
272 SDKString defaultIcon = (*i).fDefaultIcon;
273 SDKString editCommandLine = (*i).fShellEditCommandLine;
274 SDKString openCommandLine = (*i).fShellOpenCommandLine;
275 ExpandVariables (&defaultIcon);
276 ExpandVariables (&editCommandLine);
277 ExpandVariables (&openCommandLine);
278 try {
279 registryAssoc.SetAssociatedProgIDAndOpenCommand ((*i).fFileProgID, (*i).fFileProgIDPrettyName, defaultIcon, editCommandLine, openCommandLine);
280 }
281 catch (...) {
282 // Ignore any errors updating these registry settings - not important enough to warn users about, and not clearly a problem
283 // (users may have security settings on these registration keys preventing our update).
284 }
285 }
286}
287
288bool Win32UIFileAssociationRegistrationHelper::CheckUserSaysOKToUpdate () const
289{
290 // subclasses may want to put up a dialog here asking the user. Here we just assume they want the changes
291 // applied
292 return true;
293}
294
295void Win32UIFileAssociationRegistrationHelper::ExpandVariables (SDKString* valWithVars) const
296{
297 /*
298 * Search for strings like $EXE$ and replace them with the value of the EXE location.
299 */
300 size_t varAt = 0;
301 if ((varAt = valWithVars->find (Led_SDK_TCHAROF ("$EXE$"))) != SDKString::npos) {
302 TCHAR szLongPathName[_MAX_PATH];
303 ::GetModuleFileName (fHINSTANCE, szLongPathName, _MAX_PATH);
304 valWithVars->replace (varAt, 5, szLongPathName);
305 }
306}
307#endif
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
Definition SDKString.h:38