Jump to content
zorphnog

Registry Policy File Editor

Recommended Posts

zorphnog

I needed a tool that would allow me to edit local group policy files. Registry policy files contain registry information used by the local group policy. Editing these files, followed by a `gpupdate /force` command allows you to enforce the modified local group policy. To my surprise, there aren't many editors out there for this type of file. I found a few implementations across the net, but none of them did everything I wanted or they were not free. Mainly I wanted a tool that provided read/write capabilities for the registry policy file format (.pol) via GUI and command line interface.

So I created 'GPO Registry Editor' to solve this problem and I thought I would share it with the community. The script provides read/write access .pol file through GUI and CLI. I created the editor to be compliant with the Registry Policy File Format. However, in my testing the special values **DeleteKeys and **SecureKey do not conform the the format specification (at least not on Windows XP). Although I did implement these features, they do not perform the expected results when updated via gpupdate.
 

GRE_Screen_Cap.PNG


The CLI has the following usage:

GPO Registry Editor
v1.0.0.0
Provides read and write capabilities for registry policy files.

   Usage:
    -a  --add              Add the entry specified by the key, value, type, and data parameters.
    -r  --remove           Remove the entry specified by the key and value parameters.
    -d  --data             Specifies the data of the registry entry.
    -f  --file             Specifies the registry file to load or modify. Use `computer` or `user` to specify the system policy files.
    -k  --key              Specifies the key of the registry entry.
    -s  --silent           Perform the operation silently (no GUI).
    -t  --type             Specifies the type of the registry entry.
    -v  --value            Specifies the value of the registry entry.
    -h  --help             Display this message.
    -?  --?                Display this message.

Example: Add a value to the system Computer/Machine registry file.

gre --add -f=computer -k=Software\Policies\Microsoft\MyTestKey -v="My Test Value" -t REG_SZ -d="Registry Policy File editing"

Example: Remove a value from the system User registry file and suppress GUI error messages.

gre --remove -f user -k Software\Policies\Microsoft\MyTestKey -v "My Test Value" -s

Example: Delete all values of a key from a local file.

gre --add -f=C:\Registry.pol -k=Software\Policies\Microsoft\MyTestKey -v=**DelVals

Source code:

#NoTrayIcon
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Compile_Both=y
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Res_Description=Provides read and write capabilities for registry policy files.
#AutoIt3Wrapper_Res_Fileversion=1.0.0.0
#AutoIt3Wrapper_Res_Language=1033
#AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#cs ----------------------------------------------------------------------------

Title:        GPO Registry Editor
AutoIt Version: 3.3.8.1
Author:      Michael Mims (zorphnog)

Script Function:
    Provides read/write capabilities for the registry policy file format (.pol) via GUI or command line interface.
    Registry policy files contain registry information used by the local group policy. Editing these files, followed
    by a `gpupdate /force` command will update the local group policy.

    **Note: On Windows Vista and above, administrator rights are required to save to the system group policy folder.
            Compile with the requestedExecutionLevel=requireAdministrator directive to ensure administrator access.

Reference(s):
    Registry Policy File Format - http://msdn.microsoft.com/en-us/library/windows/desktop/aa374407(v=vs.85).aspx

#ce ----------------------------------------------------------------------------


#include <EditConstants.au3>
#include <File.au3>
#include <GuiComboBox.au3>
#include <GuiConstants.au3>
#include <GuiListView.au3>
#include <Misc.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include "_OptParse.au3"

; Set Options
Opt("GUICloseOnESC", 0)
Opt("GUIOnEventMode", 1)
Opt("MustDeclareVars", 1)

; Create mutex
If @AutoItX64 And @Compiled Then
    _Singleton("GPO64_RE")
Else
    _Singleton("GPO_RE")
EndIf


; Application globals
Global Const $GRE_TITLE = "GPO Registry Editor"
Global Const $GRE_VERSION = "1.0.0.0"
Global Const $GRE_DESC = "Provides read and write capabilities for registry policy files."

; File globals
Global Const $GRE_LOG_FILE = @ScriptDir & "gre.log"
Global Const $USER_REGISTRY_FILE = @SystemDir & "GroupPolicyUserRegistry.pol"
Global Const $MACHINE_REGISTRY_FILE = @SystemDir & "GroupPolicyMachineRegistry.pol"

; Policy file format globals
Global Const $REGISTRY_FILE_SIGNATURE = 0x67655250
Global Const $REGISTRY_FILE_VERSION = 0x00000001
Global Const $REGISTRY_FILE_ENCODING = 2  ; 1 - ANSI, 2 - UTF16 LE, 3 - UTF16 BE, 4 - UTF8

; $g_aEntries array indices
Global Enum $POLENTRY_KEY, $POLENTRY_VALUE, $POLENTRY_TYPE, $POLENTRY_SIZE, $POLENTRY_DATA

; Registry globals
Global Enum $REG_NONE,$REG_SZ,$REG_EXPAND_SZ,$REG_BINARY,$REG_DWORD,$REG_DWORD_BIG_ENDIAN,$REG_LINK,$REG_MULTI_SZ, _
    $REG_RESOURCE_LIST,$REG_FULL_RESOURCE_DESCRIPTOR,$REG_RESOURCE_REQUIREMENTS_LIST,$REG_QWORD
Global Const $g_REGTYPES[12] = [ "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", _
    "REG_DWORD", "REG_DWORD_BIG_ENDIAN", "REG_LINK", "REG_MULTI_SZ", "REG_RESOURCE_LIST", _
    "REG_FULL_RESOURCE_DESCRIPTOR", "REG_RESOURCE_REQUIREMENTS_LIST", "REG_QWORD"]
Global Const $g_ENTRYREGTYPE_CONV[4] = [$REG_SZ,$REG_EXPAND_SZ,$REG_BINARY,$REG_DWORD]
Global Const $g_ENTRYREGTYPES[4] = [$g_REGTYPES[$g_ENTRYREGTYPE_CONV[0]],$g_REGTYPES[$g_ENTRYREGTYPE_CONV[1]], _
    $g_REGTYPES[$g_ENTRYREGTYPE_CONV[2]], $g_REGTYPES[$g_ENTRYREGTYPE_CONV[3]]]

; Policy entry type globals
Global Enum $ENTRY_TYPE_NORM,$ENTRY_TYPE_DEL,$ENTRY_TYPE_DELMULVALS,$ENTRY_TYPE_DELALLVALS,$ENTRY_TYPE_DELKEYS, _
    $ENTRY_TYPE_SECKEY,$_ENTRY_TYPE_SIZE
Global Const $g_ENTRYTYPES[$_ENTRY_TYPE_SIZE] = ["Normal Entry", "Delete Value", "Delete Multiple Values", _
    "Delete All Values", "Delete Subkeys", "Secure Key"]

; $g_MainCtrl array indices
Global Enum $ENT_GUI,$ENT_CBENTR,$ENT_LBKEY,$ENT_INKEY,$ENT_LBVALU,$ENT_INVALU,$ENT_LBTYPE,$ENT_CBTYPE,$ENT_LBDATA, _
    $ENT_INDATA,$ENT_LBDESC,$ENT_ILASTENTR,$_ENT_SIZE

; $g_EntryCtrl array indices
Global Enum $MAIN_GUI,$MAIN_LVENTR,$MAIN_BTNEW,$MAIN_BTEDIT,$MAIN_BTDEL,$MAIN_MIMPOL,$MAIN_MIUPOL,$MAIN_MISAVE, _
    $MAIN_MISAVA,$_MAIN_SIZE

; Command line option array indices
Global Enum $GRE_OPT_ACTION,$GRE_OPT_FILE,$GRE_OPT_KEY,$GRE_OPT_VALUE,$GRE_OPT_TYPE,$GRE_OPT_DATA,$_GRE_OPT_SIZE
Global Enum $GRE_ACTION_ADD,$GRE_ACTION_REMOVE

