Что нового

Дополнительные функции для работы с реестром

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
На досуге написал пару функций для работы с реестром. Обе используют рекурсивные вызовы и требуют наличие библиотеки WinAPIEx.au3.

_RegEnumValues() - создает список всех разделов со своими переменными, их значениями и всеми подразделами любой вложенности для указанного раздела реестра. Другими словами, создает структуру раздела реестра. Функция возвращает строку, разделителем в которой является символ с кодом 160 (Unicode) - ChrW(160). Каждая запись в этой строке имеет следующий вид:

относительный путь к разделу\\параметр=(тип)значение

Если параметр имеет строковое значение, то добавляются кавычки:

относительный путь к разделу\\параметр=(тип)"значение"

Каждый раздел начинается с его пути и заканчивается символом "\":

относительный путь к разделу\

Пример для раздела "HKEY_CLASSES_ROOT\.txt":
Код:
@=(1)"txtfile"
PerceivedType=(1)"text"
Content Type=(1)"text/plain"
Mac Comment=(1)"ASCII File (SimpleText)"
Mac Type=(1)"TEXT"
Mac Creator=(1)"ttxt"
PersistentHandler\
PersistentHandler\\@=(1)"{5e941d80-bf96-11cd-b579-08002b30bfeb}"
ShellNew\
ShellNew\\NullFile=(1)""

Для чего это нужно? Ну во-первых, вы получаете сразу структуру всего раздела за относительно небольшое время. Во-вторых, это очень удобно для сравнения двух разделов реестра на идентичность.

Замечание. Если к какому-нибудь разделу реестра в пределах заданной ветки будет отказано в доступе на чтение, то функция завершится с ошибкой.

Код:
Код:
#Include <Array.au3>
#Include <Constants.au3>
#Include <WinAPIEx.au3>

;~Global Const $REG_BINARY = 3
;~Global Const $REG_DWORD = 4
;~Global Const $REG_DWORD_BIG_ENDIAN = 5
;~Global Const $REG_DWORD_LITTLE_ENDIAN = 4
;~Global Const $REG_EXPAND_SZ = 2
;~Global Const $REG_LINK = 6
;~Global Const $REG_MULTI_SZ = 7
;~Global Const $REG_NONE = 0
Global Const $REG_QWORD = 11
;~Global Const $REG_QWORD_LITTLE_ENDIAN = 11
;~Global Const $REG_SZ = 1

Global $hKey, $Hive

