微软编程技术误区

loverP 2005-04-26 02:38:02
微软编程技术误区 (http://blog.csdn.net/dotnet_editor/archive/2005/04/26/363602.aspx)
有关微软编程技术的书籍可谓多如牛毛,但读来读去感觉还是MSDN比较权威。这里就拿一个例子来说吧,可能让很多刚开始学习Win32 API程序设计、甚至是一些已经有一定Win32 API经验的人感觉大汗淋漓。

再学习Win32 API程序设计时,“第一课”我想都会学到“事件循环”吧?很多书给出了类似这样的经典示例:

int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPCTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
...
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
...
return (int)msg.wParam;
}
没错吧?多么熟悉的事件循环,它可以很好地工作,当收到一个WM_QUIT事件的时候,GetMessage()返回0,我们的程序得以正常退出。因此,几乎任何一本讲述Win32 API程序设计的书籍或文章,不论国内的还是国外的,都会以这样一个程序作为第一章中的示例。

然而,就在前不久,和往常一样,闲来无事就翻起MSDN来,不知怎么的,就跑来看这个再熟悉不过的GetMessage()函数的参考来了。这一看不要紧,头顶顿时冒出虚汗——原来这么多年我们这么写程序,不能说是错误的,但绝对是有漏洞!来看MSDN上对于GetMessage()函数的讲解(节选):

注意:下面一段文字节选自MSDN Library Online,原文参见:
http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/messagesandmessagequeues/messagesandmessagequeuesreference/messagesandmessagequeuesfunctions/getmessage.asp

/--------------------------------\
>Return Value

>If the function retrieves a message other than WM_QUIT, the return value is nonzero.

>If the function retrieves the WM_QUIT message, the return value is zero.

>If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.

>Warning
>Because the return value can be nonzero, zero, or -1, avoid code like this:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...

>The possibility of a -1 return value means that such code can lead to fatal application errors. Instead, use code like this:

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
\--------------------------------/

草译如下,希望更多的朋友能够看清:

/--------------------------------\
返回值

如果该函数收到一个除WM_QUIT之外的事件,其返回值为一个非零值。

如果该函数收到一个WM_QUIT事件,其返回值为零。

如果该函数发生错误,其返回值为-1。例如,如果hWnd是一个无效的窗口句柄,或者lpMsg是一个无效指针,该函数就会失败。要获得额外的错误信息,请调用GetLastError。

警告

由于该函数的返回值可能是非零的、零或者-1,请避免这样做:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...

返回值-1出现的可能性意味着这样的代码会导致应用程序的致命错误。因此,我们应该编写这样的代码:

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
\--------------------------------/

看到了吗?我们这么长时间以来一直书写的代码,却在这个“警告”中被“明令禁止”了!可能有的朋友会想,这样的调用不可能出错啊,我们通常都在启动事件循环之前成功地创建了窗口,并且检查了是否成功,因此传递给GetMessage()函数的窗口句柄肯定是有效的;而且,我们通常在堆栈上分配msg,并通过求址运算符(&)来计算它的地址并传递给GetMessage()函数,也不大可能出现无效指针啊?但是,还记得程序设计的基本原理之一吗——永远不要假设任何事情!因此,看来我们该把过去写的代码拿出来好好审视一遍了。

这里仅提到了一个这样被我们忽视的技术细节,我想一定还有很多、更多这样的被忽视的东西存在!希望本文抛砖引玉,大家把你们发现的类似东西分享出来,让大家都能够写出更加安全健壮的程序吧!
...全文
5104 18 打赏 收藏 举报
写回复
18 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
ITFLY8 2005-08-08
头一次来发个贴 大家说的挺好的
  • 打赏
  • 举报
回复
megadeath 2005-06-19
我觉得这是在MSDN中把返回值标志成BOOL类型造成的混乱,摘抄一段国外的一个win32API教程,他们把这种返回值称之为“bizarre Microsoft Troolean ”

The GetMessage API is an interesting example of the bizarre Microsoft Troolean (as opposed to traditional, Boolean) logic. GetMessage is defined to return a BOOL, but the documentation specifies three types of returns, non-zero, zero and -1. I am not making it up! Here's an excerpt from the help file:
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero.
If there is an error, the return value is -1.

例子代码和楼主列的差不多,我就不贴了
  • 打赏
  • 举报
回复
fireseed 2005-06-17
liumazi,不知道弱智是谁

你可以去查一下BOOL的定义,你会看到下面的样子:

#define BOOL int

另外想告诉楼主,当你这样调用时是永远不会返回-1的:
MSG msg;
GetMessage(&msg, NULL, 0, 0);
  • 打赏
  • 举报