; Error code globals
Global Enum Step *2 $GRE_ERROR_NONE=0,$GRE_ERROR_NOFILE=1,$GRE_ERROR_NOACTION,$GRE_ERROR_NOKEY,$GRE_ERROR_NOVALUE, _
    $GRE_ERROR_NOTYPE,$GRE_ERROR_INVALIDTYPE,$GRE_ERROR_NODATA,$GRE_ERROR_FILENOTEXIST,$GRE_ERROR_INVALIDARG, _
    $GRE_ERROR_DUPLICATEARG,$GRE_ERROR_INVALIDSWITCH,$GRE_ERROR_FILEWRITE,$GRE_ERROR_INVALIDREGISTRYSIG, _
    $GRE_ERROR_INVALIDREGISTRYVERSION,$GRE_ERROR_ENTRYNOTFOUND,$GRE_ERROR_DELETEENTRY,$GRE_ERROR_INVALIDSPECVAL

Global $g_aEntries[1][5], $g_MainCtrl[$_MAIN_SIZE], $g_EntryCtrl[$_ENT_SIZE]
Global $g_sRegFile, $g_iEditLVIndex = -1, $g_bOpen = False, $g_bChanged = False, $g_bDebug = False, $g_bSilent = False

_Main()

Func _AddEntry($sKey, $sValue, $iType = 1, $vData = "")
    Local $iIndex = _FindEntry($sKey, $sValue)
    If @error Then
        $iIndex = UBound($g_aEntries)
        ReDim $g_aEntries[$iIndex + 1][5]
        $g_aEntries[$iIndex][$POLENTRY_KEY] = $sKey & ChrW(0)
        $g_aEntries[$iIndex][$POLENTRY_VALUE] = $sValue & ChrW(0)
    EndIf

    ; Force type for special values
    If StringLeft($sValue, 2) == "**" Then
        __DebugPrint("Special value; forcing REG_SZ", "_AddEntry")
        $iType = $REG_SZ
    EndIf
    $g_aEntries[$iIndex][$POLENTRY_TYPE] = Binary(Int($iType))

    ; Determine if value is string data and convert properly
    Switch $iType
        Case $REG_SZ, $REG_EXPAND_SZ
            ; Check for special values
            If StringLeft($sValue, 2) == "**" Then
                Select
                    Case StringMid($sValue, 3, 12) = "DeleteValues" Or StringMid($sValue, 3, 10) = "DeleteKeys"
                        $vData = StringToBinary(StringRegExpReplace(StringStripWS($vData, 3), "(s*;s*)", ";") & _
                            ChrW(0), $REGISTRY_FILE_ENCODING)
                    Case Else
                        $vData = StringToBinary(ChrW(0x20) & ChrW(0), $REGISTRY_FILE_ENCODING)
                EndSelect
            Else
                $vData = StringToBinary($vData & ChrW(0), $REGISTRY_FILE_ENCODING)
            EndIf
        Case $REG_DWORD
            $vData = Binary(Int($vData))
        Case Else
            $vData = Binary($vData)
    EndSwitch

    $g_aEntries[$iIndex][$POLENTRY_SIZE] = BinaryLen($vData)
    $g_aEntries[$iIndex][$POLENTRY_DATA] = $vData

    Return $iIndex
EndFunc   ;==>_AddEntry

Func _CloseFile()
    If Not $g_bOpen Then Return True
    If $g_bOpen And $g_bChanged Then
        Local $iRet = MsgBox(0x1033, $GRE_TITLE, "The current registry file has been modified. Would you like to save the changes?", 0, $g_MainCtrl[$MAIN_GUI])
        If $iRet = 2 Then  ; CANCEL
            Return False
        ElseIf $iRet = 6 Then  ; YES
            _SaveFile()
        EndIf
    EndIf
    _GUICtrlListView_DeleteAllItems($g_MainCtrl[$MAIN_LVENTR])
    _ModifyFile(False, False)
    $g_aEntries = 0
    Dim $g_aEntries[1][5]
    $g_sRegFile = ""
    Return True
EndFunc   ;==>_CloseFile

Func _CreateEntryGUI($bIsNew = True)
    Local $sTitle = "Add"
    If Not $bIsNew Then $sTitle = "Edit"
    $g_EntryCtrl[$ENT_GUI] = GUICreate($sTitle & " Registry Entry", 520, 240, -1, -1, BitOR($WS_POPUP, $WS_CAPTION), $WS_EX_TOOLWINDOW, $g_MainCtrl[$MAIN_GUI])
    GUICtrlCreateGroup("", 10, 10, 500, 185)
    $g_EntryCtrl[$ENT_CBENTR] = GUICtrlCreateCombo("", 20, 6, 135, 25, BitOR($CBS_DROPDOWNLIST,$CBS_AUTOHSCROLL))
    GUICtrlSetData(-1, _ArrayToString($g_ENTRYTYPES), $g_ENTRYTYPES[0])
    GUICtrlSetOnEvent(-1, "_Ev_cbEntryType")
    $g_EntryCtrl[$ENT_ILASTENTR] = 0
    $g_EntryCtrl[$ENT_LBKEY] = GUICtrlCreateLabel("Key:", 20, 44, 45, 17, $SS_RIGHT)
    $g_EntryCtrl[$ENT_INKEY] = GUICtrlCreateInput("", 70, 41, 430, 21)
    $g_EntryCtrl[$ENT_LBVALU] = GUICtrlCreateLabel("Value:", 20, 76, 45, 17, $SS_RIGHT)
    $g_EntryCtrl[$ENT_INVALU] = GUICtrlCreateInput("", 70, 73, 430, 21)
    $g_EntryCtrl[$ENT_LBTYPE] = GUICtrlCreateLabel("Type:", 20, 108, 45, 17, $SS_RIGHT)
    $g_EntryCtrl[$ENT_CBTYPE] = GUICtrlCreateCombo("", 70, 105, 240, 25, BitOR($CBS_DROPDOWNLIST,$CBS_AUTOHSCROLL))
    GUICtrlSetData(-1, _ArrayToString($g_ENTRYREGTYPES), $g_ENTRYREGTYPES[0])
    $g_EntryCtrl[$ENT_LBDATA] = GUICtrlCreateLabel("Data:", 20, 140, 45, 17, $SS_RIGHT)
    $g_EntryCtrl[$ENT_INDATA] = GUICtrlCreateInput("", 70, 137, 430, 42, $ES_MULTILINE)
    $g_EntryCtrl[$ENT_LBDESC] = GUICtrlCreateLabel("", 70, 100, 430, 68)
    GUICtrlSetFont(-1, Default, Default, 2)
    GUICtrlSetState(-1, $GUI_HIDE)
    GUICtrlCreateGroup("", -99, -99, 1, 1)
    GUICtrlCreateButton("OK", 350, 205, 75, 25)
    GUICtrlSetOnEvent(-1, "_Ev_btEntryOk")
    GUICtrlCreateButton("Cancel", 435, 205, 75, 25)
    GUICtrlSetOnEvent(-1, "_Ev_btEntryCancel")
EndFunc   ;==>_CreateEntryGUI

