Что нового

[Файловая система] Обход механизма перенаправления WOW64 в 64-битных системах

erlik

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

Перейдем к нашим 'баранам':
1. Выключать редирект не нужно, если программа открывает файл или папку через стандартный диалог FileSelectFolder или FileOpenDialog. Так ли это?
2. Выключать редирект нужно, если программа испльзует функции для перечисления файлов в папке - например _FileListToArray, FileFindNextFile,FileFindFirstFile и т.д.

Вопрос: корректно ли здесь использовано отключение редиректа.
Код:
;=====================================================
	If StringInStr($sPath,'system32') Then
	;если  параметр $DISABLEREDIRECTWOW64 = 1, то используем WinAPI
	Wow64Redirection(0,'LoadDllFromFoldersList')
	; либо алиас Sysnative
		If $DISABLEREDIRECTWOW64=='Sysnative' Then
			$sPath=StringReplace($sPath,'system32','Sysnative')
		EndIf
	EndIf
;=====================================================
	$aModuleList = _FileListToArray($sPath, "*.dll")
;=====================================================
; включаем обратно
	Wow64Redirection(1,'LoadDllFromFoldersList')
;=====================================================


;Функция редиректа
Код:
Func Wow64Redirection($iRedirect, $sFuncName,$sParam='')
	Local $iResult
	If $WOW64 And  $DISABLEREDIRECTWOW64 = 1 Then
	$iResult=_WinAPI_Wow64EnableWow64FsRedirection($iRedirect)
	If $iResult = 0 Then MsgBox(0,'','Ошибка установки Wow64FsRedirection, param[' & $iRedirect & '],вызов из ' & $sFuncName & ', ' & $sParam)
	EndIf
EndFunc

3. Как проверить существование виртуальной папки Sysnative, если предполагается использовать ее вместо отключения редиректа через WinAPI?
Код:
FileExists(@WindowsDir&"\Sysnative")
подойдет?
4. Макрос @SystemDir в 64-битной Windows возвращает C:\windows\system32 или C:\windows\SysWOW64 ? (есть ли зависимость от битности скомпилированного скрипта?)
5. В каких еще случаях (для каких функций AutoIT ) может понадобиться отключение редиректа?

Вопрос: нужно ли отключение редиректа здесь:
Код:
Func OpenFileFolder($sPath)

    If FileExists(@WindowsDir & "\explorer.exe") Or FileExists(@SystemDir & "\explorer.exe") Then
		Run("explorer /select, " & $sPath)
	Else
		ShellExecute(SplitPath($sPath,1), "", "", "open")
	EndIf

EndFunc

------------------------------------------------------------------------
...Нда, вот что значит невнимательно смотреть код функций, которые используешь. Оказывается функция
Код:
_WinAPI_EnumDllProc($sPath)
из WinAPIEx.au3, которую я использовал в скрипте, сама редирект отключает и значит 'обертывать' ее дополнительно совершенно точно не требуется. Будем знать ;)
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
4. Макрос @SystemDir возвратит по умолчанию C:\Windows\system32
Но если запертить использваться 64 битный Autoit, например, выствив #AutoIt3Wrapper_UseX64=N, то макрос возвратит C:\Windows\SysWOW64


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

Если компилировать версию как x86, то всегда возвращает C:\Windows\SysWOW64
Если компилировать версию как х64, то всегда возвращает C:\Windows\system32
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,722
Что такое редирект? Механизм, который позволяет изолировать 32-битные системные файлы от 64-битных. Это актуально только, если 32-битное приложение запускается в 64-битной ОС. К 64-битным приложениям это не относится. Для чего нужен редирект? Для того, чтобы не перекомпилировать 32-битные приложения для запуска в 64-битной ОС. Например, у вас есть такой вызов:

FileOpen("C:\Windows\System32\MyLog.txt")

При выполнении этой функции файл будет браться по следующему пути:

C:\Windows\System32\MyLog.txt

При запуске этого кода в 64-битной ОС путь будет автоматически изменен ОС на следующий:

C:\Windows\SysWOW64\MyLog.txt

