gpt4 book ai didi

c++ - 如何从 32 位进程获取 64 位进程的可靠内存使用信息?

转载 作者:行者123 更新时间:2023-11-28 04:53:57 25 4
gpt4 key购买 nike

我的目标是获取任意进程的内存使用信息。我从我的 32 位进程中执行以下操作:

HANDLE hProc = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, 0, pid);
if(hProc)
{
PROCESS_MEMORY_COUNTERS_EX pmx = {0};
if(::GetProcessMemoryInfo(hProc, (PROCESS_MEMORY_COUNTERS*)&pmx, sizeof(pmx)))
{
wprintf(L"Working set: %.02f MB\n", pmx.WorkingSetSize / (1024.0 * 1024.0));
wprintf(L"Private bytes: %.02f MB\n", pmx.PrivateUsage / (1024.0 * 1024.0));
}

::CloseHandle(hProc);
}

问题是,如果pid进程是64位进程,它可能分配了超过4GB的内存,这将同时溢出pmx.WorkingSetSizepmx.PrivateUsage,都是32位进程中的32位变量。所以在那种情况下而不是失败,GetProcessMemoryInfo两个指标均以 UINT_MAX 形式返回——这是错误的!

所以我想知道,是否有可靠的 API 可以从 32 位应用程序中的任意进程检索内存使用情况?

最佳答案

有一个名为“Performance Data Helpers”的可靠 API。

Windows 的标准 perfmon 实用程序是 Windows 性能计数器应用程序的经典示例。 Process Explorer 还使用它来收集过程统计信息。

它的优点是您甚至不需要 SeDebugPrivilege 来获得对其他进程的 PROCESS_VM_READ 访问权限。
请注意,访问权限仅限于属于 Performance Monitoring Users 组的用户。

PDH背后的思想是:

  • 查询对象
    • 一个或多个柜台
  • 根据要求或定期制作 sample
  • 获取您要求的数据

开始时需要做更多的工作,但最终还是很容易的。我所做的是设置一个永久性 PDH 查询,这样我就可以在我的应用程序的整个生命周期中重复使用它。

有一个缺点:默认情况下,操作系统会为具有相同名称的进程创建编号条目。这些编号的条目甚至会在进程终止或创建新进程时发生变化。因此,您必须考虑到这一点并交叉检查进程 ID (PID),实际上是在打开要获取内存使用情况的进程的句柄时。

下面是一个简单的 PDH 包装器替代 GetProcessMemoryInfo()。当然,还有足够的空间来调整以下代码或根据您的需要进行调整。我还看到有人已经创建了更多通用 C++ 包装器。

声明

#include <tuple>
#include <array>
#include <vector>
#include <stdint.h>
#include <Pdh.h>

#pragma comment(lib, "Pdh.lib")


class process_memory_info
{
private:
using pd_t = std::tuple<DWORD, ULONGLONG, ULONGLONG>; // performance data type
static constexpr size_t pidIdx = 0;
static constexpr size_t wsIdx = 1;
static constexpr size_t pbIdx = 2;
struct less_pd
{
bool operator ()(const pd_t& left, const pd_t& right) const
{
return std::get<pidIdx>(left) < std::get<pidIdx>(right);
}
};

public:
~process_memory_info();

bool setup_query();
bool take_sample();
std::pair<uintmax_t, uintmax_t> get_memory_info(DWORD pid) const;

private:
PDH_HQUERY pdhQuery_ = nullptr;
std::array<PDH_HCOUNTER, std::tuple_size_v<pd_t>> pdhCounters_ = {};
std::vector<pd_t> perfData_;
};

实现

#include <memory>
#include <execution>
#include <algorithm>
#include <stdlib.h>

using std::unique_ptr;
using std::pair;
using std::array;
using std::make_unique;
using std::get;


process_memory_info::~process_memory_info()
{
PdhCloseQuery(pdhQuery_);
}

bool process_memory_info::setup_query()
{
if (pdhQuery_)
return true;
if (PdhOpenQuery(nullptr, 0, &pdhQuery_))
return false;

size_t i = 0;
for (auto& counterPath : array<PDH_COUNTER_PATH_ELEMENTS, std::tuple_size_v<pd_t>>{ {
{ nullptr, L"Process", L"*", nullptr, 0, L"ID Process" },
{ nullptr, L"Process", L"*", nullptr, 0, L"Working Set" },
{ nullptr, L"Process", L"*", nullptr, 0, L"Private Bytes" }
}})
{
wchar_t pathStr[PDH_MAX_COUNTER_PATH] = {};

DWORD size;
PdhMakeCounterPath(&counterPath, pathStr, &(size = _countof(pathStr)), 0);
PdhAddEnglishCounter(pdhQuery_, pathStr, 0, &pdhCounters_[i++]);
}

return true;
}

bool process_memory_info::take_sample()
{
if (PdhCollectQueryData(pdhQuery_))
return false;

DWORD nItems = 0;
DWORD size;
PdhGetFormattedCounterArray(pdhCounters_[0], PDH_FMT_LONG, &(size = 0), &nItems, nullptr);
auto valuesBuf = make_unique<BYTE[]>(size);
PdhGetFormattedCounterArray(pdhCounters_[0], PDH_FMT_LONG, &size, &nItems, PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.get()));
unique_ptr<PDH_FMT_COUNTERVALUE_ITEM[]> pidValues{ PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.release()) };

valuesBuf = make_unique<BYTE[]>(size);
PdhGetFormattedCounterArray(pdhCounters_[1], PDH_FMT_LARGE, &size, &nItems, PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.get()));
unique_ptr<PDH_FMT_COUNTERVALUE_ITEM[]> wsValues{ PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.release()) };

valuesBuf = make_unique<BYTE[]>(size);
PdhGetFormattedCounterArray(pdhCounters_[2], PDH_FMT_LARGE, &size, &nItems, PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.get()));
unique_ptr<PDH_FMT_COUNTERVALUE_ITEM[]> pbValues{ PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.release()) };

perfData_.clear();
perfData_.reserve(nItems);
for (size_t i = 0, n = nItems; i < n; ++i)
{
perfData_.emplace_back(pidValues[i].FmtValue.longValue, wsValues[i].FmtValue.largeValue, pbValues[i].FmtValue.largeValue);
}
std::sort(std::execution::par_unseq, perfData_.begin(), perfData_.end(), less_pd{});

return true;
}

pair<uintmax_t, uintmax_t> process_memory_info::get_memory_info(DWORD pid) const
{
auto it = std::lower_bound(perfData_.cbegin(), perfData_.cend(), pd_t{ pid, 0, 0 }, less_pd{});

if (it != perfData_.cend() && get<pidIdx>(*it) == pid)
return { get<wsIdx>(*it), get<pbIdx>(*it) };
else
return {};
}


int main()
{
process_memory_info pmi;
pmi.setup_query();

DWORD pid = 4;

pmi.take_sample();
auto[workingSet, privateBytes] = pmi.get_memory_info(pid);

return 0;
}

关于c++ - 如何从 32 位进程获取 64 位进程的可靠内存使用信息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47585152/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com