Func _CreateGUI()
    $g_MainCtrl[$MAIN_GUI] = GUICreate($GRE_TITLE, 770, 475, -1, -1, BitOR($WS_MINIMIZEBOX, $WS_CAPTION, $WS_POPUP, $WS_SYSMENU, $WS_MAXIMIZEBOX, $WS_SIZEBOX))
    GUISetOnEvent($GUI_EVENT_CLOSE, "_Ev_miFileClose")
    GUICtrlCreateListView("Registry Key|Value|Type|Data", 10, 10, 750, 400)
    $g_MainCtrl[$MAIN_LVENTR] = GUICtrlGetHandle(-1)
    GUICtrlSendMsg(-1, 0x101E, 0, 350)
    GUICtrlSendMsg(-1, 0x101E, 1, 150)
    GUICtrlSendMsg(-1, 0x101E, 2, 100)
    GUICtrlSendMsg(-1, 0x101E, 3, 100)
    GUICtrlSetResizing(-1, $GUI_DOCKBORDERS)
    $g_MainCtrl[$MAIN_BTNEW] = GUICtrlCreateButton("New...", 10, 420, 75, 25, 0)
    GUICtrlSetState(-1, $GUI_DISABLE)
    GUICtrlSetOnEvent(-1, "_Ev_btNew")
    GUICtrlSetResizing(-1, BitOR($GUI_DOCKLEFT,$GUI_DOCKBOTTOM,$GUI_DOCKSIZE))
    $g_MainCtrl[$MAIN_BTEDIT] = GUICtrlCreateButton("Edit...", 95, 420, 75, 25, 0)
    GUICtrlSetState(-1, $GUI_DISABLE)
    GUICtrlSetOnEvent(-1, "_Ev_btEdit")
    GUICtrlSetResizing(-1, BitOR($GUI_DOCKLEFT,$GUI_DOCKBOTTOM,$GUI_DOCKSIZE))
    $g_MainCtrl[$MAIN_BTDEL] = GUICtrlCreateButton("Delete", 180, 420, 75, 25, 0)
    GUICtrlSetState(-1, $GUI_DISABLE)
    GUICtrlSetOnEvent(-1, "_Ev_btDelete")
    GUICtrlSetResizing(-1, BitOR($GUI_DOCKLEFT,$GUI_DOCKBOTTOM,$GUI_DOCKSIZE))

    ; Menu items
    Local $muFile, $muOpen
    $muFile = GUICtrlCreateMenu("File")
    GUICtrlCreateMenuItem("New", $muFile)
    GUICtrlSetOnEvent(-1, "_Ev_miFileNew")
    $muOpen = GUICtrlCreateMenu("Open", $muFile)
    $g_MainCtrl[$MAIN_MIMPOL] = GUICtrlCreateMenuItem("Machine Policy", $muOpen)
    GUICtrlSetOnEvent(-1, "_Ev_miFileOpenSystem")
    $g_MainCtrl[$MAIN_MIUPOL] = GUICtrlCreateMenuItem("User Policy", $muOpen)
    GUICtrlSetOnEvent(-1, "_Ev_miFileOpenSystem")
    GUICtrlCreateMenuItem("", $muOpen)
    GUICtrlCreateMenuItem("File...", $muOpen)
    GUICtrlSetOnEvent(-1, "_Ev_miFileOpenFile")
    $g_MainCtrl[$MAIN_MISAVE] = GUICtrlCreateMenuItem("Save", $muFile)
    GUICtrlSetState(-1, $GUI_DISABLE)
    GUICtrlSetOnEvent(-1, "_Ev_miFileSave")
    $g_MainCtrl[$MAIN_MISAVA] = GUICtrlCreateMenuItem("Save As...", $muFile)
    GUICtrlSetState(-1, $GUI_DISABLE)
    GUICtrlSetOnEvent(-1, "_Ev_miFileSave")
    GUICtrlCreateMenuItem("", $muFile)
    GUICtrlCreateMenuItem("Exit", $muFile)
    GUICtrlSetOnEvent(-1, "_Ev_miFileClose")

    GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")
    GUISetState(@SW_SHOW)
EndFunc   ;==>_CreateGUI

Func _DeleteEntry($sKey, $sValue)
    Local $iIndex = _FindEntry($sKey, $sValue)
    If @error Then Return SetError(@error, 0, -1)
    __DebugPrint("--key=" & $sKey & " --value=" & $sValue, "_DeleteEntry")
    _ArrayDelete($g_aEntries, $iIndex)
    If @error Then Return SetError($GRE_ERROR_DELETEENTRY, 0, -1)
    Return 1
EndFunc   ;==>_DeleteEntry

Func _EditEntry($iEntry = -1)
    If $iEntry == -1 Then
        $g_iEditLVIndex = $iEntry
        _CreateEntryGUI()
    Else
        _CreateEntryGUI(False)
        _LoadEntryValues($iEntry)
    EndIf

    GUISetState(@SW_DISABLE, $g_MainCtrl[$MAIN_GUI])
    GUISetState(@SW_SHOW, $g_EntryCtrl[$ENT_GUI])
EndFunc   ;==>_EditEntry

Func _FindEntry($sKey, $sValue)
    Local $iCurrent, $iIndex, $iSize
    $iCurrent = 0
    $iSize = UBound($g_aEntries)

    While $iCurrent < $iSize
        $iIndex = _ArraySearch($g_aEntries, $sValue, $iCurrent, 0, 0, 0, 1, $POLENTRY_VALUE)
        If @error Then ExitLoop
        If $g_aEntries[$iIndex][$POLENTRY_KEY] = $sKey Then
            __DebugPrint($iIndex & " " & $g_aEntries[$iIndex][$POLENTRY_KEY], "_FindEntry")
            Return $iIndex
        EndIf
        $iCurrent = $iIndex + 1
    WEnd

    Return SetError($GRE_ERROR_ENTRYNOTFOUND, 0, -1)
EndFunc   ;==>_FindEntry

Func _GetFormattedData($iIndex, $bHex = True)
    Switch $g_aEntries[$iIndex][$POLENTRY_TYPE]
        Case $REG_SZ, $REG_EXPAND_SZ
            Return BinaryToString($g_aEntries[$iIndex][$POLENTRY_DATA], $REGISTRY_FILE_ENCODING)
        Case $REG_BINARY
            Return "0x" & Hex(Int($g_aEntries[$iIndex][$POLENTRY_DATA]), 8)
        Case $REG_DWORD
            If $bHex Then Return "0x" & Hex(Int($g_aEntries[$iIndex][$POLENTRY_DATA]), 8)
            Return Int($g_aEntries[$iIndex][$POLENTRY_DATA])
        Case Else
            Return $g_aEntries[$iIndex][4]
    EndSwitch
EndFunc   ;==>_GetFormattedData

Func _LoadEntryValues($iEntry)
    Local $iEntryType, $sKey, $sValue, $iRegType, $sValue
    $iEntryType = 0
    $sKey = $g_aEntries[$iEntry][$POLENTRY_KEY]
    $sValue = $g_aEntries[$iEntry][$POLENTRY_VALUE]

    If StringLeft($sValue, 2) == "**" Then
        Select
            Case StringMid($sValue, 3, 12) = "DeleteValues"
                $iEntryType = $ENTRY_TYPE_DELMULVALS
                $sValue = BinaryToString($g_aEntries[$iEntry][$POLENTRY_DATA], $REGISTRY_FILE_ENCODING)
                $iRegType = 0
            Case StringMid($sValue, 3, 4) = "Del."
                $iEntryType = $ENTRY_TYPE_DEL
                $sValue = StringMid($sValue, 7)
                $iRegType = 0
            Case StringMid($sValue, 3, 7) = "DelVals"
                $iEntryType = $ENTRY_TYPE_DELALLVALS
                $sValue = ""
                $iRegType = 0
            Case StringMid($sValue, 3, 10) = "DeleteKeys"
                $iEntryType = $ENTRY_TYPE_DELKEYS
                $sValue = BinaryToString($g_aEntries[$iEntry][$POLENTRY_DATA], $REGISTRY_FILE_ENCODING)
                $iRegType = 0
            Case StringMid($sValue, 3, 9) = "SecureKey"
                $iEntryType = $ENTRY_TYPE_SECKEY
                $iRegType = Int(StringMid($sValue, 13, 1))
                $sValue = ""
        EndSelect
        _GUICtrlComboBox_SetCurSel($g_EntryCtrl[$ENT_CBENTR], $iEntryType)
        _Ev_cbEntryType()
        GUICtrlSetData($g_EntryCtrl[$ENT_INKEY], $sKey)
        GUICtrlSetData($g_EntryCtrl[$ENT_INVALU], $sValue)
        _GUICtrlComboBox_SetCurSel($g_EntryCtrl[$ENT_CBTYPE], $iRegType)
    Else
        $iRegType = _ArraySearch($g_ENTRYREGTYPE_CONV, $g_aEntries[$iEntry][$POLENTRY_TYPE])
        GUICtrlSetData($g_EntryCtrl[$ENT_INKEY], $sKey)
        GUICtrlSetData($g_EntryCtrl[$ENT_INVALU], $sValue)
        _GUICtrlComboBox_SetCurSel($g_EntryCtrl[$ENT_CBTYPE], $iRegType)
        GUICtrlSetData($g_EntryCtrl[$ENT_INDATA], _GetFormattedData($iEntry, False))
    EndIf
EndFunc   ;==>_LoadEntryValues

Func _Main()
    Local $aValidOpts, $aOptions, $sFilename = ""

    If @CPUArch = "X64" And Not @AutoItX64 Then
