Windows程序设计
一、起步
1.1.第一个Windows程序
main.c
代码语言:javascript复制#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
MessageBox(NULL,TEXT("Hello,Windows 98!"), TEXT("HelloMsg"), 0);
return 0;
}
说明:
- WINAPI:#define WINAPI __stdcall
- 第一个参数:实例句柄
- 第二个参数:通常总是为NULL(定义为0)
- 第三个参数:用来运行程序的命令行
- 第四个参数:用来指明程序最初如何显示:正常显示或最大化到全屏
1.2.MessageBox函数
说明:
- 第一个参数:通常是一个窗口句柄
- 第二个参数:信息框的内容
- 第三个参数:标题栏的内容
- 第四个参数:按钮类型
二、Unicode简介
2.1.宽字符和C语言
char数据类型
代码语言:javascript复制char c = 'A'; //1个字节 0x41
char * p; //4个字节
char a[] = "Hello!" //7个字节
宽字符
代码语言:javascript复制//typedef unsigned short wchar_t
wchar_t c = 'A'; //2个字节 0x0041
wchar_t * p; //4个字节
wchar_t a[] = L"Hello!" //14个字节
三、窗口与消息
3.1.HELLOWIN程序
main.c
代码语言:javascript复制#pragma comment(lib, "winmm")
#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("HelloWin");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
//水平尺寸和垂直尺寸
wndclass.style = CS_HREDRAW | CS_VREDRAW;
//WndProc窗口类的窗口过程函数:处理传递给所有基于该窗口类创建的窗口的所有消息
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
//应用程序的实例句柄
wndclass.hInstance = hInstance;
//加载图标
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
//加载鼠标光标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
//指定背景色
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
//窗口类的菜单
wndclass.lpszMenuName = NULL;
//窗口类名称
wndclass.lpszClassName = szAppName;
//为应用程序的窗口注册一个窗口类
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("this program requires Windows NT!"),szAppName,MB_ICONERROR);
return 0;
}
//基于窗口类创建一个窗口
hwnd = CreateWindow(szAppName, // 窗口类名称
TEXT("The Hello Program"), // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口风格
CW_USEDEFAULT, // 初始x坐标
CW_USEDEFAULT, // 初始y坐标
CW_USEDEFAULT, // 初始x方向尺寸
CW_USEDEFAULT, // 初始y方向尺寸
NULL, // 父窗口句柄
NULL, // 窗口菜单句柄
hInstance, // 程序实例句柄
NULL); // 创建参数
//在屏幕中显示窗口
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;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
//播放声音文件
PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); //表明窗口绘制开始
GetClientRect(hwnd, &rect); //获取窗口客户区的尺寸
//显示一个文本字符串
DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//结束窗口绘制
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
//将“退出”消息插入消息队列
PostQuitMessage(0);
return 0;
}
//执行默认的消息处理
return DefWindowProc(hwnd, message, wParam, lParam);
}
3.2.通盘考虑
1.大写标识符
标识符其实都是数值常量,前缀表明常量所属的一般类别
前缀 | 常量 |
---|---|
CS | 类风格选项 |
CW | 创建窗口选项 |
DT | 文本绘制选项 |
IDI | 图标的ID号 |
IDC | 光标的ID号 |
MB | 消息框选项 |
SND | 声音选项 |
WM | 窗口消息 |
WS | 窗口风格 |
2.新数据类型
- WPARAM:等价于UINT
- LPARAM:等价于LONG
- LRESULT:等价于LONG
3.四种数据结构
结构 | 含义 |
---|---|
MSG | 消息结构 |
WNDCLASS | 窗口类结构 |
PAINTSTRUCT | 绘制结构 |
RECT | 矩形结构 |
4.理解句柄
标识符 | 含义 |
---|---|
HINSTANCE | 实例句柄---程序本身 |
HWND | 窗口句柄 |
HDC | 设备环境句柄 |
5.匈牙利标记法
即变量名以表明该变量数据类型的小写字母开始,例如
- szCmdline的前缀sz表示“以零结束的字符串”
- hInstance的前缀h表示“句柄”
- iCmdShow的前缀i表示“整型”
变量名前缀如下
前缀 | 数据类型 |
---|---|
c | char或 WCHAR 或 TCHAR |
by | byte |
n | short |
i | int |
x,y | int,表示x坐标和y坐标 |
cx,cy | int, 表示x或y的长度, c表示count(计数) |
b或f | BOOL(int):f表示flag |
w | WORD(无符号短整数) |
l | LONG(长整数) |
dw | DWORD(无符号长整数) |
fn | 函数 |
s | 字符串 |
sz | 以零结束的字符串 |
h | 句柄 |
p | 指针 |
四、文本输出
4.1.获取设备环境
获取设备环境方法1:BeginPain和EndPaint函数
将原来的无效区域有效化
代码语言:javascript复制WINUSERAPI HDC WINAPI BeginPaint(
_In_ HWND hWnd,
_Out_ LPPAINTSTRUCT lpPaint);
两个参数
- 参数1:窗口的句柄
- 参数2:是一个类型位PAINTSTRUCT结构的变量的地址
获取设备环境方法2:GetDC
与从BeginPaint函数返回的设备句柄不同,从GetDC返回的设备句柄中的裁剪区域是整个客户区,而不仅仅
是在无效矩形里
代码语言:javascript复制hdc = GetDC(hwnd);
ReleaseDC(hwnd, hdc);
4.2.TEXTOUT函数详解
代码语言:javascript复制TextOut(hdc, x, y, psText, iLength);
说明:
- hdc:设备环境句柄,决定文本显示的特性
- x:相对于客户区的左上角。从左往右增大
- y:相对于客户区的左上角。从上往下增大
- psText:指向字符串的指针
- iLength:字符串的长度
4.3.字符大小
通过调用GetSystemMetrics函数来获取用户界面的尺寸。
通过调用GetTextMetrics函数,可以获取字体尺寸。GetTextMetrics函数需要一个设备环境句柄,因为它会返回该设备环境当前选定的字体的信息。windows将把字符尺寸的各种值复制到类型为TEXTMETRIC的结构中。
该结构体共有20个字段,我们进关心其中的前7个字段
代码语言:javascript复制typedef struct tagTEXTMETRICW
{
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading;
LONG tmExternalLeading;
LONG tmAveCharWidth;
LONG tmMaxCharWidth;
.
.
.
} TEXTMETRICW, *PTEXTMETRICW, NEAR *NPTEXTMETRICW, FAR *LPTEXTMETRICW;
- tmHeight:它是tmAscent和tmDescent的和。这两个值分别是字符在基线智商和之下的最大高度。
- tmInternalLeading(内部间距):包含在tmAscent中(也就是包含在tmHeight中),tmInternalLeading可以设置为0
- tmExternalLeading:不包含在tmHeight的值里,这个值在本书中使用的系统字体通常为0
- tmAveCharWidth和tmMaxCharWidth两个字段描述了字符的宽度,tmAveCharWidth是小写字符的加权平均宽度。tmMaxCharWidth是字体中最宽的字符的宽度。在等宽字体中,这两个值是一样的。在变宽字体中tmMaxCharWidth是tmAveCharWidth的1.5倍。
4.4.滚动条
GetScrollInfo
代码语言:javascript复制WINUSERAPI BOOL WINAPI GetScrollInfo(
_In_ HWND hwnd,
_In_ int nBar,
_Inout_ LPSCROLLINFO lpsi);
SetScrollInfo
代码语言:javascript复制WINUSERAPI int WINAPI SetScrollInfo(
_In_ HWND hwnd,
_In_ int nBar,
_In_ LPCSCROLLINFO lpsi,
_In_ BOOL redraw);
- 参数1:窗口句柄
- 参数2:SB_VERT或 SB_HORZ
- 参数4:可以是TRUE或FALSE,表示是否希望windows根据新的消息重绘滚动条
参数3是一个SCROLLINFO结构体
代码语言:javascript复制typedef struct tagSCROLLINFO
{
UINT cbSize; //设为sizeof(SCROLLINFO)
UINT fMask; //要设置或获取的值
int nMin; //范围的最小值
int nMax; //范围的最大值
UINT nPage; //页面大小
int nPos; //当前位置
int nTrackPos; //当前追踪位置
} SCROLLINFO, FAR *LPSCROLLINFO;
typedef SCROLLINFO CONST FAR *LPCSCROLLINFO;
4.5.综合使用
SysMets3.h
代码语言:javascript复制/*-----------------------------------------------
SYSMETS.H -- System metrics display structure
-----------------------------------------------*/
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
struct
{
int iIndex;
TCHAR* szLabel;
TCHAR* szDesc;
}
sysmetrics[] =
{
SM_CXSCREEN, TEXT("SM_CXSCREEN"),
TEXT("Screen width in pixels"),
SM_CYSCREEN, TEXT("SM_CYSCREEN"),
TEXT("Screen height in pixels"),
SM_CXVSCROLL, TEXT("SM_CXVSCROLL"),
TEXT("Vertical scroll width"),
SM_CYHSCROLL, TEXT("SM_CYHSCROLL"),
TEXT("Horizontal scroll height"),
SM_CYCAPTION, TEXT("SM_CYCAPTION"),
TEXT("Caption bar height"),
SM_CXBORDER, TEXT("SM_CXBORDER"),
TEXT("Window border width"),
SM_CYBORDER, TEXT("SM_CYBORDER"),
TEXT("Window border height"),
SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"),
TEXT("Dialog window frame width"),
SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"),
TEXT("Dialog window frame height"),
SM_CYVTHUMB, TEXT("SM_CYVTHUMB"),
TEXT("Vertical scroll thumb height"),
SM_CXHTHUMB, TEXT("SM_CXHTHUMB"),
TEXT("Horizontal scroll thumb width"),
SM_CXICON, TEXT("SM_CXICON"),
TEXT("Icon width"),
SM_CYICON, TEXT("SM_CYICON"),
TEXT("Icon height"),
SM_CXCURSOR, TEXT("SM_CXCURSOR"),
TEXT("Cursor width"),
SM_CYCURSOR, TEXT("SM_CYCURSOR"),
TEXT("Cursor height"),
SM_CYMENU, TEXT("SM_CYMENU"),
TEXT("Menu bar height"),
SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"),
TEXT("Full screen client area width"),
SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"),
TEXT("Full screen client area height"),
SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"),
TEXT("Kanji window height"),
SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"),
TEXT("Mouse present flag"),
SM_CYVSCROLL, TEXT("SM_CYVSCROLL"),
TEXT("Vertical scroll arrow height"),
SM_CXHSCROLL, TEXT("SM_CXHSCROLL"),
TEXT("Horizontal scroll arrow width"),
SM_DEBUG, TEXT("SM_DEBUG"),
TEXT("Debug version flag"),
SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"),
TEXT("Mouse buttons swapped flag"),
SM_CXMIN, TEXT("SM_CXMIN"),
TEXT("Minimum window width"),
SM_CYMIN, TEXT("SM_CYMIN"),
TEXT("Minimum window height"),
SM_CXSIZE, TEXT("SM_CXSIZE"),
TEXT("Min/Max/Close button width"),
SM_CYSIZE, TEXT("SM_CYSIZE"),
TEXT("Min/Max/Close button height"),
SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"),
TEXT("Window sizing frame width"),
SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"),
TEXT("Window sizing frame height"),
SM_CXMINTRACK, TEXT("SM_CXMINTRACK"),
TEXT("Minimum window tracking width"),
SM_CYMINTRACK, TEXT("SM_CYMINTRACK"),
TEXT("Minimum window tracking height"),
SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"),
TEXT("Double click x tolerance"),
SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"),
TEXT("Double click y tolerance"),
SM_CXICONSPACING, TEXT("SM_CXICONSPACING"),
TEXT("Horizontal icon spacing"),
SM_CYICONSPACING, TEXT("SM_CYICONSPACING"),
TEXT("Vertical icon spacing"),
SM_MENUDROPALIGNMENT, TEXT("SM_MENUDROPALIGNMENT"),
TEXT("Left or right menu drop"),
SM_PENWINDOWS, TEXT("SM_PENWINDOWS"),
TEXT("Pen extensions installed"),
SM_DBCSENABLED, TEXT("SM_DBCSENABLED"),
TEXT("Double-Byte Char Set enabled"),
SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"),
TEXT("Number of mouse buttons"),
SM_SECURE, TEXT("SM_SECURE"),
TEXT("Security present flag"),
SM_CXEDGE, TEXT("SM_CXEDGE"),
TEXT("3-D border width"),
SM_CYEDGE, TEXT("SM_CYEDGE"),
TEXT("3-D border height"),
SM_CXMINSPACING, TEXT("SM_CXMINSPACING"),
TEXT("Minimized window spacing width"),
SM_CYMINSPACING, TEXT("SM_CYMINSPACING"),
TEXT("Minimized window spacing height"),
SM_CXSMICON, TEXT("SM_CXSMICON"),
TEXT("Small icon width"),
SM_CYSMICON, TEXT("SM_CYSMICON"),
TEXT("Small icon height"),
SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"),
TEXT("Small caption height"),
SM_CXSMSIZE, TEXT("SM_CXSMSIZE"),
TEXT("Small caption button width"),
SM_CYSMSIZE, TEXT("SM_CYSMSIZE"),
TEXT("Small caption button height"),
SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"),
TEXT("Menu bar button width"),
SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"),
TEXT("Menu bar button height"),
SM_ARRANGE, TEXT("SM_ARRANGE"),
TEXT("How minimized windows arranged"),
SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"),
TEXT("Minimized window width"),
SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"),
TEXT("Minimized window height"),
SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"),
TEXT("Maximum draggable width"),
SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"),
TEXT("Maximum draggable height"),
SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"),
TEXT("Width of maximized window"),
SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"),
TEXT("Height of maximized window"),
SM_NETWORK, TEXT("SM_NETWORK"),
TEXT("Network present flag"),
SM_CLEANBOOT, TEXT("SM_CLEANBOOT"),
TEXT("How system was booted"),
SM_CXDRAG, TEXT("SM_CXDRAG"),
TEXT("Avoid drag x tolerance"),
SM_CYDRAG, TEXT("SM_CYDRAG"),
TEXT("Avoid drag y tolerance"),
SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"),
TEXT("Present sounds visually"),
SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"),
TEXT("Menu check-mark width"),
SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"),
TEXT("Menu check-mark height"),
SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"),
TEXT("Slow processor flag"),
SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"),
TEXT("Hebrew and Arabic enabled flag"),
SM_MOUSEWHEELPRESENT, TEXT("SM_MOUSEWHEELPRESENT"),
TEXT("Mouse wheel present flag"),
SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"),
TEXT("Virtual screen x origin"),
SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"),
TEXT("Virtual screen y origin"),
SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"),
TEXT("Virtual screen width"),
SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"),
TEXT("Virtual screen height"),
SM_CMONITORS, TEXT("SM_CMONITORS"),
TEXT("Number of monitors"),
SM_SAMEDISPLAYFORMAT, TEXT("SM_SAMEDISPLAYFORMAT"),
TEXT("Same color format flag")
};
SysMets3.c
代码语言:javascript复制#define WINVER 0x0500
#include <windows.h>
#include "sysmets.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("SysMets3");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("Program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
TEXT("Get System Metrics No. 3"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, //添加水平和垂直滚动条
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth;
HDC hdc;
int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;
PAINTSTRUCT ps;
SCROLLINFO si;
TCHAR szBuffer[10];
TEXTMETRIC tm;
switch (message)
{
case WM_CREATE:
//获取窗口的设备环境句柄
hdc = GetDC(hwnd);
//获取默认系统字体的尺寸
GetTextMetrics(hdc, &tm);
//获取平均字符宽度
cxChar = tm.tmAveCharWidth;
//tmPitchAndFamily字段的低位决定字体是否为等宽字体:1表示变宽字体,0表示等宽字体
//大写字符的平均宽度。在等宽字体中,cxCaps = cyChar。在变宽字体中,cxCaps = cyChar * 1.5
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
//获取字符的总高度
cyChar = tm.tmHeight tm.tmExternalLeading;
//释放窗口的设备环境句柄
ReleaseDC(hwnd, hdc);
// 保存三列的总宽度
iMaxWidth = 40 * cxChar 22 * cxCaps;
return 0;
//窗口大小发生变化时
case WM_SIZE:
//lParam变量的低位是客户区的宽度
cxClient = LOWORD(lParam);
//lParam变量的高位是客户区的高度
cyClient = HIWORD(lParam);
// Set vertical scroll bar range and page size
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = NUMLINES - 1;
si.nPage = cyClient / cyChar;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
// Set horizontal scroll bar range and page size
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = 2 iMaxWidth / cxChar;
si.nPage = cxClient / cxChar;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
return 0;
//垂直滚动
case WM_VSCROLL:
// Get all the vertial scroll bar information
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
// Save the position for comparison later on
iVertPos = si.nPos;
//wParam参数的低位代表鼠标在滚动条上的动作,这个值被称为“通知码”
switch (LOWORD(wParam))
{
case SB_TOP:
si.nPos = si.nMin;
break;
case SB_BOTTOM:
si.nPos = si.nMax;
break;
case SB_LINEUP:
si.nPos -= 1;
break;
case SB_LINEDOWN:
si.nPos = 1;
break;
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
case SB_PAGEDOWN:
si.nPos = si.nPage;
break;
//SB_THUMBTRACK通知码:当用户拖动滚动条的滑块时,程序会相应的滚动窗口的内容
case SB_THUMBTRACK:
//返回当前滑块的位置
si.nPos = si.nTrackPos;
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
// If the position has changed, scroll the window and update it
if (si.nPos != iVertPos)
{
ScrollWindow(hwnd, 0, cyChar * (iVertPos - si.nPos),
NULL, NULL);
UpdateWindow(hwnd);
}
return 0;
//水平滚动
case WM_HSCROLL:
// Get all the vertial scroll bar information
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
// Save the position for comparison later on
GetScrollInfo(hwnd, SB_HORZ, &si);
iHorzPos = si.nPos;
switch (LOWORD(wParam))
{
case SB_LINELEFT:
si.nPos -= 1;
break;
case SB_LINERIGHT:
si.nPos = 1;
break;
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
case SB_PAGERIGHT:
si.nPos = si.nPage;
break;
//SB_THUMBPOSITION通知码:当用户拖动滚动条的滑块,程序只有在用户松开了鼠标键时才会滚动窗口的内容
case SB_THUMBPOSITION:
si.nPos = si.nTrackPos; //返回当前滑块的位置
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
GetScrollInfo(hwnd, SB_HORZ, &si);
// If the position has changed, scroll the window
if (si.nPos != iHorzPos)
{
ScrollWindow(hwnd, cxChar * (iHorzPos - si.nPos), 0,
NULL, NULL);
}
return 0;
case WM_PAINT:
//获取设备句柄
hdc = BeginPaint(hwnd, &ps);
// Get vertical scroll bar position
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_VERT, &si);
iVertPos = si.nPos;
// Get horizontal scroll bar position
GetScrollInfo(hwnd, SB_HORZ, &si);
iHorzPos = si.nPos;
// Find painting limits
iPaintBeg = max(0, iVertPos ps.rcPaint.top / cyChar);
iPaintEnd = min(NUMLINES - 1,
iVertPos ps.rcPaint.bottom / cyChar);
for (i = iPaintBeg; i <= iPaintEnd; i )
{
x = cxChar * (1 - iHorzPos);
y = cyChar * (i - iVertPos);
TextOut(hdc, x, y,
sysmetrics[i].szLabel, //sysmetrics结构体的szLabel字段
lstrlen(sysmetrics[i].szLabel)); //长度
TextOut(hdc, x 22 * cxCaps, y,
sysmetrics[i].szDesc,
lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc, TA_RIGHT | TA_TOP); //设置为右上对齐
TextOut(hdc, x 22 * cxCaps 40 * cxChar, y, szBuffer,
wsprintf(szBuffer, TEXT("]"),
GetSystemMetrics(sysmetrics[i].iIndex)));
SetTextAlign(hdc, TA_LEFT | TA_TOP); //设置为默认左上对齐
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
五、绘图基础
5.1.获取设备环境句柄
1.在处理WM_PAINT消息时使用BeginPaint函数和EndPaint函数
代码语言:javascript复制hdc = BeginPaint(hwnd, &ps);
(other program lines)
EndPaint(hwnd, &ps);
ps是一个类型为PAINTSTRUCT的结构,PAINTSTRUCT结构还包含一个名为rcPaint的矩形结构,该结构定义了一个保卫窗口客户区无效范围的矩形。使用BeginPaint函数获取的设备环境句柄,就只能在这个矩形内绘图,调用BeginPaint函数将使这个区域有效。
2.设备环境句柄还可以在处理非WM_PAINT消息时由windows程序获取
代码语言:javascript复制hdc = GetDC(hwnd);
(other program lines)
ReleaseDC(hwnd, &hdc);
其中,设备环境句柄指的是窗口句柄为hwnd的窗口客户区,从GetDC函数返回的句柄可以在整个客户区绘制,并且GetDC和ReleaseDC函数并不使任何客户区的无效区域变为有效。
3、windows程序还可以获得用于整个窗口的,而不仅仅是窗口客户区的设备环境句柄
代码语言:javascript复制hdc = GetWindowDC(hwnd);
(other program lines)
ReleaseDC(hwnd, &hdc);
这里的设备环境句柄除了包括客户区,还包含窗口标题栏、菜单、滚动条和客户区的外框。
5.2.获取设备环境信息
GetDeviceCaps可以获取设备的信息
DevCaps1.c
代码语言:javascript复制/*---------------------------------------------------------
DEVCAPS1.C -- Device Capabilities Display Program No. 1
(c) Charles Petzold, 1998
---------------------------------------------------------*/
#include <windows.h>
#define NUMLINES ((int) (sizeof devcaps / sizeof devcaps [0]))
struct
{
int iIndex;
TCHAR* szLabel;
TCHAR* szDesc;
}
devcaps[] =
{
HORZSIZE, TEXT("HORZSIZE"), TEXT("Width in millimeters:"),
VERTSIZE, TEXT("VERTSIZE"), TEXT("Height in millimeters:"),
HORZRES, TEXT("HORZRES"), TEXT("Width in pixels:"),
VERTRES, TEXT("VERTRES"), TEXT("Height in raster lines:"),
BITSPIXEL, TEXT("BITSPIXEL"), TEXT("Color bits per pixel:"),
PLANES, TEXT("PLANES"), TEXT("Number of color planes:"),
NUMBRUSHES, TEXT("NUMBRUSHES"), TEXT("Number of device brushes:"),
NUMPENS, TEXT("NUMPENS"), TEXT("Number of device pens:"),
NUMMARKERS, TEXT("NUMMARKERS"), TEXT("Number of device markers:"),
NUMFONTS, TEXT("NUMFONTS"), TEXT("Number of device fonts:"),
NUMCOLORS, TEXT("NUMCOLORS"), TEXT("Number of device colors:"),
PDEVICESIZE, TEXT("PDEVICESIZE"), TEXT("Size of device structure:"),
ASPECTX, TEXT("ASPECTX"), TEXT("Relative width of pixel:"),
ASPECTY, TEXT("ASPECTY"), TEXT("Relative height of pixel:"),
ASPECTXY, TEXT("ASPECTXY"), TEXT("Relative diagonal of pixel:"),
LOGPIXELSX, TEXT("LOGPIXELSX"), TEXT("Horizontal dots per inch:"),
LOGPIXELSY, TEXT("LOGPIXELSY"), TEXT("Vertical dots per inch:"),
SIZEPALETTE, TEXT("SIZEPALETTE"), TEXT("Number of palette entries:"),
NUMRESERVED, TEXT("NUMRESERVED"), TEXT("Reserved palette entries:"),
COLORRES, TEXT("COLORRES"), TEXT("Actual color resolution:")
};
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("DevCaps1");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Device Capabilities"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
static int cxChar, cxCaps, cyChar;
TCHAR szBuffer[10];
HDC hdc;
int i;
PAINTSTRUCT ps;
TEXTMETRIC tm;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
cyChar = tm.tmHeight tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for (i = 0; i < NUMLINES; i )
{
TextOut(hdc, 0, cyChar * i,
devcaps[i].szLabel,
lstrlen(devcaps[i].szLabel));
TextOut(hdc, 14 * cxCaps, cyChar * i,
devcaps[i].szDesc,
lstrlen(devcaps[i].szDesc));
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, 14 * cxCaps 35 * cxChar, cyChar * i, szBuffer,
wsprintf(szBuffer, TEXT("]"),
GetDeviceCaps(hdc, devcaps[i].iIndex)));
SetTextAlign(hdc, TA_LEFT | TA_TOP);
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
5.3.设定像素
SetPixel函数将坐标为x和y的像素点设定为某个特定的颜色
代码语言:javascript复制SetPixel(hdc,x, y, crColor);
最后一个参数是COLORREF类型,用来指定颜色
GetPixel函数返回指定坐标位置的像素的颜色
代码语言:javascript复制crColor = GetPixel(hdc,x,y);
5.4.直线
画一条直线,必须调用两个函数。第一个函数用来指定直线的起点,第二个函数用来指定直线的终点
代码语言:javascript复制MoveToEx(hdc, xBeg, yBeg, NULL); //起点
LineTo(hdc, xEnd, yEnd); //终点
获取当前位置
代码语言:javascript复制GetCurrentPositionEx(hdc, &pt);
将数组的点连接成线,调用Polyline函数划线会更加容易
代码语言:javascript复制Polyline(hdc, apt, 5); //最后一个参数5表示点的个数
5.5.边框绘制函数
画矩形
代码语言:javascript复制Rectangle(hdc,xLeft, yTop, xRight, yBottom); //左上角和右下角坐标
画椭圆
代码语言:javascript复制Ellipse(hdc,xLeft, yTop, xRight, yBottom);
5.6.创建、选择和删除画笔
1.创建画笔
代码语言:javascript复制hPen = CreatePen(iPenStyle, iWidth, crColor);
参数iPenStyle决定画笔绘制的是实线(PS_SOLID)或者虚线(PS_DASH)或者点线(PS_DOT)
参数iWidth表示画笔的宽度
参数crColor执定画笔的颜色
2.选择画笔
代码语言:javascript复制SelectObject(hdc, hPen);
3.删除画笔
代码语言:javascript复制DeleteObject(hPen);
4.可以随时创建画笔,将CreatePen和SelectObject组合到一条语句中
将一支画笔选入到一个新创建的设备环境,保存SelectObject返回的画笔句柄
代码语言:javascript复制hPen = SelectObject(hdc, CreatePen(PS_DASH, 0, rgb(255,0,0)));
删除
代码语言:javascript复制DeleteObject(SelectObject(hdc, hPen));
六、键盘
6.1.击键消息
当用户按下一个键时,windows将WM_KEYDOWN或WM_SYSKEYDOWN消息放入具有输入焦点的窗口的消息队列中。
当该键被释放时,windows把WM_KEYUP或WM_SYSKEYUP消息放入相应的消息队列中。
键按下 | 键释放 | |
---|---|---|
非系统键击 | WM_KEYDOWN | WM_KEYUP |
系统键击 | WM_SYSKEYDOWN | WM_SYSKEYUP |
6.2.虚拟键代码
虚拟键代码存储在wParam参数中,此代码确定哪个键被按下或被释放。大多数虚拟机键代码命名都是以VK_开头的.
6.3.Iparam信息
wParam消息参数包含了虚拟键代码,Iparam消息参数包含了帮助理解击键的其它有用信息。32位的Iparam消息被分成了6个字段。
- 重复计数:是消息所表示的击键的数目
- OEM扫描码:是键盘硬件产生的diamante,windows程序几乎可以忽略OEM扫描码
- 扩展键标记:如果击键结果来自于IBM加强型键盘的附加减则扩展建标记为1,此项通常被忽略
- 内容代码:如果在击键的同时页按下了Alt键,则内容代码为1
- 键的先前状态:如果键以前是处于释放的,则键的先前状态为0。如果键以前是按下的,则键的先前状态为1
- 转换状态:如果键正在被按下,转换状态为0,如果键正在被释放,转换状态为1.
6.4.转义状态
当处理击键消息时,可能需要知道是否有转义键(shift,ctrl,alt键)或切换键(caps lock、num lock、和scroll lock键)被按下,可以通过GetKeyState函数获得此信息。
代码语言:javascript复制//如果shift键被按下,则iState变量为负(即最高位为1)
iState = GetKeyState(VK_SHIFT);
//如果Caps Lock键打开,则返回的值最低位置为1
iState = GetKeyState(VK_CAPITAL);
6.5.为SYSMETS加上键盘处理
给第4章SYSMETS程序添加键盘消息处理,使用SengMessage函数,可以通过给窗口过程发送假冒的消息欺骗WndProc函数,使它认为收到了滚动条消息。
代码语言:javascript复制 case WM_KEYDOWN:
switch (wParam)
{
case VK_HOME:
SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0) ;
break ;
case VK_END:
SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0) ;
break ;
case VK_PRIOR:
SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0) ;
break ;
case VK_NEXT:
SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0) ;
break ;
case VK_UP:
SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0) ;
break ;
case VK_DOWN:
SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0) ;
break ;
case VK_LEFT:
SendMessage (hwnd, WM_HSCROLL, SB_PAGEUP, 0) ;
break ;
case VK_RIGHT:
SendMessage (hwnd, WM_HSCROLL, SB_PAGEDOWN, 0) ;
break ;
}
return 0 ;
6.6.字符消息
代码语言:javascript复制while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
这是WinMain函数中的消息循环。GetMessage函数从消息队列总取出下一条消息,天然msg结构的字段。
DispatchMessage函数调用此消息的窗口过程。
TranslateMessage负责把击键消息转换为字符消息,如果击键消息时WM_KEYDOWN或WM_SYSKEYDOWN,且击键和转移状态组合产生了一个字符,则TranslateMessage函数把字符消息放入应用程序的消息队列。这个字符消息将被放在击键消息之后,GetMessage函数可从消息队列中获取此字符消息。
四类字符消息
windows程序会忽略其它三占用字符消息,仅处理WM_CHAR消息
字符 | 死字符 | |
---|---|---|
非系统字符 | WM_CHAR | WM_DEADCHAR |
系统字符 | WM_SYSCHAR | WM_SYSDEADCHAR |
6.7.消息排序
例如Caps Lock键没有锁定,则按下再释放A键时,相应的窗口过程会接收一下三个消息
消息 | 击键或代码 |
---|---|
WM_KEYDOWN | A的虚拟键代码(0x41) |
WM_CHAR | A的字符编码(0x61) |
WM_KEYUP | A的虚拟键代码(0x41) |
如果输入大写字母A:按下shift键,再按下A键,释放A键,再释放shift键,则窗口过程接收五个消息
消息 | 击键或代码 |
---|---|
WM_KEYDOWN | 虚拟键代码VK_SHIFT(0X10) |
WM_KEYDOWN | A的虚拟键代码(0x41) |
WM_CHAR | A的字符编码(0x41) |
WM_KEYUP | A的虚拟键代码(0x41) |
WM_KEYUP | 虚拟键代码VK_SHIFT(0X10) |
6.8.控制字符的处理
ctrl和字母键的组合会产生ASCII控制字符
击键 | 字符码 | 产生方法 | ANSI C 转义码 |
---|---|---|---|
空格键 | 0x08 | Ctrl-H | b |
Tab键 | 0x09 | Ctrl-I | t |
ctrl 回车 | 0x0A | Ctrl-J | n |
回车键 | 0X0D | Ctrl-M | r |
Esc键 | 0X1B | Ctrl-[ |
我们按照一下的基本规则来处理击键和字符消息
- 如果需要读取输入到窗口中的键盘字符,就处理WM_CHAR消息
- 如果需要读取光标键、功能键、Delete键、Insert键、shift、ctrl和alt键则处理WM_KEYDOWN
Tab键、回车键、空格键和esc键看作控制字符,而不是虚拟按键,通常这样处理WM_CHAR消息
代码语言:javascript复制case WM_CHAR:
switch(wParam)
{
case 'b': //backspace
case 't': //tab
case 'n': //linefeed
case 'r': //carriage return
}
6.9.插入符号
- 插入符号:当你向程序中输入文本时,通常会有下划线、竖线或方框指示你输入的下一个字符将出现在屏幕上的位置
- 光标:特指表示鼠标位置的位图图像,即鼠标指针
关于插入符号的函数
- CreateCaret:创建和窗口关联的插入符号
- SetCaretPos:设置窗口内的插入符号的位置
- ShowCaret:显示插入符号
- HideCaret:隐藏插入符号
- DestoryCaret:销毁插入符号
仅当窗口具有输入焦点时,窗口中插入符号才有意义。程序中通过处理WM_SETFOCUS消息和WM_KILLFOCUS消息来决定
它是否具有输入焦点。
使用插入符号的主要规则:在窗口过程处理WM_SETFOCUS消息时调用CreateCaret函数,处理WM_KILLFOCUS消息时调用DestoryCaret函数。
七、鼠标
7.1.客户区鼠标消息
当鼠标移经窗口客户区时,窗口过程接收WM_MOUSEMOVE消息。
按钮 | 按下 | 释放 | 第二次按下按钮 |
---|---|---|---|
左键 | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_LBUTTONDBLCLK |
中键 | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_MBUTTONDBLCLK |
右键 | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_RBUTTONDBLCLK |
对所有消息来说,参数IParam包含了鼠标的位置消息
- x = LOWORD(IParam);
- y = HIWORD(IParam);
参数wParam表示鼠标按钮、shift和ctrl键的状态
- MK_LBUTTON 按下左键
- MK_MBUTTON 按下中键
- MK_RBUTTON 按下右键
- MK_SHIFT 按下shift键
- MK_CONTROL 按下ctrl键
7.2.简单的鼠标处理示例
connect.c
代码语言:javascript复制#include <windows.h>
#include <windowsx.h>
#define MAXPOINTS 1000
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Connect");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("Program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Connect-the-Points Mouse Demo"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
static POINT pt[MAXPOINTS];
static int iCount;
HDC hdc;
int i, j;
PAINTSTRUCT ps;
switch (message)
{
case WM_LBUTTONDOWN:
iCount = 0;
InvalidateRect(hwnd, NULL, TRUE); //清空客户区
return 0;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON && iCount < 1000) //判断是否按下了左键
{
pt[iCount].x = GET_X_LPARAM(lParam);
pt[iCount ].y = GET_Y_LPARAM(lParam);
hdc = GetDC(hwnd);
//画一个黑点,并保存点的坐标
SetPixel(hdc, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
ReleaseDC(hwnd, hdc);
}
return 0;
case WM_LBUTTONUP:
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SetCursor(LoadCursor(NULL, IDC_WAIT));
ShowCursor(TRUE);
for (i = 0; i < iCount - 1; i )
for (j = i 1; j < iCount; j )
{
MoveToEx(hdc, pt[i].x, pt[i].y, NULL);
LineTo(hdc, pt[j].x, pt[j].y);
}
ShowCursor(FALSE);
SetCursor(LoadCursor(NULL, IDC_ARROW));
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
7.3.鼠标双击
如果想让窗口过程接收鼠标双击消息,那么在调用RegisterClass初始化窗口类结构时,必须在窗口风格字段中包含标识符CS_DBLCLKS
- wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
若窗口类的风格包含CS_DBLCLKS,那么双击后,窗口过程会接收如下消息:
- WM_LBUTTONDOWN
- WM_LBUTTONUP
- WM_LBUTTONDBLCLK
- WM_LBUTTONUP
7.3.非客户区鼠标消息
如果鼠标位于窗口内部除客户区外的其它区域,windows就会向窗口过程发送一个非客户取区消息。窗口的非客户区消息包括标题栏、菜单和滚动条。
系统一般不需要用户处理非客户区消息,用户只需要将这些消息发送给DefWindowProc.
如果鼠标在窗口的非客户区内移动,窗口过程就会接收WM_NCMOUSEMOVE(NC:nonclient)消息
按钮 | 按下 | 释放 | 第二次按下按钮 |
---|---|---|---|
左键 | WM_NCLBUTTONDOWN | WM_NCLBUTTONUP | WM_NCLBUTTONDBLCLK |
中键 | WM_NCMBUTTONDOWN | WM_NCMBUTTONUP | WM_NCMBUTTONDBLCLK |
右键 | WM_NCRBUTTONDOWN | WM_NCRBUTTONUP | WM_NCRBUTTONDBLCLK |
非客户区鼠标消息参数
- wParam:表示非客户区鼠标移动或单机的位置,它的值被设定成一个以HT(击中测试)为首的标识符。
- IParam:低位包含x坐标,高位包含y坐标,这些坐标都是屏幕坐标,而不是客户区坐标
7.4.击中测试消息
WM_NCHITTEST表示“非客户区击中测试(nonclient hit test)”,这个消息的优先级高于其它所有的客户区和非客户区鼠标消息。参数IParam包含鼠标位置的屏幕坐标x和y。参数wParam没有用到。windows应用程序会把这个消息发送给DefWindowProc,然后WM_NCHITTEST消息来产生所有其它和鼠标位置相关的鼠标消息。
对非客户区消息来说,DefWindowProc处理WM_NCHITTEST消息后返回一个可用于鼠标消息参数wParam的值,
这个返回值可以是任何一个非客户区鼠标消息的wParam参数的值,也可以是如下所示的值
- HTCLIENT 客户区
- HTNOWHERE 不在任何窗口
- HTTRANSPARENT 被另外一个窗口覆盖的窗口
- HTERROR 是函数DefWindowProc产生一个警示声
如果DefWindowProc在处理WM_NCHITTEST消息之后返回HTCLIENT,则windows会将屏幕坐标转换成客户区坐标,并产生一个客户区鼠标消息。
如果DefWindowProc在处理WM_NCHITTEST消息之后返回HTNOWHERE,那么程序就能有效地阻止系统向窗口发送所有客户区和非客户区鼠标消息。此时,无论鼠标位于任何位置,鼠标按钮操作都将失效。
代码语言:javascript复制case WM_NCHITTEST:
return (LRESULT)HTNOWHERE;
八、计时器
8.1.使用计时器的方法一
如果程序在整个运行过程中需要一个计时器,在WinMain函数中或处理WM-create消息时,调用SetTimer函数,在离开WinMain函数时或是处理WM_DESTROY消息时,调用KillTimer函数。
方法1
代码语言:javascript复制SetTimer(hwnd, 1, uiMsecInterval, NULL);
- 参数1:窗口句柄
- 参数2:是一个计时器ID
- 参数3:是一个32位的无符号整数,它是以毫秒为单位的时间间隔
停止WM_TIMER消息
代码语言:javascript复制KillTimer(hwnd, 1); //第二个参数是相应SetTimer使用的计时器ID
当窗口过程收到WM_TIMER消息时,wParam等于计时器的ID,IParam是0
Beeper1.c
代码语言:javascript复制#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Beeper1");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("Program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Beeper1 Timer Demo"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
static BOOL fFlipFlop = FALSE;
HBRUSH hBrush;
HDC hdc;
PAINTSTRUCT ps;
RECT rc;
switch (message)
{
case WM_CREATE:
SetTimer(hwnd, ID_TIMER, 1000, NULL); //设置计时器
return 0;
case WM_TIMER:
MessageBeep(-1);
fFlipFlop = !fFlipFlop;
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
hBrush = CreateSolidBrush(fFlipFlop ? RGB(255, 0, 0) : RGB(0, 0, 255));
FillRect(hdc, &rc, hBrush);
EndPaint(hwnd, &ps);
DeleteObject(hBrush);
return 0;
case WM_DESTROY:
KillTimer(hwnd, ID_TIMER); //删除计时器
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
8.2.使用计时器的方法二
第一种设计计时器的方法把WM_TIMER消息发送给正常的窗口过程。第二种方法则让你指挥windows把计时器消息发送到程序中的另一个函数。收到计时器消息的函数被称为回调函数。
定义一个回调函数TimerProc,这个函数值处理WM_TIMER消息
代码语言:javascript复制VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
//处理WM_TIMER消息
}
说明
- 参数1:调用SetTimer时指定的窗口句柄
- 参数2:windows只发送WM_TIMER消息到TimerProc,所以消息参数总是WM_TIMER
- 参数3:计时器的ID
- 参数4:dwTime是从GetTickCount函数返回的值,它记录了自动windows启动到现在所逝去的毫秒数
使用回调函数处理WM_TIIMER消息时,SetTimer函数的第四个参数必须设置位回调函数的地址
代码语言:javascript复制SetTimer(hwnd, iTimerID, iMsecInterval, TimerProc);
Beeper2.c
代码语言:javascript复制#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
VOID CALLBACK TimerProc(HWND, UINT, UINT, DWORD);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Beeper2");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("Program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Beeper2 Timer Demo"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
switch (message)
{
case WM_CREATE:
SetTimer(hwnd, ID_TIMER, 1000, TimerProc); //设置计时器和回调函数
return 0;
case WM_DESTROY:
KillTimer(hwnd, ID_TIMER); //删除计时器
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
static BOOL fFlipFlop = FALSE;
HBRUSH hBrush;
HDC hdc;
RECT rc;
MessageBeep(-1);
fFlipFlop = !fFlipFlop;
GetClientRect(hwnd, &rc);
hdc = GetDC(hwnd);
hBrush = CreateSolidBrush(fFlipFlop ? RGB(255, 0, 0) : RGB(0, 0, 255));
FillRect(hdc, &rc, hBrush);
ReleaseDC(hwnd, hdc);
DeleteObject(hBrush);
}
九、子窗口控件
9.1.按钮类
BTNLOOK.c
代码语言:javascript复制#include <windows.h>
//定义10中不同类型的按钮样式
struct
{
int iStyle;
TCHAR* szText;
}
button[] =
{
BS_PUSHBUTTON, TEXT("PUSHBUTTON"),
BS_DEFPUSHBUTTON, TEXT("DEFPUSHBUTTON"),
BS_CHECKBOX, TEXT("CHECKBOX"),
BS_AUTOCHECKBOX, TEXT("AUTOCHECKBOX"),
BS_RADIOBUTTON, TEXT("RADIOBUTTON"),
BS_3STATE, TEXT("3STATE"),
BS_AUTO3STATE, TEXT("AUTO3STATE"),
BS_GROUPBOX, TEXT("GROUPBOX"),
BS_AUTORADIOBUTTON, TEXT("AUTORADIO"),
BS_OWNERDRAW, TEXT("OWNERDRAW")
};
#define NUM (sizeof button / sizeof button[0])
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("BtnLook");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Button Look"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
static HWND hwndButton[NUM];
static RECT rect;
static TCHAR szTop[] = TEXT("message wParam lParam"),
szUnd[] = TEXT("_______ ______ ______"),
szFormat[] = TEXT("%-16sX-X X-X"),
szBuffer[50];
static int cxChar, cyChar;
HDC hdc;
PAINTSTRUCT ps;
int i;
switch (message)
{
case WM_CREATE:
cxChar = LOWORD(GetDialogBaseUnits()); //获取字符的默认字体的宽度
cyChar = HIWORD(GetDialogBaseUnits()); //获取字符的默认字体的高度
for (i = 0; i < NUM; i )
hwndButton[i] = CreateWindow(
TEXT("button"), //类名
button[i].szText, //窗口文本
WS_CHILD | WS_VISIBLE | button[i].iStyle, //窗口样式
cxChar, //x坐标
cyChar * (1 2 * i), //y坐标
20 * cxChar, //宽度
7 * cyChar / 4, //高度
hwnd, //父窗口
(HMENU)i, //子窗口ID
((LPCREATESTRUCT)lParam)->hInstance, //实例句柄
NULL); //额外参数
return 0;
case WM_SIZE:
rect.left = 24 * cxChar;
rect.top = 2 * cyChar;
rect.right = LOWORD(lParam);
rect.bottom = HIWORD(lParam);
return 0;
case WM_PAINT:
InvalidateRect(hwnd, &rect, TRUE);
hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, 24 * cxChar, cyChar, szTop, lstrlen(szTop));
TextOut(hdc, 24 * cxChar, cyChar, szUnd, lstrlen(szUnd));
EndPaint(hwnd, &ps);
return 0;
case WM_DRAWITEM:
case WM_COMMAND:
ScrollWindow(hwnd, 0, -cyChar, &rect, &rect);
hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
TextOut(hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1),
szBuffer,
wsprintf(szBuffer, szFormat,
message == WM_DRAWITEM ? TEXT("WM_DRAWITEM") :
TEXT("WM_COMMAND"),
HIWORD(wParam), LOWORD(wParam),
HIWORD(lParam), LOWORD(lParam)));
ReleaseDC(hwnd, hdc);
ValidateRect(hwnd, &rect);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
9.2.子窗口传递信息给父窗口
运行BTNLOOK时,在用鼠标单击一个按钮时,子窗口控件发送WM_COMMAND消息给其父窗口。
BTNLOOK俘获WM_COMMAND消息并显示wParam与IParam的值
- LOWORD(wParam) :子窗口ID
- HIWORD(wParam):通知码
- IParam:子窗口句柄
通知码
按钮通知码标识符 | 值 |
---|---|
BN_CLICKED | 0 |
BN_PAINT | 1 |
BN_HILITE或BN_PUSHED | 2 |
BN_UNHILITE或BN_UNPUSHED | 3 |
BN_DISABLE | 4 |
BN_DOUBLECLICKED或BN_DBLCLK | 5 |
BN_SETFOCUS | 6 |
BN_KILLFOCUS | 7 |
9.3.编辑类
PopPad1.c
代码语言:javascript复制/*-------------------------------------------------------
POPPAD1.C -- Popup Editor using child window edit box
(c) Charles Petzold, 1998
-------------------------------------------------------*/
#include <windows.h>
#define ID_EDIT 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
TCHAR szAppName[] = TEXT("PopPad1");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
static HWND hwndEdit;
switch (message)
{
case WM_CREATE:
hwndEdit = CreateWindow(TEXT("edit"), NULL,
WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
WS_BORDER | ES_LEFT | ES_MULTILINE | //多行左对齐
ES_AUTOHSCROLL | ES_AUTOVSCROLL, //自动换行
0, 0, 0, 0, hwnd, (HMENU)ID_EDIT,
((LPCREATESTRUCT)lParam)->hInstance, NULL);
return 0;
case WM_SETFOCUS:
SetFocus(hwndEdit);
return 0;
case WM_SIZE:
//编辑控件的大小设置位主窗口的尺寸
MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
return 0;
case WM_COMMAND:
if (LOWORD(wParam) == ID_EDIT)
if (HIWORD(wParam) == EN_ERRSPACE || HIWORD(wParam) == EN_MAXTEXT)
MessageBox(hwnd, TEXT("Edit control out of space."),szAppName, MB_OK | MB_ICONSTOP);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
9.4.编辑控件的通知消息
编辑控件向其父窗口发送WM_COMMAND消息,相应的wParam和IParam变量的含义
- LOWORD(wParam):子窗口ID
- HIWORD(wParam):通知码
- IPARAM:子窗口句柄
通知码
- EN_SETFOCUS:编辑控件得到了输入焦点
- EN_KILLFOCUS:编辑控件失去了输入焦点
- EN_CHANGE:编辑控件的内容将变化
- EN_UPDATE:编辑控件的内容已变化
- EN_ERRSPACE:编辑控件没有空间了
- EN_MAXTEXT:编辑控件没有空间完成插入了
- EN_HSCROLL:编辑控件水平滚动条被单击了
- EN_VSCROLL:编辑控件垂直滚动条被单击了
9.5.列表框类
LBS_STANDARD列表框样式,包含最常用的样式
代码语言:javascript复制#define LBS_STANDARD (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)
获取垂直滚动条的宽度
代码语言:javascript复制GetSystemMetrics(SM_CXVSCROLL);
创建列表框后,下一步就是向其中添加文本字符串。可以使用SendMessage向列表框的窗口过程发送消息来实现
添加
代码语言:javascript复制SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szString);
删除
代码语言:javascript复制SendMessage(hwndList, LB_DELETESTRING, iIndex, (LPARAM)szString);
清除所有
代码语言:javascript复制SendMessage(hwndList, LB_RESETCONTENT, 0, (LPARAM)szString);
查看列表框中有多少个项目
代码语言:javascript复制iCount = SendMessage(hwndList, LB_GETCOUNT,0,0);
在收到来自WM_COMMAND消息时,可以使用LB_GETCUUSEL得到当前选中的索引值
代码语言:javascript复制iIndex = SendMessage(hwndList, LB_GETCOrsel,0,0);
接收来自列表框的消息
- LOWORD(wParam):子窗口ID
- HIWORD(wParam):通知码
- IPARAM:子窗口句柄
通知码
- LBN_ERRSPACE: -2
- LBN_SELCHANGE: 1
- LBN_DBLCLK: 2
- LBN_SELCANCEL: 3
- LBN_SETFOCUS: 4
- LBN_KILLFOCUS: 5
9.6.列出文件
LB_DIR是列表框中功能最强的消息,下面的函数调用可将文件目录列表写入列表框中,这个文件目录可以包括子目录和有效的磁盘驱动器。
代码语言:javascript复制SendMessage(hwndList, LB_DIR,iAttr, (LPARAM)szFileSpec);
iAttr参数是文件属性代码
iAttr | 数值 | 属性 |
---|---|---|
DDL_READWRITE | 0x0000 | 普通文件 |
DDL_READONLY | 0x00001 | 只读文件 |
DDL_HIDDEN | 0x0002 | 隐藏文件 |
DDL_SYSTEM | 0x0004 | 系统文件 |
DDL_DIRECTORY | 0x0010 | 子目录 |
DDL_ARCHIVE | 0x0020 | 设置了存档位的文件 |
紧接着最高字节提供了一些额外的搜索条件
iAttr | 数值 | 选项 |
---|---|---|
DDL_DRIVERS | 0X4000 | 包括驱动器字符 |
DDL_EXCLUSIVE | 0X8000 | 只搜索指定的值 |
head.c
代码语言:javascript复制#include <windows.h>
#define ID_LIST 1
#define ID_TEXT 2
#define MAXREAD 8192
#define DIRATTR (DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM |
DDL_DIRECTORY | DDL_ARCHIVE | DDL_DRIVES)
#define DTFLAGS (DT_WORDBREAK | DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ListProc(HWND, UINT, WPARAM, LPARAM);
WNDPROC OldList;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("head");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("head"),
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
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)
{
static BOOL bValidFile;
static BYTE buffer[MAXREAD];
static HWND hwndList, hwndText;
static RECT rect;
static TCHAR szFile[MAX_PATH 1];
HANDLE hFile;
HDC hdc;
int i, cxChar, cyChar;
PAINTSTRUCT ps;
TCHAR szBuffer[MAX_PATH 1];
switch (message)
{
case WM_CREATE:
cxChar = LOWORD(GetDialogBaseUnits());
cyChar = HIWORD(GetDialogBaseUnits());
rect.left = 20 * cxChar;
rect.top = 3 * cyChar;
hwndList = CreateWindow(TEXT("listbox"), NULL,
WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD,
cxChar, cyChar * 3,
cxChar * 13 GetSystemMetrics(SM_CXVSCROLL),
cyChar * 10,
hwnd, (HMENU)ID_LIST,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
GetCurrentDirectory(MAX_PATH 1, szBuffer);
hwndText = CreateWindow(TEXT("static"), szBuffer,
WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT,
cxChar, cyChar, cxChar * MAX_PATH, cyChar,
hwnd, (HMENU)ID_TEXT,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
OldList = (WNDPROC)SetWindowLong(hwndList, GWL_WNDPROC,
(LPARAM)ListProc);
SendMessage(hwndList, LB_DIR, DIRATTR, (LPARAM)TEXT("*.*"));
return 0;
case WM_SIZE:
rect.right = LOWORD(lParam);
rect.bottom = HIWORD(lParam);
return 0;
case WM_SETFOCUS:
SetFocus(hwndList);
return 0;
case WM_COMMAND:
if (LOWORD(wParam) == ID_LIST && HIWORD(wParam) == LBN_DBLCLK)
{
if (LB_ERR == (i = SendMessage(hwndList, LB_GETCURSEL, 0, 0)))
break;
SendMessage(hwndList, LB_GETTEXT, i, (LPARAM)szBuffer);
if (INVALID_HANDLE_VALUE != (hFile = CreateFile(szBuffer,
GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL)))
{
CloseHandle(hFile);
bValidFile = TRUE;
lstrcpy(szFile, szBuffer);
GetCurrentDirectory(MAX_PATH 1, szBuffer);
if (szBuffer[lstrlen(szBuffer) - 1] != '\')
lstrcat(szBuffer, TEXT("\"));
SetWindowText(hwndText, lstrcat(szBuffer, szFile));
}
else
{
bValidFile = FALSE;
szBuffer[lstrlen(szBuffer) - 1] = '