发新话题
打印

D语言GUI入门6 GetMessage消息循环的错误分析

D语言GUI入门6 GetMessage消息循环的错误分析

知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070510

点击下面网址查看原文:
http://www.d-programming-language-china.org

by: 孙鑫(VC++深入详解) D语言论坛 http://www.d-programming-language-china.org 增加D语言相关说明

tag:GetMessage函数,windows sdk编程

消息循环的错误分析
有不少初学者学完前面内容后,编写了下面的代码:

QUOTE:

HWND hwnd;
hwnd=CreateWindow(…);

MSG msg;
while(GetMessage(&msg,hwnd,0,0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

注意代码中以粗体显示的部分。这段代码基于这样一个想法:第1章的程序只有一个窗口,而我们前面说了GetMessage函数的hWnd参数是用于指定接收属于哪一个窗口的消息,于是不少人就在消息循环中为GetMessage函数的hWnd参数指定了CreateWindow函数返回的窗口句柄。

读者可以用上述代码中的消息循环部分替换1.5节代码中的消息循环部分,然后运行程序,关闭程序。你会发现你的机器变慢了,同时按下键盘上的Ctrl + Alt + Delete键,启动Windows的任务管理器,切换到“进程”选项卡,单击“CPU”项进行排序,你会发现WinMain.exe的CPU占用率接近100,难怪机器“变慢了”。那么这是什么原因呢?实际上这个问题的答案在MSDN中就可以找到,并且就在GetMessage函数的说明文档中。不少初学者在遇到问题时,首先是头脑一片空白,接着就去找人求助,这种思想用在程序开发的学习中,没有什么好处。笔者经常遇到学员问问题,结果有不少问题的答案在MSDN关于某个函数的解释中就可看到(由于显示器的限制,有的答案需要滚动窗口才能看到 J)。所以在这里,笔者也建议读者遇到问题一定要记得查看MSDN,学会使用MSDN并从中汲取知识,将使你受用无穷。

回到正题,在前面介绍GetMessage函数时,曾说过如果hWnd参数是无效的窗口句柄或lpMsg参数是无效的指针时,GetMessage函数将返回-1。当我们关闭窗口时,调用了DestroyWindow来销毁窗口,由于窗口被销毁了,窗口的句柄当然也就是无效的句柄了,那么GetMessage将返回-1。在C/C++语言中,非0即为真,由于窗口被销毁,句柄变为无效,GetMessage总是返回-1,循环条件总是为真,于是形成了一个死循环,机器当然就“变慢了”。J

在MSDN关于GetMessage函数的说明文档中给出了下面的代码:
http://msdn2.microsoft.com/en-us/library/ms644936.aspx( 本文出处: http://www.d-programming-language-china.org )

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

针对我们这个问题,可以修改上述代码如下:

QUOTE:

HWND hwnd;
hwnd=CreateWindow(…);

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

D语言论坛 http://www.d-programming-language-china.org 按:
上面孙鑫确实讲得很细致。很多人没有看msdn的习惯,往往会忽略这点。yidabu也实验过了,窗体虽然销毁了,程序却没有退出,陷入死循环,占用CPU近100%。下面是D语言版本代码:

int nRet;
while ( (nRet = GetMessage(&msg, hwnd, 0,0)) !=0 )
{
    if (nRet==-1)
    {
        return -1;
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

读者可以再次运行修改后的程序,看看运行的结果。

现在全部D语言代码是这样:

import std.utf;        //for toUTF16z
import win32.api;        //for windows api
import std.stdio;        //for writefln

extern(C) void gc_init();
extern(C) void gc_term();
extern(C) void _minit();
extern(C) void _moduleCtor();
extern(C) void _moduleDtor();
extern(C) void _moduleUnitTests();

//TextOut函数
pragma(lib,"gdi32.lib");

const char[] ClassName = "http://www.d-programming-language-china.org";

extern(Windows)
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
{
    int result;
    gc_init();
    _minit();
    try
    {
        _moduleCtor();
        _moduleUnitTests();

        result = myWinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow);

        _moduleDtor();
    }
    catch(Exception e)
    {
        printf("catch %.*s\n",e.msg);
        result = 0;
    }
    gc_term();
    return result;
}

int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
{
    WNDCLASSEX wc;
    MSG msg;
    HWND hWnd;

    wc.cbSize = WNDCLASSEX.sizeof;
    wc.style = CS_HREDRAW | CS_VREDRAW ;
    wc.lpfnWndProc = &WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    //bbs.d-programming-language-china.org注: D语言标准库的windows.d主要服务于ascii字符集的windows api,IDI_APPLICATION被定义为CHAR*,所以这里要转换成wchar*
    //如果IDI_APPLICATION来定义是CHAR,就可以用toUTF16z来转换
    wc.hIcon = LoadIcon (cast(HINSTANCE)null,cast(wchar*)IDI_APPLICATION);
    wc.hIcon = LoadIcon (hInstance,toUTF16z("IDI_MYICON"));
    wc.hCursor = LoadCursor(cast(HINSTANCE)null,cast(wchar*)IDC_ARROW);
    wc.hbrBackground = cast(HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName = null;
    //转换类名到以0结尾的宽字符集
    wc.lpszClassName = toUTF16z(ClassName);
    wc.hIconSm = LoadIcon(hInstance,std.utf.toUTF16z("IDI_MYICON"));

    if ( !RegisterClassEx (&wc) )
        return 0;

    hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE | WS_EX_TOPMOST,
        toUTF16z(ClassName),
        std.utf.toUTF16z("D语言论坛 http://www.d-programming-language-china.org ;"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,CW_USEDEFAULT,800,600,
        HWND_DESKTOP,
        cast(HMENU) null,
        hInstance,
        null);

    if (hWnd == null)
    {
        MessageBoxEx(null,toUTF16z("欢迎来到 http://bbs.d-programming-language-china.org "),toUTF16z("error"),MB_ICONERROR | MB_HELP,0);
        return -2;
    }
    //显示和更新窗体
    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);


    int nRet;        //防止GetMessage=-1时陷入死循环
    while ( (nRet = GetMessage(&msg, cast(HWND)null, 0, 0)) != 0 )
    {
        if (nRet == -1 )
        {
            return -1;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return msg.wParam;
}


extern(Windows)
LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

    switch (uMsg)
    {
        case WM_CHAR:
            writefln("你按了键盘键 %d",wParam);
            break;
        case WM_LBUTTONDOWN:
            HDC hDc;
            hDc = GetDC(hwnd);
            wchar[] s = "你左键单击了";
            //用TextOut函数,必须加上gid32.lib库编译
            TextOut(hDc,50,50, s.ptr, s.length);
            ReleaseDC(hwnd,hDc);
            break;
        case WM_PAINT:
            HDC hDc;
            PAINTSTRUCT ps;
            hDc = BeginPaint(hwnd, &ps);
            wchar[] s = "你现在还好吗,D语言学得还好吧 http://www.d-programming-language-china.org";
            TextOut(hDc,100,200, s.ptr, s.length);
            EndPaint(hwnd,&ps);
            break;
        case WM_CLOSE:
            if ( IDYES== MessageBoxEx(hwnd, toUTF16z("真的要退出吗?"), toUTF16z("请选择"), MB_YESNO,0) )
                DestroyWindow(hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

至此,我们完整建立了一个windows GUI应用程序。建议上面所有的代码都手动输入而不是copy paste,以加深理解。理解了程序流程,windows api参数的意义,api函数的参数也就记住了。

( lastupdate:20070510 最新文章请访问http://www.d-programming-language-china.org )

关于一大步成功社区:
yidabu提倡在交流中学习,在分享中提高
收集感兴趣的知识,写下心得,通过网络与别人一起分享
理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的
网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台

TOP

发新话题