想用windows PSAPI中的EnumPageFiles函数获取页面文件的列表及使用情况,查到在MSDN中的声明是这样的:
BOOL WINAPI EnumPageFiles( __out PENUM_PAGE_CALLBACK pCallbackRoutine, __in LPVOID lpContext );
看到第一个参数是__out,感觉非常奇怪,它要输出做什么用呢?想来应该是自己定义的一个函数,然后对每一个页面文件都调用该函数,因为在MSDN中PENUM_PAGE_CALLBACK 的定义是这样的:
BOOL CALLBACK EnumPageFilesProc( [in] LPVOID pContext, [in] PENUM_PAGE_FILE_INFORMATION pPageFileInfo, [in] LPCTSTR lpFilename );很容易想到在自定义的PENUM_PAGE_CALLBACK 中通过传入的参数PENUM_PAGE_FILE_INFORMATION 去获取页面文件的使用情况,但EnumPageFiles的第
一个参数无论如何不应该是输出,于是我试了一下,定义一个变量,该变量没有初始化的时候传给EnumPageFiles,
……
PENUM_PAGE_FILE_CALLBACK pf ; // = &OnEachPageFile;
if( EnumPageFiles(pf , NULL) ){
return -1;
}
……
结果在VS中调试时会有异常抛出,是由于传入的变量没有初始化,显然该参数不应该是输出,否则没有理由要求参数必须进行初始化的,所以这个声明应该是有问题的,而事实上在psapi.h头文件的声明中并没有给EnumPageFiles的参数添加任何修饰词,例如__in或__out等,而其中绝大部分函数是有这些修饰词的,显然与MSDN文档不一致,而PENUM_PAGE_FILE_CALLBACK其实就是个宏,其定义及实际定义如下:
#define PENUM_PAGE_FILE_CALLBACK PENUM_PAGE_FILE_CALLBACKA
#define EnumPageFiles EnumPageFilesA
typedef BOOL (* PENUM_PAGE_FILE_CALLBACKA) (LPVOID pContext, PENUM_PAGE_FILE_INFORMATION pPageFileInfo, LPCSTR lpFilename);
这ASCII版的定义,本文只说ACII版的,UNICODE版的代码类似。可见EnumPageFiles 与MSDN文档中的定义也不一致。根据头文件中的定义,定义自己的EnumPageFilesProc,像这样:
BOOL OnEachPageFile(LPVOID pContext,
PENUM_PAGE_FILE_INFORMATION pPageFileInfo,
LPCTSTR lpFilename)
{
……
}
则在运行时会发生内存非法访问的异常,所以怀疑是头文件的声明与DLL中的实现不一致,关健就在于一个CALLBACK的修饰,因为CALLBACK其实是一个宏定义
#define CALLBACK __stdcall
而VC中C/C++的默认函数调用方式是__cdecl的,DLL中的实现是__stdcall,这会导致调用函数的堆栈的维护方式不同,__cdecl是用函数调用者清理堆栈,而__stdcall是windows API的函数调用方式,它是由被调用函数清理堆栈,所以如果声明与实现不一致会导致堆栈的非访问。
于是试着去改psapi.h中的声明,将PENUM_PAGE_FILE_CALLBACKA的定义改为:
typedef BOOL (CALLBACK * PENUM_PAGE_FILE_CALLBACKA) (LPVOID pContext, PENUM_PAGE_FILE_INFORMATION pPageFileInfo, LPCSTR lpFilename);
然后再测试,一切正常,当然自定义的函数的定义中也要有CALLBACK的修饰,完整代码如下:
#include <windows.h><br>#include <psapi.h><br>#pragma comment(lib, "psapi") <p></p><p>#include <string><br>#include <vector><br>#include <iostream><p>using std::string;<br>using std::vector; </p> <p></p> <p>struct SwapInfo{<br> string name;<br> string path;<br> string type;<br> long long free;<br> long long size;<br> long long used;<br>}; </p> <p></p> <p></p> <p>BOOL CALLBACK OnEachPageFile(LPVOID pContext,<br> PENUM_PAGE_FILE_INFORMATION pPageFileInfo,<br> LPCTSTR lpFilename)<br>{<br> SwapInfo si;<br> vector<swapinfo>* ls = static_cast<vector>*>(pContext); <p> si.free = pPageFileInfo->TotalSize - pPageFileInfo->TotalInUse;<br> si.name = lpFilename;<br> si.path = lpFilename;<br> si.size = pPageFileInfo->TotalSize;<br> si.type = "Pagefile";<br> si.used = pPageFileInfo->TotalInUse;<br> ls->push_back(si);<br> return TRUE;<br>} </p> <p></p> <p>int get_swap_info(vector<swapinfo> & swapinfo)<br>{<br> vector<swapinfo> ls;<br> SwapInfo info;<br> PENUM_PAGE_FILE_CALLBACK pf = &OnEachPageFile;<br> ls.clear();<br> if( EnumPageFiles(pf, &ls) ){<br> swapinfo.clear();<br> swapinfo = ls;<br> return 0;<br> }<br> return -1; <p>} </p> <p></p> <p>int main( int argc, char* argv[])<br>{<br> vector<swapinfo> pgfiles; <p> if( get_swap_info(pgfiles) != 0){<br> std::cerr return -1;<br> }<br> std::cout for( vector<swapinfo>::const_iterator it = pgfiles.begin(); it != pgfiles.end(); ++it){<br> std::coutnameusedsize }<br> return 0;<br>}</swapinfo></p></swapinfo></p></swapinfo></swapinfo></p></vector></swapinfo></p></iostream></vector></string></p></psapi.h></windows.h>
输出结果:
F:/Projects/EnumPageFiles/Debug>EnumPageFiles.exe
Pagefiles:
C:/pagefile.sys: 14532/131072 pages
D:/pagefile.sys: 14579/32768 pages
E:/pagefile.sys: 14310/32768 pages
有一点值得说明,这里的EnumPageFiles函数会自动对每一个页面文件都调用一次OnEachPageFile,不论有多少个页面文件只需要调用一次EnumPageFiles。
结论: MSDN中绝大部分说明是正确的,除了EnumPageFiles第一个参数的修饰__out,应该使用__in,psapi.h中的声明错误,应该使用MSDN中的声明。