Это и есть результат работы редиректа. Т.е. система сама исправляет пути, ссылающиеся на System32 на SysWOW64, т.к. в первой находятся 64-битные версии библиотек, а 32-битные во второй. Если бы этого не было, то любой явно указанный вызов из системных библиотек привел бы к краху 32-битного приложения запущенного в 64-битной ОС. Например, следующий вызов в 32-битном приложении будет всегда работать, независимо от того, на какой ОС будет запущен (32- или 64-битной), если включен редирект:

Код:
DllCall("C:\Windows\System32\kernel32.dll", ...)


Но если отключить редирект, то приложение вылетит в случае его запуска в 64-битной ОС. Все это хорошо, но бывают случаи (например ваша программа), когда необходимо из 32-битного приложения добраться до файлов, находящихся в System32, а не SysWOW64. Для этого и существует возможность отключать редирект. В противном случае, это было бы невозможно. Почему рекомендуется отключать редирект с осторожностью? См. пример с DllCall() выше.

Что касается AutoIt и функций File..., то, естественно, AutoIt не вмешивается в работу редиректа. Это должны делать именно вы. Вообще, запомните следующее, если 32-битное приложение запускается на 64-битной ОС, то по умолчанию при любых обращениях к System32 система будет перенаправлять такие вызовы к SysWOW64. Насчет редиректа, Microsoft рекомендует поступать так (если это нужно): отключить редирект, получить хэндл файла, включить редирект. В AutoIt я обычно делаю так:

Код:
#Include <WinAPIEx.au3>

Global Const $WOW64 = _WinAPI_IsWow64Process()

...

; Отключаем перенаправление
If $WOW64 Then
	_WinAPI_Wow64EnableWow64FsRedirection(0)
EndIf

; Что-то делаем с System32

; Включаем перенаправление
If $WOW64 Then
	_WinAPI_Wow64EnableWow64FsRedirection(1)
EndIf

...
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Отключение редиректа происходит только для текущего процесса или для всей системы сразу?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,722
inververs сказал(а):
Отключение редиректа просиходит только для текущего процесса или для всей системы сразу?

Естественно для текущего процесса, точнее даже для текущего потока.
 
Автор
E

erlik

Продвинутый
Сообщения
317
Репутация
84
Yashied
Спасибо за развернутый ответ :smile:
Я правильно понимаю, что
Код:
ShellExecute("c:\windows\system32", "", "", "open")

откроет на самом деле папку c:\windows\SysWoW64,
а вот что будет в таком случае:
Код:
Run("explorer /select, " & "c:\windows\system32\некий файл")
?
Опять попаду в SysWoW64?

Это я к тому, что проводник вроде как не редиректит пути...
Кто-нибудь, проверьте пожалуйста код с запуском explorer'а.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Yashied
А с реестром как? Тоже есть записи для 64 битных и 32 отдельно?
Что это за загадочная приставка 64 для всех ключей, типа HKLM64?
 

joiner

Модератор
Локальный модератор
Сообщения
3,497
Репутация
619
erlik [?]
Кто-нибудь, проверьте пожалуйста код с запуском explorer'а.
первый и второй коды открывают папку system32 . не нужно отключать перенаправление.
то есть при запуске их как 32-битное приложение в ОС х64.
а теперь попробуйте запустить то же самое, но используя макросы

Код:
ShellExecute(@SystemDir, "", "", "open")
Run("explorer /select, " & @SystemDir &"\cmd.exe")

вот тут как раз и будет перенаправление



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

путь к 32-битным dll в реестре
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
параметр DllDirectory32
значение
%SystemRoot%\syswow64

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

Код:
If @OSArch = 'x64' Then
	ShellExecute(@WindowsDir & '\system32')
	ShellExecute(@WindowsDir & '\syswow64')
ElseIf @OSArch = 'x86' Then
	ShellExecute(@SystemDir)
EndIf


в других случаях, возможно, нужно будет использовать отключение перенаправления

хотя, в той же функции _WinAPI_EnumDllProc()
тоже не обязательно использовать отключение перенаправления, достаточно в самом скрипте сделать проверку битности системы и выбрать нужную строчку
Код:
_WinAPI_EnumDllProc(@WindowsDir & '\system32\ххх.dll')