;~       __DebugPrint("X64 architecture detected.", "_Main", True)
        Local $sDrive, $sDir, $sFile, $sExt, $sExec
        _PathSplit(@ScriptFullPath, $sDrive, $sDir, $sFile, $sExt)
        $sExec = @ScriptDir & "" & $sFile & "_x64" & $sExt
;~       __DebugPrint("Attempting 64 bit launch: " & $sExec, "_Main", True)
        If FileExists($sExec) Then Exit(RunWait($sExec & " " & _ArrayToString($CmdLine, " ", 1), @WorkingDir))
    EndIf

    If _ParseOptions($aValidOpts, $aOptions) Then
        Local $iOption, $iMatches = 0

        If _OptParse_MatchOption("h,help,?", $aOptions, $iOption) Then
            _OptParse_ShowUsage($aValidOpts, 1)
            Exit($GRE_ERROR_NONE)
        EndIf

        If _OptParse_MatchOption("D,debug", $aOptions, $iOption) Then
            $iMatches += 1
            $g_bDebug = True
            __PrintEnvironment()
        EndIf

        If _OptParse_MatchOption("s,silent", $aOptions, $iOption) Then
            __DebugPrint("Silent mode set", "_Main")
            $iMatches += 1
            $g_bSilent = True
            _OptParse_SetDisplay(0)
        EndIf

        If _OptParse_MatchOption("f,file", $aOptions, $iOption) Then
            $iMatches += 1
            Switch StringLower($aOptions[$iOption][1])
                Case "c","comp","computer","m","machine"
                    $sFilename = $MACHINE_REGISTRY_FILE
                Case "u","user"
                    $sFilename = $USER_REGISTRY_FILE
                Case Else
                    $sFilename = _PathFull($aOptions[$iOption][1])
            EndSwitch
            __DebugPrint("[file] " & $sFilename, "_Main")
        EndIf

        ; Some command line parameters for testing
        ; --add -f Registry.pol -k SoftwareMyTestKey -v TestMe -t REG_SZ -d "test, test, test"
        ; --remove -f Registry.pol -k SoftwareMyTestKey -v TestMe
        If _OptParse_MatchOption("a,add,r,remove", $aOptions, $iOption) Then
            Local $iReturn, $aValidated = _ValidateOptions($aOptions)

            _ReadPolFile($sFilename)
            If @error Then Exit(@error)

            If $aValidated[$GRE_OPT_ACTION] == $GRE_ACTION_ADD Then
                _AddEntry($aValidated[$GRE_OPT_KEY], $aValidated[$GRE_OPT_VALUE], $aValidated[$GRE_OPT_TYPE], _
                    $aValidated[$GRE_OPT_DATA])
            ElseIf $aValidated[$GRE_OPT_ACTION] == $GRE_ACTION_REMOVE Then
                _DeleteEntry($aValidated[$GRE_OPT_KEY], $aValidated[$GRE_OPT_VALUE])
                If @error Then Exit(@error)
            EndIf

            _WritePolFile($sFilename)
            If @error Then Exit(@error)

            Exit($GRE_ERROR_NONE)
        ElseIf $iMatches < $aOptions[0][0] Then
            _OptParse_Display("No action specified. Use --add or --remove to modify a policy file.", "Error")
            Exit($GRE_ERROR_NOACTION)
        EndIf
    EndIf

    _CreateGUI()
    If $sFilename <> "" Then _OpenFile($sFilename)

    While 1
        Sleep(10)
    WEnd
EndFunc   ;==>_Main

Func _ModifyFile($bNewOpen, $bNewChanged)
    Local $sTitle, $aClientSize, $a_iCall
    __DebugPrint(StringFormat("[%s, %s] => [%s, %s]", $g_bOpen, $g_bChanged, $bNewOpen, $bNewChanged), "_ModifyFile")
    If $bNewOpen <> $g_bOpen Then
        If $bNewOpen Then
            If $g_sRegFile == "" Then
                $sTitle = "New Policy File - " & $GRE_TITLE
            Else
                $sTitle = $g_sRegFile & " - " & $GRE_TITLE
            EndIf
            $aClientSize = WinGetClientSize($g_MainCtrl[$MAIN_GUI])
            $a_iCall = DllCall("shlwapi.dll", "int", "PathCompactPathW", _
                    "hwnd", 0, _
                    "wstr", $sTitle, _
                    "dword", $aClientSize[0] - 100)
            If Not @error Then $sTitle = $a_iCall[2]
            WinSetTitle($g_MainCtrl[$MAIN_GUI], 0, $sTitle)
            GUICtrlSetState($g_MainCtrl[$MAIN_MISAVA], $GUI_ENABLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_BTNEW], $GUI_ENABLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_BTEDIT], $GUI_ENABLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_BTDEL], $GUI_ENABLE)
        Else
            WinSetTitle($g_MainCtrl[$MAIN_GUI], 0, $GRE_TITLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_MISAVE], $GUI_DISABLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_MISAVA], $GUI_DISABLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_BTNEW], $GUI_DISABLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_BTEDIT], $GUI_DISABLE)
            GUICtrlSetState($g_MainCtrl[$MAIN_BTDEL], $GUI_DISABLE)
            $g_bChanged = False
        EndIf
        $g_bOpen = $bNewOpen
        Return
    EndIf
    If $bNewChanged <> $g_bChanged And $g_bOpen Then
        If $bNewChanged Then
            If $g_sRegFile == "" Then
                $sTitle = "New Policy File* - " & $GRE_TITLE
            Else
                $sTitle = $g_sRegFile & "* - " & $GRE_TITLE
            EndIf
            $aClientSize = WinGetClientSize($g_MainCtrl[$MAIN_GUI])
            $a_iCall = DllCall("shlwapi.dll", "int", "PathCompactPathW", _
                    "hwnd", 0, _
                    "wstr", $sTitle, _
                    "dword", $aClientSize[0] - 100)
            If Not @error Then $sTitle = $a_iCall[2]
            WinSetTitle($g_MainCtrl[$MAIN_GUI], 0, $sTitle)
            GUICtrlSetState($g_MainCtrl[$MAIN_MISAVE], $GUI_ENABLE)
        Else
            If $g_sRegFile == "" Then
                $sTitle = "New Policy File - " & $GRE_TITLE
            Else
                $sTitle = $g_sRegFile & " - " & $GRE_TITLE
            EndIf
            $aClientSize = WinGetClientSize($g_MainCtrl[$MAIN_GUI])
            $a_iCall = DllCall("shlwapi.dll", "int", "PathCompactPathW", _
                    "hwnd", 0, _
                    "wstr", $sTitle, _
                    "dword", $aClientSize[0] - 100)
            If Not @error Then $sTitle = $a_iCall[2]
            WinSetTitle($g_MainCtrl[$MAIN_GUI], 0, $sTitle)
            GUICtrlSetState($g_MainCtrl[$MAIN_MISAVE], $GUI_DISABLE)
        EndIf
    EndIf
    $g_bChanged = $bNewChanged
EndFunc   ;==>_ModifyFile

Func _OpenFile($sFilename = "")
    If Not _CloseFile() Then Return
    If $sFilename <> "" Then
        GUISetCursor(15, 1, $g_MainCtrl[$MAIN_GUI])
        _ReadPolFile($sFilename)
        If @error Then
            GUISetCursor()
            Return
        EndIf
        _UpdateListView()
        GUISetCursor()
    EndIf
    $g_sRegFile = $sFilename
    _ModifyFile(True, False)
EndFunc   ;==>_OpenFile

