سؤال

I am writing an "add-on" for Windows Picture viewer that will need to send commands to it (like "Show next/previous image") and obtain file path to currently selected image. I managed to implement sending commands via SendMessage, but I don't know how to request info from a process. Is this possible? So far I can only extract filename from window title, but this restricts usage to just one folder, I need the full path.

[EDIT] I did some search and found, that there's (undocumented?) possibility to find list of all handles used by process, using function NTQuerySystemInformation (As seen here Delphi - get what files are opened by an application). The problem is, however, that the example provided there doesn't show file handles for me at all (only non-harddrive device handles), and while I found working example here http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/, seems like Picture Viewer doesn't hold any handle to previewed file when launched from explorer.

هل كانت مفيدة؟

المحلول

You can get the Process "Current Directory" (as shown in Process Explorer).
Take a look at Two ways to get the command line of another process using Delphi by RRUZ.
Based on that article, we could get the CurrentDirectory found in the RTL_USER_PROCESS_PARAMETERS (offset 36) structure:

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;

Here is how to obtain 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;    

نصائح أخرى

You can't do this, as the application has no COM interfaces defined that would provide that info on request. You could get it if, as you've indicated, it displayed the path and filename in the window caption, but because it doesn't the information is unavailable.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top