#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 & "\GroupPolicy\User\Registry.pol"
Global Const $MACHINE_REGISTRY_FILE = @SystemDir & "\GroupPolicy\Machine\Registry.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 Software\MyTestKey -v TestMe -t REG_SZ -d "test, test, test"
		; --remove -f Registry.pol -k Software\MyTestKey -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.\r\nExample: 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.\r\nExample: 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 %s\r\n", _
		@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:       %s\r\n", @OSVersion)
	$sEnv &= StringFormat("  OSArch:          %s\r\n", @OSArch)
	$sEnv &= StringFormat("  AutoItX64:       %s\r\n", @AutoItX64)
	$sEnv &= StringFormat("  WindowsDir:      %s\r\n", @WindowsDir)
	$sEnv &= StringFormat("  SystemDir:       %s\r\n", @SystemDir)
	$sEnv &= StringFormat("  TempDir:         %s\r\n", @TempDir)
	$sEnv &= StringFormat("  ComSpec:         %s\r\n", @ComSpec)
	$sEnv &= StringFormat("  ProgramFilesDir: %s", @ProgramFilesDir)

	__DebugPrint($sEnv, "__PrintEnvironment", True)
EndFunc   ;==>__PrintEnvironment