Func _ParseOptions(ByRef $aValidOpts, ByRef $aOptions)
    Local $local_CmdLine = $CmdLine
    $aValidOpts = 0
    $aOptions = 0

    ; Create the valid options list
    _OptParse_Init($aValidOpts, $GRE_TITLE & "n", "v" & $GRE_VERSION & "n", $GRE_DESC & "n")
    _OptParse_Add($aValidOpts, "a", "add", $OPT_ARG_NONE, "Add the entry specified by the key, value, type, and data parameters.")
    _OptParse_Add($aValidOpts, "r", "remove", $OPT_ARG_NONE, "Remove the entry specified by the key and value parameters.")
    _OptParse_Add($aValidOpts, "D", "debug", BitOR($OPT_ARG_NONE,$OPT_ARG_HIDDEN), "Record debugging information.")
    _OptParse_Add($aValidOpts, "d", "data", $OPT_ARG_REQ, "Specifies the data of the registry entry.")
    _OptParse_Add($aValidOpts, "f", "file", $OPT_ARG_REQ, "Specifies the registry file to load or modify. Use `computer` or `user` to specify the system policy files.")
    _OptParse_Add($aValidOpts, "k", "key", $OPT_ARG_REQ, "Specifies the key of the registry entry.")
    _OptParse_Add($aValidOpts, "s", "silent", $OPT_ARG_NONE, "Perform the operation silently (no GUI).")
    _OptParse_Add($aValidOpts, "t", "type", $OPT_ARG_REQ, "Specifies the type of the registry entry.")
    _OptParse_Add($aValidOpts, "v", "value", $OPT_ARG_REQ, "Specifies the value of the registry entry.")
    _OptParse_Add($aValidOpts, "h", "help", $OPT_ARG_NONE, "Display this message.")
    _OptParse_Add($aValidOpts, "?", "?", $OPT_ARG_NONE, "Display this message.")

    $aOptions = _OptParse_GetOpts($local_CmdLine, $aValidOpts)
    Switch @error
        Case 0
            _OptParse_SetDisplay(1)  ; Display via MsgBox
            Return True
        Case 1
            _OptParse_SetDisplay(1)  ; Display via MsgBox
            Return False
        Case 2
            Exit($GRE_ERROR_INVALIDARG)
        Case 3
            Exit($GRE_ERROR_DUPLICATEARG)
        Case 4
            Exit($GRE_ERROR_INVALIDSWITCH)
    EndSwitch
EndFunc   ;==>_ParseOptions

Func _ReadPolFile($sFilename)
    Local $hInfile, $bBuffer, $iStart, $iCurrent, $iLength, $iColon, $iNumEntries, $bTemp

    If Not FileExists($sFilename) Then
        If Not $g_bSilent Then MsgBox(0x1010, $GRE_TITLE, "ERROR: File not found: " & $sFilename)
        Return SetError($GRE_ERROR_FILENOTEXIST, 0, -1)
    EndIf
    $hInfile = FileOpen($sFilename, 16)
    $bBuffer = FileRead($hInfile)
    FileClose($hInfile)
    $iLength = BinaryLen($bBuffer)

    If BinaryMid($bBuffer, 1, 4) <> Binary($REGISTRY_FILE_SIGNATURE) Then
        If Not $g_bSilent Then MsgBox(0x1010, $GRE_TITLE, "ERROR: Invalid file signature. File will not be processed.")
        Return SetError($GRE_ERROR_INVALIDREGISTRYSIG, 0, -1)
    EndIf
    If BinaryMid($bBuffer, 5, 4) <> Binary($REGISTRY_FILE_VERSION) Then
        If Not $g_bSilent Then MsgBox(0x1010, $GRE_TITLE, "ERROR: Invalid registry file version. File will not be processed.")
        Return SetError($GRE_ERROR_INVALIDREGISTRYVERSION, 0, -1)
    EndIf
    $iStart = 9
    $iCurrent = 9
    $iColon = 0
    $iNumEntries = 0
    $g_aEntries = 0
    Dim $g_aEntries[1][5]

    ; The body consists of registry values in the following format.
    ; [key;value;type;size;data]
    While $iCurrent < $iLength
        $bTemp = BinaryMid($bBuffer, $iCurrent, 2)
        Switch $bTemp
            Case 0x005B ; Unicode => [
                ReDim $g_aEntries[$iNumEntries+1][5]
                $iColon = 0
                $iStart = $iCurrent + 2
            Case 0x005D ; Unicode => ]
                $iStart = $iCurrent + 2
                $iNumEntries += 1
            Case 0x003B ; Unicode => ;
                Switch $iColon
                    Case 0
                        $g_aEntries[$iNumEntries][$POLENTRY_KEY] = BinaryToString(BinaryMid($bBuffer, $iStart, $iCurrent - $iStart), $REGISTRY_FILE_ENCODING)
                        $iColon += 1
                        $iStart = $iCurrent + 2
                    Case 1
                        $g_aEntries[$iNumEntries][$POLENTRY_VALUE] = BinaryToString(BinaryMid($bBuffer, $iStart, $iCurrent - $iStart), $REGISTRY_FILE_ENCODING)
                        $iColon += 1
                        $iStart = $iCurrent + 2
                    Case 2
;~                       _ArrayDisplay($g_aEntries)
                        $g_aEntries[$iNumEntries][$POLENTRY_TYPE] = Int(BinaryMid($bBuffer, $iStart, $iCurrent - $iStart))
                        $iColon += 1
                        $iStart = $iCurrent + 2
                    Case 3
                        $g_aEntries[$iNumEntries][$POLENTRY_SIZE] = Int(BinaryMid($bBuffer, $iStart, $iCurrent - $iStart))
                        $iColon += 1
                        $iStart = $iCurrent + 2
                        $g_aEntries[$iNumEntries][$POLENTRY_DATA] = BinaryMid($bBuffer, $iStart, $g_aEntries[$iNumEntries][$POLENTRY_SIZE])
                        $iCurrent = $iStart + $g_aEntries[$iNumEntries][$POLENTRY_SIZE] - 2
                    Case Else
                        $iColon += 1
                        $iStart = $iCurrent + 2
                EndSwitch
        EndSwitch
        $iCurrent += 2
    WEnd
EndFunc   ;==>_ReadPolFile

Func _SaveFile($bSaveAs = False)
    If $bSaveAs Or Not FileExists($g_sRegFile) Then
        Local $sFilename = FileSaveDialog("Save GPO Registry File...", "", "GPO Registry Files (*.pol)", 18, "", $g_MainCtrl[$MAIN_GUI])
        If @error Then Return False
        If StringRight($sFilename, 4) <> ".pol" Then $sFilename &= ".pol"
        $g_sRegFile = $sFilename
    EndIf
    _WritePolFile($g_sRegFile)
    If @error Then Return False
    _ModifyFile($g_bOpen, False)
EndFunc   ;==>_SaveFile

Func _UpdateListView()
    Local $i, $iMax
    _GUICtrlListView_BeginUpdate($g_MainCtrl[$MAIN_LVENTR])
    _GUICtrlListView_DeleteAllItems($g_MainCtrl[$MAIN_LVENTR])
    $iMax = UBound($g_aEntries) - 1
    For $i=0 To $iMax
        _GUICtrlListView_AddItem($g_MainCtrl[$MAIN_LVENTR], $g_aEntries[$i][$POLENTRY_KEY])
        _GUICtrlListView_AddSubItem($g_MainCtrl[$MAIN_LVENTR], $i, $g_aEntries[$i][$POLENTRY_VALUE], 1)
        _GUICtrlListView_AddSubItem($g_MainCtrl[$MAIN_LVENTR], $i, $g_REGTYPES[$g_aEntries[$i][$POLENTRY_TYPE]], 2)
        _GUICtrlListView_AddSubItem($g_MainCtrl[$MAIN_LVENTR], $i, _GetFormattedData($i), 3)
    Next
    _GUICtrlListView_EndUpdate($g_MainCtrl[$MAIN_LVENTR])
EndFunc   ;==>_UpdateListView

