代码改变世界

C语言Windows程序设计->第六天->GDI与设备环境

2012-10-11 14:30  wid  阅读(3344)  评论(0编辑  收藏  举报

·GDI简介

  GDI(Graphics Device Interface), 图形设备接口, 负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出。利用GDI所提供的函数就可以方便的在屏幕、打印机及其它输出设备上输出图形, 文本等操作。GDI的目的在于将应用程序上的输出转化为在硬件上的输出, 使应用程序无需关心硬件设备以及设备驱动, 降低了Windows程序设计的难度, 提高开发效率。

GDI的相关特点:

  1>. 开发者无需关心物理硬件的设备类型;

  2>. 不允许应用程序直接访问物理显示硬件, 通过获取显示设备的"设备环境"句柄实现与显示硬件的间接通信;

  3>. 程序与物理显示设备进行通信时, 必须首先获得与特定窗口相关联的设备环境;

  4. Windows根据设备环境的数据结构完成信息的输出。

 

 

·再谈WM_PAINT消息

  "当窗口显示区域的一部分显示内容或者全部变为"无效", 以致于必须"更新画面"时,应用程序将得到此消息。"这是我们在前面的学习中以及了解到的概念, 并且还学习了一些令窗口"客户区"变成无效的一些情况:

  1>. 首次被创建时, 这时整个客户区都是无效的;

  2>. 调整窗口的尺寸时, 这时客户区也会变得无效;

  3>. 最小化窗口后再将其恢复显示时;

  4>. 拖拽调整窗口位置时;

除此之外以下这些情况也能导致窗口客户区变成无效, 这些情况为:

  1>. 窗口滚动条被拖动;

  2>. 显式调用了InvalidateRect或者InvalidateRgn函数以产生WM_PAINT消息;

还有一些情况则可能导致客户区变为无效:

  1>. 菜单被拉下然后又重新释放;

  2>. 关闭一个覆盖在客户区上的对话框;

  3>. 窗口控件显示提示信息。

一般来说, 当程序进入消息循环前都会调用UpdateWindows来产生一个 最初的WM_PAINT 消息来通知窗口重绘客户区。

 

实际上, 当窗口客户区的某一部分需求重绘时, 并不需要对整个窗口客户区进行重绘, 通常只需要更新这一部分区域即可, 这部分无效的区域也被称为"无效矩形"。

 

 

·使用TextOut函数在客户区显示字符

  在创建窗口的示例程序中, 我们尝试使用了DrawText对字符文本进行了格式化输出, 但是DrawText相对来说不够灵活, 使用最频繁的字符输出函数当数TextOut, 该函数可以使用系统当前选择的字体、背景颜色和正文颜色将一个字符串输出到指定位置。函数的原型如下:

BOOL TextOut(
  HDC hdc,          // 设备环境句柄
  int nXStart,        // 字符串的开始位置 x坐标
  int nYStart,        // 字符串的开始位置 y坐标
  LPCTSTR lpString,    // 待输出的字符串
  int cchString      // 字符串字符个数
);

  如果我们要使用TextOut进行字符的输出, 根据TextOut函数的参数所示, 首先我们应该获取设备环境的句柄。

  设备环境(DC, Device Context)是一个抽象的概念, 通过设备环境就可以使用GDI中的函数。

 

  如何获取设备环境句柄

    方法一:

      使用BeginPaint函数。这种方法比较适合在处理WM_PAINT消息时使用, BeainPaint函数的原型如下:

HDC BeginPaint(
  HWND hwnd,          // 窗口的句柄
  LPPAINTSTRUCT lpPaint    // 绘制信息结构
);

    参数一为窗口句柄, 参数二为绘制信息结构PAINTSTRUCT对象的地址。

 

    关于绘制信息结构

    PAINTSTRUCT结构包含了应用程序用来绘制它所拥有的窗口客户区所需要的信息。PAINTSTRUCT的结构定义如下:

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

    当调用BeginPaint函数时, Windows将自动填充这个结构体中的成员相关属性, 程序仅能使用前三个成员, 其他为Windows内部使用。 参数一HDC hdc即为设备环境句柄, BeginPaint函数的返回值也就是这里的设备环境句柄, 简单来说就是先填充再返回; 参数二fErase决定是否擦出客户区背景, 如果为非零值则擦除背景,否则不擦除背景; 参数三rcPaint 通过指定客户区左上角和右下角的坐标确定一个要绘制的矩形范围, 即使需要更新的无效区域不是一个矩形, Windows也会把需要重绘的部分裁剪为一个矩形。

    如果你仍想重绘整个客户区, 可以在BeginPaint函数之前调用InvalidateRect( hwnd, NULL, TRUE );使整个客户区无效化。

 

    方法二:

    使用设备环境句柄有时不仅仅是在处理WM_PAINT消息时才会用到, 我们还可能使用设备环境句柄用作其他用途。

 

      1>. 通过GetDC函数来获得

       GetDC函数的原型如下:

 

HDC GetDC(HWND hWnd);

 

      该函数的参数为一个窗口句柄, 返回值为窗口客户区的设备环境句柄, 在GetDC中得到的裁剪矩形为整个客户区,。 在使用完成后需要调用ReleaseDC( hwnd, hdc  )函数将设备环境释放,。

      需要注意的是, GetDC不会将无效区域有效化, 如果需要对其进行有效化需要调用ValidateRect函数。

 

      2>. 通过GetWindowDC函数来获得

       GetWindowDC函数与GetDC不同的是: GetDC返回的是整个窗口客户区的设备环境句柄, 而GetWindow返回得是整个窗口的设备环境的句柄, 通过这个句柄你还可以在窗口的标题栏进行输出, 在使用完成后同样要对其进行释放。

 

下面尝试使用TextOut对文本进行输出, 代码如下:

#include <windows.h>

LRESULT CALLBACK WinProc( HWND, UINT, WPARAM, LPARAM );

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
{
    static TCHAR szAppName[] = TEXT( "CreateMyWinow" ) ;
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

    wndclass.lpfnWndProc = WinProc ;
    wndclass.hInstance = hInstance ;
    wndclass.lpszClassName = szAppName ;
    wndclass.style = CS_HREDRAW | CS_VREDRAW ;
    wndclass.cbClsExtra = 0 ;
    wndclass.cbWndExtra = 0 ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ;
    wndclass.lpszMenuName = NULL ;
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW) ;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION) ;

    if( !RegisterClass(&wndclass) )
    {
        MessageBox( NULL, TEXT("窗口注册失败!"), TEXT("错误"), MB_OK | MB_ICONERROR ) ;
        return 0 ;
    }

    hwnd = CreateWindow(
                szAppName,
                TEXT("创建窗口"),
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                600,
                400,
                NULL,
                NULL,
                hInstance,
                NULL
    ) ;

    ShowWindow( hwnd, iCmdShow ) ;
    UpdateWindow( hwnd ) ;

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

    return msg.wParam ;
}

LRESULT CALLBACK WinProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    HDC hdc ;
    PAINTSTRUCT ps ;
    RECT rect ;

    TCHAR szTextBuffer[1024];          //存储字符的缓冲区大小
    size_t iLength;                    //字符串长度

    switch( message )
    {
    case WM_CREATE:
        MessageBox( hwnd, TEXT("窗口已建立!"), TEXT("成功"), MB_OK | MB_ICONINFORMATION ) ;
        return 0 ;

    case WM_PAINT:
        hdc = BeginPaint( hwnd, &ps );
        GetClientRect( hwnd, &rect ) ;
        iLength = wsprintf(szTextBuffer, TEXT("使用TextOut进行文字输出!") ) ;        //向缓冲区写入待输出的文字
        TextOut( hdc, 200, 100, szTextBuffer,  iLength ) ;
        EndPaint( hwnd, &ps ) ;
        return 0 ;

    case WM_DESTROY:
        PostQuitMessage( 0 ) ;
        return 0 ;
    }
    return DefWindowProc( hwnd, message, wParam, lParam ) ;
}

 


对于TextOut函数的详细解释以及更加完善的输出方式将在下一篇随笔中进行。

 

-------------