или
Код:
_WinAPI_EnumDllProc(@WindowsDir & '\syswow64\ххх.dll')

можете сами попробовать, удалив строчки вызова функции отключения перенаправления в функции _WinAPI_EnumDllProc() в библиотеке Winapiex.au3
так же с папками Program Files. проверяем разрядность системы и выбираем

Код:
ShellExecute(@HomeDrive & '\Program Files (x86)')
;или
	ShellExecute(@HomeDrive & '\Program Files')


к тому же,
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
параметр DllDirectory32
значение
%SystemRoot%\syswow64
мы видим, что в реестре в переменной указывается только путь к папке Windows, а системные папки четко обозначены именами system32 или syswow64, значит мы также можем применять макрос пути к системному диску или папке Windows и указывать точно имя конечной папки. как я показал в примерах выше.
насчет %WinDir%\Sysnative , то, как я понял из этой статьи http://support.microsoft.com/kb/942589, данная фича не поддерживается семеркой
вобщем, поразмышляв на эту тему прихожу к выводу, что отключением перенаправления в большинстве случаев можно не пользоваться. или даже не нужно.

теперь интересно бы узнать в каких случаях отключение перенаправления будет более эффективным моментом, чем указание путей вручную?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,722
joiner сказал(а):
теперь интересно бы узнать в каких случаях отключение перенаправления будет более эффективным моментом, чем указание путей вручную?

Перенаправление нужно использовать тогда, когда вы заранее не знаете путь. Например, пользователь указал System32, и нужно использовать именно ее. Если путь известен заранее, то, естественно, отключать перенаправление нет смысла. Узнать соответствующие пути можно с помощью функции _WinAPI_ShellGetSpecialFolderPath() или _WinAPI_ShellGetKnownFolderPath().

Код:
ConsoleWrite(_WinAPI_ShellGetSpecialFolderPath($CSIDL_SYSTEM) & @CR)
ConsoleWrite(_WinAPI_ShellGetSpecialFolderPath($CSIDL_SYSTEMX86) & @CR)

ConsoleWrite(_WinAPI_ShellGetKnownFolderPath($FOLDERID_System) & @CR)
ConsoleWrite(_WinAPI_ShellGetKnownFolderPath($FOLDERID_SystemX86) & @CR)


P.S

@SystemDir всегда содержит путь к системной папке, соответствующей разрядности приложения.

В 32-битной системе:

System32

В 64-битной системе:

x86 - SysWOW64
x64 - System32
 

joiner

Модератор
Локальный модератор
Сообщения
3,497
Репутация
619
Yashied [?]
Перенаправление нужно использовать тогда, когда вы заранее не знаете путь. Например, пользователь указал System32, и нужно использовать именно ее

честно говоря не могу представить пример, когда заранее неизвестно. ведь когда пишем код, то учитываем, что его могут запустить и ОС х86 и в ОС х64.
единственный момент это считывание параметров реестра и внесении изменений в него же. там как не указывай путь все равно переадресует. но с реестром уже отработано - HKLM или HKLM64
насчет работы с файловой системой и спецдиректориями, то лучше программу писать с такой же разрядностью что и ОС
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,722
joiner сказал(а):
честно говоря не могу представить пример, когда заранее неизвестно. ведь когда пишем код, то учитываем, что его могут запустить и ОС х86 и в ОС х64.

Например DLL Helper. Независимо от того, на какой ОС будет запущена программа, пользователь может выбрать DLL любой разрядности и в любой папке. Если он укажет System32, то сработает перенаправление на SysWOW64, и файл будет открыт уже другой. Таким образом, если бы я этого не учел и не отключал перенаправление, то пользователь просто не имел бы возможности открывать файлы из папки System32. А делать одну версию программы для просмотра 32-битных DLL, а другую для 64-битных, это, мягко говоря, глупо.

joiner сказал(а):
насчет работы с файловой системой и спецдиректориями, то лучше программу писать с такой же разрядностью что и ОС