Func _ValidateOptions($aOptions)
    Local $aReturn[$_GRE_OPT_SIZE], $iOption

    ; Check for policy file definition
    If Not _OptParse_MatchOption("f,file", $aOptions, $iOption) Then
        _OptParse_Display("File required. Use --file to specify the policy file to modify.", "Error")
        Exit($GRE_ERROR_NOFILE)
    EndIf

    ; Check for key definition
    If _OptParse_MatchOption("k,key", $aOptions, $iOption) Then
        __DebugPrint("[key] " & $aOptions[$iOption][1], "_ValidateOptions")
        $aReturn[$GRE_OPT_KEY] = $aOptions[$iOption][1]
    Else
        _OptParse_Display("Key required. Use --key to specify the registry key to modify.", "Error")
        Exit($GRE_ERROR_NOKEY)
    EndIf

    ; Check for value definition
    If _OptParse_MatchOption("v,value", $aOptions, $iOption) Then
        __DebugPrint("[value] " & $aOptions[$iOption][1], "_ValidateOptions")
        $aReturn[$GRE_OPT_VALUE] = $aOptions[$iOption][1]
    Else
        _OptParse_Display("Value required. Use --value to specify the registry value to modify.", "Error")
        Exit($GRE_ERROR_NOVALUE)
    EndIf

    If _OptParse_MatchOption("r,remove", $aOptions, $iOption) Then
        __DebugPrint("[action] remove", "_ValidateOptions")
        $aReturn[$GRE_OPT_ACTION] = $GRE_ACTION_REMOVE

        Return $aReturn
    ElseIf _OptParse_MatchOption("a,add", $aOptions, $iOption) Then
        Local $sType, $sData, $bTypeRequired = True, $bDataRequired = True
        __DebugPrint("[action] add", "_ValidateOptions")
        $aReturn[$GRE_OPT_ACTION] = $GRE_ACTION_ADD

        ; Handle special values and determine whether data is required
        If StringLeft($aReturn[$GRE_OPT_VALUE], "2") == "**" Then
            Select
                Case StringMid($aReturn[$GRE_OPT_VALUE], 3, 4) = "Del." Or _
                     StringMid($aReturn[$GRE_OPT_VALUE], 3, 7) = "DelVals" Or _
                     StringMid($aReturn[$GRE_OPT_VALUE], 3, 9) = "SecureKey"
                    __DebugPrint("Special value detected; data not required.", "_ValidateOptions")
                    $bTypeRequired = False
                    $bDataRequired = False
                Case StringMid($aReturn[$GRE_OPT_VALUE], 3, 12) = "DeleteValues" Or _
                     StringMid($aReturn[$GRE_OPT_VALUE], 3, 10) = "DeleteKeys"
                    __DebugPrint("Special value detected; data required.", "_ValidateOptions")
                    $bTypeRequired = False
                Case Else
                    _OptParse_Display("Invalid special value: " & $aReturn[$GRE_OPT_VALUE], "Error")
                    Exit($GRE_ERROR_INVALIDSPECVAL)
            EndSelect
        EndIf

        ; Check for type definition
        If _OptParse_MatchOption("t,type", $aOptions, $iOption) Then
            __DebugPrint("[type] " & $aOptions[$iOption][1], "_ValidateOptions")
            $aReturn[$GRE_OPT_TYPE] = StringUpper($aOptions[$iOption][1])
            $aReturn[$GRE_OPT_TYPE] = _ArraySearch($g_REGTYPES, $aReturn[$GRE_OPT_TYPE])
            If @error Then
                _OptParse_Display("Invalid type argument: " & $aOptions[$iOption][1], "Error")
                Exit($GRE_ERROR_INVALIDTYPE)
            EndIf
        ElseIf $bTypeRequired Then
            _OptParse_Display("Type required. Use --type to specify the registry type to add.", "Error")
            Exit($GRE_ERROR_NOTYPE)
        EndIf

        ; Check for data definition
        If _OptParse_MatchOption("d,data", $aOptions, $iOption) Then
            __DebugPrint("[data] " & $aOptions[$iOption][1], "_ValidateOptions")
            $aReturn[$GRE_OPT_DATA] = $aOptions[$iOption][1]
        ElseIf $bDataRequired Then
            _OptParse_Display("Data required. Use --data to specify the registry data to add.", "Error")
            Exit($GRE_ERROR_NODATA)
        EndIf

        Return $aReturn
    EndIf
EndFunc   ;==>_ValidateOptions

Func _WritePolFile($sFilename)
    Local $hOutfile, $iMax

    ; Open file for writing
    __DebugPrint("Writing output file...", "_WritePolFile")
    $hOutfile = FileOpen($sFilename, 26)
    If $hOutfile = -1 Then
        If Not $g_bSilent Then MsgBox(0x1010, $GRE_TITLE, "ERROR: File could not be created [" & $sFilename & "].")
        Return SetError($GRE_ERROR_FILEWRITE, 0, -1)
    EndIf

    ; Write header information
    FileWrite($hOutfile, Binary($REGISTRY_FILE_SIGNATURE))
    FileWrite($hOutfile, Binary($REGISTRY_FILE_VERSION))

    ; Write entries
    $iMax = UBound($g_aEntries) - 1
    For $i = 0 To $iMax
        FileWrite($hOutfile, StringToBinary("[" & $g_aEntries[$i][$POLENTRY_KEY], $REGISTRY_FILE_ENCODING))
        FileWrite($hOutfile, StringToBinary(";", $REGISTRY_FILE_ENCODING))
        FileWrite($hOutfile, StringToBinary($g_aEntries[$i][$POLENTRY_VALUE], $REGISTRY_FILE_ENCODING))
        FileWrite($hOutfile, StringToBinary(";", $REGISTRY_FILE_ENCODING))
        FileWrite($hOutfile, $g_aEntries[$i][$POLENTRY_TYPE])
        FileWrite($hOutfile, StringToBinary(";", $REGISTRY_FILE_ENCODING))
        FileWrite($hOutfile, $g_aEntries[$i][$POLENTRY_SIZE])
        FileWrite($hOutfile, StringToBinary(";", $REGISTRY_FILE_ENCODING))
        FileWrite($hOutfile, $g_aEntries[$i][$POLENTRY_DATA])
        FileWrite($hOutfile, StringToBinary("]", $REGISTRY_FILE_ENCODING))
    Next

    ; Close file
    FileClose($hOutfile)
EndFunc   ;==>_WritePolFile

Func _Ev_btDelete()
    Local $iIndex = _GUICtrlListView_GetSelectedIndices($g_MainCtrl[$MAIN_LVENTR])
    If $iIndex <> "" Then
        _DeleteEntry(_GUICtrlListView_GetItemText($g_MainCtrl[$MAIN_LVENTR], $iIndex), _GUICtrlListView_GetItemText($g_MainCtrl[$MAIN_LVENTR], $iIndex, 1))
        If Not @error Then
            _GUICtrlListView_DeleteItem($g_MainCtrl[$MAIN_LVENTR], $iIndex)
            _ModifyFile($g_bOpen, True)
        EndIf
    EndIf
EndFunc   ;==>_Ev_btDelete

Func _Ev_btEdit()
    Local $iEntry, $iIndex = _GUICtrlListView_GetSelectedIndices($g_MainCtrl[$MAIN_LVENTR])
    If $iIndex <> "" Then
        $iEntry = _FindEntry(_GUICtrlListView_GetItemText($g_MainCtrl[$MAIN_LVENTR], $iIndex), _GUICtrlListView_GetItemText($g_MainCtrl[$MAIN_LVENTR], $iIndex, 1))
        If Not @error Then
            $g_iEditLVIndex = $iIndex
            _EditEntry($iEntry)
        EndIf
    EndIf
EndFunc   ;==>_Ev_btEdit

Func _Ev_btEntryCancel()
    GUISetState(@SW_ENABLE, $g_MainCtrl[$MAIN_GUI])
    GUIDelete($g_EntryCtrl[$ENT_GUI])
EndFunc   ;==>_Ev_btEntryCancel

