获取当前在 Windows Picture & 中打开的文件的路径传真查看器

发布于 2025-01-04 22:22:30 字数 704 浏览 2 评论 0 原文

我正在为 Windows 图片查看器编写一个“附加组件”,需要向其发送命令(例如“显示下一个/上一个图像”)并获取当前所选图像的文件路径。我设法通过 SendMessage 实现发送命令,但我不知道如何从进程请求信息。这可能吗? 到目前为止,我只能从窗口标题中提取文件名,但这限制了只能使用一个文件夹,我需要完整路径。

[编辑]我做了一些搜索,发现(未记录?)可以使用函数 NTQuerySystemInformation 找到进程使用的所有句柄的列表(如此处所示 Delphi - 获取应用程序打开的文件)。 然而,问题是,那里提供的示例根本没有为我显示文件句柄(仅显示非硬盘设备句柄),而我在这里找到了工作示例 http://www.codeguru.com/Cpp/WP/system/processesmodules/article.php/c2827/,似乎图片查看器在从资源管理器启动时不持有任何预览文件的句柄。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

太阳哥哥 2025-01-11 22:22:30

您可以获取进程“当前目录”(如 进程浏览器)。
看一下 使用Delphi获取另一个进程的命令行的两种方法 by RRUZ
根据该文章,我们可以获取在 RTL_USER_PROCESS_PARAMETERSoffset 36)结构中找到的 CurrentDirectory

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;    

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;    
握住你手 2025-01-11 22:22:30

您无法执行此操作,因为应用程序没有定义可根据请求提供该信息的 COM 接口。正如您所指出的,如果它在窗口标题中显示路径和文件名,您就可以获得它,但因为它没有显示该信息,所以该信息不可用。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文