回复
nlstone 2005-06-09
在这种小问题上较真无意义,知道也好,不知道也没什么要紧...

对这种问题的"无知",还不至于让人"大汗淋漓"
  • 打赏
  • 举报
回复
sxxny 2005-06-08
这里错在微软. 不该把返回值标明为BOOL
可能这个函数的设计者就是想让调用者可以写这样的代码
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
但是后来又发现返回指不能够简单的用对和错来表示.这时就利用了Window下面的BOOL 其实是一个int的特点, 说返回-1是可能的.
从逻辑上来说, 你既然函数原形返回BOOL型, 就只能是有对和错两种情况. 不应该出现三种不同的情况.
  • 打赏
  • 举报
回复
麻子 2005-06-03

恕我直言,楼上的家伙可真够弱智的,难怪别人说用Delphi的普遍素质差,还是看看GetMessage()在Windows.pas中的定义吧:

function GetMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT): BOOL; stdcall;

返回值定义为BOOL意味着什么? 也就是只要不是0, 就视为'真', 其实这和C语言里的情况是类似的,拜托下次别乱说了! 丢脸!
  • 打赏
  • 举报
回复
chw_csdn_chw 2005-06-02
呵呵,delphi不存在这样的问题,while(n)根本编译不过去,所以说pascal虽然严谨,古板,但安全性高。
  • 打赏
  • 举报
回复
BlueTrees 2005-05-27
如果消息循环都无法进行了,那不如直接退出了,况且,即便GetMessage出错也不会影响后面的Trans和dispatch,就算错了又如何?这几个函数都很沉默的,程序一点反应都不会有。
  • 打赏
  • 举报
回复
天外怪魔 2005-05-26
Nice
  • 打赏
  • 举报
回复
alaisalaix 2005-05-18
呵呵,虚惊一场
  • 打赏
  • 举报
回复
gongla 2005-05-17
我有不同的观点。本文的作者没有认真地阅读 SDK 文档。请大家注意:SDK 文档中的前一个 GetMessage 与 后者的参数是不同的,前者为 GetMessage(lpMsg, hWnd, 0, 0),而后者为 GetMessage(&msg, NULL, 0, 0). hWnd 参数表示获取指定窗口的消息,而 lpMsg 是一个指针变量。如果你这样编写代码,则永远不会返回 -1:

MSG msg; // 由编译器分配存储空间
while (GetMessage(&msg, NULL, 0, 0)) // 获取所有窗口的消息
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;

如果你这样编写代码,则有可能返回 -1:

MSG *pMsg = malloc(sizeof(MSG)); // malloc 函数可能返回 NULL
while (GetMessage(pMsg, NULL, 0, 0))
{
TranslateMessage(pMsg);
DispatchMessage(pMsg);
}
return pMsg->wParam;

或者当调用 GetMessage 时指定了窗口句柄并且 hWnd 参数是
一个无效的窗口句柄时才会返回 -1。
  • 打赏
  • 举报
回复
剑风 2005-05-14
兄弟..我刚刚学,谢谢了.
  • 打赏
  • 举报
回复
gjzhou 2005-05-11
用API都要检查返回值的,只是你没在意罢了
  • 打赏
  • 举报
回复
sirguan 2005-05-11
这个也敢叫误区,你整说明你看msdn不小心,我早就发现了。。。
  • 打赏
  • 举报
回复
YorksenPlus 2005-05-02
哦不,还是得写成if(value),if(!value),因为BOOL, 以非0(1 in VC, -1 in VB)为TRUE,0为FALSE.
只能归结为微软PostMessage函数定义失误.
  • 打赏
  • 举报
回复
YorksenPlus 2005-04-30
我冒个泡吧!
以后判断BOOL的时候不要以为写成if(value),if(!value)很帅,不然要被打屁股.
一定要写成 if( value == TRUE) if(value == FALSE).看了《高质量C++编程》的朋友特别要注意!
  • 打赏
  • 举报
回复
csfreebird 2005-04-29
最大的误区就是中国程序员大多数迷恋mfc和桌面应用开发,而不知道应该努力做做分布式系统。几乎没多少人会用com+。
  • 打赏
  • 举报
回复
loverP 2005-04-26
请大家踊跃发言!!尤其尤其尤其欢迎指出其他被我们忽略的一些技术细节!
  • 打赏
  • 举报
回复
发帖
《新程序员》
加入

5781

社区成员

《新程序员》读者俱乐部
社区管理员
  • 《程序员》杂志社区
  • SoftwareTeacher
  • 《新程序员》编辑部
帖子事件
创建了帖子
2005-04-26 02:38
社区公告
暂无公告