【问题标题】:Get path to file currently open in Windows Picture & Fax viewer获取当前在 Windows 图片和传真查看器中打开的文件的路径
【发布时间】:2012-02-10 15:00:57
【问题描述】:

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

[编辑] 我做了一些搜索,发现(未记录?)有可能使用函数 NTQuerySystemInformation 找到进程使用的所有句柄的列表(如此处所示Delphi - get what files are opened by an application)。 然而,问题是,那里提供的示例根本没有为我显示文件句柄(只有非硬盘驱动器设备句柄),虽然我在这里找到了工作示例http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/,但似乎图片查看器没有任何从资源管理器启动时处理预览文件。

【问题讨论】:

  • 除非程序有一些 SDK 或文档化的 API,或者至少有用户可定义的附加组件支持,否则你没有合法的方式去做。
  • 如果无法通过接口获取信息,那其他方式呢?可能会找到查看器窗口组件的特征(例如其 TImage 替代项)或查看进程内存以查找文件名。
  • @Seedmanc:你没有阅读你得到的答案和cmets吗?您不能以任何有记录的方式执行此操作。窥视进程内存将是让您的产品被防病毒软件标记并从许多系统中删除的好方法。如果你需要尝试那样做,那你就做错了。
  • 很抱歉,在发表我的评论时,我还没有看到第一个。至于法律方面的东西,也许我在这里用错了词,而不是“插件”,基本上我将使用图片查看器而不是自己实现 TImage 和相应的例程,因为现有的实现让我满意。我真的没有看到任何伤害,因为我不会以任何方式更改它的 .exe,没有类似的。
  • @Seedmanc:即使您尝试这种方式,您的应用程序也将依赖于该程序的特定版本而不是特定的操作系统版本和更新。

标签: windows delphi photoviewer


【解决方案1】:

可以获取进程“当前目录”(如Process Explorer所示)。
看看Two ways to get the command line of another process using Delphi 的RRUZ。
根据那篇文章,我们可以在 RTL_USER_PROCESS_PARAMETERS (offset 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;    

【讨论】:

  • 感谢链接和代码,但似乎我在这里不走运。当我将基于查看器的窗口句柄找到的 PID 传递给此函数时,我得到“找不到进程”。当我手动将 PID 设置为 Process Explorer 找到的 PID 时,它给了我“C:\Documents and Settings\"username" 而不是实际目录。也许在那之前我做错了什么?这是我的一些代码:@ 987654329@可能是因为Viewer实际上是Explorer进程,而不是Rundll。
  • 您无法正常使用GetWindowThreadProcessId。您正在返回ThreadID。你应该使用GetWindowThreadProcessId(Wnd, @PID)。另外,您在 PE 中看到的当前目录是否适合您的需求?
  • 另外,如果您的“实际”目录是 My Documents 并且结果是 `C:\Documents and Settings[user]\My Documents`,这意味着它是有效的实际目录,一切都很好.
  • 感谢指正,现在这段代码重复了我在 PE 中看到的内容。但是,它只能部分工作......问题是,查看器仅在从命令行或通过右键单击启动时作为单独的进程(Rundll32)存在 - 打开方式。在这种情况下,CurrentDirectory 非常适合我的需要。但是当通过双击图像启动时(这是更常见的方式),它仅作为 explorer.exe 的单独线程存在 - 没有创建实际进程。然后 CurrentDirectory 不起作用,总是返回 ,因为这是 explorer 的属性。也许它需要替代 ThreadID。
  • 我怀疑你用 Windows XP 测试过它。在 Win7 上创建一个 COM 对象,进程所有者是 dllhost。一个全新的不同故事......
【解决方案2】:

您不能这样做,因为应用程序没有定义可根据请求提供该信息的 COM 接口。如您所指出的,如果它在窗口标题中显示路径和文件名,则可以得到它,但因为它没有,所以信息不可用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多