发新话题
打印

D语言GUI入门5 窗口过程函数WindowProc

D语言GUI入门5 窗口过程函数WindowProc

知识若不分享 实在没有意义 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:windowproc是什么,D语言教程,vc windowproc

WindowProc窗口过程函数

在完成上述步骤后,剩下的工作就是编写一个窗口过程函数,用于处理发送给窗口的消息。一个Windows应用程序的主要代码部分就集中在窗口过程函数中。在MSDN中可以查到窗口过程函数的声明形式,如下所示:

QUOTE:
LRESULT CALLBACK WindowProc(    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

窗口过程函数的名字可以随便取,如WinSunProc,但函数定义的形式必须和上述声明的形式相同。

提示:系统通过窗口过程函数的地址(指针)来调用窗口过程函数,而不是名字。

WindowProc函数的4个参数分别对应消息的窗口句柄、消息代码、消息代码的两个附加参数。一个程序可以有多个窗口,窗口过程函数的第1个参数hwnd就标识了接收消息的特定窗口。

在窗口过程函数内部使用switch/case语句来确定窗口过程接收的是什么消息,以及如何对这个消息进行处理。我们看下面的代码:

WinMain.cpp( 本文出处: http://www.d-programming-language-china.org )

……

QUOTE:
1.LRESULT CALLBACK WinSunProc(
2.    HWND hwnd,    // handle to window
3.    UINT uMsg,    // message identifier
4.    WPARAM wParam,    // first message parameter
5.    LPARAM lParam    // second message parameter
6.)
7.{
8.    switch(uMsg)
9.    {
10.    case WM_CHAR:
11.    char szChar[20];
12.    sprintf(szChar,"char code is %d",wParam);
13.    MessageBox(hwnd,szChar,"char",0);
14.    break;
15.    case WM_LBUTTONDOWN:
16.    MessageBox(hwnd,"mouse clicked","message",0);
17.    HDC hdc;
18.    hdc=GetDC(hwnd);
19.    TextOut(hdc,0,50,"程序员之家",strlen("程序员之家"));
20.    ReleaseDC(hwnd,hdc);
21.    break;
22.    case WM_PAINT:
23.    HDC hDC;
24.    PAINTSTRUCT ps;
25.    hDC=BeginPaint(hwnd,&ps);
26.    TextOut(hDC,0,0,"http://www.sunxin.org",strlen("http://www.sunxin.org"));
27.    EndPaint(hwnd,&ps);
28.    break;
29.    case WM_CLOSE:
30.    if(IDYES==MessageBox(hwnd,"是否真的结束?","message",MB_YESNO))
31.    {
32.    DestroyWindow(hwnd);
33.    }
34.    break;
35.    case WM_DESTROY:
36.    PostQuitMessage(0);
37.    break;
38.    default:
39.    return DefWindowProc(hwnd,uMsg,wParam,lParam);
40.    }
41.    return 0;
42.}

处理WM_CHAR消息

10~14行代码:当用户在窗口中按下一个字符键,程序将得到一条WM_CHAR消息(通过调用TranslateMessage函数转换得到),在其wParam参数中含有字符的ASCII码值。

D语言论坛按:
在D语言里这样打印出WM_CHAR

case WM_CHAR:
    import std.stdio;        //放在源程序开始处
    writefln("你按了键盘: %d",wParam);
    break;

MessageBox函数(其用法,请读者查看MSDN,并结合本章程序来学习)弹出一个包含了显示信息的消息框,如果我们按下字母“a”键(注意大小写),程序将弹出如图1.3所示的消息框。

处理WM_LBUTTONDOWN消息

15~21行代码:当用户在窗口中按下鼠标左键时,将产生WM_ LBUTTONDOWN消息。为了证实这一点,我们在WM_LBUTTONDOWN消息的响应代码中,调用MessageBox函数弹出一个提示信息,告诉用户“点击了鼠标”。接下来,我们在窗口中(0,50)的位置处输出一行文字。要在窗口中输出文字或者显示图形,需要用到设备描述表(Device Context),简称DC。DC是一个包含设备(物理输出设备,如显示器,以及设备驱动程序)信息的结构体,在Windows平台下,所有的图形操作都是利用DC来完成的。( 本文出处: http://www.d-programming-language-china.org )

关于DC,我们可以用一个形象的比喻来说明它的作用。现在有一个美术老师,他让他的学生画一幅森林的图像,有的学生采用素描,有的学生采用水彩画,有的学生采用油画,每个学生所作的图都是森林,然而表现形式却各不相同。如果让我们来画图,老师指定了一种画法(例如用水彩画),我们就要去学习它,然后才能按照要求画出图形。如果画法(工具)经常变换,我们就要花大量的时间和精力去学习和掌握它。在这里,画法就相当于计算机中的图形设备及其驱动程序。我们要想作一幅图,就要掌握我们所用平台的图形设备和它的驱动程序,调用驱动程序的接口来完成图形的显示。不同图形设备的设备驱动程序是不一样的,对于程序员来说,要掌握各种不同的驱动程序,工作量就太大了。因此,Windows就给我们提供了一个DC,让我们从学生的角色转变为老师的角色,只要下命令去画森林这幅图,由DC去和设备驱动程序打交道,完成图形的绘制。至于图形的效果,就要由所使用的图形设备来决定了。对于老师来说,只要画出的是森林图像就可以了。对于程序员来说,充当老师的角色,只需要获取DC(DC也是一种资源)的句柄,利用这个句柄去作图就可以了。

使用DC,程序不用为图形的显示与打印输出分别处理了。无论显示,还是打印,都是直接在DC上操作,然后由DC映射到这些物理设备上。

第17行代码:定义了一个类型为HDC的变量hdc。

第18行代码:用hdc保存GetDC函数返回的与特定窗口相关联的DC的句柄。为什么DC要和窗口相关联呢?想像一下,我们在作图时,需要有画布;利用计算机作图,窗口就相当于画布,因此,在获取DC的句柄时,总是和一个指定的窗口相关联。

第19行代码:TextOut函数利用得到的DC句柄在指定的位置(x坐标为0,y坐标为50)处输出一行文字。( 本文出处: http://www.d-programming-language-china.org )

D语言论坛 http://www.d-programming-language-china.org 按:
查了msdn:
http://msdn2.microsoft.com/en-us/library/ms534019.aspx

TextOut函数的语法是:
The TextOut function writes a character string at the specified location, using the currently selected font, background color, and text color.

QUOTE:
BOOL TextOut(
    HDC hdc,    // handle to DC
    int nXStart,    // x-coordinate of starting position
    int nYStart,    // y-coordinate of starting position
    LPCTSTR lpString,    // character string
    int cbString    // number of characters
);

要注意的是这句:( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
lpString
[in] Pointer to the string to be drawn. The string does not need to be zero-terminated, since cbString specifies the length of the string.

如果是unicode api,LPCTSTR就是LPCWSTR,LP是指针,C是const常量,W是wide character,STR是string。在D语言里可以方便地用s.ptr得到指向字符串首字符的指针。因为在D语言里字符串就是一个数组,ptr是数组的一个属性,length也是。

在D语言里这样使用TextOut:( 本文出处: http://www.d-programming-language-china.org )

HDC hDc;//得到设备(相当于“画笔”,)
hDc = GetDc(hwnd);    //还要"画布",不然画在空气里可不好玩
wchar[] s = "你左键单击了";    //在D语言里,两字节的utf字符是wchar类型
TextOut(hDc,10,10, s.ptr, s.length);
ReleaseDc(hwnd,hDc);

第20行代码:在执行图形操作时,如果使用GetDC函数来得到DC的句柄,那么在完成图形操作后,必须调用ReleaseDC函数来释放DC所占用的资源,否则会引起内存泄漏。

处理WM_PAINT消息

第22~28行代码:对WM_PAINT消息进行处理。当窗口客户区的一部分或者全部变为“无效”时,系统会发送WM_PAINT消息,通知应用程序重新绘制窗口。当窗口刚创建的时候,整个客户区都是无效的。因为这个时候程序还没有在窗口上绘制任何东西,当调用UpdateWindow函数时,会发送WM_PAINT消息给窗口过程,对窗口进行刷新。当窗口从无到有、改变尺寸、最小化后再恢复、被其他窗口遮盖后再显示时,窗口的客户区都将变为无效,此时系统会给应用程序发送WM_PAINT消息,通知应用程序重新绘制。

提示:窗口大小发生变化时是否发生重绘,取决于WNDCLASS结构体中style成员是否设置了CS_HREDRAW和CS_VREDRAW标志。

第25行,调用BeginPaint函数得到DC的句柄。BeginPaint函数的第1个参数是窗口的句柄,第二个参数是PAINTSTRUCT结构体的指针,该结构体对象用于接收绘制的信息。在调用BeginPaint时,如果客户区的背景还没有被擦除,那么BeginPaint会发送WM_ERASEBKGND消息给窗口,系统就会使用WNDCLASS结构体的hbrBackground成员指定的画刷来擦除背景。

D语言论坛 http://www.d-programming-language-china.org 按:
PAINTSTRUCTR结构原型如下:
http://msdn2.microsoft.com/en-us/library/ms534910.aspx( 本文出处: http://www.d-programming-language-china.org )

QUOTE:
typedef struct tagPAINTSTRUCT {
    HDC    hdc;
    BOOL fErase;
    RECT rcPaint;
    BOOL fRestore;
    BOOL fIncUpdate;
    BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;

写成D语言的格式是:

struct PAINTSTRUCT {
    HDC    hdc;
    BOOL    fErase;
    RECT    rcPaint;
    BOOL    fRestore;
    BOOL    fIncUpdate;
    BYTE    rgbReserved[32];
}
alias PAINTSTRUCT* PPAINTSTRUCT, NPPAINTSTRUCT, LPPAINTSTRUCT;

第26行,调用TextOut函数在(0,0)的位置输出一个网址“http://www.sunxin.org”。当发生重绘时,窗口中的文字和图形都会被擦除。在擦除背景后,TextOut函数又一次执行,在窗口中再次绘制出 “http://www.sunxin.org”。这个过程对用户来说是透明的,用户并不知道程序执行的过程,给用户的感觉就是你在响应WM_PAINT消息的代码中输出的文字或图形始终保持在窗口中。换句话说,如果我们想要让某个图形始终在窗口中显示,就应该将图形的绘制操作放到响应WM_PAINT消息的代码中。

那么系统为什么不直接保存窗口中的图形数据,而要由应用程序不断地进行重绘呢?这主要是因为在图形环境中涉及的数据量太大,为了节省内存的使用,提高效率,而采用了重绘的方式。

在响应WM_PAINT消息的代码中,要得到窗口的DC,必须调用BeginPaint函数。BeginPaint函数也只能在WM_PAINT消息的响应代码中使用,在其他地方,只能使用GetDC来得到DC的句柄。另外,BeginPaint函数得到的DC,必须用EndPaint函数去释放。

处理WM_CLOSE消息

29~34行代码:当用户单击窗口上的关闭按钮时,系统将给应用程序发送一条WM_CLOSE消息。在这段消息响应代码中,我们首先弹出一个消息框,让用户确认是否结束。如果用户选择“否”,则什么也不做;如果用户选择“是”,则调用DestroyWindow函数销毁窗口,DestroyWindow函数在销毁窗口后会向窗口过程发送WM_DESTROY消息。注意,此时窗口虽然销毁了,但应用程序并没有退出。有不少初学者错误地在WM_DESTROY消息的响应代码中,提示用户是否退出,而此时窗口已经销毁了,即使用户选择不退出,也没有什么意义了。所以如果你要控制程序是否退出,应该在WM_CLOSE消息的响应代码中完成。( 本文出处: http://www.d-programming-language-china.org )

对WM_CLOSE消息的响应并不是必须的,如果应用程序没有对该消息进行响应,系统将把这条消息传给DefWindowProc函数(参见第39行),而DefWindowProc函数则调用DestroyWindow函数来响应这条WM_CLOSE消息。

写成D语言代码是这样:

case WM_CLOSE:
    if ( IDYES== MessageBoxEx(hwnd,toUTF16z("你真的要退出吗?"),toUTF16z("请确认"),MB_YESNO,0)
        //bbs.d-programming-language-china.org注: DestroyWindow定义在core32的winuser.d中。
        DestroyWindow(hwnd);
    break;

处理WM_DESTROY消息

35~37行代码:DestroyWindow函数在销毁窗口后,会给窗口过程发送WM_DESTROY消息,我们在该消息的响应代码中调用PostQuitMessage函数(第36行)。PostQuitMessage函数向应用程序的消息队列中投递一条WM_QUIT消息并返回。我们在1.4.3小节介绍过,GetMessage函数只有在收到WM_QUIT消息时才返回0,此时消息循环才结束,程序退出。要想让程序正常退出,我们必须响应WM_DESTROY消息,并在消息响应代码中调用PostQuitMessage,向应用程序的消息队列中投递WM_QUIT消息。传递给PostQuitMessage函数的参数值将作为WM_QUIT消息的wParam参数,这个值通常用做WinMain函数的返回值。

38、39行代码:DefWindowProc函数调用默认的窗口过程,对应用程序没有处理的其他消息提供默认处理。对于大多数的消息,应用程序都可以直接调用DefWindowProc函数进行处理。在编写窗口过程时,应该将DefWindowProc函数的调用放到default语句中,并将该函数的返回值作为窗口过程函数的返回值。

读者可以试着将38、39行代码注释起来,运行一下,看看会有什么结果。提示:运行之后,在NT4.0/Win2000下启动任务管理器,切换到进程标签,查看程序是否运行。( 本文出处: http://www.d-programming-language-china.org )

现在为止全部D语言代码如下:

import std.utf;
import win32.api;
import std.stdio;

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("我的第一个程序"),
        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);

    while (GetMessage(&msg, cast(HWND)null, 0, 0))
    {
        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;
}
( lastupdate:20070510 最新文章请访问http://www.d-programming-language-china.org )

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

TOP

发新话题