【问题标题】:Get full path from a process in Windows从 Windows 中的进程获取完整路径
【发布时间】:2018-11-15 10:42:34
【问题描述】:

这个 api 有问题。我已经检查了返回功能。没有错误。但什么也不输出。

HANDLE hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapProcess, &process);
do
{
    if (process.th32ProcessID != 0)
    {
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
        if (hProcess != NULL) 
        {
            wchar_t filePath[MAX_PATH];
            if (!GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
            {
                std::wcout << filePath << std::endl;
            }
        }
        CloseHandle(hProcess);
    }

} while (Process32Next(hSnapProcess, &process));
CloseHandle(hSnapProcess);

【问题讨论】:

  • 我很困惑。代码完全按照它应该做的:如果GetModuleFileNameExW 成功则不输出任何内容,如果失败则转储未初始化变量的内容。为什么您希望将任何内容写入控制台?
  • @IInspectable 它只是为了检查它是否输出以供以后使用。顺便问一下,你的意思是未初始化的变量?
  • @jab:不,它未初始化的。此 API 不对失败时的输出参数做出任何保证。如果您想知道,请调用GetModuleFileNameExW 传递nullptr 作为进程句柄。
  • @IInspectable 是的,你当然是对的,我错误地认为filePathstd::string,但它显然必须是LPWSTR。正在删除评论。
  • 要检索远程进程的主要可执行模块的名称,请使用GetProcessImageFileNameQueryFullProcessImageName 函数。这比使用 NULL 模块句柄调用 GetModuleFileNameEx 函数更有效、更可靠。

标签: c++ windows visual-studio winapi


【解决方案1】:

在您的代码中可以看到两个明显的错误,都可以通过阅读文档来理解。

首先,在GetModuleFileNameEx

句柄必须具有 PROCESS_QUERY_INFORMATION 和 PROCESS_VM_READ 访问权限。

你的句柄只有PROCESS_QUERY_INFORMATION

其次,再次在GetModuleFileNameEx

如果函数成功,返回值指定复制到缓冲区的字符串长度。

如果函数失败,返回值为零。

你的逻辑是从后到前的。替换:

if (!GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))

if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))

【讨论】:

  • 仅使用 PROCESS_QUERY_INFORMATION 在我的平台 (Windows 10) 上有效。 (无论如何,我仍然赞成你的回答)。
  • @DavidHeffernan 当然,但我仍然想知道为什么它在我的平台上只有PROCESS_QUERY_INFORMATION
  • @Jabberwocky,Windows 团队在某些时候改变了hModuleNULL 的情况,使其与QueryFullProcessImageName 相同,它基于NtQueryInformationProcess 系统调用,只需要PROCESS_QUERY_LIMITED_INFORMATION 访问,因此可以查询高完整性和受保护进程的名称。这是一个未记录的实现细节。请改用QueryFullProcessImageName。老的实现,还是用来查询DLL的,需要读取PEB,需要查询所有信息的权限,读取进程的内存。
【解决方案2】:

这个 API 没有问题,唯一的问题是你的代码。

文档明确指出GetModuleFileNameExW的返回值是复制到缓冲区的字符串长度。

如果返回值为0,则函数失败。

所以你只需要这样写:

...
if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH) != 0)
{
   // handle "success" case
}
...

顺便说一句,CloseHandle(hProcess); 应该在 if (hProcess != NULL) 块内。

带有错误检查的完整工作示例

#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>

int main()
{
  HANDLE hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

  if (hSnapProcess != INVALID_HANDLE_VALUE)
  {
    PROCESSENTRY32 process;
    process.dwSize = sizeof(PROCESSENTRY32);
    Process32First(hSnapProcess, &process);
    do
    {
      if (process.th32ProcessID != 0)
      {
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
        if (hProcess != NULL)
        {
          wchar_t filePath[MAX_PATH];
          if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
          {
            std::wcout << filePath << std::endl;
          }
          else
          {
            std::wcout << L"GetModuleFileNameExW failed with error" << GetLastError() << std::endl;
          }

          CloseHandle(hProcess);
        }
      }

    } while (Process32Next(hSnapProcess, &process));

    CloseHandle(hSnapProcess);
  }
  else
  {
    std::wcout << L"CreateToolhelp32Snapshot failed with error" << GetLastError() << std::endl;
  }
}

【讨论】:

  • 如文档所说,如果失败,它将返回 0。这就是为什么我只是把 ! 放在前面。但事实并非如此。 filePath 不输出进程的完整路径。
  • 你的逻辑错了。 GetModuleFileNameExW 在出现错误时返回 0,这就是为什么你应该使用 if (GetModuleFileNameEx(hProcess, NULL, filePath, MAX_PATH))(没有 !)或 if (GetModuleFileNameEx(hProcess, NULL, filePath, MAX_PATH) != 0)
  • @Dwersonbusch 通过将! 放在前面,当返回值为0,即GetModuleFileNameExW 失败时,您使if 分支工作......
  • 好的,我现在明白了。但它不输出。这里有什么问题?
  • @Dwersonbusch 问题中的代码是重要的,而不是您在屏幕上看到的。我们不是调试服务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-11
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
相关资源
最近更新 更多