$hKey = _WinAPI_RegOpenKey($HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall')
_RegEnumValues($hKey, $Hive)
_WinAPI_RegCloseKey($hKey)

$Hive = StringSplit($Hive, ChrW(160))

_ArrayDisplay($Hive)

Func _RegEnumValues($hRoot, ByRef $sData, $iLevel = -1, $iLenght = 0, $iParam = 0, $sParam = 0)

	If (($iLevel > -1) And ($iParam > $iLevel)) Or (($iLenght) And (StringLen($sData) > $iLenght)) Then
		Return SetError(1, -1, 0)
	EndIf

	If Not $iParam Then
		$sData = ''
	EndIf

	$iParam += 1

	Local $hKey, $sKey, $aKey, $sVal, $aText, $sText, $Quotes, $Size, $Struct, $Type, $Sep = ChrW(160), $Error = 0
	Local $tData = DllStructCreate('byte[1048576]')
	Local $pData = DllStructGetPtr($tData)

	If $sParam Then
		$sData &= $sParam & '\' & $Sep
	EndIf
	$aKey = _WinAPI_RegQueryInfoKey($hRoot)
	If @error Then
		Return SetError(1, @extended, 0)
	EndIf
	For $i = 0 To $aKey[2] - 1
		$sVal = _WinAPI_RegEnumValue($hRoot, $i)
		If @error Then
			Return SetError(1, @extended, 0)
		EndIf
		$Size = _WinAPI_RegQueryValue($hRoot, $sVal, $tData)
		If @error Then
			Return SetError(1, @extended, 0)
		EndIf
		$Type = @extended
		If Not $sVal Then
			$sVal = '@'
		EndIf
		If Not $Size Then
			$sText = ''
		Else
			Switch $Type
				Case $REG_MULTI_SZ
					$sText = ''
					$aText = _WinAPI_StructToArray($pData)
					If @error Then
						Return SetError(1, -1, 0)
					EndIf
					For $j = 1 To $aText[0] - 1
						$sText &= $aText[$j] & @LF
					Next
					If $aText[0] Then
						$sText &= $aText[$aText[0]]
					EndIf
					$Quotes = 1
				Case Else
					$Quotes = 0
					Switch $Type
						Case $REG_DWORD, $REG_DWORD_BIG_ENDIAN
							$Struct = 'dword'
						Case $REG_QWORD
							$Struct = 'uint64'
						Case $REG_SZ, $REG_EXPAND_SZ
							$Struct = 'wchar[' & $Size & ']'
							$Quotes = 1
						Case Else
							$Struct = 'byte[' & $Size & ']'
					EndSwitch
					$sText = DllStructGetData(DllStructCreate($Struct, $pData), 1)
					If $Quotes Then
						$sText = '"' & $sText & '"'
					EndIf
			EndSwitch
		EndIf
		If $sParam Then
			$sData &= $sParam & '\\'
		EndIf
		$sData &= $sVal & '=(' & $Type & ')' & $sText & $Sep
	Next
	For $i = 0 To $aKey[0] - 1
		$sKey = _WinAPI_RegEnumKey($hRoot, $i)
		If @error Then
			Return SetError(1, @extended, 0)
		EndIf
		$hKey = _WinAPI_RegOpenKey($hRoot, $sKey, $KEY_READ)
		If @error Then
			Return SetError(1, @extended, 0)
		EndIf
		If $sParam Then
			_RegEnumValues($hKey, $sData, $iLevel, $iLenght, $iParam, $sParam & '\' & $sKey)
		Else
			_RegEnumValues($hKey, $sData, $iLevel, $iLenght, $iParam, $sKey)
		EndIf
		If @error Then
			$Error = @extended
			If Not $Error Then
				$Error = -1
			EndIf
		EndIf
		_WinAPI_RegCloseKey($hKey)
		If $Error Then
			ExitLoop
		EndIf
	Next
	If $iParam = 1 Then
		If Not $Error Then
			$sData = StringRegExpReplace($sData, $Sep & '*\Z', '')
		Else
			$sData = ''
		EndIf
	EndIf
	Return SetError($Error <> 0, $Error, $Error = 0)
EndFunc   ;==>_RegEnumValues

_RegEnumTimes() - возвращает уникальный идентификатор, основанный на времени последней модификации указанного раздела реестра и всех его подразделов любой вложенности. Если вы измените что-нибудь в указанном разделе или в любом из его подразделов и т.д., то значение, возвращаемое этой функцией изменится. Эта функция на порядок быстрее _RegEnumValues(). Смысловой нагрузки значение идентификатора никакой не несет, важен лишь факт его изменения. Это очень удобно для отслеживания факта изменения раздела реестра.

Так же, как и в предыдущей функции, _RegEnumTimes() завершится с ошибкой при отказе в доступе на чтение к любому подразделу указанного раздела реестра.

Код:
Код:
#Include <Crypt.au3>
#Include <WinAPIEx.au3>

Global $hKey, $ID

$hKey = _WinAPI_RegOpenKey($HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall')
_RegEnumTimes($hKey, $ID)
_WinAPI_RegCloseKey($hKey)

ConsoleWrite($ID & @CR)

Func _RegEnumTimes($hRoot, ByRef $sData, $iParam = 0)

	If Not $iParam Then
		$sData = ''
	EndIf

	$iParam += 1

	Local $hKey, $sKey, $aKey, $tTime, $Error = 0

	$tTime = _WinAPI_RegQueryLastWriteTime($hRoot)
	If @error Then
		Return SetError(1, @extended, 0)
	EndIf
	For $i = 1 To 2
		$sData &= DllStructGetData($tTime, $i)
	Next
	$aKey = _WinAPI_RegQueryInfoKey($hRoot)
	If @error Then
		Return SetError(1, @extended, 0)
	EndIf
	For $i = 0 To $aKey[0] - 1
		$sKey = _WinAPI_RegEnumKey($hRoot, $i)
		If @error Then
			Return SetError(1, @extended, 0)
		EndIf
		$hKey = _WinAPI_RegOpenKey($hRoot, $sKey, $KEY_READ)
		If @error Then
			Return SetError(1, @extended, 0)
		EndIf
		_RegEnumTimes($hKey, $sData, $iParam)
		If @error Then
			$Error = @extended
			If Not $Error Then
				$Error = -1
			EndIf
		EndIf
		_WinAPI_RegCloseKey($hKey)
		If $Error Then
			ExitLoop
		EndIf
	Next
	If $iParam = 1 Then
		If Not $Error Then
			$sData = StringTrimLeft(_Crypt_HashData($sData, $CALG_MD5), 2)
		Else
			$sData = ''
		EndIf
	EndIf
	Return SetError($Error <> 0, $Error, $Error = 0)
EndFunc   ;==>_RegEnumTimes

Жду отзывов.

:smile:
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

Yashied,
Спасибо! Очень полезные функции. _RegEnumValues() практически мгновенно сделала копию раздела. Я результат в файл записал - несколько не привычный формат записи, но это не страшно :smile:. А с помощью _RegEnumTimes() можно, например раздел автозагрузки периодически проверять, что тоже очень полезно. ИМХО, обе функции надо в WinAPIEx.au3 добавлять.
:IL_AutoIt_1::thumbs_up:
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

madmasles сказал(а):
Спасибо! Очень полезные функции.

Пожалуйста.

:smile:

madmasles сказал(а):
..несколько не привычный формат записи, но это не страшно.

Можно и в .reg формате сделать, но там будут длиннее строки, что отразится на скорости + не очень удобно будет анализировать записи ("dword" и т.д.), в случае построения дерева раздела. Записать раздел в файл можно и с помощью regedit.exe. Здесь у меня задача стояла именно в сравнении нескольких веток в HKEY_CLASSES_ROOT с целью их дальнейшего отсеивания.

madmasles сказал(а):
...обе функции надо в WinAPIEx.au3 добавлять.

Нет, не буду. Это довольно специфические функции, а WinAPIEx и так уже раздулся почти до мегабайта.
 

assch

Новичок
Сообщения
166
Репутация
4
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

Уважаемый Yashied

А если стоит задача сделать снимок всего реестра, потом что нибудь добавить и сделать второй снимок.
потом их сравнить и выявить изменения, и записать в файл.
Чтобы потом эти изменения можно было бы внести обратно (при их отсутствии)
например с помощью regedit.exe.
Как это делает Тотал юнистал.
Эти функции не помогут?
И вообще такой алгоритм трудно сделать силами Autoit?
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

assch сказал(а):
А если стоит задача сделать снимок всего реестра, потом что нибудь добавить и сделать второй снимок.
потом их сравнить и выявить изменения, и записать в файл.
Чтобы потом эти изменения можно было бы внести обратно (при их отсутствии)
например с помощью regedit.exe.
Как это делает Тотал юнистал.
Эти функции не помогут?
И вообще такой алгоритм трудно сделать силами Autoit?

Можно, но ждать, когда отработает функция придется вечность.

:smile:
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

Вот простая функция для экспорта раздела реестра в файл или в переменную при помощи regedit.exe.

Код:
#Include <WinAPIEx.au3>

ConsoleWrite(_RegExport('HKCR\AutoIt3Script') & @CR)

Func _RegExport($sKey, $sFile = '')

	Local $PID, $Path, $Root, $Text, $Data = 0

	$Path = _WinAPI_PathSearchAndQualify('regedit.exe')
	If Not FileExists($Path) Then
		Return 0
	EndIf
	$Text = StringSplit($sKey, '\', 2)
	If IsArray($Text) Then
		$Text = $Text[0]
	Else
		$Text = $sKey
	EndIf
	Switch $Text
		Case 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE', 'HKEY_USERS', 'HKEY_CURRENT_CONFIG'
			$Root = $Text
		Case 'HKCR'
			$Root = 'HKEY_CLASSES_ROOT'
		Case 'HKCU'
			$Root = 'HKEY_CURRENT_USER'
		Case 'HKLM'
			$Root = 'HKEY_LOCAL_MACHINE'
		Case 'HKU'
			$Root = 'HKEY_USERS'
		Case 'HKCC'
			$Root = 'HKEY_CURRENT_CONFIG'
		Case Else
			Return 0
	EndSwitch
	$sFile = StringStripWS($sFile, 3)
	If Not $sFile Then
		Do
			For $i = 1 To 4
				$sFile &= Chr(Random(97, 122, 1))
			Next
			$sFile = StringRegExpReplace(@TempDir & '\~reg' & $sFile & '.tmp', '\\+', '\\')
		Until Not FileExists($sFile)
		$Data = 1
	Else
		If (FileExists($sFile)) And (Not FileDelete($sFile)) Then
			Return 0
		EndIf
	EndIf
	$PID = Run($Path & ' /e /s "' & $sFile & '" "' & StringReplace($sKey, $Text, $Root, 1) & '"')
	If @error Then
		Return 0
	EndIf
	ProcessWaitClose($PID)
	If Not $Data Then
		$Data = FileExists($sFile)
	Else
		$Data = StringRegExpReplace(FileRead($sFile), '\A[^[]*|' & '[' & @LF & ',' & @CR & ']*\Z', '')
		FileDelete($sFile)
		If Not $Data Then
			$Data = 0
		EndIf
	EndIf
	Return $Data
EndFunc   ;==>_RegExport
 

assch

Новичок
Сообщения
166
Репутация
4
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

Уважаемый Yashied

При запуске этой функции Func _RegExport($sKey, $sFile = '') в консоле выводится просто '0'
Честно говоря не могу разобраться (вы уж извините)
Подскажете пожалуйста как именно с помощью этой функции
скопировать например весь раздел реестра HKEY_CURRENT_USER
и записать всё это например в 1.txt
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

Эх, я почему-то думал, что в FileExists() не обязательно нужно передавать полный путь. Это моя ошибка, поправил функцию.

Код:
_RegExport('HKCU', '1.txt')
 

assch

Новичок
Сообщения
166
Репутация
4
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

Yashied [?]
_RegEnumTimes() - возвращает уникальный идентификатор, основанный на времени последней модификации указанного раздела реестра и всех его подразделов любой вложенности. Если вы измените что-нибудь в указанном разделе или в любом из его подразделов и т.д., то значение, возвращаемое этой функцией изменится. Эта функция на порядок быстрее _RegEnumValues(). Смысловой нагрузки значение идентификатора никакой не несет, важен лишь факт его изменения. Это очень удобно для отслеживания факта изменения раздела реестра.

Если задействовать этот идентификатор то с помощью какой конструкции можно отследить и записать последние изменения например в ветки - HKCR
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

assch, я не понял вопроса.

:wacko:

Идентификатор предназначен только для сравнения его с полученным ранее идентификатором. Если они различаются, то это однозначно свидетельствует о том, что в данной ветки что-то изменилось. Достаточно удалить один параметр и создать на его месте точно такой же, чтобы идентификатор изменился, несмотря на то, что структура раздела осталась прежней. Для отслеживания непосредственно изменений корневых разделов целиком (HKCR, HKCU и т.д.), лучше использовать _RegExport() в переменную и затем парсить эти данные (задача не из простых).

Не нужно выделять и раскрашивать текст, я все прекрасно вижу.
 

assch

Новичок
Сообщения
166
Репутация
4
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

И ещё не подскажете почему если снять снимок ветки например - HKLM
то файл у меня получился весом 33.6 мб
А когда я снимаю снимок этой же ветки функцией из библиотеки ''Registry_UDFs.au3"
Которую я помню давно скачал с сайта CreatLab
то файл у меня получается 23.1 мб
Не подскажете почему?
 

assch

Новичок
Сообщения
166
Репутация
4
Re: _RegEnumTimes() и _RegEnumValues() - работа с реестром

Я могу только догадыватся что парсить данные из двух файлов
а потом записать результат изменений это не просто.
Интересно какими алгоритмами хотя бы примерно работают
программы подобной тематики.
например Тотал юнистал для сравнения тратит не больше 5 секунд
и после сравнения у него можно взять регфайл установки и удаления.
Просто я как и все надеюсь что мир не без добрых людей
кто нибудь что нибудь и подскажет.
В разделе для новичков я открыл тему
"Как можно выявить изменения произошедшие на локальном диске"
Не без помощи других уже есть начало этой темы.
в частности две функции одна снимает снимок файловой системы (как правило диск С)
а другая ищет изменения, правда уходит на это около 2 минут.
Интересно что если сделать Батник - fc 1.txt 2.txt >> Inf.txt
то результат выведиться в файл Inf.txt за долю секунды.
Но для больших сравнений она не подходит.
Она без труда (доля секунды) пропарсит два файла размером скажем около 2 мб каждый
но результат выведит в файл не больше 64 кб, то есть если произошли большие изменения
то она покажет соответственно обрезанную информацию.
Я интересовался почему и мне сказали что это функция из досовского наследия поэтому 64кб и всё.
А жалко очень быстрая функция.
Хотя для сравнения эти функции наверно не подойдут так как у регфайлов своеобразный
стиль написания, если только как нибудь смодифицировать функцию.
Ну ладно, сами понимаете Попытка не пытка будем искать.
Буду признателен любой информации.
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Написал функцию _RegExportEx() для экспорта данных из реестра в переменную или файл в формате .reg. В отличии от _RegExport(), эта функция не использует ни regedit.exe, ни каких-либо временных файлов. IMHO для AutoIt, это самое быстрое, что только можно было придумать (если у кого-нибудь получится быстрее, пожалуйста поделитесь здесь). Файлы размером в несколько тысяч строк создает почти мгновенно. Для файлов, количество строк в которых измеряется десятками тысяч, возможно будет целесообразнее использовать _RegExport(). Например на экспорт раздела "HKEY_CLASSES_ROOT\CLSID" (> 72000) у меня ушло ~10 секунд, _RegExport() справляется за 2-3 секунды. Но если необходимо экспортировать кучу отдельных веток реестра (с последующим их объединением), то тут _RegExportEx() вне конкуренции.

Код:
#Include <Constants.au3>
#Include <WinAPIEx.au3>

$sReg = _RegExportEx('HKEY_CLASSES_ROOT\AutoIt3Script')
If @error Then
	ConsoleWrite(_WinAPI_GetErrorMessage(@extended) & @CR)
Else
	ConsoleWrite($sReg & @CR)
EndIf

Func _RegExportEx($sKey, $sFile = '', $fAssurance = 0)

	Local $aData = StringSplit(StringUpper($sKey), '\', 2)
	Local $tData, $hRoot, $sRoot, $sReg = ''
	Local $Error = 1

	If Not IsArray($aData) Then
		Return SetError(1, 0, '')
	EndIf

	Switch $aData[0]
		Case 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE', 'HKEY_USERS', 'HKEY_CURRENT_CONFIG'
			$sRoot = $aData[0]
		Case 'HKCR'
			$sRoot = 'HKEY_CLASSES_ROOT'
		Case 'HKCU'
			$sRoot = 'HKEY_CURRENT_USER'
		Case 'HKLM'
			$sRoot = 'HKEY_LOCAL_MACHINE'
		Case 'HKU'
			$sRoot = 'HKEY_USERS'
		Case 'HKCC'
			$sRoot = 'HKEY_CURRENT_CONFIG'
		Case Else
			Return SetError(1, 0, '')
	EndSwitch
	$sKey = StringTrimLeft($sKey, StringLen($aData[0]) + 1)
	Do
		$hRoot = _WinAPI_RegOpenKey(Eval($sRoot), $sKey, BitOR($KEY_ENUMERATE_SUB_KEYS, $KEY_QUERY_VALUE))
		If @error Then
			ExitLoop
		EndIf
		$tData = DllStructCreate('byte[1048576]')
		If Not _RegEnumProc($hRoot, StringRegExpReplace($sRoot & '\' & $sKey, '\\*\Z', ''), $fAssurance, $sReg, $tData) Then
			ExitLoop
		EndIf
		$Error = 0
	Until 1
	If $Error Then
		Switch @extended
			Case 0
				$Error = -1
			Case Else
				$Error = @extended
		EndSwitch
	EndIf
	If $hRoot Then
		_WinAPI_RegCloseKey($hRoot)
	EndIf
	If $Error Then
		Return SetError(2, $Error, '')
	EndIf
	If $sFile Then
		$hFile = FileOpen($sFile, 2 + 32)
		If $hFile = -1 Then
			Return SetError(3, 0, '')
		EndIf
		If Not FileWrite($hFile, 'Windows Registry Editor Version 5.00' & @CRLF & @CRLF & $sReg) Then
			$Error = 1
		EndIf
		FileClose($hFile)
		If $Error Then
			Return SetError(3, 0, '')
		EndIf
		Return 1
	EndIf
	Return $sReg
EndFunc   ;==>_RegExportEx

Func _RegEnumProc($hRoot, $sRoot, $fAssurance, ByRef $sData, ByRef $tData, $sParam = 0)

	Local $hKey, $sKey, $sVal, $Pos, $Size, $Type
	Local $pData = DllStructGetPtr($tData)
	Local $Count = 0, $Error = 0

	If $sParam Then
		$sData &= @CRLF & '[' & $sRoot & '\' & $sParam & ']' & @CRLF
	Else
		$sData = '[' & $sRoot & ']' & @CRLF
	EndIf
	While 1
		$sVal = _WinAPI_RegEnumValue($hRoot, $Count)
		If @error Then
			Switch @extended
				Case 259 ; ERROR_NO_MORE_ITEMS
					ExitLoop
				Case Else
					Return SetError(1, @extended, 0)
			EndSwitch
		EndIf
		$Size = _WinAPI_RegQueryValue($hRoot, $sVal, $tData)
		If @error Then
			Return SetError(1, @extended, 0)
		EndIf
		$Type = @extended
		If $sVal Then
			$sVal = '"' & StringReplace(StringReplace($sVal, '\', '\\'), '"', '\"') & '"='
		Else
			$sVal = '@='
		EndIf
		If Not $Size Then
			Switch $Type
				Case 1 ; REG_SZ
					$sVal &= '""'
				Case Else
					Switch $Type
						Case 3 ; REG_BINARY
							$sVal &= 'hex:'
						Case Else
							$sVal &= 'hex(' & StringLower(Hex($Type, 1)) & '):'
					EndSwitch
			EndSwitch
		Else
			Switch $Type
				Case 1 ; REG_SZ
					$sVal &= '"' & StringReplace(StringReplace(DllStructGetData(DllStructCreate('wchar[' & $Size & ']', $pData), 1), '\', '\\'), '"', '\"') & '"'
				Case 4 ; REG_DWORD
					$sVal &= 'dword:' & StringLower(Hex(Number(DllStructGetData(DllStructCreate('dword', $pData), 1))))
				Case Else
					Switch $Type
						Case 3 ; REG_BINARY
							$sVal &= 'hex:'
						Case Else
							$sVal &= 'hex(' & StringLower(Hex($Type, 1)) & '):'
					EndSwitch
					$sVal &= StringTrimRight(StringRegExpReplace(StringLower(StringTrimLeft(DllStructGetData(DllStructCreate('byte[' & $Size & ']', $pData), 1), 2)), '(.{2})', '\1,'), 1)
					$Pos = StringInStr($sVal, ',', 0, 1, 77)
					If $Pos Then
						$sVal = StringLeft($sVal, $Pos) & '\' & @CRLF & '  ' & StringRegExpReplace(StringTrimLeft($sVal, $Pos), '(.{75})', '\1\\' & @CRLF & '  ')
					EndIf
			EndSwitch
		EndIf
		$sData &= $sVal & @CRLF
		$Count += 1
	WEnd
	$Count = 0
	While 1
		$sKey = _WinAPI_RegEnumKey($hRoot, $Count)
		If @error Then
			Switch @extended
				Case 259 ; ERROR_NO_MORE_ITEMS
					ExitLoop
				Case Else
					Return SetError(1, @extended, 0)
			EndSwitch
		EndIf
		$hKey = _WinAPI_RegOpenKey($hRoot, $sKey, BitOR($KEY_ENUMERATE_SUB_KEYS, $KEY_QUERY_VALUE))
		If @error Then
			If $fAssurance Then
				Return SetError(1, @extended, 0)
			EndIf
		Else
			If $sParam Then
				_RegEnumProc($hKey, $sRoot, $fAssurance, $sData, $tData, $sParam & '\' & $sKey)
			Else
				_RegEnumProc($hKey, $sRoot, $fAssurance, $sData, $tData, $sKey)
			EndIf
			If @error Then
				Switch @extended
					Case 0
						$Error = -1
					Case Else
						$Error = @extended
				EndSwitch
			EndIf
		EndIf
		If $hKey Then
			_WinAPI_RegCloseKey($hKey)
		EndIf
		$Count += 1
		If $Error Then
			ExitLoop
		EndIf
	WEnd
	If $Error Then
		Return SetError(1, $Error, 0)
	Else
		Return 1
	EndIf
EndFunc   ;==>_RegEnumProc
 

assch

Новичок
Сообщения
166
Репутация
4
Отличная функция Yashied
Я занимаюсь темой выявления изменений в реестре Пока работаю по такому алгоритму:
Снимаю снимок ключей (одних ключей без их значений) скажем ветки HKEY_CLASSES_ROOT функцией:
Код:
#Include <File.au3>
#include <Array.au3>

Global $Array
Global $key = "HKEY_CLASSES_ROOT"
Global $p = 0
_KeyList($key)

FileWrite("1.txt", $Array)
FileClose("1.txt")

Func _KeyList($key)
$i = 1
While 1
$var = RegEnumKey($key, $i)
If @error <> 0 then ExitLoop 
$Array &= $key&"\"&$var & @CRLF
If $i = 1 then  
$p += 1
EndIf
$i = $i + 1
_KeyList($key&"\"&$var)             
WEnd   
EndFunc

Вроде бы простенькая функция ничего лишнего но она затрачивает на это 58 секунд.
Потом второй снимок, потом сравнение ну и соответственно из получившегося списка
добавленых ключей делаю Регфайл. (ваша функция для этого подойдёт наверное просто идеально)
Но интересная деталь - Если вашей функцией сделать снимок ветки HKEY_CLASSES_ROOT
а ваша функция помимо всех ключей ещё и значения к ним добавляет то у меня ушло на это всего 26 секунд.
Выходит ваша функция даже с большим объёмом работает быстрее чем та.
Интересно что нужно подправить в вашей функции чтобы применить её к моему первому этапу
снятия снимка только одних ключей этой ветки, ведь если на всю информацию с ключами и значениями
она затрачивает 26 секунд то по логики на информацию только одних ключей она должна затратить ещё меньше.
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
assch сказал(а):
Интересно что нужно подправить в вашей функции чтобы применить её к моему первому этапу...

Просто закомментировать часть кода. Получится в ~2 раза быстрее.

Код:
#Include <Constants.au3>
#Include <WinAPIEx.au3>

$sReg = _RegExportEx('HKEY_CLASSES_ROOT', 'r.reg')
If @error Then
    ConsoleWrite(_WinAPI_GetErrorMessage(@extended) & @CR)
Else
;    ConsoleWrite($sReg & @CR)
EndIf

Func _RegExportEx($sKey, $sFile = '', $fAssurance = 0)

    Local $aData = StringSplit(StringUpper($sKey), '\', 2)
    Local $tData, $hRoot, $sRoot, $sReg = ''
    Local $Error = 1

    If Not IsArray($aData) Then
        Return SetError(1, 0, '')
    EndIf

    Switch $aData[0]
        Case 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE', 'HKEY_USERS', 'HKEY_CURRENT_CONFIG'
            $sRoot = $aData[0]
        Case 'HKCR'
            $sRoot = 'HKEY_CLASSES_ROOT'
        Case 'HKCU'
            $sRoot = 'HKEY_CURRENT_USER'
        Case 'HKLM'
            $sRoot = 'HKEY_LOCAL_MACHINE'
        Case 'HKU'
            $sRoot = 'HKEY_USERS'
        Case 'HKCC'
            $sRoot = 'HKEY_CURRENT_CONFIG'
        Case Else
            Return SetError(1, 0, '')
    EndSwitch
    $sKey = StringTrimLeft($sKey, StringLen($aData[0]) + 1)
    Do
        $hRoot = _WinAPI_RegOpenKey(Eval($sRoot), $sKey, BitOR($KEY_ENUMERATE_SUB_KEYS, $KEY_QUERY_VALUE))
        If @error Then
            ExitLoop
        EndIf
        $tData = DllStructCreate('byte[1048576]')
        If Not _RegEnumProc($hRoot, StringRegExpReplace($sRoot & '\' & $sKey, '\\*\Z', ''), $fAssurance, $sReg, $tData) Then
            ExitLoop
        EndIf
        $Error = 0
    Until 1
    If $Error Then
        Switch @extended
            Case 0
                $Error = -1
            Case Else
                $Error = @extended
        EndSwitch
    EndIf
    If $hRoot Then
        _WinAPI_RegCloseKey($hRoot)
    EndIf
    If $Error Then
        Return SetError(2, $Error, '')
    EndIf
    If $sFile Then
        $hFile = FileOpen($sFile, 2 + 32)
        If $hFile = -1 Then
            Return SetError(3, 0, '')
        EndIf
        If Not FileWrite($hFile, 'Windows Registry Editor Version 5.00' & @CRLF & @CRLF & $sReg) Then
            $Error = 1
        EndIf
        FileClose($hFile)
        If $Error Then
            Return SetError(3, 0, '')
        EndIf
        Return 1
    EndIf
    Return $sReg
EndFunc   ;==>_RegExportEx

Func _RegEnumProc($hRoot, $sRoot, $fAssurance, ByRef $sData, ByRef $tData, $sParam = 0)

    Local $hKey, $sKey, $sVal, $Pos, $Size, $Type
    Local $pData = DllStructGetPtr($tData)
    Local $Count = 0, $Error = 0

    If $sParam Then
        $sData &= @CRLF & '[' & $sRoot & '\' & $sParam & ']' & @CRLF
    Else
        $sData = '[' & $sRoot & ']' & @CRLF
    EndIf

#cs

    While 1
        $sVal = _WinAPI_RegEnumValue($hRoot, $Count)
        If @error Then
            Switch @extended
                Case 259 ; ERROR_NO_MORE_ITEMS
                    ExitLoop
                Case Else
                    Return SetError(1, @extended, 0)
            EndSwitch
        EndIf
        $Size = _WinAPI_RegQueryValue($hRoot, $sVal, $tData)
        If @error Then
            Return SetError(1, @extended, 0)
        EndIf
        $Type = @extended
        If $sVal Then
            $sVal = '"' & StringReplace(StringReplace($sVal, '\', '\\'), '"', '\"') & '"='
        Else
            $sVal = '@='
        EndIf
        If Not $Size Then
            Switch $Type
                Case 1 ; REG_SZ
                    $sVal &= '""'
                Case Else
                    Switch $Type
                        Case 3 ; REG_BINARY
                            $sVal &= 'hex:'
                        Case Else
                            $sVal &= 'hex(' & StringLower(Hex($Type, 1)) & '):'
                    EndSwitch
            EndSwitch
        Else
            Switch $Type
                Case 1 ; REG_SZ
                    $sVal &= '"' & StringReplace(StringReplace(DllStructGetData(DllStructCreate('wchar[' & $Size & ']', $pData), 1), '\', '\\'), '"', '\"') & '"'
                Case 4 ; REG_DWORD
                    $sVal &= 'dword:' & StringLower(Hex(Number(DllStructGetData(DllStructCreate('dword', $pData), 1))))
                Case Else
                    Switch $Type
                        Case 3 ; REG_BINARY
                            $sVal &= 'hex:'
                        Case Else
                            $sVal &= 'hex(' & StringLower(Hex($Type, 1)) & '):'
                    EndSwitch
                    $sVal &= StringTrimRight(StringRegExpReplace(StringLower(StringTrimLeft(DllStructGetData(DllStructCreate('byte[' & $Size & ']', $pData), 1), 2)), '(.{2})', '\1,'), 1)
                    $Pos = StringInStr($sVal, ',', 0, 1, 77)
                    If $Pos Then
                        $sVal = StringLeft($sVal, $Pos) & '\' & @CRLF & '  ' & StringRegExpReplace(StringTrimLeft($sVal, $Pos), '(.{75})', '\1\\' & @CRLF & '  ')
                    EndIf
            EndSwitch
        EndIf
        $sData &= $sVal & @CRLF
        $Count += 1
    WEnd
    $Count = 0

#ce

    While 1
        $sKey = _WinAPI_RegEnumKey($hRoot, $Count)
        If @error Then
            Switch @extended
                Case 259 ; ERROR_NO_MORE_ITEMS
                    ExitLoop
                Case Else
                    Return SetError(1, @extended, 0)
            EndSwitch
        EndIf
        $hKey = _WinAPI_RegOpenKey($hRoot, $sKey, BitOR($KEY_ENUMERATE_SUB_KEYS, $KEY_QUERY_VALUE))
        If @error Then
            If $fAssurance Then
                Return SetError(1, @extended, 0)
            EndIf
        Else
            If $sParam Then
                _RegEnumProc($hKey, $sRoot, $fAssurance, $sData, $tData, $sParam & '\' & $sKey)
            Else
                _RegEnumProc($hKey, $sRoot, $fAssurance, $sData, $tData, $sKey)
            EndIf
            If @error Then
                Switch @extended
                    Case 0
                        $Error = -1
                    Case Else
                        $Error = @extended
                EndSwitch
            EndIf
        EndIf
        If $hKey Then
            _WinAPI_RegCloseKey($hKey)
        EndIf
        $Count += 1
        If $Error Then
            ExitLoop
        EndIf
    WEnd
    If $Error Then
        Return SetError(1, $Error, 0)
    Else
        Return 1
    EndIf
EndFunc   ;==>_RegEnumProc
 

assch

Новичок
Сообщения
166
Репутация
4
Отлично
Спасибо Yashied
Прости за наглость не подскажешь как избавится от пробелов
между строками а то файл получается в два раза больше
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Код:
#Include <Constants.au3>
#Include <WinAPIEx.au3>

$sReg = _RegExportEx('HKEY_CLASSES_ROOT', 'r.reg')
If @error Then
    ConsoleWrite(_WinAPI_GetErrorMessage(@extended) & @CR)
Else
;    ConsoleWrite($sReg & @CR)
EndIf

Func _RegExportEx($sKey, $sFile = '', $fAssurance = 0)

    Local $aData = StringSplit(StringUpper($sKey), '\', 2)
    Local $tData, $hRoot, $sRoot, $sReg = ''
    Local $Error = 1

    If Not IsArray($aData) Then
        Return SetError(1, 0, '')
    EndIf

    Switch $aData[0]
        Case 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE', 'HKEY_USERS', 'HKEY_CURRENT_CONFIG'
            $sRoot = $aData[0]
        Case 'HKCR'
            $sRoot = 'HKEY_CLASSES_ROOT'
        Case 'HKCU'
            $sRoot = 'HKEY_CURRENT_USER'
        Case 'HKLM'
            $sRoot = 'HKEY_LOCAL_MACHINE'
        Case 'HKU'
            $sRoot = 'HKEY_USERS'
        Case 'HKCC'
            $sRoot = 'HKEY_CURRENT_CONFIG'
        Case Else
            Return SetError(1, 0, '')
    EndSwitch
    $sKey = StringTrimLeft($sKey, StringLen($aData[0]) + 1)
    Do
        $hRoot = _WinAPI_RegOpenKey(Eval($sRoot), $sKey, BitOR($KEY_ENUMERATE_SUB_KEYS, $KEY_QUERY_VALUE))
        If @error Then
            ExitLoop
        EndIf
        $tData = DllStructCreate('byte[1048576]')
        If Not _RegEnumProc($hRoot, StringRegExpReplace($sRoot & '\' & $sKey, '\\*\Z', ''), $fAssurance, $sReg, $tData) Then
            ExitLoop
        EndIf
        $Error = 0
    Until 1
    If $Error Then
        Switch @extended
            Case 0
                $Error = -1
            Case Else
                $Error = @extended
        EndSwitch
    EndIf
    If $hRoot Then
        _WinAPI_RegCloseKey($hRoot)
    EndIf
    If $Error Then
        Return SetError(2, $Error, '')
    EndIf
    If $sFile Then
        $hFile = FileOpen($sFile, 2 + 32)
        If $hFile = -1 Then
            Return SetError(3, 0, '')
        EndIf
        If Not FileWrite($hFile, 'Windows Registry Editor Version 5.00' & @CRLF & @CRLF & $sReg) Then
            $Error = 1
        EndIf
        FileClose($hFile)
        If $Error Then
            Return SetError(3, 0, '')
        EndIf
        Return 1
    EndIf
    Return $sReg
EndFunc   ;==>_RegExportEx

Func _RegEnumProc($hRoot, $sRoot, $fAssurance, ByRef $sData, ByRef $tData, $sParam = 0)

    Local $hKey, $sKey, $sVal, $Pos, $Size, $Type
    Local $pData = DllStructGetPtr($tData)
    Local $Count = 0, $Error = 0

    If $sParam Then
        $sData &= '[' & $sRoot & '\' & $sParam & ']' & @CRLF
    Else
        $sData = '[' & $sRoot & ']' & @CRLF
    EndIf

#cs

    While 1
        $sVal = _WinAPI_RegEnumValue($hRoot, $Count)
        If @error Then
            Switch @extended
                Case 259 ; ERROR_NO_MORE_ITEMS
                    ExitLoop
                Case Else
                    Return SetError(1, @extended, 0)
            EndSwitch
        EndIf
        $Size = _WinAPI_RegQueryValue($hRoot, $sVal, $tData)
        If @error Then
            Return SetError(1, @extended, 0)
        EndIf
        $Type = @extended
        If $sVal Then
            $sVal = '"' & StringReplace(StringReplace($sVal, '\', '\\'), '"', '\"') & '"='
        Else
            $sVal = '@='
        EndIf
        If Not $Size Then
            Switch $Type
                Case 1 ; REG_SZ
                    $sVal &= '""'
                Case Else
                    Switch $Type
                        Case 3 ; REG_BINARY
                            $sVal &= 'hex:'
                        Case Else
                            $sVal &= 'hex(' & StringLower(Hex($Type, 1)) & '):'
                    EndSwitch
            EndSwitch
        Else
            Switch $Type
                Case 1 ; REG_SZ
                    $sVal &= '"' & StringReplace(StringReplace(DllStructGetData(DllStructCreate('wchar[' & $Size & ']', $pData), 1), '\', '\\'), '"', '\"') & '"'
                Case 4 ; REG_DWORD
                    $sVal &= 'dword:' & StringLower(Hex(Number(DllStructGetData(DllStructCreate('dword', $pData), 1))))
                Case Else
                    Switch $Type
                        Case 3 ; REG_BINARY
                            $sVal &= 'hex:'
                        Case Else
                            $sVal &= 'hex(' & StringLower(Hex($Type, 1)) & '):'
                    EndSwitch
                    $sVal &= StringTrimRight(StringRegExpReplace(StringLower(StringTrimLeft(DllStructGetData(DllStructCreate('byte[' & $Size & ']', $pData), 1), 2)), '(.{2})', '\1,'), 1)
                    $Pos = StringInStr($sVal, ',', 0, 1, 77)
                    If $Pos Then
                        $sVal = StringLeft($sVal, $Pos) & '\' & @CRLF & '  ' & StringRegExpReplace(StringTrimLeft($sVal, $Pos), '(.{75})', '\1\\' & @CRLF & '  ')
                    EndIf
            EndSwitch
        EndIf
        $sData &= $sVal & @CRLF
        $Count += 1
    WEnd
    $Count = 0

#ce

    While 1
        $sKey = _WinAPI_RegEnumKey($hRoot, $Count)
        If @error Then
            Switch @extended
                Case 259 ; ERROR_NO_MORE_ITEMS
                    ExitLoop
                Case Else
                    Return SetError(1, @extended, 0)
            EndSwitch
        EndIf
        $hKey = _WinAPI_RegOpenKey($hRoot, $sKey, BitOR($KEY_ENUMERATE_SUB_KEYS, $KEY_QUERY_VALUE))
        If @error Then
            If $fAssurance Then
                Return SetError(1, @extended, 0)
            EndIf
        Else
            If $sParam Then
                _RegEnumProc($hKey, $sRoot, $fAssurance, $sData, $tData, $sParam & '\' & $sKey)
            Else
                _RegEnumProc($hKey, $sRoot, $fAssurance, $sData, $tData, $sKey)
            EndIf
            If @error Then
                Switch @extended
                    Case 0
                        $Error = -1
                    Case Else
                        $Error = @extended
                EndSwitch
            EndIf
        EndIf
        If $hKey Then
            _WinAPI_RegCloseKey($hKey)
        EndIf
        $Count += 1
        If $Error Then
            ExitLoop
        EndIf
    WEnd
    If $Error Then
        Return SetError(1, $Error, 0)
    Else
        Return 1
    EndIf
EndFunc   ;==>_RegEnumProc
 

assch

Новичок
Сообщения
166
Репутация
4
:IL_AutoIt_1:
Интересно вроде бы та функция была простенькая обычная рекурсия а работает медленнее
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Потому что RegEnum... являются самодостаточными функциями, т.е. при каждом вызове они открывают раздел, считывают данные, а затем закрывают раздел. При следующем вызове все повторяется. Плюс внутренний код и всякие проверки. Если использовать _WinAPI_Reg... функции, то достаточно один раз открыть раздел, сделать там все, что нужно, и затем закрыть его. Сильное падение скорости для RegEnum... происходит именно из-за постоянных открытий-закрытий. Но зато они относительно просты в использовании.
 

assch

Новичок
Сообщения
166
Репутация
4
Спасибо за разъяснение.
Функция и в правду работает быстрее делает снимок ключей этой ветки за 13 секунд.
Для справки:
Не подскажешь можно ли эту функцию или часть этого кода перегнать на ассемблер
то есть сделать ассемблерную вставку, работа с регистрами наверное будет идти
быстрее чем с переменными или нет?


Добавлено:
Сообщение автоматически объединено:

Интересная деталь
если функцию прогнать так
Код:
$sReg = _RegExportEx('HKEY_CLASSES_ROOT', 'r.reg')

то она создаст файл r.reg весом 5.44мб
а если так
Код:
$sReg = _RegExportEx('HKEY_CLASSES_ROOT')
FileWrite("r.reg", $sReg)
FileClose("r.reg")

то она создаст такой же файл с таким же количеством строк (кроме первой строчки)
но вес у него 2.72мб
 
Верх