Func _Ev_btEntryOk()
    Local $iIndex, $iEntry, $sKey, $sValue, $iType, $vData

    $sKey = GUICtrlRead($g_EntryCtrl[$ENT_INKEY])
    $sValue = GUICtrlRead($g_EntryCtrl[$ENT_INVALU])
    $iType = $g_ENTRYREGTYPE_CONV[_GUICtrlComboBox_GetCurSel($g_EntryCtrl[$ENT_CBTYPE])]
    $vData = GUICtrlRead($g_EntryCtrl[$ENT_INDATA])

    Switch _GUICtrlComboBox_GetCurSel($g_EntryCtrl[$ENT_CBENTR])
        Case $ENTRY_TYPE_DEL
            $sValue = "**Del." & $sValue
            $iType = $REG_SZ
        Case $ENTRY_TYPE_DELMULVALS
            $vData = $sValue
            $sValue = "**DeleteValues"
            $iType = $REG_SZ
        Case $ENTRY_TYPE_DELALLVALS
            $sValue = "**DelVals."
            $iType = $REG_SZ
        Case $ENTRY_TYPE_DELKEYS
            $vData = $sValue
            $sValue = "**DeleteKeys" & ChrW(0)
            $iType = $REG_SZ
        Case $ENTRY_TYPE_SECKEY
            $sValue = "**SecureKey=" & _GUICtrlComboBox_GetCurSel($g_EntryCtrl[$ENT_CBTYPE])
            $iType = $REG_SZ
    EndSwitch

    $iEntry = _AddEntry($sKey, $sValue, $iType, $vData)
    If $g_iEditLVIndex == -1 Then
        $iIndex = _GUICtrlListView_AddItem($g_MainCtrl[$MAIN_LVENTR], $g_aEntries[$iEntry][$POLENTRY_KEY])
        If $iIndex <> -1 Then
            _GUICtrlListView_AddSubItem($g_MainCtrl[$MAIN_LVENTR], $iIndex, $g_aEntries[$iEntry][$POLENTRY_VALUE], 1)
            _GUICtrlListView_AddSubItem($g_MainCtrl[$MAIN_LVENTR], $iIndex, $g_REGTYPES[$g_aEntries[$iEntry][$POLENTRY_TYPE]], 2)
            _GUICtrlListView_AddSubItem($g_MainCtrl[$MAIN_LVENTR], $iIndex, _GetFormattedData($iEntry), 3)
        EndIf
    Else
        Local $sLVKey, $sLVValue
        $sLVKey = _GUICtrlListView_GetItemText($g_MainCtrl[$MAIN_LVENTR], $g_iEditLVIndex)
        $sLVValue = _GUICtrlListView_GetItemText($g_MainCtrl[$MAIN_LVENTR], $g_iEditLVIndex, 1)

        ; Update list view entry
        _GUICtrlListView_SetItemText($g_MainCtrl[$MAIN_LVENTR], $g_iEditLVIndex, $g_aEntries[$iEntry][$POLENTRY_KEY])
        _GUICtrlListView_SetItemText($g_MainCtrl[$MAIN_LVENTR], $g_iEditLVIndex, $g_aEntries[$iEntry][$POLENTRY_VALUE], 1)
        _GUICtrlListView_SetItemText($g_MainCtrl[$MAIN_LVENTR], $g_iEditLVIndex, $g_REGTYPES[$g_aEntries[$iEntry][$POLENTRY_TYPE]], 2)
        _GUICtrlListView_SetItemText($g_MainCtrl[$MAIN_LVENTR], $g_iEditLVIndex, _GetFormattedData($iEntry), 3)

        ; Delete old entry if necessary
        If $sLVKey <> $sKey Or $sLVValue <> $sValue Then _DeleteEntry($sLVKey, $sLVValue)
    EndIf

    _ModifyFile($g_bOpen, True)
    GUISetState(@SW_ENABLE, $g_MainCtrl[$MAIN_GUI])
    GUIDelete($g_EntryCtrl[$ENT_GUI])
EndFunc   ;==>_Ev_btEntryOk

Func _Ev_btNew()
    _EditEntry()
EndFunc   ;==>_Ev_btNew

Func _Ev_cbEntryType()
    Local $iSelected = _GUICtrlComboBox_GetCurSel($g_EntryCtrl[$ENT_CBENTR])
    If $iSelected = $g_EntryCtrl[$ENT_ILASTENTR] Then Return
    Switch $iSelected
        Case $ENTRY_TYPE_NORM
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDESC], $GUI_HIDE)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBVALU], "Value:")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBVALU], $GUI_SHOW)
            GUICtrlSetState($g_EntryCtrl[$ENT_INVALU], $GUI_SHOW)
            GUICtrlSetPos($g_EntryCtrl[$ENT_LBTYPE], 20, 108)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBTYPE], "Type:")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBTYPE], $GUI_SHOW)
            GUICtrlSetPos($g_EntryCtrl[$ENT_CBTYPE], 70, 105, 240)
            _GUICtrlComboBox_ResetContent($g_EntryCtrl[$ENT_CBTYPE])
            GUICtrlSetData($g_EntryCtrl[$ENT_CBTYPE], _ArrayToString($g_ENTRYREGTYPES), $g_ENTRYREGTYPES[0])
            GUICtrlSetState($g_EntryCtrl[$ENT_CBTYPE], $GUI_SHOW)
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDATA], $GUI_SHOW)
            GUICtrlSetState($g_EntryCtrl[$ENT_INDATA], $GUI_SHOW)
        Case $ENTRY_TYPE_DEL
            GUICtrlSetState($g_EntryCtrl[$ENT_LBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_CBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDATA], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_INDATA], $GUI_HIDE)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBVALU], "Value:")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBVALU], $GUI_SHOW)
            GUICtrlSetState($g_EntryCtrl[$ENT_INVALU], $GUI_SHOW)
            GUICtrlSetPos($g_EntryCtrl[$ENT_LBDESC], 70, 100)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBDESC], "Deletes the value from the associated key.")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDESC], $GUI_SHOW)
        Case $ENTRY_TYPE_DELMULVALS
            GUICtrlSetState($g_EntryCtrl[$ENT_LBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_CBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDATA], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_INDATA], $GUI_HIDE)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBVALU], "Values:")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBVALU], $GUI_SHOW)
            GUICtrlSetState($g_EntryCtrl[$ENT_INVALU], $GUI_SHOW)
            GUICtrlSetPos($g_EntryCtrl[$ENT_LBDESC], 70, 100)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBDESC], _
                StringFormat("Deletes multiple values from the associated key. Specify multiple values using a " & _
                "semicolon-delimited list.rnExample: type;size;NoRun;NoFind"))
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDESC], $GUI_SHOW)
        Case $ENTRY_TYPE_DELALLVALS
            GUICtrlSetState($g_EntryCtrl[$ENT_LBVALU], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_INVALU], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_LBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_CBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDATA], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_INDATA], $GUI_HIDE)
            GUICtrlSetPos($g_EntryCtrl[$ENT_LBDESC], 70, 68)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBDESC], "Deletes all values from the associated key.")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDESC], $GUI_SHOW)
        Case $ENTRY_TYPE_DELKEYS
            GUICtrlSetState($g_EntryCtrl[$ENT_LBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_CBTYPE], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDATA], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_INDATA], $GUI_HIDE)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBVALU], "Subkeys:")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBVALU], $GUI_SHOW)
            GUICtrlSetState($g_EntryCtrl[$ENT_INVALU], $GUI_SHOW)
            GUICtrlSetPos($g_EntryCtrl[$ENT_LBDESC], 70, 100)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBDESC], _
                StringFormat("Deletes multiple subkeys from the associated key. Specify multiple subkeys using a " & _
                "semicolon-delimited list.rnExample: type;size;NoRun;NoFind"))
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDESC], $GUI_SHOW)
        Case $ENTRY_TYPE_SECKEY
            GUICtrlSetState($g_EntryCtrl[$ENT_LBVALU], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_INVALU], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDATA], $GUI_HIDE)
            GUICtrlSetState($g_EntryCtrl[$ENT_INDATA], $GUI_HIDE)
            GUICtrlSetPos($g_EntryCtrl[$ENT_LBTYPE], 20, 76)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBTYPE], "Secure:")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBTYPE], $GUI_SHOW)
            GUICtrlSetPos($g_EntryCtrl[$ENT_CBTYPE], 70, 73, 50)
            _GUICtrlComboBox_ResetContent($g_EntryCtrl[$ENT_CBTYPE])
            GUICtrlSetData($g_EntryCtrl[$ENT_CBTYPE], "False|True", "True")
            GUICtrlSetState($g_EntryCtrl[$ENT_CBTYPE], $GUI_SHOW)
            GUICtrlSetPos($g_EntryCtrl[$ENT_LBDESC], 70, 100)
            GUICtrlSetData($g_EntryCtrl[$ENT_LBDESC], _
                "Secures the associated key, giving administrators and the system full control, and giving " & _
                "users read-only access. If set to False, access to the key resets to whatever is set on the root.")
            GUICtrlSetState($g_EntryCtrl[$ENT_LBDESC], $GUI_SHOW)
    EndSwitch
    $g_EntryCtrl[$ENT_ILASTENTR] = $iSelected
EndFunc   ;==>_Ev_cbEntryType

Func _Ev_miFileClose()
    If Not _CloseFile() Then Return
    Exit($GRE_ERROR_NONE)
