C语言Windows程序设计->第六天->GDI与设备环境
2012-10-11 14:30 wid 阅读(3359) 评论(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函数的详细解释以及更加完善的输出方式将在下一篇随笔中进行。
-------------