Получить путь к файлу, открытому в данный момент в средстве просмотра изображений и факсов Windows
Я пишу "дополнение" для средства просмотра изображений Windows, которое должно будет отправлять ему команды (например, "Показать следующее / предыдущее изображение") и получать путь к файлу для выбранного в данный момент изображения. Мне удалось реализовать отправку команд через SendMessage, но я не знаю, как запросить информацию из процесса. Это возможно? Пока я могу извлечь только имя файла из заголовка окна, но это ограничивает использование только одной папки, мне нужен полный путь.
[EDIT] Я провел некоторый поиск и обнаружил, что есть (недокументированная?) Возможность найти список всех дескрипторов, используемых процессом, используя функцию NTQuerySystemInformation (Как видно здесь, Delphi - получить, какие файлы открываются приложением). Проблема, однако, в том, что приведенный там пример не показывает мне файловые дескрипторы (только дескрипторы устройств без жесткого диска), и хотя я нашел рабочий пример здесь http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/, похоже, Picture Viewer не держит дескриптор для предварительного просмотра файла при запуске из проводника.
2 ответа
Вы можете получить процесс "Текущий каталог" (как показано в Process Explorer).
Взгляните на Два способа получить командную строку другого процесса, использующего Delphi от RRUZ.
Основываясь на этой статье, мы могли бы получить CurrentDirectory
найдено в RTL_USER_PROCESS_PARAMETERS
(смещение 36) структура:
type
Uint4B = Cardinal;
Uint2B = Word;
UChar = Byte;
Ptr32 = Pointer;
TUNICODE_STRING = UNICODE_STRING;
TCURDIR = packed record
DosPath : TUNICODE_STRING;
Handle : Ptr32;
end;
TRTL_USER_PROCESS_PARAMETERS = packed record
MaximumLength : Uint4B;
Length : Uint4B;
Flags : Uint4B;
DebugFlags : Uint4B;
ConsoleHandle : Ptr32;
ConsoleFlags : Uint4B;
StandardInput : Ptr32;
StandardOutput : Ptr32;
StandardError : Ptr32;
CurrentDirectory : TCURDIR;
DllPath : TUNICODE_STRING;
ImagePathName : TUNICODE_STRING;
CommandLine : TUNICODE_STRING;
Environment : Ptr32;
StartingX : Uint4B;
StartingY : Uint4B;
CountX : Uint4B;
CountY : Uint4B;
CountCharsX : Uint4B;
CountCharsY : Uint4B;
FillAttribute : Uint4B;
WindowFlags : Uint4B;
ShowWindowFlags : Uint4B;
WindowTitle : TUNICODE_STRING;
DesktopInfo : TUNICODE_STRING;
ShellInfo : TUNICODE_STRING;
RuntimeData : TUNICODE_STRING;
// +0x090 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR
end;
Вот как получить CurrentDirectory
:
function GetCurrentDirectoryFromPid(PID: THandle): string;
const
STATUS_SUCCESS = $00000000;
SE_DEBUG_NAME = 'SeDebugPrivilege';
OffsetProcessParametersx32 = $10; //16
OffsetCurrentDirectoryx32 = $24; //36
var
ProcessHandle : THandle;
rtlUserProcAddress : Pointer;
CurrentDirectory : TCURDIR;
CurrentDirectoryContents : WideString;
ProcessBasicInfo : PROCESS_BASIC_INFORMATION;
ReturnLength : Cardinal;
TokenHandle : THandle;
lpLuid : TOKEN_PRIVILEGES;
OldlpLuid : TOKEN_PRIVILEGES;
begin
Result:='';
if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, TokenHandle) then
begin
try
if not LookupPrivilegeValue(nil, SE_DEBUG_NAME, lpLuid.Privileges[0].Luid) then
RaiseLastOSError
else
begin
lpLuid.PrivilegeCount := 1;
lpLuid.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
ReturnLength := 0;
OldlpLuid := lpLuid;
//Set the SeDebugPrivilege privilege
if not AdjustTokenPrivileges(TokenHandle, False, lpLuid, SizeOf(OldlpLuid), OldlpLuid, ReturnLength) then RaiseLastOSError;
end;
ProcessHandle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, PID);
if ProcessHandle=0 then RaiseLastOSError
else
try
// get the PROCESS_BASIC_INFORMATION to access to the PEB Address
if (NtQueryInformationProcess(ProcessHandle,0{=>ProcessBasicInformation},@ProcessBasicInfo, sizeof(ProcessBasicInfo), @ReturnLength)=STATUS_SUCCESS) and (ReturnLength=SizeOf(ProcessBasicInfo)) then
begin
//get the address of the RTL_USER_PROCESS_PARAMETERS struture
if not ReadProcessMemory(ProcessHandle, Pointer(Longint(ProcessBasicInfo.PEBBaseAddress) + OffsetProcessParametersx32), @rtlUserProcAddress, sizeof(Pointer), ReturnLength) then
RaiseLastOSError
else
if ReadProcessMemory(ProcessHandle, Pointer(Longint(rtlUserProcAddress) + OffsetCurrentDirectoryx32), @CurrentDirectory, sizeof(CurrentDirectory), ReturnLength) then
begin
SetLength(CurrentDirectoryContents, CurrentDirectory.DosPath.length);
//get the CurrentDirectory field
if ReadProcessMemory(ProcessHandle, CurrentDirectory.DosPath.Buffer, @CurrentDirectoryContents[1], CurrentDirectory.DosPath.Length, ReturnLength) then
Result := WideCharLenToString(PWideChar(CurrentDirectoryContents), CurrentDirectory.DosPath.length div 2)
else
RaiseLastOSError;
end;
end
else
RaiseLastOSError;
finally
CloseHandle(ProcessHandle);
end;
finally
CloseHandle(TokenHandle);
end;
end
else
RaiseLastOSError;
end;
Вы не можете сделать это, так как в приложении не определены COM-интерфейсы, которые предоставляли бы эту информацию по запросу. Вы можете получить его, если, как вы указали, он отображал путь и имя файла в заголовке окна, но из-за того, что это не так, информация недоступна.