Только, если это обоснованно самим кодом и будет иметь хоть какие-то преимущества. Пример Control Viewer.
 

joiner

Модератор
Локальный модератор
Сообщения
3,497
Репутация
619
Yashied
не нашел в твоей программе ( в исходниках) переадресации. может ты ее как то по другому делал. можешь указать участок кода где это происходит. сложно копаться в чужих исходниках.

я писал выше, что в функции
Код:
_WinAPI_EnumDllProc()
можно удалить переадресацию
я вырезал эту функцию из Winapiex.au3 и чуть урезал.
пример ее применения без переадресации
Код:
#include <array.au3>
#include <winapiex.au3>

$fileopen = _WinAPI_OpenFileDlg()
$ret = _WinAPI_EnumDllProc($fileopen)
_ArrayDisplay($ret)

Func _WinAPI_EnumDllProc($sPath, $sMask = '', $iFlags = 0)

	Local $PE, $Error, $Ret, $Ver, $hLibrary = 0

	If Not __DLL('dbghelp.dll') Then
		Return SetError(3, 0, 0)
	EndIf

	$__Enum = 0

$Ver = __Ver('dbghelp.dll')
	If $Ver < 0x0501 Then
		Return SetError(2, 0, 0)
	EndIf
;	If _WinAPI_IsWow64Process() Then
	;	$Ret = DllCall('kernel32.dll', 'int', 'Wow64DisableWow64FsRedirection', 'ptr*', 0)
	;	If (@error) Or (Not $Ret[0]) Then

	;	Else
	;		$WOW64 = $Ret[1]
	;	EndIf
;	EndIf
	Do
		$Error = 1
		$Ret = DllCall('kernel32.dll', 'dword', 'SearchPathW', 'ptr', 0, 'wstr', $sPath, 'ptr', 0, 'dword', 4096, 'wstr', '', 'ptr', 0)
		If (@error) Or (Not $Ret[0]) Then
			ExitLoop
		EndIf
		$__Ext = $Ret[5]
		$Error = 4
		$PE = _WinAPI_GetPEType($__Ext)
		Switch $PE
			Case 0x014C
				; (x86): IMAGE_FILE_MACHINE_I386
			Case 0x0200, 0x8664
				; (x64): IMAGE_FILE_MACHINE_IA64, IMAGE_FILE_MACHINE_AMD64
			Case Else
				ExitLoop
		EndSwitch
		$hLibrary = _WinAPI_LoadLibraryEx($__Ext, 0x00000003)
		If Not $hLibrary Then
			ExitLoop
		EndIf
		$Error = 5
		If $Ver >= 0x0600 Then
			__EnumDllProcW($hLibrary, $sMask, $iFlags)
		Else
			__EnumDllProcA($hLibrary, $sMask, $iFlags)
		EndIf
		If @error Then
			ExitLoop
		EndIf
	$Error = 0
	Until 1
	If $hLibrary Then
		_WinAPI_FreeLibrary($hLibrary)
	EndIf
	;If Not IsKeyword($WOW64) Then
	;	DllCall('kernel32.dll', 'int', 'Wow64RevertWow64FsRedirection', 'ptr*', $WOW64)
	;EndIf
	Return SetError($Error, $PE, $__Enum)
EndFunc   ;==>_WinAPI_EnumDllProc
Yashied, но не могу понять зачем в твоей программе переадресация (ну то, что она х86 это понятно), если диалог открытия файла возвращает полный путь к нему, а выше мы выяснили, что если есть путь, то переадресация не нужна.
извини за дотошность и непонятливость, я многого не знаю. заранее спасибо за разъяснения




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

вобщем, не работает мой пример без отключения переадресации. в данном случае она нужна
если, конечно, скрипт компилировать как х64. тогда переадресация не нужна. в функции _WinAPI_EnumDllProc() как раз сделана проверка на разрядность самой программы. успешно открываются файлы из двух системных папок через функцию диалога открытия файлов
Yashied, но тогда следует, что в среде х64 64-битная версия твоей программы будет успешно просматривать dll из обеих системных папок. тем более что в твоей программе они открываются через диалог открытия файлов.
единственный аргумент "за" компиляцию твоей проги как х86 это чтобы применять ее одинаково в системах разной разрядности.
 

