`
yidongkaifa
  • 浏览: 4060794 次
文章分类
社区版块
存档分类
最新评论

windows PSAPI.h 的一个BUG ?

 
阅读更多

想用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>*&gt;(pContext); <p> si.free = pPageFileInfo-&gt;TotalSize - pPageFileInfo-&gt;TotalInUse;<br> si.name = lpFilename;<br> si.path = lpFilename;<br> si.size = pPageFileInfo-&gt;TotalSize;<br> si.type = "Pagefile";<br> si.used = pPageFileInfo-&gt;TotalInUse;<br> ls-&gt;push_back(si);<br> return TRUE;<br>} </p> <p></p> <p>int get_swap_info(vector<swapinfo> &amp; swapinfo)<br>{<br> vector<swapinfo> ls;<br> SwapInfo info;<br> PENUM_PAGE_FILE_CALLBACK pf = &amp;OnEachPageFile;<br> ls.clear();<br> if( EnumPageFiles(pf, &amp;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中的声明。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics