PSAPI.h 的 QueryWorkingSet 函数中使用的 PSAPI_WORKING_SET_INFORMATION 缓冲区的实际大小是多少

What's the actual size of PSAPI_WORKING_SET_INFORMATION buffer used in QueryWorkingSet function of PSAPI.h

我想使用函数 QueryWorkingSet available in PSAPI,但我在实际定义缓冲区 pv 的大小时遇到​​了问题。这是代码:

#include <Windows.h>
#include <Psapi.h>
#include <iostream>


void testQueryWorkingSet()
{
    unsigned int counter;
    HANDLE thisProcess = GetCurrentProcess();
    SYSTEM_INFO si;
    PSAPI_WORKING_SET_INFORMATION wsi, wsi2;

    GetSystemInfo(&si);
    QueryWorkingSet(thisProcess, &wsi, sizeof(wsi));

    DWORD wsi2_buffer_size = (wsi.NumberOfEntries) * sizeof(PSAPI_WORKING_SET_BLOCK);

    if (!QueryWorkingSet(thisProcess, &wsi2, wsi2_buffer_size))
    {
        std::cout << "ERROR CODE : " << GetLastError() << std::endl;
        abort();
    }
}

int main(int argc, char * argv[])
{
    testQueryWorkingSet();

    int* test = new int[1000000];

    testQueryWorkingSet();
}

我总是以 abort() 被调用结束,并且在第一次调用 testQueryWorkingSet() 期间出现错误代码 24 or 998。我分别解释为:wsi2_buffer_size 太低,wsi2_buffer_size 太大。

现在我不知道这个变量应该取什么值,我试过了:

在我们应该使用此功能的方式中必须有一些我不理解的东西,但我找不到什么。我尝试修改给定的代码 there,即:

#include <Windows.h>
#include <Psapi.h>
#include <iostream>


void testQueryWorkingSet()
{
    unsigned int counter;
    HANDLE thisProcess = GetCurrentProcess();
    SYSTEM_INFO si;
    PSAPI_WORKING_SET_INFORMATION wsi_1, * wsi;
    DWORD wsi_size;

    GetSystemInfo(&si);

    wsi_1.NumberOfEntries = 0;
    QueryWorkingSet(thisProcess, (LPVOID)&wsi_1, sizeof(wsi));

#if !defined(_WIN64)
    wsi_1.NumberOfEntries--;
#endif
    wsi_size = sizeof(PSAPI_WORKING_SET_INFORMATION)
        + sizeof(PSAPI_WORKING_SET_BLOCK) * wsi_1.NumberOfEntries;
    wsi = (PSAPI_WORKING_SET_INFORMATION*)HeapAlloc(GetProcessHeap(),
        HEAP_ZERO_MEMORY, wsi_size);

    if (!QueryWorkingSet(thisProcess, (LPVOID)wsi, wsi_size)) {
        printf("# Second QueryWorkingSet failed: %lu\n"
            , GetLastError());
        abort();
    }
}

int main(int argc, char * argv[])
{
    testQueryWorkingSet();

    int* test = new int[1000000];

    testQueryWorkingSet();
}

此代码仅适用于对 testQueryWorkingSet() 的 1 次调用,第二次调用中止,错误代码为 24。以下是简要问题:

  1. 你会如何在一个可以连续调用多次的函数中使用 QueryWorkingSet
  2. 给定 PSAPI_WORKING_SET_INFORMATIONthe documentation 的参数 cb 的值代表什么?

两个例子都完全忽略了 QueryWorkingSet() 第一次调用的 return 值和错误代码。您仅在第二次调用时进行错误处理。

你的第一个例子失败了,因为你在为 QueryWorkingSet() 的第二次调用计算 wsi2_buffer_size 时没有考虑 PSAPI_WORKING_SET_INFORMATION 的整个大小。即使第一次调用成功,如果 NumberOfEntries returned > 1.

,您也不会为第二次调用分配任何额外的内存来填充

您的第二个示例将错误的缓冲区大小值传递给 QueryWorkingSet() 的第一次调用的 cb 参数。您传入的只是单个指针的大小,而不是整个 PSAPI_WORKING_SET_INFORMATION 的大小。错误 24 是 ERROR_BAD_LENGTH。您需要使用 sizeof(wsi_1) 而不是 sizeof(wsi)

我建议在循环中调用 QueryWorkingSet(),以防工作集在查询其大小的调用和获取其数据的调用之间实际发生变化。

此外,请确保在使用完毕后释放分配的内存。

话虽如此,请尝试更多生活:

void testQueryWorkingSet()
{
    HANDLE thisProcess = GetCurrentProcess();

    PSAPI_WORKING_SET_INFORMATION *wsi, *wsi_new;
    DWORD wsi_size;
    ULONG_PTR count = 1; // or whatever initial size you want...

    do
    {
        wsi_size = offsetof(PSAPI_WORKING_SET_INFORMATION, WorkingSetInfo[count]);

        wsi = (PSAPI_WORKING_SET_INFORMATION*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wsi_size);
        if (!wsi)
        {
            printf("HeapAlloc failed: %lu\n", GetLastError());
            abort();
        }

        if (QueryWorkingSet(thisProcess, wsi, wsi_size))
            break;

        if (GetLastError() != ERROR_BAD_LENGTH)
        {
            printf("QueryWorkingSet failed: %lu\n", GetLastError());
            HeapFree(GetProcessHeap(), 0, wsi);
            abort();
        }

        count = wsi->NumberOfEntries;
        HeapFree(GetProcessHeap(), 0, wsi);
    }
    while (true);

    // use wsi as needed...

    HeapFree(GetProcessHeap(), 0, wsi);
}