(1)在内存设备环境中绘图(与真实DC不同的是,内存DC的显示表面是个位图)
(2)GetTextExtentPoint32函数:用于确定文本字符串的像素大小。(此大小就是与视频显示兼容的位图的尺寸)。
参数 |
说明 |
hdc |
设备环境句柄 |
lpString |
文本字符串,如szText |
cbString |
文本字符串中字符的个数。如lstrlen(szText) |
lpSize |
指向一个结构体,用来存放结果 |
(3)当显示器的颜色深度和大小改变时,windows会自动改变内存设备环境的彩色分辩率。也就是内存设备环境与视频设备环境仍然会保持兼容。所以在WM_DISPLAYCHANGE消息中不用去关心这个问题。
【HelloBit程序】
效果图
/*------------------------------------------------------------ HELLOBIT.C -- Bitmap Demostration (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("HelloBit"); HWND hwnd; MSG msg; WNDCLASSEX wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance, szAppName); wndclass.hIconSm = LoadIcon(hInstance, szAppName); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = szAppName; wndclass.lpszClassName = szAppName; if (!RegisterClassEx(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("HelloBit"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; static HDC hdcMem; PAINTSTRUCT ps; static int cxBitmap, cyBitmap, cxClient, cyClient, iSize = IDM_BIG; static HBITMAP hBitmap; static TCHAR* szText = TEXT("Hello,World!"); SIZE size; HMENU hMenu; switch (message) { case WM_CREATE: hdc = GetDC(hwnd); hdcMem = CreateCompatibleDC(hdc); GetTextExtentPoint32(hdc, szText, lstrlen(szText), &size); cxBitmap = size.cx; cyBitmap = size.cy; hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap); ReleaseDC(hwnd, hdc); SelectObject(hdcMem, hBitmap); TextOut(hdcMem, 0, 0, szText, lstrlen(szText)); return 0; case WM_COMMAND: hMenu = GetMenu(hwnd); switch (LOWORD(wParam)) //菜单ID { case IDM_BIG: case IDM_SMALL: CheckMenuItem(hMenu, iSize, MF_UNCHECKED); iSize = LOWORD(wParam); CheckMenuItem(hMenu, iSize, MF_CHECKED); InvalidateRect(hwnd, NULL, TRUE); break; } return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); switch (iSize) { case IDM_BIG: StretchBlt(hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY); break; case IDM_SMALL: for (int y = 0; y < cyClient; y += cyBitmap) for (int x = 0; x < cxClient; x += cxBitmap) { BitBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMem, 0, 0, SRCCOPY); } break; } EndPaint(hwnd, &ps); return 0; case WM_DESTROY: DeleteDC(hdcMem); DeleteObject(hBitmap); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by HelloBit.rc // #define IDM_BIG 40001 #define IDM_SMALL 40002 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40003 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//HelloBit.rc
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Menu // HELLOBIT MENU DISCARDABLE BEGIN POPUP "&Size" BEGIN MENUITEM "&Big", IDM_BIG, CHECKED MENUITEM "&Small", IDM_SMALL END END #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
(1)EnumDisplaySettings:该函数用来获取显示设备的参数信息,要想获取所有显示设备的所有信息,就要循环使用该函数。
参数 |
说明 |
lpszDeviceName |
设备的名称,以NULL结尾的设备名称字符串。 当NULL表示当前使用的显示设备 |
iModeNum |
①获取设备类型信息。该值可以是索引号或也可以使下值之一: ENUM_CURRENT_SETTINGS:获取当前显示设备的配置 ENUM_REGISTRY_SETTINGS:注册表中所有当前存储的显示设备的配置。 ②如果从0开始,要获得所有显示设备的物理模式,就要循环调用EnumDisplaySettings函数,直到返回返回值为FALSE。 ③当以iModeNum为0而调用函数时,操作系统将初始化和填充相关显示设备信息。 ④当以iModeNum为非0而调用函数时,操作系统将返回最后一次填充的信息。 |
lpDevMode |
该参数是一个结构体用来接收物理模式的信息参数,在使用此结构体前要先初始化该结构体。函数会获取到以下五个字段的值:dmBitsPerpel、dmPelsWidth、dmPelsHeight(设备宽度和高度,以像素为单位,不会随分辨率而改变,可以用来测量显示器的大小!)、dmDisplayFlags、dmDisplayFrequency。 |
(2)【Sketch程序】——先将图绘到内存设备,再绘到显示设备的DC环境。
效果图
/*------------------------------------------------------------ SKETCH.C -- Shadow Bitmap Demonstration (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("Sketch"); HWND hwnd; MSG msg; WNDCLASSEX wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance, szAppName); wndclass.hIconSm = LoadIcon(hInstance, szAppName); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClassEx(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("Sketch"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters //因为在CreateWindow会发送WM_CREATE,在WM_CREATE消息中会创建一个位图 //如果位图太大,会导致创建失败,WM_CREATE会返回-1,如下处理这种情况. if (hwnd == NULL) { MessageBox(NULL, TEXT("Not enough memory to create bitmap!"), szAppName, MB_ICONERROR); return 0; } ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //获得最大可显示的位图 void GetLargestDisplayMode(int* pcxBitmap, int* pcyBitmap) { DEVMODE devmode; int iModeNum = 0; *pcxBitmap = *pcyBitmap = 0; ZeroMemory(&devmode, sizeof(DEVMODE)); devmode.dmSize = sizeof(DEVMODE); //这个要记得设置! //EnumDisplaySettins第1个参数为NULL,表示当前正在使用的显示设备,可以不止一个。 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: hdc = GetDC(hwnd); GetLargestDisplayMode(&cxBitmap, &cyBitmap); hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap); hdcMem = CreateCompatibleDC(hdc); ReleaseDC(hwnd, hdc); 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 = LOWORD(lParam); yMouse = HIWORD(lParam); fLeftButtonDown = TRUE; return 0; case WM_LBUTTONUP: if (fLeftButtonDown) //SetCapture(NULL); ReleaseCapture();//课本用SetCapture(NULL); fLeftButtonDown = FALSE; return 0; //右键用来擦图(用白色绘图,等于在擦图) case WM_RBUTTONDOWN: if (!fLeftButtonDown) //因为当左键按下时,说明正在用白色擦图,此时鼠标是被捕捉的 SetCapture(hwnd); xMouse = LOWORD(lParam); yMouse = HIWORD(lParam); fRightButtonDown = TRUE; return 0; case WM_RBUTTONUP: if (fRightButtonDown) //SetCapture(NULL); ReleaseCapture(); fRightButtonDown = FALSE; return 0; case WM_MOUSEMOVE: //己知问题:当鼠标在客户区内按下一个键时并拖动绘图,到客户区外依次释放两个键, //重回客户区时,这里鼠标己经松开,但仍会画图——原因是当客户区外释放时,就ReleaseCapture //了,导致其中一个鼠标按键的ButtonUp消息收不到,即仍处于fButtonDown状态,从而下列的判断会通过, //所以仍会执行后面的代码 if (!fLeftButtonDown && !fRightButtonDown) //左右键都没按下时,退出 return 0; hdc = GetDC(hwnd); //左键按下时,选黑色画笔,右键时白色画笔。同时按下时仍然是黑色的画笔 SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN)); SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN)); //同时在屏幕dc与内存dc中绘图 MoveToEx(hdc, xMouse, yMouse, NULL); MoveToEx(hdcMem, xMouse, yMouse, NULL); xMouse = LOWORD(lParam); yMouse = HIWORD(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_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
14.4.9 在菜单中使用位图
(1)GetMenuItemInfo(hMenu,uMenuItem,fByPosition,lpmii);
①uMenuItem:菜单ID或位置索引(看fByPositon是否是TRUE)
②lpmii,指一个MENUITEMINFO结构体
MENUITEMINFO结构体 |
|
字段 |
说明 |
cbSize |
结构的大小(字节) |
fMask |
MIIM_CHECKMARKS:获取或设置hbmpChecked和hbmpUnchecked成员 MIIM_DATA:获取或设置dwItemData成员 MIIM_ID :获取或设置wID成员 MIIM_STATE: 获取或设置fState成员 MIIM_SUBMENU: 获取或设置hSubMenu成员 MIIM_TYPE :获取或设置fType和dwTypeData成员 |
fType |
菜单项类型 MFT_BITMAP:使用一个位图显示菜单项.dwTypeData低位字是该位图的句柄.并且cch被忽视 MFT_MENUBARBREAK:放置菜单项在新行上(适用于菜单栏)或在新列内 MFT_SEPARATOR:指定那个菜单项是一个分隔条 MFT_STRING:用一个文本字符串显示菜单项.dwTypeData成员指示一个以NULL结尾的字符串 MFT_RADIOCHECK:如果hbmpChecked成员是NULL,显示选中的菜单项使用一个单选按钮来代替一个复选标记 |
fState |
菜单项的状态 MFS_CHECKED:复选的菜单项.至于更多关于菜单项选中的信息,看hbmpChecked成员 MFS_DISABLED:菜单项无效并变灰使得它不能被选择.等效于MFS_GRAYED MFS_ENABLED: 激活菜单项使它可以被选择 MFS_GRAYED:菜单项无效并变灰使得它不能被选择.等效于MFS_DISABLED MFS_HILITE:菜单项高亮显示 MFS_UNCHECKED:取消复选菜单项 MFS_UNHILITE:移除菜单项的高亮显示,这是默认状态 |
wID |
菜单项ID, 只有在设置了fMask的MIIM_ID时才能使用 |
hSubMenu |
菜单项相关联的下拉菜单或子菜单的的句柄。如果菜单项不是一个打开的下拉菜单或子菜单,那这个成员是NULL, 该项只有在设置了fMask的MIIM_SUBMENU时才能使用 |
hbmpChecked |
菜单项被选中时显示在一侧的位图的句柄。 |
hbmpUnchecked |
菜单项没有被选中时显示在一侧的位图的句柄 |
dwItemData |
应用程序定义的菜单项相关联的值,该项只有在设置了fMask的MIIM_DATA时才能使用 |
dwTypeData |
菜单项的内容,它的具体意义依赖于fTYPE值,并且它只能在fMask设置了MIIM_TYPE标记时才能被使用; 要获取一个MFT_STRING类型的菜单项,首先要得到该字符串的大小,通过设置MENUITEMINFO结构的dwTypeData值为空并调用函数GetMenuItemInfo得到的cch值就是字符串的大小,然后分配一个字符串大小的缓冲区,把指向缓冲区的指针存赋给dwTypeData并再次调用GetMenuItemInfo函数用字符串来填充缓冲区。 如果获取其它类型的菜单项,GetMenuItemInfo函数会赋给dwTypeData一个类型由fType成员指定的值。当使用SetMenuItemInfo函数时,dwTypeData必须包含一个类型由fType成员指定的值,该项只有在设置了fMask成员的MIIM_STRING标记时才能使用。 |
cch |
当检索一个MFT_STRING类型菜单项的信息时,为菜单项文本(TCHAR)的长度 |
hbmpItem |
菜单项上显示位图的句柄,它可能是以下标记中的一个,该项只有在设置了fMask成员的MIIM_BITMAP标记时才能使用。 HBMMENU_CALLBACK:一个由拥有该菜单的窗口绘制的位图 HBMMENU_MBAR_CLOSE:菜单栏的关闭按钮 HBMMENU_MBAR_MINIMIZE:菜单栏的最小化按钮 HBMMENU_MBAR_RESTORE:菜单栏的还原按钮 …… |
(2)使用位图做菜单项时的注意事项
①Windows会调整菜单栏高度来适应最高的位图,其他位图(包括文字)和菜单上沿顶端对齐。
②如果把一个位图放入顶级菜单,那么SM_CYMENU调用和GetSystemMetrics得到的菜单高度值不同。
③当菜单包含文本时,Windows会自动添加键盘接口。但一旦加入位图,就没有键盘接口了。可以使用WM_MENUCHAR消息,在按下Alt键以及没有映射到任何菜单项的某个字母键时,Windows会向应用程序发送一个WM_MENUCHAR消息。wParam为被按下键的ASCII码。如果和某个菜单项对应,就要向Windows返回一个双字(即return的返回值),并将高位字设为2(即MNC_EXECUTE),将低位字设成对应菜单项的索引。Windows会向窗口发送WM_COMMAND消息。
【GrafMenu程序】
效果图
/*------------------------------------------------------------ SKETCH.C -- Shadow Bitmap Demonstration (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("Sketch"); HWND hwnd; MSG msg; WNDCLASSEX wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance, szAppName); wndclass.hIconSm = LoadIcon(hInstance, szAppName); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClassEx(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("Sketch"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters //因为在CreateWindow会发送WM_CREATE,在WM_CREATE消息中会创建一个位图 //如果位图太大,会导致创建失败,WM_CREATE会返回-1,如下处理这种情况. if (hwnd == NULL) { MessageBox(NULL, TEXT("Not enough memory to create bitmap!"), szAppName, MB_ICONERROR); return 0; } ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //获得最大可显示的位图 void GetLargestDisplayMode(int* pcxBitmap, int* pcyBitmap) { DEVMODE devmode; int iModeNum = 0; *pcxBitmap = *pcyBitmap = 0; ZeroMemory(&devmode, sizeof(DEVMODE)); devmode.dmSize = sizeof(DEVMODE); //这个要记得设置! //EnumDisplaySettins第1个参数为NULL,表示当前正在使用的显示设备,可以不止一个。 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: hdc = GetDC(hwnd); GetLargestDisplayMode(&cxBitmap, &cyBitmap); hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap); hdcMem = CreateCompatibleDC(hdc); ReleaseDC(hwnd, hdc); 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 = LOWORD(lParam); yMouse = HIWORD(lParam); fLeftButtonDown = TRUE; return 0; case WM_LBUTTONUP: if (fLeftButtonDown) //SetCapture(NULL); ReleaseCapture();//课本用SetCapture(NULL); fLeftButtonDown = FALSE; return 0; //右键用来擦图(用白色绘图,等于在擦图) case WM_RBUTTONDOWN: if (!fLeftButtonDown) //因为当左键按下时,说明正在用白色擦图,此时鼠标是被捕捉的 SetCapture(hwnd); xMouse = LOWORD(lParam); yMouse = HIWORD(lParam); fRightButtonDown = TRUE; return 0; case WM_RBUTTONUP: if (fRightButtonDown) //SetCapture(NULL); ReleaseCapture(); fRightButtonDown = FALSE; return 0; case WM_MOUSEMOVE: //己知问题:当鼠标在客户区内按下一个键时并拖动绘图,到客户区外依次释放两个键, //重回客户区时,这里鼠标己经松开,但仍会画图——原因是当客户区外释放时,就ReleaseCapture //了,导致其中一个鼠标按键的ButtonUp消息收不到,即仍处于fButtonDown状态,从而下列的判断会通过, //所以仍会执行后面的代码 if (!fLeftButtonDown && !fRightButtonDown) //左右键都没按下时,退出 return 0; hdc = GetDC(hwnd); //左键按下时,选黑色画笔,右键时白色画笔。同时按下时仍然是黑色的画笔 SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN)); SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN)); //同时在屏幕dc与内存dc中绘图 MoveToEx(hdc, xMouse, yMouse, NULL); MoveToEx(hdcMem, xMouse, yMouse, NULL); xMouse = LOWORD(lParam); yMouse = HIWORD(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_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by GrafMenu.rc // #define IDM_FONT_COUR 101 #define IDM_FONT_ARIAL 102 #define IDM_FONT_TIMES 103 #define IDM_HELP 104 #define IDM_EDIT_UNDO 40005 #define IDM_EDIT_CUT 40006 #define IDM_EDIT_COPY 40007 #define IDM_EDIT_PASTE 40008 #define IDM_EDIT_CLEAR 40009 #define IDM_FILE_NEW 40010 #define IDM_FILE_OPEN 40011 #define IDM_FILE_SAVE 40012 #define IDM_FILE_SAVE_AS 40013 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 108 #define _APS_NEXT_COMMAND_VALUE 40014 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 105 #endif #endif
//GrafMenu.rc
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Menu // MENUFILE MENU DISCARDABLE BEGIN MENUITEM "&New", IDM_FILE_NEW MENUITEM "&Open...", IDM_FILE_OPEN MENUITEM "&Save", IDM_FILE_SAVE MENUITEM "Save &As...", IDM_FILE_SAVE_AS END MENUEDIT MENU DISCARDABLE BEGIN MENUITEM "&Undo", IDM_EDIT_UNDO MENUITEM SEPARATOR MENUITEM "Cu&t", IDM_EDIT_CUT MENUITEM "&Copy", IDM_EDIT_COPY MENUITEM "&Paste", IDM_EDIT_PASTE MENUITEM "De&lete", IDM_EDIT_CLEAR END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // BITMAPFONT BITMAP DISCARDABLE "Fontlabl.bmp" BITMAPHELP BITMAP DISCARDABLE "Bighelp.bmp" BITMAPEDIT BITMAP DISCARDABLE "Editlabl.bmp" BITMAPFILE BITMAP DISCARDABLE "Filelabl.bmp" #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED