小心,进程句柄!

//=====================================================================
//TITLE:
// 小心,进程句柄!
//AUTHOR:
// norains
//DATE:
// Friday 20-August-2010
//Environment:
// NULL
//=====================================================================

最近在调试一个跨平台的界面库,突然发现,一直显示得很好的图片,居然在Windows CE6.0里面哑火了!出问题的是这行代码:

LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_BITMAP1));

调用GetLastError,根据返回的数值确认是句柄有误。句柄有误?奇了怪了。要知道,这行代码已经已经在Windows CE 5.0和Windows XP这两个架构不同的系统测试过,完全是正常的,怎么一到Windows CE 6.0就哑火了?再仔细查看,发现问题出在于GetModuleHandle函数!

在说这问题之前,我们先看看程序的入口函数WinMain:

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

我们只需要注意第一个形参hInstance,这个是进程的句柄。问题点就来了,在Windows CE 5.0和Windows XP中,调用GetModuleHandle(NULL)的返回值,和hInstance的数值是完全相同的;而对于Windows CE 6.0,情况就截然相反,返回的是一个不知道从哪里来的数值,该数值和hInstance根本扯不上边。如果将GetModuleHandle(NULL)用hInstance的数值代替,比如:

LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP1));

那么在Windows CE 6.0中就能正常。

 那么,究竟Windows CE 6.0中,GetModuleHandle(NULL)返回的是个什么东西?查看MSDN,发现有这么一段描述:If this parameter is NULL, GetModuleHandle returns the process identifier of the calling process.
  
 如果是Windows CE 5.0,则是另外的解释:If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process.

结果很明显,在Windows CE 5.0中,当GetModuleHandle的形参为NULL时,返回的是进程的实例句柄,而在Widows CE 6.0,返回的却是进程的ID!似乎一切都迎刃而解了,但实际上还没完。在Windows CE 6.0中,虽然说返回的是进程ID,但这个ID却不知道用在何处!因为如果直接将这返回值赋予OpenProcess,那么函数调用失败,因为GetModuleHandle的返回值和GetProcessID返回的截然不同,而后者却是能用于OpenProcess中!那么,究竟在Windows CE 6.0中的GetModuleHandle(NULL)返回值有何作用?我也不知道。如果读者大人您知道,麻烦一定要告诉我。

先不讨论GetModuleHandle(NULL)的作用,我们还是先想想如何获得进程的实例句柄吧。难道我们一定要在WinMain函数中用一个全局变量保存hInstance数值,然后在整个程序中都用该全局变量?其实可以不用那么复杂,我们只要用GetCurrentProcessId替代就好。比如,之前的图片读取,可以如此:

LoadBitmap(GetCurrentProcessId (),MAKEINTRESOURCE(IDB_BITMAP1));

但是这行代码只能在Windows CE 5.0和Windows CE 6.0中运行,如果是放到Windows XP中则又是一个哑火,因为GetCurrentProcessId在Windows XP中的返回值并不是hInstance所代表的数值。

最后,让我们以如下列表看看不同函数在不同系统中调用的迥异吧:

代码

WINCE5.0

WINCE6.0

WINXP

hInstance

0x63ab5fbe

0x050d001a

0x00400000

hGetModuleHandle =

GetModuleHandle(NULL)

0x63ab5fbe

0x00000042

0x00400000

hGetCurrentProcess =

GetCurrentProcess()

0x00000042

0x00000042

0xffffffff

dwGetCurrentProcessId =

GetCurrentProcessId()

0x63ab5fbe

0x050d001a

0x00001768

hOpenProcess_GetModuleHandle =

OpenProcess(0,

FALSE,

(DWORD)hGetModuleHandle)

0x63ab5fbe

0x00000000

0x00000000

hOpenProcess_GetCurrentProcessId =

OpenProcess(0,

FALSE,

dwGetCurrentProcessId)

0x63ab5fbe

0x004d4103

0x00000000

该表格的测试代码如下:

#ifdef _WIN32_WCE int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) #else int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) #endif //#ifdef _WIN32_WCE { HANDLE hGetModuleHandle = GetModuleHandle(NULL); HANDLE hGetCurrentProcess = GetCurrentProcess(); DWORD dwGetCurrentProcessId = GetCurrentProcessId(); HANDLE hOpenProcess_GetModuleHandle = OpenProcess(0,FALSE,reinterpret_cast<DWORD>(hGetModuleHandle)); HANDLE hOpenProcess_GetCurrentProcessId = OpenProcess(0,FALSE,dwGetCurrentProcessId); }

当然,还少不了实际调试时的IDE截图。

首先是Windows CE 6.0:

  其次是Windows CE 5.0:

最后是Windows XP:

最后的最后,还是再一次看看LoadBitmap的调用。其实对于Windows CE 6.0,我们设置可以不用实例句柄,直接用NULL即可:

LoadBitmap(NULL,MAKEINTRESOURCE(IDB_BITMAP1));

不过呢,这样的调用方式在不同系统又有不同表现哦,如下表所示:

系统

LoadBitmap(NULL,MAKEINTRESOURCE(IDB_BITMAP1))

Windows XP

返回NULLerror code1814,找不到映像文件中指定的资源名。

Windows CE 5.0

返回NULLerror code6,句柄无效。

Windows CE 6.0

调用成功,能够正常获得图像句柄