Yashied

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

Если путь ссылается на System32, то он автоматом переадресуется на SysWOW64. И с этои вы ничего не сможете поделать до тех пор, пока не отключите перенаправление.

joiner сказал(а):
в функции _WinAPI_EnumDllProc() как раз сделана проверка на разрядность самой программы

Ну я как бы об этом знаю, т.к. сам и писал эту функцию. :smile: Да и не разрядность там проверяется, а запуск в WOW64. Как я писал выше, перенаправление имеет смысл только в том случае, когда 32-битная программа запущена в 64-битной ОС, т.е. в WOW64.

joiner сказал(а):
единственный аргумент "за" компиляцию твоей проги как х86 это чтобы применять ее одинаково в системах разной разрядности

Это даже не аргумент, а необходимость.
 
Автор
E

erlik

Продвинутый
Сообщения
317
Репутация
84
Прочитал все вышенаисанное и все таки меня терзают смутные сомнения :scratch:
Код:
_WinAPI_ShellObjectProperties("c:\windows\system32\ntdll.dll")
откроет свойства 32-битного файла из SysWoW64 (то есть сработает редирект) или все таки откроет родной 64-битный файл?
-----------------------------------
...Чтобы не задавать такие наивные вопросы нужно понять как система обрабатывает запрос 32-битных программ к 64-битным файлам. А вот это как раз мне непонятно. Просто фильтрует по строке пути? Есть в пути system32 - редиректим, нет - ничего не делаем.Как то так?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,722
erlik сказал(а):
Просто фильтрует по строке пути? Есть в пути system32 - редиректим, нет - ничего не делаем.Как то так?

Да.


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

erlik сказал(а):
Прочитал все вышенаисанное и все таки меня терзают смутные сомнения :scratch:
Код:
_WinAPI_ShellObjectProperties("c:\windows\system32\ntdll.dll")
откроет свойства 32-битного файла из SysWoW64 (то есть сработает редирект) или все таки откроет родной 64-битный файл?

Да, без отключения редиректа 64-битный файл открыть не получится.
 

joiner

Модератор
Локальный модератор
Сообщения
3,497
Репутация
619
Yashied [?]
Например DLL Helper. Независимо от того, на какой ОС будет запущена программа, пользователь может выбрать DLL любой разрядности и в любой папке. Если он укажет System32, то сработает перенаправление на SysWOW64, и файл будет открыт уже другой. Таким образом, если бы я этого не учел и не отключал перенаправление, то пользователь просто не имел бы возможности открывать файлы из папки System32
кстати, не работает в этой проге перенаправление. и открыть файлы из system32 невозможно.
при открытии файла библиотеки все равно открывает папку SysWOW64. хотя в адресной строке диалогового окна прописан путь в system32. но в самом окне видно содержание папки SysWOW64
 
Автор
E

erlik

Продвинутый
Сообщения
317
Репутация
84
Появилась информация, что отключение редиректа не срабатывает для диалогов открытия файлов\папок
Код:
FileSelectFolder
или
Код:
FileOpenDialog
или даже
Код:
_WinAPI_OpenFileDlg

Как же тогда быть? Есть какие-нибудь соображения по этому поводу?
-------------------------------------------------------------------------------
В качестве наивного предположения: раз редирект работает только для потока его вызвавшего, то так как нативные функции Autoit представляют из себя лишь обертки для вызова системных функций(возможно сначала вызывается С код и только потом системная функция), то последующий их вызов происходит уже в другом потоке для которого редирект не отключался. Отсюда и проблема.
Хотелось бы услышать мнения на этот счет.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,722
erlik сказал(а):
Появилась информация, что отключение редиректа не срабатывает для диалогов открытия файлов\папок
Код:
FileSelectFolder
или
Код:
FileOpenDialog
или даже
Код:
_WinAPI_OpenFileDlg

Как же тогда быть? Есть какие-нибудь соображения по этому поводу?

Не могу сейчас проверить, но все должно работать. Данные функции не создают никаких потоков.
 
Верх