Sometimes we need to save a piece of data for the whole machine globally, not for a current user only. Unfortunately HKEY_LOCAL_MACHINE is write-protected for anybody except administrators. But you may create e.g. a key "a_key" below HKEY_LOCAL_MACHINE with admin rights and then - assign "write-everybody" permission to "a_key", so unprivileged users will share the same data and will be allowed to modify it.
The following function _RegSetKeySecuritySetNullDacl sets "write-everybody" permission to a Registry key.
Usage sample:
Local $szKey = "HKEY_LOCAL_MACHINE\SOFTWARE\YourCompany\a_key"
Local $code, $msg
Local $ok = _RegSetKeySecuritySetNullDacl($szKey, $code, $msg)
If Not $ok Then MsgBox(0, "Exited with code " & $code, $msg)
The library:
#include-once
#include <StructureConstants.au3>
#include <WinAPIError.au3>
#include <WinAPI.au3>
;
; Return value:
; 0 - failure
; 1 - success
;
Func _RegSetKeySecuritySetNullDacl($szKey, ByRef $errorCode, ByRef $errorMessage)
Local $hKey = 0
Local $ret = _RegSetKeySecuritySetNullDaclInternal($szKey, $hKey)
If $hKey Then DllCall('AdvAPI32.dll', 'Long', 'RegCloseKey', "ulong_ptr", $hKey)
If $ret Then
If StringIsInt($ret) Then
$errorCode = $ret
$errorMessage = _WinAPI_GetErrorMessageByCode($ret)
Else
$errorCode = 1
$errorMessage = $ret
EndIf
Return 0
Else
$errorCode = 0
$errorMessage = 0
Return 1
EndIf
EndFunc
;
; Return values:
; 0 - success
; nnn - Windows error code
; "..." - error description
;
Func _RegSetKeySecuritySetNullDaclInternal($szKey, ByRef $hKey)
Local $HKEY_CLASSES_ROOT = 0x80000000
Local $HKEY_CURRENT_USER = 0x80000001
Local $HKEY_LOCAL_MACHINE = 0x80000002
Local $HKEY_USERS = 0x80000003
Local $HKEY_CURRENT_CONFIG = 0x80000005
; Parse key hive.
Local $hRoot = StringLeft($szKey, StringInStr($szKey, '\') - 1)
If $hRoot = "" Then $hRoot = $szKey ; passed a root key
Switch StringUpper($hRoot)
Case "HKEY_LOCAL_MACHINE", "HKLM"
$hRoot = $HKEY_LOCAL_MACHINE
Case "HKEY_USERS", "HKU"
$hRoot = $HKEY_USERS
Case "HKEY_CURRENT_USER", "HKCU"
$hRoot = $HKEY_CURRENT_USER
Case "HKEY_CLASSES_ROOT", "HKCR"
$hRoot = $HKEY_CLASSES_ROOT
Case "HKEY_CURRENT_CONFIG", "HKCC"
$hRoot = $HKEY_CURRENT_CONFIG
Case Else
Return "Unknown hive name: " & $hRoot
EndSwitch
Local $szSubkey = StringTrimLeft($szKey, StringInStr($szKey, '\'))
If $szSubkey = $szKey Then $szSubkey = "" ; root key
; Open existed registry key.
Local $aRet = DllCall( _
"advapi32.dll", "long", "RegOpenKeyExW", _
"ulong_ptr", $hRoot, _
"wstr", $szSubkey, _
"dword", 0, _
"ulong", 0x40000, _ ; WRITE_DAC
"ulong_ptr*", 0 _
)
If @error Then Return "RegOpenKeyExW loading failed: @error=" & @error
If $aRet[0] Then Return $aRet[0]
$hKey = $aRet[5]
; The size of SECURITY_DESCRIPTOR is 20 bytes. We just
; need a block of memory the right size, we aren't going to
; access any members directly so it's not important what
; the members are, just that the total size is correct.
Local $pSecurityAttributes = 0
Local $aRet = 0
Local $tSecurityDescriptor = DllStructCreate("dword[5]")
Local $pSecurityDescriptor = DllStructGetPtr($tSecurityDescriptor)
; Initialize the security descriptor.
$aRet = DllCall( _
"advapi32.dll", "bool", "InitializeSecurityDescriptor", _
"ptr", $pSecurityDescriptor, _
"dword", 1 _
)
If @error Then Return "InitializeSecurityDescriptor loading failed: @error=" & @error
If Not $aRet[0] Then Return $aRet[0]
; Add the NULL DACL specifying access to everybody.
$aRet = DllCall( _
"advapi32.dll", "bool", "SetSecurityDescriptorDacl", _
"ptr", $pSecurityDescriptor, _
"bool", 1, _
"ptr", 0, _
"bool", 0 _
)
If @error Then Return "SetSecurityDescriptorDacl loading failed: @error=" & @error
If Not $aRet[0] Then Return $aRet[0]
; Set permissions.
$aRet = DllCall( _
"Advapi32.dll", "long", "RegSetKeySecurity", _
"ulong_ptr", $hKey, _
"dword", BitOr(0x00000004, 0x80000000), _
"ptr", $pSecurityDescriptor _
)
If @error Then Return "RegSetKeySecurity loading failed: @error=" & @error
If $aRet[0] Then Return $aRet[0]
Return 0
EndFunc
;
; Convert Windows error code to message.
;
Func _WinAPI_GetErrorMessageByCode($code)
Local $tBufferPtr = DllStructCreate("ptr")
Local $pBufferPtr = DllStructGetPtr($tBufferPtr)
Local $nCount = _WinAPI_FormatMessage(BitOR($__WINAPICONSTANT_FORMAT_MESSAGE_ALLOCATE_BUFFER, $__WINAPICONSTANT_FORMAT_MESSAGE_FROM_SYSTEM), _
0, $code, 0, $pBufferPtr, 0, 0)
If @error Then Return SetError(@error, 0, "")
Local $sText = ""
Local $pBuffer = DllStructGetData($tBufferPtr, 1)
If $pBuffer Then
If $nCount > 0 Then
Local $tBuffer = DllStructCreate("wchar[" & ($nCount+1) & "]", $pBuffer)
$sText = DllStructGetData($tBuffer, 1)
EndIf
_WinAPI_LocalFree($pBuffer)
EndIf
Return $sText
EndFunc ;==>_WinAPI_GetErrorMessageByCode