DDB:设备相关位图,属于GDI对象,跟画笔、画刷同样在GDI模块内部存储。
HBITMAP hBitmap;//存储一个指向DDB的句柄windows
(1)hBitmap=CreateBitmap(cx,cy,cPlanes,cBitsPixel,bits)
/
该函数是第一个建立GDI位图对象,也叫设备相关位图(DDB)。
该函数的第3、四个参数的设置有两种状况:
1.cPlanes,cBitsPixel都为1,表明该位图是单色位图。
2.cPlanes,cBitsPixel值等于某个设备环境中的值,经过PLANES和BITSPIXEL索引调用GetDeviceCaps函数获取这两个参数的值。
/数组
(2)hBitmap=CreateCompatibleBitmap(hdc,cx,cy)
/*
该函数建立一个与设备兼容的GDI位图对象。
什么是设备兼容?其实就是位图和真实的设备(视频显示器)有一样的颜色组织(cPlanes,cBitsPixel)。为何要与设备兼容?你想一想,若是我建立的位图的颜色组织和设备的颜色组织不同,那么有可能位图想显示的颜色在设备中可能没法显示出来,那不就出错了。ide
该函数使用参数1指定的设备环境句柄得到GetDeviceCaps返回的信息,而后把这些信息传给CreateBitmap,再返回GDI位图对象句柄。
*/函数
(3)hBitmap=CreateBitmapIndirect(&bitmap)
/*
参数bitmap是BITMAP类型的结构,该类型结构定义以下:测试
typedef struct tagBITMAP { LONG bmType;//位图类型,该字段必须为0 LONG bmWidth;//横向像素个数 LONG bmHeight;//纵向像素个数 LONG bmWidthBytes;//不须要设置,Windows会自动计算这个字段的值 WORD bmPlanes;//颜色平面数 WORD bmBitsPixel;//每一个像素的颜色位数 LPVOID bmBits;//设为NULL或某个带有值的字节类型数组的地址来初始化位图的全部像素位 } BITMAP, *PBITMAP;
能够利用GetObject函数获取指定位图对象的信息。例如:
BITMAP bitmap;
GetObject(hBitmap,sizeof(BITMAP),&bitmap);
//参数1:指定须要获取的GDI位图对象
*/字体
总结:这些函数都可以建立GDI位图对象,而后Windows为这些位图对象在内存中分配内存并初始化,以及存储位图对象的信息,例如:横纵像素数、颜色平面数、每一个像素的颜色位数和每一个像素的颜色值。注意,当建立GDI位图对象并使用完该对象后,必须调用DeleteObject函数删除在程序中建立的全部位图。ui
咱们都知道一个位图由许多矩形的像素组成,每一个像素的颜色位数要么1位(单色位图)、要么多位(彩色位图),那么每一个像素有着颜色值。Windows提供了两个函数能在位图被建立后设置或获取位图的全部像素位。
1.SetBitmapBits(hBitmap,cBytes,&bits);
2.GetBitmapBits(hBitmap,cBytes,&bits);spa
言归正传,假设某个DDB(每一个像素8个颜色位)的某个像素的颜色值为0x37,这表明什么颜色。其实,DDB没有颜色表,但当这个DDB显示在屏幕上时,被显示的这个像素对应的是视频卡的颜色查找表中索引为0x37的RGB颜色,因此这就叫设备相关。位图中的像素的颜色与设备相关,因此这就叫设备相关性。分析到这里咱们就能够想到,若是咱们不知道视频卡的颜色查找表对应的颜色,那么咱们在建立DDB时,能够自定义的初始化位图的全部像素位吗?不,这是不能够的。因此,Windows有了这样的规则:对于彩色DDB,不该该调用CreateBitmap或CreateBitmapIndirect或SetBitmapBits函数来设置位图的全部像素位,只有对单色DDB,才能够放心地设置像素位,由于单色只有0或1,谁都知道。指针
咱们建立了GDI位图对象(DDB)后,使用GDI位图对象就要用到内存设备环境,由于只有内存设备环境才能够选入位图,其余种类的设备环境都不能够。而内存设备环境却能够选入其余GDI对象(画笔、画刷、字体、调色板和区域)。通常地设备环境都是对应于特定的图形输出设备(视频显示器或打印机),而内存设备环境只存在于内存,它不是真实的图形输出设备,但它却和这些真实的设备“兼容”,意思是说内存设备环境和真实设备的设备环境的颜色组织相同。为了能实现内存设备环境和真实的设备“兼容”,那么在建立内存设备环境时,要有一个对应于真实设备的设备句柄,例如:
hdcMem=CreateCompatibleDC(hdc);//若是参数设为NULL,那么Windows将建立与视频显示器兼容的内存设备环境。code
真实的设备,例如:视频显示器,都是一个点阵设备,而内存设备环境也有一个显示表面,然而这个显示表面只有1个像素宽,1个像素高,单色的。因此,若是要在内存设备环境搞事情,必须得增大显示表面,为此,能够把刚刚建立的GDI位图对象选入内存设备环境。例如:
SelectObject(hdcMem,hBitmap);
注意:SelectObject函数调用是否有效,取决于两种状况:
(1)位图是单色位图。
(2)位图与内存设备环境兼容的真实设备有一样的颜色组织。意思是说,在建立GDI位图对象时,只有位图和真实设备兼容,SelectObject函数才会有效。
一句话归纳:若是位图是单×××,畅通无阻,能够直接选入内存设备环境。建立彩色位图须要兼容真实设备,而建立内存设备环境须要兼容真实设备,那么调用SelectObject函数将位图选入内存设备环境才会成功。
咱们在内存设备环境中能够像在真实的设备环境中作任何事,例如:画图等。注意,绘图操做是在位图上绘画,本来的位图被新的位图覆盖了。
除了利用位图建立函数来建立位图对象,也能够经过加载位图资源,这种方法能够保证的是加载进来的位图对象和视频显示器有着相同的颜色组织,即与视频显示器兼容。
例如:
hBitmap=LoadBitmap(hInstance,szBitmapName);
/
这个函数的使用跟加载图标、鼠标指针同样,但值得注意的是:使用系统位图时指定系统预设的标识符会报错,须要在windows.h头文件前添加#define OEMRESOURCE。
/
全部经过LoadBitmap函数加载的位图对象,最后都应该用DeleteObject函数删除。
代码例子:
#define OEMRESOURCE #include<windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow) { static TCHAR szAppName[] = TEXT("Bricks1"); HWND hWnd;//窗口句柄 MSG mSg;//消息结构体 //建立窗口类 WNDCLASSEX wndClass; //设置窗口类各种属性 wndClass.cbSize = sizeof(WNDCLASSEX);//设置窗口类结构体大小 wndClass.cbClsExtra = 0;//窗口类尾部的一部分额外的空间 wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance;//应用程序当前的实例句柄 wndClass.hCursor = LoadCursor(NULL, IDC_HELP); wndClass.hIcon = NULL; wndClass.hIconSm = NULL; wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpfnWndProc = WndProc;//回调函数的地址(窗口消息处理程序) wndClass.lpszClassName = szAppName;//窗口类的名字,也就是窗口的标识,后面用于建立窗口函数的参数。 wndClass.lpszMenuName = NULL;//菜单的名字,没有为NULL。 wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口类的样式,它的值能够是窗口样式值的任意组合。CS_HREDRAW CS_VREDRAW,这个是垂直刷新和水平刷新,窗口尺寸改变,重画活动区域。 //注册对话框类 if (!RegisterClassEx(&wndClass)) { DWORD error_code = GetLastError(); MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL); return 0; } hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&mSg, NULL, 0, 0)) { TranslateMessage(&mSg); DispatchMessage(&mSg); } return (int)mSg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; int x, y; static int cxClient, cyClient,cxSource,cySource; static HBITMAP hBitmap; HDC hdc, hdcMem; HINSTANCE hInstance; BITMAP bitmap; switch (message) { case WM_CREATE: //获取应用程序实例句柄 hInstance = ((LPCREATESTRUCT)lParam)->hInstance; /* 加载位图资源,该位图是一个8*8的单色位图 除了加载自定义的位图资源,也能够加载系统位图资源。 例如: hBitmap = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_DNARROW));//注意,OBM开头的标识符会提示不存在,须要在windows.h头文件前添加#define OEMRESOURCE */ hBitmap = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_DNARROW)); //hBitmap = LoadBitmap(hInstance, TEXT("Bricks")); //获取加载GDI位图对象的信息 GetObject(hBitmap, sizeof(BITMAP), &bitmap); cxSource = bitmap.bmWidth; cySource = bitmap.bmHeight; return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps);//开始绘制窗口 hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hBitmap); for (y = 0; y < cyClient; y += cySource) { for (x = 0; x < cxClient; x += cxSource) { //将内存设备环境的位图对象传输到整个客户区,看上去像墙同样 BitBlt(hdc, x, y, cxSource, cySource, hdcMem, 0, 0, SRCCOPY); } } DeleteDC(hdcMem); EndPaint(hwnd, &ps); return 0; case WM_CLOSE: if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("对话框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION)) { DestroyWindow(hwnd); } else { return 0; } case WM_DESTROY: DeleteObject(hBitmap); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
由前面可知,Windows提供了一个规则:对于彩色DDB,不该该调用CreateBitmap或CreateBitmapIndirect或SetBitmapBits函数来设置位图的全部像素位,只有对单色DDB,才能够放心地设置像素位,由于单色只有0或1,谁都知道。因此,若是位图只是单色位图,那我就能够本身手动建立一个位图对象,并用一个字节数组初始化位图的全部像素位,由于像素位都是1位,1(白色)或者0(黑色)而已。
代码例子:
/
下面的代码是在客户区重复显示内存设备环境的位图,造成一个真正的砖墙啦!
/
#include<windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow) { static TCHAR szAppName[] = TEXT("Bricks2"); HWND hWnd;//窗口句柄 MSG mSg;//消息结构体 //建立窗口类 WNDCLASSEX wndClass; //设置窗口类各种属性 wndClass.cbSize = sizeof(WNDCLASSEX);//设置窗口类结构体大小 wndClass.cbClsExtra = 0;//窗口类尾部的一部分额外的空间 wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance;//应用程序当前的实例句柄 wndClass.hCursor = LoadCursor(NULL, IDC_HELP); wndClass.hIcon = NULL; wndClass.hIconSm = NULL; wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpfnWndProc = WndProc;//回调函数的地址(窗口消息处理程序) wndClass.lpszClassName = szAppName;//窗口类的名字,也就是窗口的标识,后面用于建立窗口函数的参数。 wndClass.lpszMenuName = NULL;//菜单的名字,没有为NULL。 wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口类的样式,它的值能够是窗口样式值的任意组合。CS_HREDRAW CS_VREDRAW,这个是垂直刷新和水平刷新,窗口尺寸改变,重画活动区域。 //注册对话框类 if (!RegisterClassEx(&wndClass)) { DWORD error_code = GetLastError(); MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL); return 0; } hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&mSg, NULL, 0, 0)) { TranslateMessage(&mSg); DispatchMessage(&mSg); } return (int)mSg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BITMAP bitmap = { 0, 8, 8, 2, 1, 1 }; static BYTE bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0, 0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 }; PAINTSTRUCT ps; int x, y; static int cxClient, cyClient,cxSource,cySource; static HBITMAP hBitmap; HDC hdc, hdcMem; switch (message) { case WM_CREATE: bitmap.bmBits = bits; hBitmap = CreateBitmapIndirect(&bitmap); cxSource = bitmap.bmWidth; cySource = bitmap.bmHeight; return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps);//开始绘制窗口 hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hBitmap); for (y = 0; y < cyClient; y += cySource) { for (x = 0; x < cxClient; x += cxSource) { //将内存设备环境的位图对象传输到整个客户区,看上去像墙同样 BitBlt(hdc, x, y, cxSource, cySource, hdcMem, 0, 0, SRCCOPY); } } DeleteDC(hdcMem); EndPaint(hwnd, &ps); return 0; case WM_CLOSE: if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("对话框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION)) { DestroyWindow(hwnd); } else { return 0; } case WM_DESTROY: DeleteObject(hBitmap); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
呃,这个其实没什么好说,就是将位图对象添加进画刷,而后用这个画刷绘制客户区背景而已。
代码例子:
#include<windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow) { static TCHAR szAppName[] = TEXT("Bricks2"); HWND hWnd;//窗口句柄 MSG mSg;//消息结构体 //建立窗口类 WNDCLASSEX wndClass; HBITMAP hBitmap; HBRUSH hBrush; hBitmap = LoadBitmap(hInstance, TEXT("Bricks")); hBrush = CreatePatternBrush(hBitmap); DeleteObject(hBitmap); //设置窗口类各种属性 wndClass.cbSize = sizeof(WNDCLASSEX);//设置窗口类结构体大小 wndClass.cbClsExtra = 0;//窗口类尾部的一部分额外的空间 wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance;//应用程序当前的实例句柄 wndClass.hCursor = LoadCursor(NULL, IDC_HELP); wndClass.hIcon = NULL; wndClass.hIconSm = NULL; wndClass.hbrBackground = hBrush; wndClass.lpfnWndProc = WndProc;//回调函数的地址(窗口消息处理程序) wndClass.lpszClassName = szAppName;//窗口类的名字,也就是窗口的标识,后面用于建立窗口函数的参数。 wndClass.lpszMenuName = NULL;//菜单的名字,没有为NULL。 wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口类的样式,它的值能够是窗口样式值的任意组合。CS_HREDRAW CS_VREDRAW,这个是垂直刷新和水平刷新,窗口尺寸改变,重画活动区域。 //注册对话框类 if (!RegisterClassEx(&wndClass)) { DWORD error_code = GetLastError(); MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL); return 0; } hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&mSg, NULL, 0, 0)) { TranslateMessage(&mSg); DispatchMessage(&mSg); } return (int)mSg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CLOSE: if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("对话框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION)) { DestroyWindow(hwnd); } else { return 0; } case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
先建立一个位图对象,再将位图对象选入内存设备环境,而后在内存设备环境绘图,那么本来的位图就被绘图后的位图给覆盖了。接着调用BitBlt或者StretchBlt函数将内存设备环境的位图复制进客户区设备环境。
#include<windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow) { static TCHAR szAppName[] = TEXT("HelloBit"); HWND hWnd;//窗口句柄 MSG mSg;//消息结构体 //建立窗口类 WNDCLASSEX wndClass; //设置窗口类各种属性 wndClass.cbSize = sizeof(WNDCLASSEX);//设置窗口类结构体大小 wndClass.cbClsExtra = 0;//窗口类尾部的一部分额外的空间 wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance;//应用程序当前的实例句柄 wndClass.hCursor = LoadCursor(NULL, IDC_HELP); wndClass.hIcon = NULL; wndClass.hIconSm = NULL; wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpfnWndProc = WndProc;//回调函数的地址(窗口消息处理程序) wndClass.lpszClassName = szAppName;//窗口类的名字,也就是窗口的标识,后面用于建立窗口函数的参数。 wndClass.lpszMenuName = szAppName;//菜单的名字,没有为NULL。 wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口类的样式,它的值能够是窗口样式值的任意组合。CS_HREDRAW CS_VREDRAW,这个是垂直刷新和水平刷新,窗口尺寸改变,重画活动区域。 //注册对话框类 if (!RegisterClassEx(&wndClass)) { DWORD error_code = GetLastError(); MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL); return 0; } hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&mSg, NULL, 0, 0)) { TranslateMessage(&mSg); DispatchMessage(&mSg); } return (int)mSg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HBITMAP hBitmap; static HDC hdcMem; static int cxBitmap, cyBitmap, cxClient, cyClient, iSize = ID_SIZE_BIG; static TCHAR *szText = TEXT("Hello,world!"); HDC hdc; HMENU hMenu; int x, y; PAINTSTRUCT ps; SIZE size; switch (message) { case WM_CREATE: hdc = GetDC(hwnd); hdcMem = CreateCompatibleDC(hdc); //获取文本字符串的横纵像素数 GetTextExtentPoint32(hdc, szText, lstrlen(szText), &size); cxBitmap = size.cx; cyBitmap = size.cy; //建立和szText字符串同样宽度与高度的兼容性GDI位图对象 hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap); ReleaseDC(hwnd, hdc); SelectObject(hdcMem, hBitmap); TextOut(hdcMem, 0, 0, szText, lstrlen(szText)); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_COMMAND: hMenu = GetMenu(hwnd); switch (LOWORD(wParam)) { case ID_SIZE_BIG: case ID_SIZE_SMALL: //在选择菜单项以前,初始时是选中ID_SIZE_BIG的 CheckMenuItem(hMenu, iSize, MF_UNCHECKED); iSize = LOWORD(wParam); CheckMenuItem(hMenu, iSize, MF_CHECKED); InvalidateRect(hwnd, NULL, TRUE); break; } return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps);//开始绘制窗口 switch (iSize) { case ID_SIZE_BIG: StretchBlt(hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY); break; case ID_SIZE_SMALL: for (y = 0; y < cyClient; y += cyBitmap) { for (x = 0; x < cxClient; x += cxBitmap) { //将内存设备环境的位图对象传输到整个客户区,看上去像墙同样 BitBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMem, 0, 0, SRCCOPY); } } } EndPaint(hwnd, &ps); return 0; case WM_CLOSE: if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("对话框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION)) { DestroyWindow(hwnd); } else { return 0; } case WM_DESTROY: DeleteDC(hdcMem); DeleteObject(hBitmap); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
按下鼠标左键而后拖动鼠标,就用黑色画笔绘画,按下鼠标右键而后拖动鼠标,就用白色画笔绘画。值得注意的是,咱们在代码例子里调用了SetCapture(hwnd),说明当鼠标在客户区外,也能接收到鼠标消息,咱们能够这样作个小测试,先注释掉WM_PAINT消息处理中的BitBlt(hdc, 0,0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY);,发现没有,若是按下鼠标左键并拖动鼠标,当拖动到客户区外,其实在内存设备环境里已经保存了在客户区外绘制的图,由于内存设备环境的尺寸是依据客户区放最大后的尺寸,而后咱们改变窗口大小,客户区啥都没有了,这是由于没有继续从内存设备环境复制保存的位图到客户区设备环境上。
代码例子:
#include<windows.h> #include<windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow) { static TCHAR szAppName[] = TEXT("Sketch"); HWND hWnd;//窗口句柄 MSG mSg;//消息结构体 //建立窗口类 WNDCLASSEX wndClass; //设置窗口类各种属性 wndClass.cbSize = sizeof(WNDCLASSEX);//设置窗口类结构体大小 wndClass.cbClsExtra = 0;//窗口类尾部的一部分额外的空间 wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance;//应用程序当前的实例句柄 wndClass.hCursor = LoadCursor(NULL, IDC_HELP); wndClass.hIcon = NULL; wndClass.hIconSm = NULL; wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpfnWndProc = WndProc;//回调函数的地址(窗口消息处理程序) wndClass.lpszClassName = szAppName;//窗口类的名字,也就是窗口的标识,后面用于建立窗口函数的参数。 wndClass.lpszMenuName = NULL;//菜单的名字,没有为NULL。 wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口类的样式,它的值能够是窗口样式值的任意组合。CS_HREDRAW CS_VREDRAW,这个是垂直刷新和水平刷新,窗口尺寸改变,重画活动区域。 //注册对话框类 if (!RegisterClassEx(&wndClass)) { DWORD error_code = GetLastError(); MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL); return 0; } hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&mSg, NULL, 0, 0)) { TranslateMessage(&mSg); DispatchMessage(&mSg); } return (int)mSg.wParam; } void GetLargestDisplayMode(int *pcxBitmap, int *pcyBitmap) { DEVMODE devmode; int iModeNum = 0; *pcxBitmap = *pcyBitmap = 0; ZeroMemory(&devmode, sizeof(DEVMODE)); devmode.dmSize = sizeof(DEVMODE); while (EnumDisplaySettings(NULL, iModeNum++, &devmode)) { *pcxBitmap = max(*pcxBitmap, (int)devmode.dmPelsWidth); *pcyBitmap = max(*pcyBitmap, (int)devmode.dmPelsHeight); } } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL fLeftButtonDown, fRightButtonDown; static HBITMAP hBitmap; static HDC hdcMem; static int cxBitmap, cyBitmap, cxClient, cyClient, xMouse,yMouse; HDC hdc; PAINTSTRUCT ps; switch (message) { case WM_CREATE: GetLargestDisplayMode(&cxBitmap, &cyBitmap); hdc = GetDC(hwnd); hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap); hdcMem = CreateCompatibleDC(hdc); ReleaseDC(hwnd, hdc); if (!hBitmap) { DeleteDC(hdcMem); return -1; } SelectObject(hdcMem, hBitmap); PatBlt(hdcMem, 0, 0, cxBitmap, cyBitmap, WHITENESS); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_LBUTTONDOWN: if (!fRightButtonDown) { SetCapture(hwnd); } xMouse = GET_X_LPARAM(lParam); yMouse = GET_Y_LPARAM(lParam); fLeftButtonDown = TRUE; return 0; case WM_LBUTTONUP: if (fLeftButtonDown) { SetCapture(NULL); } fLeftButtonDown = FALSE; return 0; case WM_RBUTTONDOWN: if (!fLeftButtonDown) { SetCapture(hwnd); } xMouse = GET_X_LPARAM(lParam); yMouse = GET_Y_LPARAM(lParam); fRightButtonDown = TRUE; return 0; case WM_RBUTTONUP: if (fRightButtonDown) { SetCapture(NULL); } fRightButtonDown = FALSE; return 0; case WM_MOUSEMOVE: if (!fLeftButtonDown&&!fRightButtonDown) { return 0; } hdc = GetDC(hwnd); SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN)); SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN)); MoveToEx(hdc, xMouse, yMouse, NULL); MoveToEx(hdcMem, xMouse, yMouse, NULL); xMouse = GET_X_LPARAM(lParam); yMouse = GET_Y_LPARAM(lParam); LineTo(hdc, xMouse, yMouse); LineTo(hdcMem, xMouse, yMouse); ReleaseDC(hwnd, hdc); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps);//开始绘制窗口 BitBlt(hdc, 0,0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY); EndPaint(hwnd, &ps); return 0; case WM_CLOSE: if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("对话框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION)) { DestroyWindow(hwnd); } else { return 0; } case WM_DESTROY: DeleteDC(hdcMem); DeleteObject(hBitmap); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }