OWLNext    7.0
Borland's Object Windows Library for the modern age
Loading...
Searching...
No Matches
registry.cpp
Go to the documentation of this file.
1//----------------------------------------------------------------------------
2// ObjectWindows
3// Copyright (c) 1994, 1996 by Borland International, All Rights Reserved
4//
5/// \file
6/// General Registry access & registration implementation
7//----------------------------------------------------------------------------
8#include <owl/pch.h>
9
10#include <owl/registry.h>
11#include <owl/system.h>
12#include <owl/pointer.h>
13#include <utility>
14#include <type_traits>
15#include <vector>
16
17#if defined(__BORLANDC__)
18# pragma option -w-ccc // Disable "Condition is always true/false"
19#endif
20
21using namespace std;
22
23namespace owl {
24
25//
26/// Special predefined root key used by shell and OLE applications (`HKEY_CLASSES_ROOT`).
27/// Registry entries subordinate to this key define types (or classes) of documents and the
28/// properties associated with those types. Shell and COM applications use the information
29/// stored under this key.
30/// \sa https://docs.microsoft.com/en-us/windows/win32/sysinfo/hkey-classes-root-key
31//
33{
34 static TRegKey ClassesRoot(HKEY_CLASSES_ROOT, true, _T("HKEY_CLASSES_ROOT"));
35 return ClassesRoot;
36}
37
38//
39/// Commonly used subkey by shell and OLE applications (`HKEY_CLASSES_ROOT\CLSID`).
40//
42{
43 static TRegKey ClassesRootClsid(GetClassesRoot(), _T("CLSID"), KEY_ALL_ACCESS, NoCreate);
44 return ClassesRootClsid;
45}
46
47//
48/// Special predefined root key containing information about the current hardware profile of the
49/// local computer system (`HKEY_CURRENT_CONFIG`).
50/// The information under this key describes only the differences between the current hardware
51/// configuration and the standard configuration. Information about the standard hardware
52/// configuration is stored under the Software and System keys of `HKEY_LOCAL_MACHINE`.
53/// \sa https://technet.microsoft.com/library/286f12b7-265b-4632-a4e1-987d025023e6
54//
56{
57 static TRegKey CurrentConfig(HKEY_CURRENT_CONFIG, true, _T("HKEY_CURRENT_CONFIG"));
58 return CurrentConfig;
59}
60
61//
62/// Special predefined root key defining the preferences of the current user (`HKEY_CURRENT_USER`).
63/// These preferences include the settings of environment variables, data about program groups,
64/// colors, printers, network connections, and application preferences. This key makes it easier to
65/// establish the current user's settings; the key maps to the current user's branch in HKEY_USERS.
66/// Under this key, software vendors store the current user-specific preferences to be used within
67/// their applications. Microsoft, for example, creates the `HKEY_CURRENT_USER\Software\Microsoft`
68/// key for its applications to use, with each application creating its own subkey under the
69/// Microsoft key.
70/// \sa https://technet.microsoft.com/library/6b6d2dcc-a083-4c49-9000-6f1324b20877
71//
73{
74 static TRegKey CurrentUser(HKEY_CURRENT_USER, true, _T("HKEY_CURRENT_USER"));
75 return CurrentUser;
76}
77
78//
79/// Special predefined root key defining the physical state of the computer (`HKEY_LOCAL_MACHINE`).
80/// Includes data about the bus type, system memory, and installed hardware and software. It
81/// contains subkeys that hold current configuration data, including Plug and Play information
82/// (the Enum branch, which includes a complete list of all hardware that has ever been on the
83/// system), network logon preferences, network security information, software-related information
84/// (such as server names and the location of the server), and other system information.
85/// \sa https://technet.microsoft.com/library/f4704e81-0b33-4ecd-b2e4-e41b50bb758c
86//
88{
89 static TRegKey LocalMachine(HKEY_LOCAL_MACHINE, true, _T("HKEY_LOCAL_MACHINE"));
90 return LocalMachine;
91}
92
93//
94/// Special predefined root key used to obtain performance data (`HKEY_PERFORMANCE_DATA`).
95/// The data is not actually stored in the registry; the registry functions cause the system to
96/// collect the data from its source.
97//
99{
100 static TRegKey PerformanceData(HKEY_PERFORMANCE_DATA, true, _T("HKEY_PERFORMANCE_DATA"));
101 return PerformanceData;
102}
103
104//
105/// Special predefined root key defining the default user configuration (`HKEY_USERS`).
106/// Registry entries subordinate to this key define the default user configuration for new users
107/// on the local computer and the user configuration for the current user.
108/// \sa TRegKey::GetCurrentUser
109//
111{
112 static TRegKey Users(HKEY_USERS, true, _T("HKEY_USERS"));
113 return Users;
114}
115
116//
117/// Creates or opens a key given a base key and a subkey name.
118/// The user can specify the desired access rights to the key and provide an ok-to-create or
119/// open-only indicator. To check whether a subkey exists before creating a TRegKey, you can call
120/// TRegKey::HasSubkey on the base key.
121///
122/// \returns Throws TXRegistry on failure.
123///
124/// \note Passing an empty string for the key name is allowed but not very useful. It will just
125/// give you a new handle to the base key. In most cases, you should use the aliasing constructor
126/// instead; TRegKey::TRegKey(THandle aliasKey, bool shouldClose, tstring keyName).
127//
129 : Key{}, Name{move(keyName)}, SubkeyCount{}, ValueCount{}, ShouldClose{true}
130{
131 // Use a local lambda to handle SEH system exceptions (not compatible with C++ exception unwinding).
132 // A system SEH exception with code EXCEPTION_ACCESS_VIOLATION is translated to ERROR_ACCESS_DENIED.
133 // Any other SEH exception is not handled.
134 //
135 const auto init = [&]() noexcept -> long // Error code.
136 {
137 __try
138 {
139 if (createOK == CreateOK)
140 {
141 auto disposition = DWORD{};
142 const auto r = ::RegCreateKeyEx(baseKey, Name.c_str(), 0 /* Reserved */, nullptr /* Class */,
143 REG_OPTION_NON_VOLATILE, samDesired, nullptr /* SecurityAttributes */, &Key, &disposition);
144 if (r != ERROR_SUCCESS) return r;
145 }
146 else
147 {
148 const auto r = ::RegOpenKeyEx(baseKey, Name.c_str(), 0 /* Options */, samDesired, &Key);
149 if (r != ERROR_SUCCESS) return r;
150 }
151 const auto r = QueryInfo(nullptr, nullptr, &SubkeyCount, nullptr, nullptr, &ValueCount, nullptr, nullptr, nullptr, nullptr);
152 if (r != ERROR_SUCCESS && r != ERROR_INSUFFICIENT_BUFFER) return r;
153 return ERROR_SUCCESS;
154 }
155 __except (true)
156 {
157 const auto x = GetExceptionCode();
159 RaiseException(x, 0, 0, nullptr);
160 return -1; // Never executed.
161 }
162 };
163 const auto r = init();
164 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegKey::TRegKey: Init failed"), Name, r};
165 CHECK(Key); // Postcondition.
166}
167
168//
169/// LPCTSTR overload.
170/// Forwards to TRegKey::TRegKey(THandle baseKey, tstring keyName, REGSAM, TCreateOK).
171/// \note If you pass `nullptr` for the key name, it will be translated to an empty string.
172//
178
179//
180/// Constructs a key given the current position of a TRegKeyIterator.
181/// \returns Throws TXRegistry on failure.
182//
184 : Key{}, Name{}, SubkeyCount{}, ValueCount{}, ShouldClose{true}
185{
186 // Use a local lambda to handle SEH system exceptions (not compatible with C++ exception unwinding).
187 // A system SEH exception with code EXCEPTION_ACCESS_VIOLATION is translated to ERROR_ACCESS_DENIED.
188 // Any other SEH exception is not handled.
189 //
190 const auto init = [&]() noexcept -> long // Error code.
191 {
192 __try
193 {
194 tchar name[_MAX_PATH + 1];
195 const auto re = iter.BaseKey().EnumKey(iter.Current(), &name[0], COUNTOF(name));
196 if (re != ERROR_SUCCESS) return re;
197 Name = name;
198 const auto ro = ::RegOpenKeyEx(iter.BaseKey(), Name.c_str(), 0, samDesired, &Key);
199 if (ro != ERROR_SUCCESS) return ro;
200 const auto rq = QueryInfo(nullptr, nullptr, &SubkeyCount, nullptr, nullptr, &ValueCount, nullptr, nullptr, nullptr, nullptr);
201 if (rq != ERROR_SUCCESS && rq != ERROR_INSUFFICIENT_BUFFER) return rq;
202 return ERROR_SUCCESS;
203 }
204 __except (true)
205 {
206 const auto x = GetExceptionCode();
208 RaiseException(x, 0, 0, nullptr);
209 return -1; // Never executed.
210 }
211 };
212 const auto r = init();
213 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegKey::TRegKey: Init failed"), Name, r};
214 CHECK(Key); // Postcondition.
215}
216
217//
218//
219/// Constructs a TRegKey object around an existing key.
220/// Commonly used for wrapping the preexisting pseudo-keys, such as HKEY_CURRENT_USER.
221/// If `true` is passed for parameter `shouldClose`, then the object takes ownership of the handle,
222/// and the handle will be closed when the object is destructed.
223///
224/// \returns Throws TXRegistry on failure.
225///
226/// \sa The preexisting pseudo-keys can be accessed by the static member functions
227/// TRegKey::GetClassesRoot, TRegKey::GetClassesRootClsid, TRegKey::GetCurrentConfig,
228/// TRegKey::GetCurrentUser, TRegKey::GetLocalMachine, TRegKey::GetPerformanceData and
229/// TRegKey::GetUsers.
230//
232 : Key{aliasKey}, Name{move(keyName)}, SubkeyCount{}, ValueCount{}, ShouldClose{shouldClose}
233{
234 // Use a local lambda to handle SEH system exceptions (not compatible with C++ exception unwinding).
235 // A system SEH exception with code EXCEPTION_ACCESS_VIOLATION is translated to ERROR_ACCESS_DENIED.
236 // Any other SEH exception is not handled.
237 //
238 const auto init = [&]() noexcept -> long // Error code.
239 {
240 __try
241 {
242 const auto r = QueryInfo(nullptr, nullptr, &SubkeyCount, nullptr, nullptr, &ValueCount, nullptr, nullptr, nullptr, nullptr);
243 if (r != ERROR_SUCCESS && r != ERROR_INSUFFICIENT_BUFFER) return r;
244 return ERROR_SUCCESS;
245 }
246 __except (true)
247 {
248 const auto x = GetExceptionCode();
250 RaiseException(x, 0, 0, nullptr);
251 return -1; // Never executed.
252 }
253 };
254 const auto r = init();
255 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegKey::TRegKey: Init failed"), Name, r};
256 CHECK(Key); // Postcondition.
257}
258
259//
260/// LPCTSTR overload.
261/// Forwards to TRegKey::TRegKey(THandle, bool shouldClose, tstring keyName).
262/// \note If you pass `nullptr` for the key name, it will be translated to an empty string.
263//
269
270//
271/// Move constructor; steals the innards of the given object.
272//
274 : Key{}, Name{}, SubkeyCount{}, ValueCount{}, ShouldClose{}
275{
276 using std::swap;
277 swap(Name, v.Name);
278 swap(Key, v.Key);
279 swap(SubkeyCount, v.SubkeyCount);
280 swap(ValueCount, v.ValueCount);
281 swap(ShouldClose, v.ShouldClose);
282 WARN(!Key, _T("TRegKey::TRegKey: Moved from invalid key"));
283}
284
285//
286/// Closes the underlying key handle, if owned.
287//
289{
290 if (Key && ShouldClose)
291 {
292 [[maybe_unused]] const auto r = ::RegCloseKey(Key);
293 WARN(r != ERROR_SUCCESS, _T("TRegKey::~TRegKey: RegCloseKey failed, error: ") << r);
294 }
295}
296
297//
298/// Retrieves information about this registry key.
299/// Wrapper for RegQueryInfoKey in the Windows API.
300/// \sa https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryinfokeya
301//
320
321//
322/// Functional-style overload.
323/// \returns A data structure containing information about the key.
324/// If an error occurs, TXRegistry is thrown.
325//
327{
328 auto i = TInfo{};
329 auto classSize = DWORD{MAX_PATH};
330 i.Class.resize(classSize); // Should include space for the null-terminator, according to the Windows API documentation.
331 const auto p = const_cast<LPTSTR>(i.Class.data()); // Workaround for pre-C++17 compilers.
332 const auto r = QueryInfo(
333 p,
334 &classSize,
335 &i.SubkeyCount,
336 &i.MaxSubkeyNameSize,
337 &i.MaxSubkeyClassSize,
338 &i.ValueCount,
339 &i.MaxValueNameSize,
340 &i.MaxValueDataSize,
341 &i.SecurityDescriptorSize,
342 &i.LastWriteTime);
343 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegKey::QueryInfo failed"), *this, r};
344 i.Class.resize(classSize); // Returned classSize does not include the null-terminator, according to the Windows API documentation.
345 i.Class.shrink_to_fit();
346 return i;
347}
348
349//
350/// Returns true if this key has a subkey with the given name.
351/// If an error occurs, e.g. access is denied, it is assumed the subkey exists.
352/// The key name may include a path with subkey names separated by backslash, e.g. "Software\\Program".
353//
354auto TRegKey::HasSubkey(const tstring& keyName) const -> bool
355{
356 PRECONDITION(Key);
357 auto subkey = HKEY{};
358 const auto r = ::RegOpenKeyEx(Key, keyName.c_str(), 0, KEY_READ, &subkey);
359 if (subkey)
361 return r != ERROR_FILE_NOT_FOUND;
362}
363
364//
365/// Returns the subkey with the given name, if it exists.
366/// If the key does not exist, std::nullopt is returned.
367/// If access is denied, or any other error occurs, a TXRegistry exception is thrown.
368/// The key name may include a path with subkey names separated by backslash, e.g. "Software\\Program".
369///
370/// \param samDesired A mask that specifies the desired access rights to the key to be opened.
371/// For possible values of this parameter, see the Windows API documentation.
372///
373/// \sa https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexa
374/// \sa https://docs.microsoft.com/en-gb/windows/win32/sysinfo/registry-key-security-and-access-rights
375//
377{
378 PRECONDITION(Key);
379 auto subkey = HKEY{};
380 const auto r = ::RegOpenKeyEx(Key, keyName.c_str(), 0, samDesired, &subkey);
381 if (r != ERROR_SUCCESS && r != ERROR_FILE_NOT_FOUND) throw TXRegistry{_T("TRegKey::GetSubKey: RegOpenKeyEx failed"), keyName, r};
382 return subkey ? make_optional<TRegKey>(subkey, true, keyName) : nullopt; // TRegKey takes ownership.
383}
384
385//
386/// Return true if this key has a value with the given name.
387/// If the given name is empty, the default value is assumed, which always exists.
388/// If an error occurs, e.g. access is denied, it is assumed the value exists.
389//
390auto TRegKey::HasValue(const tstring& valueName) const -> bool
391{
392 PRECONDITION(Key);
393 return QueryValue(valueName, nullptr, nullptr, nullptr) != ERROR_FILE_NOT_FOUND;
394}
395
396//
397/// Returns the value with the given name, if it exists within this key.
398/// If the value does not exist, std::nullopt is returned.
399/// If the given name is empty, the default value is assumed, which always exists.
400/// If access is denied, or any other error occurs, a TXRegistry exception is thrown.
401//
402auto TRegKey::GetValue(const tstring& valueName) const -> optional<TRegValue>
403{
404 PRECONDITION(Key);
405 return HasValue(valueName) ? make_optional<TRegValue>(*this, valueName) : nullopt;
406}
407
408//
409/// Completely eliminates a child key, including any of its subkeys.
410//
412{
413 // DeleteKey fails if a key has subkeys, so we must nuke them first.
414 //
415 const auto nukeSubkeys = [&](LPCTSTR keyName)
416 {
417 auto key = GetSubkey(keyName, KEY_ALL_ACCESS);
418 if (!key) return ERROR_FILE_NOT_FOUND;
419 auto subkeys = vector<tstring>{};
420 for (auto i = TRegKeyIterator{*key}; i; ++i)
421 subkeys.push_back(TRegKey{i}.GetName());
422 for (const auto& s : subkeys)
423 {
424 const auto r = key->NukeKey(s);
425 if (r != ERROR_SUCCESS) return r;
426 }
427 return ERROR_SUCCESS;
428 };
429 const auto r = nukeSubkeys(subKeyName);
430 if (r != ERROR_SUCCESS) return r;
431 return DeleteKey(subKeyName);
432}
433
434//
435/// Retrieves a copy of the security descriptor protecting the specified open registry key.
436///
437/// To read the owner, group, or discretionary access control list (DACL) from the key's security
438/// descriptor, the calling process must have been granted READ_CONTROL access when the handle was
439/// opened. To get READ_CONTROL access, the caller must be the owner of the key or the key's DACL
440/// must grant the access.
441///
442/// To read the system access control list (SACL) from the security descriptor, the calling process
443/// must have been granted ACCESS_SYSTEM_SECURITY access when the key was opened. The correct way
444/// to get this access is to enable the SE_SECURITY_NAME privilege in the caller's current token,
445/// open the handle for ACCESS_SYSTEM_SECURITY access, and then disable the privilege.
446///
447/// \param infoRequested indicates the requested security information.
448/// \exception TXRegistry is thrown on failure.
449///
450/// \sa <a href="https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-reggetkeysecurity">
451/// RegGetKeySecurity</a> in the Windows API.
452//
454 : Data(key.QueryInfo().SecurityDescriptorSize)
455{
456 auto n = static_cast<DWORD>(Data.size());
457 const auto r = key.GetSecurity(infoRequested, Data.data(), &n);
458 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TSecurityDescriptor::TSecurityDescriptor: GetSecurity failed"), key, r};
459 CHECK(n == static_cast<DWORD>(Data.size()));
460}
461
462//
463/// Determines whether the components of the security descriptor are valid.
464/// \returns `true` if the security descriptor is valid.
465/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-isvalidsecuritydescriptor">
466/// IsValidSecurityDescriptor</a> in the Windows API.
467//
469{
470 return ::IsValidSecurityDescriptor(const_cast<SECURITY_DESCRIPTOR*>(GetData())) != FALSE;
471}
472
473//
474/// Retrieves the control information from the security descriptor.
475/// Call TSecurityDescriptor::GetRevision to get the revision number of the structure.
476///
477/// \returns If successful, the control information is returned.
478///
479/// \exception TXRegistry is thrown on failure.
480///
481/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorcontrol">
482/// GetSecurityDescriptorControl</a> in the Windows API.
483//
485{
487 auto revision = DWORD{};
488 const auto ok = ::GetSecurityDescriptorControl(const_cast<SECURITY_DESCRIPTOR*>(GetData()), &c, &revision);
489 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetControl: GetSecurityDescriptorControl failed"), tstring{}, static_cast<long>(::GetLastError())};
490 return c;
491}
492
493//
494/// Returns the length, in bytes, of the security descriptor.
495/// The length includes the length of all associated structures.
496///
497/// \returns If successful, the length is returned.
498/// \exception TXRegistry is thrown if the security descriptor is invalid.
499///
500/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorlength">
501/// GetSecurityDescriptorLength</a> in the Windows API.
502//
504{
505 if (!IsValid()) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetLength failed"), tstring{}};
506 return ::GetSecurityDescriptorLength(const_cast<SECURITY_DESCRIPTOR*>(GetData()));
507}
508
509//
510/// Retrieves the discretionary access control list (DACL) from the security descriptor.
511/// Call TSecurityDescriptor::IsDaclDefaulted to determine whether the DACL was set by default
512/// means or user specified.
513///
514/// \returns If this security descriptor contains a DACL, a `PACL` is returned pointing to the
515/// DACL. Note that `nullptr` is a valid value for the `PACL`. A `nullopt` is returned, if a DACL
516/// is not present.
517///
518/// \exception TXRegistry is thrown on failure.
519///
520/// \note To simply determine whether a DACL is present in the security descriptor, you can call
521/// TSecurityDescriptor::IsDaclPresent.
522///
523/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptordacl">
524/// GetSecurityDescriptorDacl</a> in the Windows API.
525//
527{
528 auto daclPresent = BOOL{};
529 auto dacl = PACL{};
530 auto daclDefaulted = BOOL{};
531 const auto ok = ::GetSecurityDescriptorDacl(const_cast<SECURITY_DESCRIPTOR*>(GetData()), &daclPresent, &dacl, &daclDefaulted);
532 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetDacl: GetSecurityDescriptorDacl failed"), tstring{}, static_cast<long>(::GetLastError())};
534}
535
536//
537/// Retrieves the primary group information from the security descriptor.
538/// Call TSecurityDescriptor::IsGroupDefaulted to determine whether the group was set by default
539/// means or user specified.
540///
541/// \returns If the security descriptor contains a primary group, a `PSID` pointing to the security
542/// identifier that identifies the primary group is returned. Otherwise, `nullptr` is returned.
543///
544/// \exception TXRegistry is thrown on failure.
545///
546/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorgroup">
547/// GetSecurityDescriptorGroup</a> in the Windows API.
548//
550{
551 auto group = PSID{};
552 auto groupDefaulted = BOOL{};
553 const auto ok = ::GetSecurityDescriptorGroup(const_cast<SECURITY_DESCRIPTOR*>(GetData()), &group, &groupDefaulted);
554 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetGroup: GetSecurityDescriptorGroup failed"), tstring{}, static_cast<long>(::GetLastError())};
555 return group;
556}
557
558//
559/// Retrieves the owner information from the security descriptor.
560/// Call TSecurityDescriptor::IsOwnerDefaulted to determine whether the owner was set by default
561/// means or user specified.
562///
563/// \returns If the security descriptor contains an owner, a `PSID` pointing to the security
564/// identifier that identifies the owner is returned. Otherwise, `nullptr` is returned.
565///
566/// \exception TXRegistry is thrown on failure.
567///
568/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorowner">
569/// GetSecurityDescriptorOwner</a> in the Windows API.
570//
572{
573 auto owner = PSID{};
574 auto ownerDefaulted = BOOL{};
575 const auto ok = ::GetSecurityDescriptorOwner(const_cast<SECURITY_DESCRIPTOR*>(GetData()), &owner, &ownerDefaulted);
576 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetOwner: GetSecurityDescriptorOwner failed"), tstring{}, static_cast<long>(::GetLastError())};
577 return owner;
578}
579
580//
581/// Retrieves the revision number of the security descriptor structure.
582/// Call TSecurityDescriptor::GetControl to get the control information of the structure.
583///
584/// \returns If successful, the revision number is returned.
585///
586/// \exception TXRegistry is thrown on failure.
587///
588/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorcontrol">
589/// GetSecurityDescriptorControl</a> in the Windows API.
590//
592{
594 auto revision = DWORD{};
595 const auto ok = ::GetSecurityDescriptorControl(const_cast<SECURITY_DESCRIPTOR*>(GetData()), &c, &revision);
596 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetRevision: GetSecurityDescriptorControl failed"), tstring{}, static_cast<long>(::GetLastError())};
597 return revision;
598}
599
600//
601/// Retrieves the resource manager control bits from the security descriptor.
602/// \returns If the SE_RM_CONTROL_VALID bit flag is set in the security descriptor, a `UCHAR`
603/// containing the control bits is returned. Otherwise, a `nullopt` is returned.
604///
605/// \exception TXRegistry is thrown on failure.
606///
607/// \note To simply determine whether resource manager control bits are present in the security
608/// descriptor, you can call TSecurityDescriptor::IsRmControlValid.
609///
610/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorrmcontrol">
611/// GetSecurityDescriptorRMControl</a> in the Windows API.
612//
614{
615 auto rmControl = UCHAR{};
616 const auto r = ::GetSecurityDescriptorRMControl(const_cast<SECURITY_DESCRIPTOR*>(GetData()), &rmControl);
617 if (r == ERROR_INVALID_DATA) return nullopt;
618 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetRmControl: GetSecurityDescriptorRMControl failed"), tstring{}, static_cast<long>(r)};
619 return make_optional(rmControl);
620}
621
622//
623/// Retrieves the system access control list (SACL) from the security descriptor.
624/// Call TSecurityDescriptor::IsSaclDefaulted to determine whether the SACL was set by default
625/// means or user specified.
626///
627/// \returns If this security descriptor contains a SACL, a `PACL` is returned pointing to the
628/// SACL. Note that `nullptr` is a valid value for the `PACL`. A `nullopt` is returned, if a SACL
629/// is not present.
630///
631/// \exception TXRegistry is thrown on failure.
632///
633/// \note To simply determine whether a SACL is present in the security descriptor, you can call
634/// TSecurityDescriptor::IsSaclPresent.
635///
636/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorsacl">
637/// GetSecurityDescriptorSacl</a> in the Windows API.
638//
640{
641 auto saclPresent = BOOL{};
642 auto sacl = PACL{};
643 auto saclDefaulted = BOOL{};
644 const auto ok = ::GetSecurityDescriptorSacl(const_cast<SECURITY_DESCRIPTOR*>(GetData()), &saclPresent, &sacl, &saclDefaulted);
645 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::GetSacl: GetSecurityDescriptorSacl failed"), tstring{}, static_cast<long>(::GetLastError())};
647}
648
649//
650/// Initializes a new security descriptor.
651///
652/// The security descriptor is initialized to have no system access control list (SACL), no
653/// discretionary access control list (DACL), no owner, no primary group, and all control flags set
654/// to `FALSE` (`NULL`). Thus, except for its revision level, it is empty.
655///
656/// \param revision must be set to SECURITY_DESCRIPTOR_REVISION (the default).
657/// \exception TXRegistry is thrown on failure.
658///
659/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-initializesecuritydescriptor">
660/// InitializeSecurityDescriptor</a> in the Windows API.
661//
663{
664 const auto ok = ::InitializeSecurityDescriptor(GetData(), revision);
665 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::Initialize: InitializeSecurityDescriptor failed"), tstring{}, static_cast<long>(::GetLastError())};
666}
667
668//
669/// Removes the discretionary access control list (DACL) from the security descriptor.
670/// The flags SE_DACL_PRESENT and SE_DACL_DEFAULTED are cleared in the SECURITY_DESCRIPTOR_CONTROL
671/// structure, and the DACL `PACL` is set to `nullptr`.
672///
673/// \exception TXRegistry is thrown on failure.
674///
675/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptordacl">
676/// SetSecurityDescriptorDacl</a> in the Windows API.
677//
679{
680 const auto ok = ::SetSecurityDescriptorDacl(GetData(), FALSE, nullptr, FALSE);
681 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::RemoveDacl: SetSecurityDescriptorDacl failed"), tstring{}, static_cast<long>(::GetLastError())};
682}
683
684//
685/// Removes the primary group information from the security descriptor.
686/// Sets the group `PSID` to `nullptr`. This marks the security descriptor as having no primary
687/// group. Also, the flag SE_GROUP_DEFAULTED is cleared in the SECURITY_DESCRIPTOR_CONTROL
688/// structure.
689///
690/// \exception TXRegistry is thrown on failure.
691///
692/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorgroup">
693/// SetSecurityDescriptorGroup</a> in the Windows API.
694//
696{
697 const auto ok = ::SetSecurityDescriptorGroup(GetData(), nullptr, FALSE);
698 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::RemoveGroup: SetSecurityDescriptorGroup failed"), tstring{}, static_cast<long>(::GetLastError())};
699}
700
701//
702/// Removes the owner information from the security descriptor.
703/// Sets the owner `PSID` to `nullptr`. This marks the security descriptor as having no owner.
704/// Also, the flag SE_OWNER_DEFAULTED is cleared in the SECURITY_DESCRIPTOR_CONTROL structure.
705///
706/// \exception TXRegistry is thrown on failure.
707///
708/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorowner">
709/// SetSecurityDescriptorOwner</a> in the Windows API.
710//
712{
713 const auto ok = ::SetSecurityDescriptorOwner(GetData(), nullptr, FALSE);
714 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::RemoveOwner: SetSecurityDescriptorOwner failed"), tstring{}, static_cast<long>(::GetLastError())};
715}
716
717//
718/// Removes the resource manager control bits from the security descriptor.
719/// The flag SE_RM_CONTROL_VALID in cleared in the SECURITY_DESCRIPTOR_CONTROL structure.
720///
721/// \exception TXRegistry is thrown on failure.
722///
723/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorrmcontrol">
724/// SetSecurityDescriptorRMControl</a> in the Windows API.
725//
727{
728 const auto r = ::SetSecurityDescriptorRMControl(GetData(), nullptr);
729 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::RemoveRmControl: SetSecurityDescriptorRMControl failed"), tstring{}, static_cast<long>(r)};
730}
731
732//
733/// Removes the system access control list (SACL) from the security descriptor.
734/// The flags SE_SACL_PRESENT and SE_SACL_DEFAULTED are cleared in the SECURITY_DESCRIPTOR_CONTROL
735/// structure, and the SACL `PACL` is set to `nullptr`.
736///
737/// \exception TXRegistry is thrown on failure.
738///
739/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorsacl">
740/// SetSecurityDescriptorSacl</a> in the Windows API.
741//
743{
744 const auto ok = ::SetSecurityDescriptorSacl(GetData(), FALSE, nullptr, FALSE);
745 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::RemoveSacl: SetSecurityDescriptorSacl failed"), tstring{}, static_cast<long>(::GetLastError())};
746}
747
748//
749/// Sets the discretionary access control list (DACL) of the security descriptor.
750/// If a DACL is already present in the security descriptor, the DACL is replaced.
751/// On success, the flag SE_DACL_PRESENT is set in the SECURITY_DESCRIPTOR_CONTROL structure.
752///
753/// \param dacl points to an ACL structure that specifies the DACL for the security descriptor.
754/// The DACL is referenced by, not copied into, the security descriptor. If this parameter is a
755/// `nullptr`, a **NULL** DACL is assigned to the security descriptor, which allows all access to
756/// the object.
757///
758/// \param daclDefaulted indicates whether the DACL information was derived from a default
759/// mechanism or explicitly specified by a user. The function stores this value in the flag
760/// SE_DACL_DEFAULTED within the SECURITY_DESCRIPTOR_CONTROL structure.
761///
762/// \exception TXRegistry is thrown on failure.
763///
764/// \note Passing a `nullptr` to this function does not remove the DACL; rather it sets a **NULL**
765/// DACL. To remove the DACL from the security descriptor, call TSecurityDescriptor::RemoveDacl.
766///
767/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptordacl">
768/// SetSecurityDescriptorDacl</a> in the Windows API.
769//
771{
772 const auto ok = ::SetSecurityDescriptorDacl(GetData(), TRUE, dacl, daclDefaulted ? TRUE : FALSE);
773 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::SetDacl: SetSecurityDescriptorDacl failed"), tstring{}, static_cast<long>(::GetLastError())};
774}
775
776//
777/// Sets the primary group information of the security descriptor.
778/// Any primary group information already present in the security descriptor is replaced.
779///
780/// \param group points to a SID structure for the security descriptor's new primary group. The SID
781/// structure is referenced by, not copied into, the security descriptor. If this parameter is
782/// `nullptr`, the security descriptor's primary group information is cleared. This marks the
783/// security descriptor as having no primary group.
784///
785/// \param groupDefaulted indicates whether the primary group information was derived from a
786/// default mechanism or explicitly specified by a user. The function stores this value in the flag
787/// SE_GROUP_DEFAULTED within the SECURITY_DESCRIPTOR_CONTROL structure.
788///
789/// \exception TXRegistry is thrown on failure.
790///
791/// \note Rather than call this function with a `nullptr`, instead you should prefer to call
792/// TSecurityDescriptor::RemoveGroup.
793///
794/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorgroup">
795/// SetSecurityDescriptorGroup</a> in the Windows API.
796//
798{
799 const auto ok = ::SetSecurityDescriptorGroup(GetData(), group, groupDefaulted ? TRUE : FALSE);
800 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::SetGroup: SetSecurityDescriptorGroup failed"), tstring{}, static_cast<long>(::GetLastError())};
801}
802
803//
804/// Sets the owner information of the security descriptor.
805/// Any owner information already present in the security descriptor is replaced.
806///
807/// \param owner points to a SID structure for the security descriptor's new owner. The SID
808/// structure is referenced by, not copied into, the security descriptor. If this parameter is
809/// `nullptr`, the security descriptor's primary owner information is cleared. This marks the
810/// security descriptor as having no owner.
811///
812/// \param ownerDefaulted indicates whether the owner information was derived from a default
813/// mechanism or explicitly specified by a user. The function stores this value in the flag
814/// SE_OWNER_DEFAULTED within the SECURITY_DESCRIPTOR_CONTROL structure.
815///
816/// \exception TXRegistry is thrown on failure.
817///
818/// \note Rather than call this function with a `nullptr`, instead you should prefer to call
819/// TSecurityDescriptor::RemoveOwner.
820///
821/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorowner">
822/// SetSecurityDescriptorOwner</a> in the Windows API.
823//
825{
826 const auto ok = ::SetSecurityDescriptorOwner(GetData(), owner, ownerDefaulted ? TRUE : FALSE);
827 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::SetOwner: SetSecurityDescriptorOwner failed"), tstring{}, static_cast<long>(::GetLastError())};
828}
829
830//
831/// Sets the resource manager control bits of the security descriptor.
832/// On success, the function also sets the SE_RM_CONTROL_VALID flag in the
833/// SECURITY_DESCRIPTOR_CONTROL structure.
834///
835/// \param rmControl contains the control bits.
836///
837/// \exception TXRegistry is thrown on failure.
838///
839/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorrmcontrol">
840/// SetSecurityDescriptorRMControl</a> in the Windows API.
841//
843{
844 const auto r = ::SetSecurityDescriptorRMControl(GetData(), &rmControl);
845 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::SetRmControl: SetSecurityDescriptorRMControl failed"), tstring{}, static_cast<long>(r)};
846}
847
848//
849/// Sets the system access control list (SACL) of the security descriptor.
850/// If a SACL is already present in the security descriptor, the SACL is replaced.
851/// On success, the flag SE_SACL_PRESENT is set in the SECURITY_DESCRIPTOR_CONTROL structure.
852///
853/// \param sacl points to an ACL structure that specifies the SACL for the security descriptor.
854/// The SACL is referenced by, not copied into, the security descriptor. If this parameter is a
855/// `nullptr`, a **NULL** SACL is assigned to the security descriptor.
856///
857/// \param saclDefaulted indicates whether the SACL information was derived from a default
858/// mechanism or explicitly specified by a user. The function stores this value in the flag
859/// SE_SACL_DEFAULTED within the SECURITY_DESCRIPTOR_CONTROL structure.
860///
861/// \exception TXRegistry is thrown on failure.
862///
863/// \note Passing a `nullptr` to this function does not remove the SACL; rather it sets a **NULL**
864/// SACL. To remove the SACL from the security descriptor, call TSecurityDescriptor::RemoveSacl.
865///
866/// \sa <a href="https://docs.microsoft.com/en-gb/windows/win32/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptorsacl">
867/// SetSecurityDescriptorSacl</a> in the Windows API.
868//
870{
871 const auto ok = ::SetSecurityDescriptorSacl(GetData(), TRUE, sacl, saclDefaulted ? TRUE : FALSE);
872 if (!ok) throw TXRegistry{_T("TRegKey::TSecurityDescriptor::SetSacl: SetSecurityDescriptorSacl failed"), tstring{}, static_cast<long>(::GetLastError())};
873}
874
875//
876/// Creates a registry value object from the specified registry key and name.
877/// If the name is null or empty, the object will represent the key's default value.
878//
880:
881 Key(key), Name(name ? name : _T("")), DataType(REG_NONE), Data(0), DataSize(0)
882{
883 QueryTypeAndSize();
884}
885
886//
887/// String-aware overload
888/// If the name is empty, the object will represent the key's default value.
889//
891:
892 Key(key), Name(name), DataType(REG_NONE), Data(0), DataSize(0)
893{
894 QueryTypeAndSize();
895}
896
897#if defined(BI_COMP_BORLANDC)
898
899// Circumvent bug in std::optional (requires default constructor for the contained object type).
900// TODO: Remove this when the library is standard compliant.
901//
902TRegValue::TRegValue() : Key{TRegKey::GetCurrentUser()} {}
903
904#endif
905
906//
907/// Query the registry for the value type and data size, though not the actual data.
908/// The value data itself is retrieved lazily; see RetrieveOnDemand.
909/// Returns the error code returned by the query.
910//
911long TRegValue::QueryTypeAndSize()
912{
913 long r = Key.QueryValue(Name, &DataType, nullptr, &DataSize);
914 WARN(r != ERROR_SUCCESS, _T("TRegValue::QueryTypeAndSize: Query failed: ") << Key.GetName() << _T('\\') << Name << _T(", error: ") << r);
915 return r;
916}
917
918//
919/// Creates a registry object from the current location of the specified iterator.
920//
922:
923 Key(iter.BaseKey()), Name(), DataType(REG_NONE), Data(0), DataSize(0)
924{
925 uint32 nameSize = 16383; // See Windows API documentation; http://msdn.microsoft.com/en-us/library/windows/desktop/ms724872.aspx
926 std::vector<tchar> nameBuffer(nameSize);
927 long r = Key.EnumValue(iter.Current(), &nameBuffer[0], nameSize, &DataType, nullptr, &DataSize);
928 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegValue::TRegValue(const TRegValueIterator&): EnumValue failed"), Key, r};
929 nameBuffer.resize(nameSize); // EnumValue sets nameSize to the number of characters stored, excluding null-termination.
930 Name.assign(nameBuffer.begin(), nameBuffer.end());
931}
932
933//
934/// Returns the data associated with this value as a 32-bit unsigned integer.
935/// Throws an exception if the value is of incompatible type.
936//
937TRegValue::operator uint32() const
938{
939 RetrieveOnDemand();
940 if (DataType != REG_DWORD && DataType != REG_DWORD_BIG_ENDIAN)
941 throw TXRegistry{_T("TRegValue::operator uint32: Incompatible data type"), Key};
942
943 CHECK(DataSize == sizeof(uint32));
944 return *reinterpret_cast<uint32*>(&Data[0]);
945}
946
947//
948/// Returns the data associated with this value as a 64-bit unsigned integer.
949/// Throws an exception if the value is of incompatible type.
950//
951TRegValue::operator uint64() const
952{
953 RetrieveOnDemand();
954 if (DataType != REG_QWORD)
955 throw TXRegistry{_T("TRegValue::operator uint64: Incompatible data type"), Key};
956
957 CHECK(DataSize == sizeof(uint64));
958 return *reinterpret_cast<uint64*>(&Data[0]);
959}
960
961//
962/// Returns the data associated with this value as a null-terminated string.
963/// Throws an exception if the value is of incompatible type or is otherwise invalid.
964//
965TRegValue::operator LPCTSTR() const
966{
967 RetrieveOnDemand();
968 if (DataType != REG_SZ &&
969 DataType != REG_MULTI_SZ &&
970 DataType != REG_EXPAND_SZ &&
971 !(DataType == REG_LINK && sizeof(tchar) == 2) // REG_LINK is always a Unicode UTF-16 string.
972 )
973 throw TXRegistry{_T("TRegValue::operator LPCTSTR: Incompatible data type"), Key};
974
975 // Ensure null-termination.
976 // The data may have been stored without proper null-termination.
977 // Also, check that the string has room for null-termination.
978 //
979 CHECK(!Data.empty());
980 LPTSTR s = reinterpret_cast<LPTSTR>(&Data[0]);
981 const size_t n = Data.size() / sizeof(tchar);
982 if (n == 0) throw TXRegistry{_T("TRegValue::operator LPCTSTR: Data size is zero"), Key};
983 WARN((Data.size() % sizeof(tchar)) != 0, _T("TRegValue::operator LPCTSTR: Oddly sized string"));
984 WARN(s[n - 1] != _T('\0'), _T("TRegValue::operator LPCTSTR: String lacks null-termination"));
985 s[n - 1] = _T('\0');
986 if (DataType == REG_MULTI_SZ && n > 1)
987 {
988 WARN(s[n - 2] != _T('\0'), _T("TRegValue::operator LPCTSTR: Multi-string lacks double null-termination"));
989 s[n - 2] = _T('\0');
990 }
991 return s;
992}
993
994//
995/// Returns the data associated with this value.
996//
997const uint8*
999{
1000 RetrieveOnDemand();
1001 return Data.empty() ? nullptr : &Data[0];
1002}
1003
1004//
1005/// Sets the data associated with this value. 'type' describes the type of the
1006/// value. 'data' is the address of the data. 'size' specifies the length in
1007/// characters.
1008//
1009long
1011{
1012 long r = Key.SetValue(Name, type, data, dataSize);
1013 if (r != ERROR_SUCCESS) return r;
1014
1015 // If same size, copy new data, otherwise retrieve the data on demand later.
1016 //
1017 DataType = type;
1018 if (dataSize == Data.size() && !Data.empty())
1019 {
1020 memcpy(&Data[0], data, Data.size());
1021 }
1022 else
1023 {
1024 DataSize = dataSize;
1025 Data.clear();
1026 }
1027 return ERROR_SUCCESS;
1028}
1029
1030//
1031/// Sets the data associated with this value.
1032/// The type of the value is set to REG_DWORD.
1033//
1034long
1036{
1037 const uint8* data = reinterpret_cast<const uint8*>(&v);
1038 uint32 dataSize = sizeof(uint32);
1039 return Set(REG_DWORD, data, dataSize);
1040}
1041
1042//
1043/// Sets the data associated with this value.
1044/// The type of the value is set to REG_QWORD.
1045//
1046long
1048{
1049 const uint8* data = reinterpret_cast<const uint8*>(&v);
1050 uint32 dataSize = sizeof(uint64);
1051 return Set(REG_QWORD, data, dataSize);
1052}
1053
1054//
1055/// Sets the data associated with this value.
1056/// The type of the value is set to REG_SZ.
1057//
1058long
1060{
1061 const uint8* data = reinterpret_cast<const uint8*>(v);
1062 uint32 dataSize = static_cast<uint32>((::_tcslen(v) + 1) * sizeof(tchar));
1063 return Set(REG_SZ, data, dataSize);
1064}
1065
1066//
1067/// Sets the data associated with this value.
1068/// If the string contains a terminating null, then it is stored in the registry as a multi-string,
1069/// i.e. as a REG_MULTI_SZ value type, e.g. "multi\0string\0\0". Else, it is stored as a REG_SZ.
1070//
1071long
1073{
1074 bool isMultiString = (!v.empty() && *v.rbegin() == _T('\0'));
1075 const uint8* data = reinterpret_cast<const uint8*>(v.c_str());
1076 uint32 dataSize = static_cast<uint32>((v.size() + 1) * sizeof(tstring::value_type));
1078 return Set(dataType, data, dataSize);
1079}
1080
1081//
1082/// Forwards to Set (uint32).
1083/// Throws TXRegistry if data assignment fails.
1084//
1085TRegValue&
1087{
1088 long r = Set(v);
1089 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegValue::operator =(uint32): Set failed"), Key, r};
1090 return *this;
1091}
1092
1093//
1094/// Forwards to Set (uint64).
1095/// Throws TXRegistry if data assignment fails.
1096//
1097TRegValue&
1099{
1100 long r = Set(v);
1101 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegValue::operator =(uint64): Set failed"), Key, r};
1102 return *this;
1103}
1104
1105//
1106/// Forwards to Set (LPCTSTR).
1107/// Throws TXRegistry if data assignment fails.
1108//
1109TRegValue&
1111{
1112 long r = Set(v);
1113 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegValue::operator =(LPCTSTR): Set failed"), Key, r};
1114 return *this;
1115}
1116
1117//
1118/// Forwards to Set (const tstring&).
1119/// Throws TXRegistry if data assignment fails.
1120//
1121TRegValue&
1123{
1124 long r = Set(v);
1125 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegValue::operator =(const tstring&): Set failed"), Key, r};
1126 return *this;
1127}
1128
1129//
1130/// Removes the value from its associated key, and clears the state of this object.
1131//
1132long
1134{
1135 long r = Key.DeleteValue(Name);
1136 if (r != ERROR_SUCCESS) return r;
1137 DataType = REG_NONE;
1138 try {std::vector<uint8>().swap(Data);} // This deallocation idiom may throw, so guard.
1139 catch (...) {Data.clear();} // Simply clear instead, if things go awry.
1140 DataSize = 0;
1141 return ERROR_SUCCESS;
1142}
1143
1144//
1145/// Retrieves and stores the data for the value when requested for the first time (i.e. lazily).
1146/// Note that this function may fail due to synchronisation issues; the registry value
1147/// may have been changed, e.g. by another process, since the object was constructed.
1148//
1149void
1150TRegValue::RetrieveOnDemand() const
1151{
1152 if (!Data.empty()) return;
1153 if (DataType == REG_NONE || DataSize == 0) throw TXRegistry{_T("TRegValue::RetrieveOnDemand: Value is void"), Key};
1154
1155 uint32 dataType = DataType;
1156 Data.resize(DataSize);
1157 uint32 dataSize = DataSize;
1158 long r = Key.QueryValue(Name, &dataType, &Data[0], &dataSize);
1159 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegValue::RetrieveOnDemand: QueryValue failed"), Key, r};
1160 if (dataType != DataType) throw TXRegistry{_T("TRegValue::RetrieveOnDemand: Type changed"), Key};
1161 if (dataSize != DataSize) throw TXRegistry{_T("TRegValue::RetrieveOnDemand: Size changed"), Key};
1162}
1163
1164//----------------------------------------------------------------------------
1165
1166//
1167// Initialize object with the passed-in data.
1168// Counts the number of items in the list.
1169//
1171:
1172 BaseKey(basekey),
1173 List(list)
1174{
1175 PRECONDITION(List);
1176
1177 for (Count = 0; List[Count]; Count++)
1178 ;
1179 EnabledFlags = new int8[Count];
1180}
1181
1182//
1183// Destructor deletes any previously allocated memory.
1184//
1186{
1187 delete[] EnabledFlags;
1188}
1189
1190//
1191// Enable items from the set.
1192//
1193void
1195{
1196 for (LPCTSTR pc = set; *pc != 0; pc++)
1197 EnabledFlags[*pc - 1] = 0; // Selectively allow enable
1198}
1199
1200//
1201// Activate the items in the set.
1202//
1203void
1205{
1206 for (LPCTSTR pc = set; *pc != 0; pc++)
1207 EnabledFlags[*pc - 1]++;
1208}
1209
1210//----------------------------------------------------------------------------
1211
1212//
1213// Initialize the object with the passed-in data.
1214// Counts the number of items in the list.
1215//
1217:
1218 List(list)
1219{
1220 PRECONDITION(List);
1221
1222 for (Count = 0; List[Count].Param; Count++)
1223 ;
1224 typedef LPCTSTR cpchar;
1225 Values = new cpchar[Count];
1226}
1227
1228//
1229// Destructor deletes any previously allocated memory.
1230//
1232{
1233 delete[] Values;
1234}
1235
1236//
1237// Reset all values to their default values.
1238//
1239void
1241{
1242 for (int i = Count; --i >= 0; )
1243 Values[i] = List[i].Default;
1244}
1245
1246//
1247/// Looks for a given param in this param list.
1248/// Associative lookup of value by param.
1249//
1251{
1253 if (!param)
1254 return -1;
1255
1256 int i = Count;
1257 while (--i >= 0) {
1258 if (_tcscmp(List[i].Param, param) == 0)
1259 break;
1260 }
1261 return i;
1262}
1263
1264//----------------------------------------------------------------------------
1265
1266//
1267// Initialize the symbol table with the data.
1268//
1271:
1272 Templates(basekey, tplList),
1273 Params(paramList)
1274{
1275 UserKeyCount = 0;
1276}
1277
1278//
1279// Enable all templates, or if filter is given enable only those
1280// Initialize paramater values with defaults
1281//
1282void
1284{
1285 if (filter) {
1288 }
1289 else {
1291 }
1292
1294 UserKeyCount = 0;
1295}
1296
1297//
1298// Scan through provided items assinging values from the item to the matching
1299// param. A langId is needed for locale translation on assignment.
1300//
1301void
1303{
1304 for (; item && item->Key; item++) {
1305 // Note presence of user-specified key and value, process when streaming
1306 //
1307 if (*item->Key == ' ') {
1308 UserKeyCount++;
1309 }
1310 else {
1311 // Replace default with user-specified parameter value
1312 //
1313 const auto k = to_tstring(item->Key);
1314 int i = Params.Find(k);
1315 if (i < 0) throw TXRegistry{_T("TRegSymbolTable::UpdateParams failed"), k};
1316 Params.Value(i) = item->Value.Translate(lang);
1317
1318 // Activate all templates invoked by parameter
1319 //
1320 Templates.Activate(Params[i].TemplatesNeeded);
1321 }
1322 }
1323}
1324
1325//
1326// Scan thru and process enabled templates, substituting filled in parameter
1327// values and streaming the resulting strings into the out stream provided.
1328//
1329void
1331{
1332#if defined(UNICODE)
1335# define _A2WB_(val) _A2WB(val,(tchar*)conv_buf)
1336#else
1337# define _A2WB_(val) val
1338#endif
1339 for (int itpl = 1; itpl <= Templates.GetCount() || UserKeyCount--; itpl++) {
1340
1341 // Setup pt from standard templates, or from userKeys in item prepended
1342 // by spaces.
1343 //
1344 LPCTSTR userval = 0;
1345 LPCTSTR pt;
1346 if (itpl <= Templates.GetCount()) { // Processing standard template array
1347 if (!Templates.IsActive(itpl))
1348 continue;
1349 pt = Templates[itpl];
1350 }
1351 else { // Now processing user-defined templates
1352 while (*(pt = _A2WB_(item->Key)) != _T(' '))
1353 item++;
1354 pt++;
1355 userval = item->Value;
1356 }
1357
1358 // Walk thru template (pt) copying it into buf, replacing <params> on the
1359 // way.
1360 //
1361 const int bufSize = 512;
1362 TAPointer<tchar> buf(new tchar[bufSize]);
1363
1364 tchar* data = 0; // Optional data for keys-value=data
1365 LPCTSTR pc; // Point where param token began
1366 tchar* pb = buf; // Working buffer write pointer
1367 for (;;) {
1368 tchar c = *pt++; // Current character being copied
1369 switch (c) {
1370 case _T('<'):
1371 pc = pb; // Remember where param token starts
1372 continue;
1373 case _T('>'): {
1374 *pb = 0; // Terminate param token
1375
1376 // Lookup param in param list
1377 //
1378 int i = Params.Find(pc);
1379 if (i < 0) throw TXRegistry{_T("TRegSymbolTable::StreamOut failed"), tstring{pc}};
1380
1381 // Now get the value obtained above
1382 //
1383 pb = (tchar*)pc; // Move buffer ptr back
1384 pc = Params.Value(i);
1385 if (!pc) throw TXRegistry{_T("TRegSymbolTable::StreamOut failed"), tstring{Params[i].Param}};
1386
1387 if (*pc == 0 && *pt == _T(' '))
1388 pt++;
1389
1390 // Copy value to buf
1391 //
1392 while (*pc != 0)
1393 *pb++ = *pc++;
1394 continue;
1395 }
1396 case _T('='):
1397 while (*(pb-1) == _T(' ')) // Remove trailing space before =
1398 pb--;
1399 *pb++ = 0; // Terminate keys-value
1400 data = pb; // Rest of pt will be data written from here
1401 while (*pt == _T(' '))
1402 pt++;
1403 continue;
1404 default:
1405 *pb++ = c; // Copy character to buf. Param name will be overwriten
1406 continue;
1407 case 0:
1408 *pb++ = 0;
1409 if (!userval)
1410 break;
1411 pt = userval;
1412 userval = nullptr;
1413 data = pb;
1414 continue;
1415 }
1416 if (!c)
1417 break;
1418 }
1419 // Now write out a key with an optional assignment value to output stream
1420 //
1421 out
1423 << _T('\\') << (LPCTSTR)buf;
1424 if (data)
1425 out << _T(" = ") << data;
1426 out << _T('\n');
1427 }
1428}
1429
1430//----------------------------------------------------------------------------
1431
1432//
1433/// Walks through an input stream and uses basekey\\key\\key=data lines to set
1434/// registry entries.
1435///
1436/// Has named value support in the form: basekey\\key\\key|valuename=data
1437//
1439{
1440 // Loop thru all lines in input stream
1441 //
1442 while (in.good()) {
1443 // Get line into entry buffer, skip over base key if it exists, ignore line
1444 // if it doesn't
1445 //
1446 tchar entry[512];
1447 in.get(entry, ::_tcslen(baseKey.GetName())+1);
1448 if (_tcscmp(entry, baseKey.GetName()) != 0)
1449 continue;
1450 if (in.peek() == _T('\\'))
1451 in.ignore(); // Eat the '\' separator.
1452 in.getline(entry, 512);
1453
1454 // Split entry into keys-value and data strings
1455 //
1456 tchar* data = _tcschr(entry, _T('='));
1457 if (data) {
1458 tchar* pc = data;
1459 while (*(pc-1) == _T(' '))
1460 pc--;
1461 *pc = _T('\0');
1462 while (*(++data) == _T(' '))
1463 ;
1464 }
1465 tchar* valName = _tcschr(entry, _T('|'));
1466 if (valName)
1467 *valName++ = _T('\0'); // Terminate key at value name separator
1468
1470 LPCTSTR s = data ? data : _T("");
1471 const uint8* buf = reinterpret_cast<const uint8*>(s);
1472 const uint32 bufSize = static_cast<uint32>((_tcslen(s) + 1) * sizeof(*s));
1473 const auto r = subKey.SetValue(valName, REG_SZ, buf, bufSize);
1474 if (r != ERROR_SUCCESS) throw TXRegistry{_T("TRegistry::Update: SetValue failed"), subKey};
1475 }
1476}
1477
1478//
1479/// Walks through an input stream and uses basekey\\key\\key=data lines to check
1480/// registry entries.
1481/// Returns the number of differences. Zero means a complete match.
1482///
1483/// Has named value support in the following form: basekey\\key\\key|valuename=data
1484//
1486{
1487 int diffCount = 0;
1488 while (in.good()) {
1489 // Get line into entry buffer, skip over base key if it exists, ignore line
1490 // if it doesn't
1491 //
1492 tchar entry[512];
1493 in.get(entry, ::_tcslen(baseKey.GetName())+1);
1494 if (_tcscmp(entry, baseKey.GetName()) != 0)
1495 continue;
1496 if (in.peek() == _T('\\'))
1497 in.ignore(); // Eat the '\' separator.
1498 in.getline(entry, 512);
1499
1500 // Split entry into keys-value and data strings
1501 //
1502 tchar* data = _tcschr(entry, _T('='));
1503 if (data) {
1504 tchar* pc = data;
1505 while (*(pc-1) == _T(' '))
1506 pc--;
1507 *pc = _T('\0');
1508 while (*(++data) == _T(' '))
1509 ;
1510 }
1511 tchar* valName = _tcschr(entry, _T('|'));
1512 if (valName)
1513 *valName++ = _T('\0'); // Terminate key at value name separator
1514
1515 // Now lookup keys-value part to get its data and see if it matches the
1516 // data value from the instream
1517 //
1520 tchar buf[300];
1521 uint32 bufSize = 300;
1522 if (subKey.QueryValue(valName, &dataType, reinterpret_cast<uint8*>(buf), &bufSize) != ERROR_SUCCESS
1523 || (data && _tcscmp(data, buf) != 0))
1524 diffCount++;
1525 }
1526 return diffCount;
1527}
1528
1529//
1530/// Unregisters entries given a reglist. An optional overrides regItem. Returns
1531/// the number of errors from deleting keys.
1532//
1533int
1535{
1536#if defined(UNICODE)
1539# undef _A2WB_
1540# undef _W2AB_
1541# define _A2WB_(val) _A2WB(val,(tchar*)conv_buf)
1542# define _W2AB_(val) _W2AB(val,(tchar*)conv_buf)
1543#else
1544# undef _A2WB_
1545# undef _W2AB_
1546# define _A2WB_(val) val
1547# define _W2AB_(val) val
1548#endif
1549 int errorCount = 0;
1550
1551 // Loop thru unregister params to nuke each root level key, thus cleaning
1552 // up all nested keys too
1553 //
1554 for (int i = 0; params[i].Name && params[i].BaseKey; i++) {
1555 LPCTSTR regKey = regInfo[_W2AB_(params[i].Name)];
1556
1557 // If the param not found & it matches the overrides item, then use that
1558 // value
1559 //
1560 if (!regKey && overrides && _tcscmp(_A2WB_(overrides->Key), params[i].Name) == 0)
1561 regKey = overrides->Value;
1562
1563 // If key-value was found, unregister it
1564 //
1565 if (regKey) {
1566 tchar buff[16];
1567 if (params[i].Prepend) { // Special case prepending char to key
1568 buff[0] = _T('.');
1569 ::_tcscpy(buff+1, regKey);
1570 regKey = buff;
1571 }
1572
1573 // Nuke the key, using the basekey as a reference point
1574 //
1575 if (params[i].BaseKey->NukeKey(regKey))
1576 errorCount++; // Should throw exception if certain errors?
1577 }
1578 }
1579 return errorCount;
1580}
1581
1582} // OWL namespace
1583/* ========================================================================== */
#define CHECK(condition)
Definition checks.h:239
#define WARN(condition, message)
Definition checks.h:273
#define PRECONDITION(condition)
Definition checks.h:227
auto GetLength() const -> DWORD
Returns the length, in bytes, of the security descriptor.
Definition registry.cpp:503
void RemoveGroup()
Removes the primary group information from the security descriptor.
Definition registry.cpp:695
void SetSacl(PACL sacl, bool saclDefaulted=false)
Sets the system access control list (SACL) of the security descriptor.
Definition registry.cpp:869
auto GetDacl() const -> std::optional< PACL >
Retrieves the discretionary access control list (DACL) from the security descriptor.
Definition registry.cpp:526
auto GetOwner() const -> PSID
Retrieves the owner information from the security descriptor.
Definition registry.cpp:571
void RemoveOwner()
Removes the owner information from the security descriptor.
Definition registry.cpp:711
auto IsValid() const noexcept -> bool
Determines whether the components of the security descriptor are valid.
Definition registry.cpp:468
TSecurityDescriptor(const TRegKey &key, SECURITY_INFORMATION infoRequested)
Retrieves a copy of the security descriptor protecting the specified open registry key.
Definition registry.cpp:453
void SetRmControl(UCHAR rmControl)
Sets the resource manager control bits of the security descriptor.
Definition registry.cpp:842
void Initialize(DWORD revision=SECURITY_DESCRIPTOR_REVISION)
Initializes a new security descriptor.
Definition registry.cpp:662
void SetOwner(PSID owner, bool ownerDefaulted=false)
Sets the owner information of the security descriptor.
Definition registry.cpp:824
void SetGroup(PSID group, bool groupDefaulted=false)
Sets the primary group information of the security descriptor.
Definition registry.cpp:797
auto GetControl() const -> SECURITY_DESCRIPTOR_CONTROL
Retrieves the control information from the security descriptor.
Definition registry.cpp:484
auto GetRevision() const -> DWORD
Retrieves the revision number of the security descriptor structure.
Definition registry.cpp:591
void RemoveDacl()
Removes the discretionary access control list (DACL) from the security descriptor.
Definition registry.cpp:678
void SetDacl(PACL dacl, bool daclDefaulted=false)
Sets the discretionary access control list (DACL) of the security descriptor.
Definition registry.cpp:770
auto GetRmControl() const -> std::optional< UCHAR >
Retrieves the resource manager control bits from the security descriptor.
Definition registry.cpp:613
auto GetGroup() const -> PSID
Retrieves the primary group information from the security descriptor.
Definition registry.cpp:549
void RemoveSacl()
Removes the system access control list (SACL) from the security descriptor.
Definition registry.cpp:742
auto GetSacl() const -> std::optional< PACL >
Retrieves the system access control list (SACL) from the security descriptor.
Definition registry.cpp:639
void RemoveRmControl()
Removes the resource manager control bits from the security descriptor.
Definition registry.cpp:726
Encapsulates a registration key in the Windows Registry.
Definition registry.h:115
long EnumValue(int index, TCHAR *valueName, uint32 &valueNameSize, uint32 *type=nullptr, uint8 *data=nullptr, uint32 *dataSize=nullptr) const
Enumerates the values associated with this key.
Definition registry.h:1087
static auto GetClassesRoot() -> TRegKey &
Special predefined root key used by shell and OLE applications (HKEY_CLASSES_ROOT).
Definition registry.cpp:32
long NukeKey(LPCTSTR subKeyName)
Completely eliminates a child key, including any of its subkeys.
Definition registry.cpp:411
static auto GetUsers() -> TRegKey &
Special predefined root key defining the default user configuration (HKEY_USERS).
Definition registry.cpp:110
LPCTSTR GetName() const
Returns a string identifying this key.
Definition registry.h:1008
static auto GetCurrentConfig() -> TRegKey &
Special predefined root key containing information about the current hardware profile of the local co...
Definition registry.cpp:55
auto HasSubkey(const tstring &keyName) const -> bool
Returns true if this key has a subkey with the given name.
Definition registry.cpp:354
DWORD SubkeyCount
Number of subkeys.
Definition registry.h:394
long DeleteKey(LPCTSTR subKeyName)
Deletes the specified subkey of this registry key.
Definition registry.h:878
tstring Name
This Key's Name.
Definition registry.h:393
THandle Key
This Key's Handle.
Definition registry.h:392
auto GetSubkey(const tstring &keyName, REGSAM samDesired=KEY_READ) const -> std::optional< TRegKey >
Returns the subkey with the given name, if it exists.
Definition registry.cpp:376
long SetValue(LPCTSTR valName, uint32 type, const uint8 *data, uint32 dataSize) const
Associates a value with this key.
Definition registry.h:1016
long DeleteValue(LPCTSTR valName) const
Removes a named value from this registry key.
Definition registry.h:1054
auto GetSecurity(SECURITY_INFORMATION infoRequested, PSECURITY_DESCRIPTOR secDesc, DWORD *secDescSize) const -> long
Retrieves a copy of the security descriptor protecting this registry key.
Definition registry.h:897
~TRegKey()
Closes the underlying key handle, if owned.
Definition registry.cpp:288
auto GetValue(const tstring &valueName) const -> std::optional< TRegValue >
Returns the value with the given name, if it exists within this key.
Definition registry.cpp:402
bool ShouldClose
Should this key be closed on destruction.
Definition registry.h:396
long QueryValue(LPCTSTR valName, uint32 *type, uint8 *data, uint32 *dataSize) const
Retrieves the value associated with the unnamed value for this key in the registry.
Definition registry.h:1042
auto QueryInfo() const -> TInfo
Functional-style overload.
Definition registry.cpp:326
static auto GetCurrentUser() -> TRegKey &
Special predefined root key defining the preferences of the current user (HKEY_CURRENT_USER).
Definition registry.cpp:72
static auto GetClassesRootClsid() -> TRegKey &
Commonly used subkey by shell and OLE applications (HKEY_CLASSES_ROOT\CLSID).
Definition registry.cpp:41
static auto GetPerformanceData() -> TRegKey &
Special predefined root key used to obtain performance data (HKEY_PERFORMANCE_DATA).
Definition registry.cpp:98
auto HasValue(const tstring &valueName) const -> bool
Return true if this key has a value with the given name.
Definition registry.cpp:390
TRegKey(THandle baseKey, tstring keyName, REGSAM samDesired=KEY_ALL_ACCESS, TCreateOK createOK=CreateOK)
Creates or opens a key given a base key and a subkey name.
Definition registry.cpp:128
DWORD ValueCount
Number of value entries.
Definition registry.h:395
static auto GetLocalMachine() -> TRegKey &
Special predefined root key defining the physical state of the computer (HKEY_LOCAL_MACHINE).
Definition registry.cpp:87
TCreateOK
Enumeration used to specify whether a key should be created (or simply opened).
Definition registry.h:123
@ CreateOK
Create key if it does not exist.
Definition registry.h:124
Iterator for walking thru the subkeys of a key.
Definition registry.h:403
A registration parameter table, composed of a list of TRegItems.
Definition registry.h:531
const tchar *& Value(int i)
Return the value of the param entry at the passed index.
Definition registry.h:1470
TRegParamList(const TEntry *)
int Find(LPCTSTR param)
Looks for a given param in this param list.
void StreamOut(TRegItem *item, tostream &out)
void Init(LPCTSTR filter)
TRegTemplateList Templates
Definition registry.h:822
void UpdateParams(TLangId lang, TRegItem *item)
TRegParamList Params
Definition registry.h:823
TRegSymbolTable(TRegKey &basekey, LPCTSTR tplList[], const TRegParamList::TEntry *paramList)
void DisableAll()
Disables all templates in this list.
Definition registry.h:1398
bool IsActive(int i) const
Returns true if the template at the passed index is active, false otherwise.
Definition registry.h:1437
void Activate(int i)
Activates the template at the passed index.
Definition registry.h:1426
TRegTemplateList(TRegKey &basekey, LPCTSTR _list[])
int GetCount() const
Returns the number of templates in this list.
Definition registry.h:1369
TRegKey & GetBaseKey()
Returns the registry key upon which these templates are based.
Definition registry.h:1377
void Enable(int i)
Enables the template at the passed index.
Definition registry.h:1415
void EnableAll()
Enables all templates in this list.
Definition registry.h:1406
Encapsulates a value-data entry within one registration key.
Definition registry.h:51
const uint8 * GetData() const
Returns the data associated with this value.
Definition registry.cpp:998
TRegValue(const TRegKey &key, LPCTSTR name)
Creates a registry value object from the specified registry key and name.
Definition registry.cpp:879
long Delete()
Removes the value from its associated key, and clears the state of this object.
TRegValue & operator=(uint32 value)
Forwards to Set (uint32).
long Set(uint32 type, const uint8 *data, uint32 dataSize)
Sets the data associated with this value.
Iterator for walking through the values of a key.
Definition registry.h:429
static void Update(TRegKey &baseKey, tistream &in)
Writes lines to registry.
static int Unregister(TRegList &regInfo, const TUnregParams *params, const TRegItem *overrides=nullptr)
Unregisters entries given a reglist.
static int Validate(TRegKey &baseKey, tistream &in)
Returns number of mismatched entries.
Thrown for errors within the Registry classes.
Definition registry.h:457
#define _tcscmp
Definition cygwin.h:75
#define _tcscpy
Definition cygwin.h:79
#define _MAX_PATH
Definition cygwin.h:97
#define MAX_PATH
Definition cygwin.h:98
#define _tcslen
Definition cygwin.h:74
#define _T(x)
Definition cygwin.h:51
#define _tcschr
Definition cygwin.h:85
#define _USES_CONVERSION
Definition memory.h:217
Object Windows Library (OWLNext Core)
Definition animctrl.h:22
unsigned char uint8
Definition number.h:32
unsigned long uint32
Definition number.h:34
owl::uint16 TLangId
Holds a language ID, a predefined number that represents a base language and dialect.
Definition lclstrng.h:26
char tchar
Definition defs.h:77
std::istream tistream
Definition strmdefs.h:39
unsigned __int64 uint64
Definition number.h:43
std::string tstring
Definition defs.h:79
std::ostream tostream
Definition strmdefs.h:40
auto to_tstring(const T &v) -> tstring
Definition defs.h:82
#define COUNTOF(s)
Array element count Important: Only use this with an argument of array type.
Definition defs.h:376
Various types of smart pointer templatized classes.
#define _A2WB_(val)
#define _W2AB_(val)
General Registry access & registration implementation TRegKey, TRegValue, TRegKeyIterator,...
const tchar * Translate(TLangId lang)
translate string
Definition locale.cpp:344
A single registration list entry.
Definition registry.h:508
TLocaleString Value
Localizable value for parameter or subkey.
Definition registry.h:510
const char * Key
Non-localized parameter or registry subkey.
Definition registry.h:509
Data structure returned by QueryInfo.
Definition registry.h:305
Definition registry.h:784
LPCTSTR Param
Substituted parameter name.
Definition registry.h:785
Definition of TSystem, a system information provider class.
#define __try
Definition unixxcpt.cpp:142
#define __except
Definition unixxcpt.cpp:144
#define GetExceptionCode()
Definition unixxcpt.cpp:74