EndFunc   ;==>_Ev_miFileClose

Func _Ev_miFileNew()
    If Not _CloseFile() Then Return
    _OpenFile()
EndFunc   ;==>_Ev_miFileNew

Func _Ev_miFileOpenFile()
    If Not _CloseFile() Then Return
    Local $sFilename = FileOpenDialog("Select GPO Registry File", "", "GPO Registry Files (*.pol)|All Files (*.*)", 3, "", $g_MainCtrl[$MAIN_GUI])
    If @error Then Return
    _OpenFile($sFilename)
EndFunc   ;==>_Ev_miFileOpenFile

Func _Ev_miFileOpenSystem()
    If Not _CloseFile() Then Return
    Switch @GUI_CtrlId
        Case $g_MainCtrl[$MAIN_MIMPOL]
            _OpenFile($MACHINE_REGISTRY_FILE)
        Case $g_MainCtrl[$MAIN_MIUPOL]
            _OpenFile($USER_REGISTRY_FILE)
    EndSwitch
EndFunc   ;==>_Ev_miFileOpenSystem

Func _Ev_miFileSave()
    Switch @GUI_CtrlId
        Case $g_MainCtrl[$MAIN_MISAVE]
            _SaveFile()
        Case $g_MainCtrl[$MAIN_MISAVA]
            _SaveFile(True)
    EndSwitch
EndFunc   ;==>_Ev_miFileSave

Func _WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $tNMHDR, $hWndFrom, $iCode

    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")

    If $hWndFrom = $g_MainCtrl[$MAIN_LVENTR] And $iCode == $NM_DBLCLK Then _Ev_btEdit()

    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_NOTIFY

Func __DebugPrint($sMessage, $sSource = "Info", $bOverride = False)
    If Not $g_bDebug And Not $bOverride Then Return
    Local $sLine = StringFormat("%02d%02d%04d %02d:%02d:%02d   %-20s %srn", _
        @MON, @MDAY, @YEAR, @HOUR, @MIN, @SEC, $sSource, $sMessage)
    If @Compiled Then
        FileWrite($GRE_LOG_FILE, $sLine)
    Else
        ConsoleWrite($sLine)
    EndIf
EndFunc   ;==>__DebugPrint

Func __PrintEnvironment()
    Local $sEnv = @CRLF
    $sEnv &= StringFormat("  OSVersion:    %srn", @OSVersion)
    $sEnv &= StringFormat("  OSArch:          %srn", @OSArch)
    $sEnv &= StringFormat("  AutoItX64:    %srn", @AutoItX64)
    $sEnv &= StringFormat("  WindowsDir:      %srn", @WindowsDir)
    $sEnv &= StringFormat("  SystemDir:    %srn", @SystemDir)
    $sEnv &= StringFormat("  TempDir:        %srn", @TempDir)
    $sEnv &= StringFormat("  ComSpec:        %srn", @ComSpec)
    $sEnv &= StringFormat("  ProgramFilesDir: %s", @ProgramFilesDir)

    __DebugPrint($sEnv, "__PrintEnvironment", True)
EndFunc   ;==>__PrintEnvironment

Enjoy and please let me know if there are any issues.

GRE_source.zip

**Note: On Windows Vista and above, administrator rights are required to save to the system group policy folder. Compile with the requestedExecutionLevel=requireAdministrator directive to ensure administrator access.

Edited by zorphnog
  • Like 1

Share this post


Link to post
Share on other sites
eimhym

Its very usefull! I always find a way to script it do like block executable/script on USB stick. If you do it manually you will have to create new path rules for each targetted drive letter. One question, is this tool already have export/import feature builtin?

Share this post


Link to post
Share on other sites
zorphnog

One question, is this tool already have export/import feature builtin?

Currently, there is not an import/export feature. It would certainly be a nice feature to implement, and I may try to add it when I have some free time.

Share this post


Link to post
Share on other sites
eimhym

Cool, I look forward to the next version ;) Great tool already, thanks for sharing

Share this post


Link to post
Share on other sites
KNARZ

GREAT TOOL!

Edited by KNARZ

Share this post


Link to post
Share on other sites
h82loze

THANK YOU!  This was exactly what I was looking for.  I also wanted to add a couple of notes.

1. When specifying the key with -k, you need to add in the '' which isn't listed in your example.

2. If you will be using this on both x86 and x64 computers, you will need to compile 2 versions.  One for each architecture.

Share this post


Link to post
Share on other sites
Homes32

very nice tool. thanks for sharing! :)

Share this post


Link to post
Share on other sites
Adonix

Well i downloaded this but what to know that how to use it :P

I got 2 files gre.au3 and _OptParse.au3.

Help please.

Share this post


Link to post
Share on other sites
danielqc

Hello, I'm brand new on script compiling etc.

So when I'm running this script I'm getting an error:

line 73 cannot assign values to constants

Do you have an idea ?

thank you

Share this post


Link to post
Share on other sites
Jos

This example is 5 years old and the languages has seen many changes.
Doing a au3check on this source gives many errors, so there is some work to check all of those.

Jos

 


SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource        Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites
Melba23

danielqc,

Welcome to the AutoIt forums.

Those values are obviously now defined in one of the included files - try commenting out lines 73 & 74 and see if that helps.

M23


Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites
danielqc

Thank you Jos an Melba23,

I commented out these line and script runs. Now I need to figure why there is no registry.pol file in /windows/system32/grouppolicy/user/

Dan

Share this post


Link to post
Share on other sites
neonred

Thank you for the tool zorphnog, and for the update Melba23.

I recently altered the local machine zone policy in such a way to render access to group policy editor impossible.

This tool help me down all the zone info the local machine, keep the rest of my group policy and give me back access to group policy editor.

However, I had to delete many entries one at a time, totaling about 100 entries.

Is there an easy way to add multiple selection for delete?

 

Thank you,

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Similar Content

    • GeorgeB
      By GeorgeB
      I'm writing a little applet that basically tells you when Windows was installed.  There is a REG_DWORD in Windows that gives you this. It's basically a value that is the # of seconds from 1970.
      The location is:  "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate"
      So if I run this in AutoIT, I should get the value displayed within the msgbox:
      MsgBox($MB_SYSTEMMODAL, "InstallDate Test", RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallDate"))
      However, what happens is it always returns a value of "0"  I tried this on several machines (Windows 8, Windows 8.1 and Windows 10). 
      Am I missing something?  If I manually view this REG_DWORD with RegEdit, it shows me the HEX value, or I can view the Decimal value. I don't care which value AutoIT reads, as I can always convert back and forth, but I just don't see why it can't read a value from this REG_DWORD.  As a test, I've read other REG_DWORD values, and with most it doesn't return any value, not even a 0.
      Please, even if you guys have some other (perhaps better) way to read the Windows install date, I would still like to find a resolution to this problem, because I want to understand why I am having so much difficulty with reading REG_DWORD values from the Windows Registry with AutoIT.
      Thanks for any help!
       
       
       
       
       
    • AnonymousX
      By AnonymousX
      So I just got a new computer, and instantly went and installed Autoit onto it. I selected run script as default action when clicking on an au3 file, however every time I do this it opens the script in the editor. I can from there run the script no problem.
      I've tried reinstalling and checking the settings by hitting "Ctrl+1" and have confirmed it is set to run, but yet it just continues to open in the editor.
      Any ideas why this is happening or how to solve?
      New system is windows 10, old was windows 7
    • ademon
      By ademon
      Hi. I can open the editor with out problems but I can not compile because it is placed on another partition diferent from C.
      Can you provide an update of this bug?


    • careca
      By careca
      This is a tool to back up/export current settings in the group policy, or to import.
      The export button simply exports the group policies to a folder with a random name.
      The import prompts for a folder selection, and then tries to import the policy present in that folder.
      I made this because i found it was the most reliable and simpler way of doing it, now i just saved the exported folder, renamed it, and when i install windows again i just
      import the folder and all settings are placed.
    • cahsobo
      By cahsobo
      Hello,

      I need help inserting these keys and values to registry
       
      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DirectDraw\Compatibility\WK.1.exe]
      "Flags"=hex:00,08,00,00
      "ID"=hex:ec,33,74,3b
       
      Still can't figure out how...
×