OWLNext    7.0
Borland's Object Windows Library for the modern age
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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