Получить путь к файлу, открытому в данный момент в программе просмотра изображений и факсов - PullRequest
3 голосов
/ 10 февраля 2012

Я пишу «дополнение» для средства просмотра изображений Windows, которое должно будет отправлять ему команды (например, «Показать следующее / предыдущее изображение») и получать путь к файлу к выбранному в данный момент изображению.Мне удалось реализовать отправку команд через SendMessage, но я не знаю, как запросить информацию из процесса.Это возможно?Пока что я могу извлечь только имя файла из заголовка окна, но это ограничивает использование только одной папкой, мне нужен полный путь.

[РЕДАКТИРОВАТЬ] Я провел поиск и обнаружил, что есть (недокументированная?) Возможностьнайти список всех дескрипторов, используемых процессом, используя функцию NTQuerySystemInformation (как видно здесь Delphi - получить файлы, которые открываются приложением ).Проблема, однако, в том, что приведенный здесь пример вообще не показывает мне файловые дескрипторы (только дескрипторы устройств без жесткого диска), и хотя я нашел здесь рабочий пример http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/,, похоже, Picture Viewer не показываетдержать любой дескриптор для предварительного просмотра файла при запуске из проводника.

Ответы [ 2 ]

4 голосов
/ 12 февраля 2012

Вы можете получить процесс «Текущий каталог» (как показано в Process Explorer ).
Взгляните на Два способа получить командную строку другого процесса, используя Delphi by RRUZ .
На основании этой статьи мы можем получить CurrentDirectory, найденный в структуре RTL_USER_PROCESS_PARAMETERS ( offset 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;    
1 голос
/ 10 февраля 2012

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

...