返回顶部
扩大
缩小

Zhang_derek

【读书笔记】Windows程序设计5

Windows程序设计

一、起步

1.1.第一个Windows程序

main.c

#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数据类型

char c = 'A';			//1个字节  0x41
char * p;    			//4个字节
char a[] = "Hello!"		//7个字节

宽字符

//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

#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函数

将原来的无效区域有效化

WINUSERAPI HDC WINAPI BeginPaint(
    _In_ HWND hWnd,
    _Out_ LPPAINTSTRUCT lpPaint);

两个参数

  • 参数1:窗口的句柄
  • 参数2:是一个类型位PAINTSTRUCT结构的变量的地址

获取设备环境方法2:GetDC

与从BeginPaint函数返回的设备句柄不同,从GetDC返回的设备句柄中的裁剪区域是整个客户区,而不仅仅

是在无效矩形里

hdc = GetDC(hwnd);

ReleaseDC(hwnd, hdc);

4.2.TEXTOUT函数详解

TextOut(hdc, x, y, psText, iLength);

说明:

  • hdc:设备环境句柄,决定文本显示的特性
  • x:相对于客户区的左上角。从左往右增大
  • y:相对于客户区的左上角。从上往下增大
  • psText:指向字符串的指针
  • iLength:字符串的长度

4.3.字符大小

通过调用GetSystemMetrics函数来获取用户界面的尺寸。

通过调用GetTextMetrics函数,可以获取字体尺寸。GetTextMetrics函数需要一个设备环境句柄,因为它会返回该设备环境当前选定的字体的信息。windows将把字符尺寸的各种值复制到类型为TEXTMETRIC的结构中。

该结构体共有20个字段,我们进关心其中的前7个字段

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

WINUSERAPI BOOL WINAPI GetScrollInfo(
    _In_ HWND hwnd,
    _In_ int nBar,
    _Inout_ LPSCROLLINFO lpsi);

SetScrollInfo

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结构体

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

/*-----------------------------------------------
   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

#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("%5d"),
                    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函数

hdc = BeginPaint(hwnd, &ps);    
	(other program lines)
EndPaint(hwnd, &ps);

ps是一个类型为PAINTSTRUCT的结构,PAINTSTRUCT结构还包含一个名为rcPaint的矩形结构,该结构定义了一个保卫窗口客户区无效范围的矩形。使用BeginPaint函数获取的设备环境句柄,就只能在这个矩形内绘图,调用BeginPaint函数将使这个区域有效。

2.设备环境句柄还可以在处理非WM_PAINT消息时由windows程序获取

hdc = GetDC(hwnd);
	(other program lines)
ReleaseDC(hwnd, &hdc);

其中,设备环境句柄指的是窗口句柄为hwnd的窗口客户区,从GetDC函数返回的句柄可以在整个客户区绘制,并且GetDC和ReleaseDC函数并不使任何客户区的无效区域变为有效。

3、windows程序还可以获得用于整个窗口的,而不仅仅是窗口客户区的设备环境句柄

hdc = GetWindowDC(hwnd);
	(other program lines)
ReleaseDC(hwnd, &hdc);

这里的设备环境句柄除了包括客户区,还包含窗口标题栏、菜单、滚动条和客户区的外框。

5.2.获取设备环境信息

GetDeviceCaps可以获取设备的信息

DevCaps1.c

/*---------------------------------------------------------
   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("%5d"),
                    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的像素点设定为某个特定的颜色

SetPixel(hdc,x, y, crColor);

最后一个参数是COLORREF类型,用来指定颜色

GetPixel函数返回指定坐标位置的像素的颜色

crColor = GetPixel(hdc,x,y);

5.4.直线

画一条直线,必须调用两个函数。第一个函数用来指定直线的起点,第二个函数用来指定直线的终点

MoveToEx(hdc, xBeg, yBeg, NULL);   //起点

LineTo(hdc, xEnd, yEnd);     //终点

获取当前位置

GetCurrentPositionEx(hdc, &pt);

将数组的点连接成线,调用Polyline函数划线会更加容易

Polyline(hdc, apt, 5);     //最后一个参数5表示点的个数

5.5.边框绘制函数

画矩形

Rectangle(hdc,xLeft, yTop, xRight, yBottom);   //左上角和右下角坐标

画椭圆

Ellipse(hdc,xLeft, yTop, xRight, yBottom);

5.6.创建、选择和删除画笔

1.创建画笔

hPen = CreatePen(iPenStyle, iWidth, crColor);

参数iPenStyle决定画笔绘制的是实线(PS_SOLID)或者虚线(PS_DASH)或者点线(PS_DOT)

参数iWidth表示画笔的宽度

参数crColor执定画笔的颜色

2.选择画笔

SelectObject(hdc, hPen);

3.删除画笔

DeleteObject(hPen);

4.可以随时创建画笔,将CreatePen和SelectObject组合到一条语句中

将一支画笔选入到一个新创建的设备环境,保存SelectObject返回的画笔句柄

hPen = SelectObject(hdc, CreatePen(PS_DASH, 0, rgb(255,0,0)));

删除

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个字段。

image-20221206210242289

  • 重复计数:是消息所表示的击键的数目

  • OEM扫描码:是键盘硬件产生的diamante,windows程序几乎可以忽略OEM扫描码

  • 扩展键标记:如果击键结果来自于IBM加强型键盘的附加减则扩展建标记为1,此项通常被忽略

  • 内容代码:如果在击键的同时页按下了Alt键,则内容代码为1

  • 键的先前状态:如果键以前是处于释放的,则键的先前状态为0。如果键以前是按下的,则键的先前状态为1

  • 转换状态:如果键正在被按下,转换状态为0,如果键正在被释放,转换状态为1.

6.4.转义状态

当处理击键消息时,可能需要知道是否有转义键(shift,ctrl,alt键)或切换键(caps lock、num lock、和scroll lock键)被按下,可以通过GetKeyState函数获得此信息。

//如果shift键被按下,则iState变量为负(即最高位为1)
iState = GetKeyState(VK_SHIFT);

//如果Caps Lock键打开,则返回的值最低位置为1
iState = GetKeyState(VK_CAPITAL);

6.5.为SYSMETS加上键盘处理

给第4章SYSMETS程序添加键盘消息处理,使用SengMessage函数,可以通过给窗口过程发送假冒的消息欺骗WndProc函数,使它认为收到了滚动条消息。

     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.字符消息

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消息

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

#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,那么程序就能有效地阻止系统向窗口发送所有客户区和非客户区鼠标消息。此时,无论鼠标位于任何位置,鼠标按钮操作都将失效。

case WM_NCHITTEST:
	return (LRESULT)HTNOWHERE;

八、计时器

8.1.使用计时器的方法一

如果程序在整个运行过程中需要一个计时器,在WinMain函数中或处理WM-create消息时,调用SetTimer函数,在离开WinMain函数时或是处理WM_DESTROY消息时,调用KillTimer函数。

方法1

SetTimer(hwnd, 1, uiMsecInterval, NULL);
  • 参数1:窗口句柄
  • 参数2:是一个计时器ID
  • 参数3:是一个32位的无符号整数,它是以毫秒为单位的时间间隔

停止WM_TIMER消息

KillTimer(hwnd, 1);   //第二个参数是相应SetTimer使用的计时器ID

当窗口过程收到WM_TIMER消息时,wParam等于计时器的ID,IParam是0

Beeper1.c

#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消息

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函数的第四个参数必须设置位回调函数的地址

SetTimer(hwnd, iTimerID, iMsecInterval, TimerProc);

Beeper2.c

#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

#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("%-16s%04X-%04X    %04X-%04X"),
        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

/*-------------------------------------------------------
   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列表框样式,包含最常用的样式

#define LBS_STANDARD          (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)

获取垂直滚动条的宽度

GetSystemMetrics(SM_CXVSCROLL);

创建列表框后,下一步就是向其中添加文本字符串。可以使用SendMessage向列表框的窗口过程发送消息来实现

添加

SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szString);

删除

SendMessage(hwndList, LB_DELETESTRING, iIndex, (LPARAM)szString);

清除所有

SendMessage(hwndList, LB_RESETCONTENT, 0, (LPARAM)szString);

查看列表框中有多少个项目

iCount = SendMessage(hwndList, LB_GETCOUNT,0,0);

在收到来自WM_COMMAND消息时,可以使用LB_GETCUUSEL得到当前选中的索引值

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是列表框中功能最强的消息,下面的函数调用可将文件目录列表写入列表框中,这个文件目录可以包括子目录和有效的磁盘驱动器。

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

#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] = '\0';

                // If setting the directory doesn't work, maybe it's
                // a drive change, so try that.

                if (!SetCurrentDirectory(szBuffer + 1))
                {
                    szBuffer[3] = ':';
                    szBuffer[4] = '\0';
                    SetCurrentDirectory(szBuffer + 2);
                }

                // Get the new directory name and fill the list box.

                GetCurrentDirectory(MAX_PATH + 1, szBuffer);
                SetWindowText(hwndText, szBuffer);
                SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
                SendMessage(hwndList, LB_DIR, DIRATTR,
                    (LPARAM)TEXT("*.*"));
            }
            InvalidateRect(hwnd, NULL, TRUE);
        }
        return 0;

    case WM_PAINT:
        if (!bValidFile)
            break;

        if (INVALID_HANDLE_VALUE == (hFile = CreateFile(szFile, GENERIC_READ,
            FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)))
        {
            bValidFile = FALSE;
            break;
        }

        ReadFile(hFile, buffer, MAXREAD, &i, NULL);
        CloseHandle(hFile);

        // i now equals the number of bytes in buffer.
        // Commence getting a device context for displaying text.

        hdc = BeginPaint(hwnd, &ps);
        SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
        SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
        SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));

        // Assume the file is ASCII

        DrawTextA(hdc, buffer, i, &rect, DTFLAGS);

        EndPaint(hwnd, &ps);
        return 0;

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

LRESULT CALLBACK ListProc(HWND hwnd, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    if (message == WM_KEYDOWN && wParam == VK_RETURN)
        SendMessage(GetParent(hwnd), WM_COMMAND,
            MAKELONG(1, LBN_DBLCLK), (LPARAM)hwnd);

    return CallWindowProc(OldList, hwnd, message, wParam, lParam);
}

十、菜单和其他资源

10.1.向程序添加图标

IconDemo.c

#include <windows.h>
#include "resource.h"

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    TCHAR    szAppName[] = TEXT("IconDemo");
    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(hInstance, MAKEINTRESOURCE(IDI_ICON));
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = 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("Icon 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 HICON hIcon;
    static int   cxIcon, cyIcon, cxClient, cyClient;
    HDC          hdc;
    HINSTANCE    hInstance;
    PAINTSTRUCT  ps;
    int          x, y;

    switch (message)
    {
    case WM_CREATE:
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
        //获取图标的句柄
        hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
        //获取图标的大小
        cxIcon = GetSystemMetrics(SM_CXICON);
        cyIcon = GetSystemMetrics(SM_CYICON);
        return 0;

    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;

    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        for (y = 0; y < cyClient; y += cyIcon)
            for (x = 0; x < cxClient; x += cxIcon)
                DrawIcon(hdc, x, y, hIcon);   //显示图标

        EndPaint(hwnd, &ps);
        return 0;

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

10.2.菜单

和菜单有关的概念

  • 紧挨着标题栏下面的菜单栏叫做主菜单或顶级菜单
  • 顶级菜单的下拉菜单叫弹出菜单(popup menu)或子菜单
  • 弹出菜单的菜单项可以被选中(checked)

菜单结构,每个菜单项由三个特征定义

  • 第一个特征:菜单显示什么
  • 第二个特征:是一个ID号或一个指向弹出菜单的句柄
  • 第三个特征:描述了菜单的属性,包括该菜单项是否被禁用、变灰或选中

定义菜单

  • 在为菜单中的一项输入文本时,可以输入一个符号&来指示windows在显示菜单时给紧接着&的下一个字符显示下划线
  • 如果选择inactive选项,则表示该菜单项是非活动的
  • Separator选项会在弹出菜单上绘制一条水平的分割线

在程序中引用菜单

大多数windows应用程序只有一个菜单,可以给菜单指定一个与程序名一样的文本名字

wndclass.IPszMenuName = szAppName;

也可以用LoadMenu函数把菜单资源加载到内存

    wndclass.lpszMenuName = "IDR_MENU1";   

	HMENU hMenu;
    hMenu = LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
	
	//创建窗口的时候指定菜单句柄
	hwnd = CreateWindow(szAppName, TEXT("Menu Demonstration"),
                    WS_OVERLAPPEDWINDOW,
                    CW_USEDEFAULT, CW_USEDEFAULT,
                    CW_USEDEFAULT, CW_USEDEFAULT,
                    NULL, hMenu, hInstance, NULL);

菜单和消息

当用户在菜单项之间移动光标或鼠标时,程序可以收到许多WM_MENUSELECT消息,WM_MENUSELECT的参数如下

  • LOWORD(wParam):所选的菜单项:菜单ID或弹出菜单的索引
  • HIWORD(wParam):选择标记
  • IParam:包含所选项的菜单句柄

windows要显示弹出菜单时,会向窗口过程发送一个带有如下参数的WM_INITMENUPOPUP消息

  • wParam:弹出菜单的句柄

  • LOWORD(wParam):弹出菜单的索引

  • HIWORD(wParam):1代表系统菜单,0代表其它菜单

最重要的菜单消息时WM_COMMAND,这个消息表示用户已经从窗口菜单中选择了一个被启用的菜单项

菜单 控件
LOWORD(wParam) 菜单ID 控件ID
HIWORD(wParam) 0 通知码
IParam 0 子窗口句柄

示例程序MenuDemo

IDR_MENU1.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&New",                        ID_FILE_NEW
        MENUITEM "&Open",                       ID_FILE_OPEN
        MENUITEM "&Save",                       ID_FILE_SAVE
        MENUITEM "Save &As...",                 ID_FILE_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       ID_FILE_EXIT
    END
    POPUP "&Edit"
    BEGIN
        MENUITEM "&Undo",                       ID_EDIT_UNDO
        MENUITEM SEPARATOR
        MENUITEM "C&ut",                        ID_EDIT_CUT
        MENUITEM "&Copy",                       ID_EDIT_COPY
        MENUITEM "&Paste",                      ID_EDIT_PASTE
        MENUITEM "De&lete",                     ID_EDIT_DELETE
    END
    POPUP "&Background"
    BEGIN
        MENUITEM "&White",                      ID_BACKGROUND_WHITE, CHECKED
        MENUITEM "&Light Gray",                 ID_BACKGROUND_WHITEGRAY
        MENUITEM "&Gray",                       ID_BACKGROUND_GRAY
        MENUITEM "&Dark Gray",                  ID_BACKGROUND_DARKGRAY
        MENUITEM "&Black",                      ID_BACKGROUND_BLACK
    END
    POPUP "&Timer"
    BEGIN
        MENUITEM "&Start",                      ID_TIMER_START
        MENUITEM "S&top",                       ID_TIMER_STOP, INACTIVE
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&Help...",                    ID_HELP_HELP
        MENUITEM "&About MenuDemo...",          ID_HELP_ABOUTMENUDEMO
    END
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 IDR_MENU1.rc 使用
//
#define IDR_MENU1                        101
#define ID_FILE_NEW                     40001
#define ID_FILE_OPEN                    40002
#define ID_FILE_SAVE                    40003
#define ID_FILE_SAVEAS                  40004
#define ID_FILE_EXIT                    40005
#define ID_EDIT_UNDO                    40006
#define ID_EDIT_CUT                     40007
#define ID_EDIT_COPY                    40008
#define ID_EDIT_PASTE                   40009
#define ID_EDIT_DELETE                  40010
#define ID_Menu                         40011
#define ID_BACKGROUND_WHITE             40012
#define ID_BACKGROUND_WHITEGRAY         40013
#define ID_BACKGROUND_GRAY              40014
#define ID_BACKGROUND_DARKGRAY          40015
#define ID_BACKGROUND_BLACK             40016
#define ID_TIMER_START                  40017
#define ID_TIMER_STOP                   40018
#define ID_HELP_HELP                    40019
#define ID_HELP_ABOUTMENUDEMO           40020

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40021
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

MenuDemo.c

#include <windows.h>
#include "resource.h"

#define ID_TIMER 1

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

TCHAR szAppName[] = TEXT("MenuDemo");
TCHAR szMenuName[] = TEXT("IDR_MENU1");

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 = szMenuName;
    wndclass.lpszClassName = szAppName;

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    //把菜单资源加载到内存
    HMENU hMenu;
    hMenu = LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));

    hwnd = CreateWindow(szAppName, TEXT("Menu Demonstration"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, hMenu, 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 idColor[5] = { WHITE_BRUSH,  LTGRAY_BRUSH, GRAY_BRUSH,
                               DKGRAY_BRUSH, BLACK_BRUSH };
    static int iSelection = ID_BACKGROUND_WHITE;
    HMENU      hMenu;

    switch (message)
    {
    case WM_COMMAND:
        hMenu = GetMenu(hwnd);

        switch (LOWORD(wParam))    //控件ID
        {
        case ID_FILE_NEW:
        case ID_FILE_OPEN:
        case ID_FILE_SAVE:
        case ID_FILE_SAVEAS:
            MessageBeep(0);
            return 0;

        case ID_FILE_EXIT:
            SendMessage(hwnd, WM_CLOSE, 0, 0);
            return 0;

        case ID_EDIT_UNDO:
        case ID_EDIT_CUT:
        case ID_EDIT_COPY:
        case ID_EDIT_PASTE:
        case ID_EDIT_DELETE:
            MessageBeep(0);
            return 0;

        case ID_BACKGROUND_WHITE:         // Note: Logic below
        case ID_BACKGROUND_WHITEGRAY:        //   assumes that IDM_WHITE
        case ID_BACKGROUND_GRAY:          //   through IDM_BLACK are
        case ID_BACKGROUND_DARKGRAY:        //   consecutive numbers in
        case ID_BACKGROUND_BLACK:         //   the order shown here.

            //取消对当前选中项的选中标注
            CheckMenuItem(hMenu, iSelection, MF_UNCHECKED);
            //iSelection设为菜单ID
            iSelection = LOWORD(wParam);
            CheckMenuItem(hMenu, iSelection, MF_CHECKED);
            //设置新的背景颜色
            SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)
                GetStockObject
                (idColor[LOWORD(wParam) - ID_BACKGROUND_WHITE]));

            InvalidateRect(hwnd, NULL, TRUE);
            return 0;

        case ID_TIMER_START:
            //启动计时器
            if (SetTimer(hwnd, ID_TIMER, 1000, NULL))
            {
                EnableMenuItem(hMenu, ID_TIMER_START, MF_GRAYED);   //start项变灰
                EnableMenuItem(hMenu, ID_TIMER_STOP, MF_ENABLED);   //stop项激活
            }
            return 0;

        case ID_TIMER_STOP:
            KillTimer(hwnd, ID_TIMER);   //终止计时器
            EnableMenuItem(hMenu, ID_TIMER_START, MF_ENABLED);  //start项激活
            EnableMenuItem(hMenu, ID_TIMER_STOP, MF_GRAYED);    //stop项变灰
            return 0;

        case ID_HELP_HELP:
            MessageBox(hwnd, TEXT("Help not yet implemented!"),
                szAppName, MB_ICONEXCLAMATION | MB_OK);
            return 0;

        case ID_HELP_ABOUTMENUDEMO:
            MessageBox(hwnd, TEXT("Menu Demonstration Program\n")
                TEXT("(c) Charles Petzold, 1998"),
                szAppName, MB_ICONINFORMATION | MB_OK);
            return 0;
        }
        break;

    case WM_TIMER:
        MessageBeep(0);
        return 0;

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

10.3.键盘加速键

指定加速键的一些规则

键盘加速键最常用的用途是用于程序中Edit菜单的菜单项,同时支持新旧两种加速键

功能 旧加速键 新加速键
Undo(撤销) Alt+Backspace Ctrl+Z
Cut(剪切) Shift+Del Ctrl+X
Copy(复制) Ctrl+Ins Ctrl+C
Paste(粘贴) Shift+Ins Ctrl+V
Delete或Clear(删除) Del Del

加速键表

  • 可以在字母前加^来指定ASCII字符与Ctrl键的组合,也可以从组合框中选择虚拟机按键码
  • 在为菜单项定义键盘加速键时,\t能将文本和加速键分开

接收加速键消息

加速键 菜单 控件
LOWORD(wParam) 加速键ID 菜单ID 控件ID
HIWORD(wParam) 1 0 通知码
IParam 0 0 子窗口句柄

实例:记事本程序PopPad2

键盘加速键IDR_ACCELERATOR1

image-20221211182949567

IDR_MENU1

image-20221211183027319

resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 PopPad2.rc 使用
//
#define IDR_MENU1                       101
//#define IDR_ACCELERATOR1                102
#define ID_FILE_NEW                     40001
#define ID_FILE_OPEN                    40002
#define ID_FILE_SAVE                    40003
#define ID_FILE_SAVEAS                  40004
#define ID_FILE_PRINT                   40005
#define ID_FILE_EXIT                    40006
#define ID_EIDT_UNDO                    40007
#define ID_EIDT_CUT                     40008
#define ID_EIDT_COPY                    40009
#define ID_EIDT_PASTE                   40010
#define ID_Menu                         40011
#define ID_EIDT_DELETE                  40012
#define ID_EIDT_SELETEALL               40013
#define ID_HELP_HELP                    40014
#define ID_HELP_ABOUTPOPPAD2            40015

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40027
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

PopPad2.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&New",                        ID_FILE_NEW
        MENUITEM "&Open",                       ID_FILE_OPEN
        MENUITEM "&Save",                       ID_FILE_SAVE
        MENUITEM "Save &As...",                 ID_FILE_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "&Print",                      ID_FILE_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       ID_FILE_EXIT
    END
    POPUP "&Eidt"
    BEGIN
        MENUITEM "&Undo\tCtrl+Z",               ID_EIDT_UNDO
        MENUITEM SEPARATOR
        MENUITEM "Cu&t\tCtrl+X",                ID_EIDT_CUT
        MENUITEM "&Copy\tCtrl+C",               ID_EIDT_COPY
        MENUITEM "&Paste\tCtrl+V",              ID_EIDT_PASTE
        MENUITEM "De&lete\tCtrl+Del",           ID_EIDT_DELETE
        MENUITEM SEPARATOR
        MENUITEM "&Selete All",                 ID_EIDT_SELETEALL
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&Help",                       ID_HELP_HELP
        MENUITEM "&About PopPad2...",           ID_HELP_ABOUTPOPPAD2
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//

POPPAD2 ACCELERATORS
BEGIN
    VK_BACK,        ID_EIDT_UNDO,           VIRTKEY, ALT, NOINVERT
    VK_DELETE,      ID_EIDT_DELETE,         VIRTKEY, NOINVERT
    VK_DELETE,      ID_EIDT_CUT,            VIRTKEY, SHIFT, NOINVERT
    VK_F1,          ID_HELP_HELP,           VIRTKEY, NOINVERT
    VK_INSERT,      ID_EIDT_COPY,           VIRTKEY, CONTROL, NOINVERT
    VK_INSERT,      ID_EIDT_PASTE,          VIRTKEY, SHIFT, NOINVERT
    "^C",           ID_EIDT_COPY,           ASCII,  NOINVERT
    "^V",           ID_EIDT_PASTE,          ASCII,  NOINVERT
    "^X",           ID_EIDT_CUT,            ASCII,  NOINVERT
    "^Z",           ID_EIDT_UNDO,           ASCII,  NOINVERT
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


PopPad2.c

#include <windows.h>
#include "resource.h"

#define ID_EDIT     1

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

TCHAR szAppName[] = TEXT("PopPad2");
TCHAR szMenuName[] = TEXT("IDR_MENU1");   

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    HACCEL   hAccel;
    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(hInstance, szAppName);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = szMenuName;
    wndclass.lpszClassName = szAppName;

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    HMENU hMenu;
    hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));

    hwnd = CreateWindow(szAppName, szAppName,
        WS_OVERLAPPEDWINDOW,
        GetSystemMetrics(SM_CXSCREEN) / 4,
        GetSystemMetrics(SM_CYSCREEN) / 4,
        GetSystemMetrics(SM_CXSCREEN) / 2,
        GetSystemMetrics(SM_CYSCREEN) / 2,
        NULL, hMenu, hInstance, NULL);

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

    //把加速键表加载到内存并获得它的句柄
    hAccel = LoadAccelerators(hInstance, szAppName);    

    while (GetMessage(&msg, NULL, 0, 0))
    {
        //处理菜单命令的快捷键。 如果指定快捷键表中有项) ,然后将WM_COMMAND或WM_SYSCOMMAND消息直接发送到
        //指定的窗口过程,则该函数会将WM_KEYDOWN或WM_SYSKEYDOWN消息转换为WM_COMMAND或WM_SYSCOMMAND消息 
        if (!TranslateAccelerator(hwnd, hAccel, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return msg.wParam;
}

AskConfirmation(HWND hwnd)
{
    return MessageBox(hwnd, TEXT("Really want to close PopPad2?"),
        szAppName, MB_YESNO | MB_ICONQUESTION);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HWND hwndEdit;
    int         iSelect, iEnable;

    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_INITMENUPOPUP:
        //Edit在菜单中位置索引为1
        if (lParam == 1)
        {
            EnableMenuItem((HMENU)wParam, ID_EIDT_UNDO,
                SendMessage(hwndEdit, EM_CANUNDO, 0, 0) ?
                MF_ENABLED : MF_GRAYED);

            //只有当剪切板当前包好文本时,Paste菜单项才应该被启用
            EnableMenuItem((HMENU)wParam, ID_EIDT_PASTE,
                IsClipboardFormatAvailable(CF_TEXT) ?
                MF_ENABLED : MF_GRAYED);

            //只有在编辑控件中有文本被选中时,Cut、Copy和Deete选项才应该被启用
            iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0);

            if (HIWORD(iSelect) == LOWORD(iSelect))
                iEnable = MF_GRAYED;
            else
                iEnable = MF_ENABLED;

            EnableMenuItem((HMENU)wParam, ID_EIDT_CUT, iEnable);
            EnableMenuItem((HMENU)wParam, ID_EIDT_COPY, iEnable);
            EnableMenuItem((HMENU)wParam, ID_EIDT_DELETE, iEnable);
            return 0;
        }
        break;

    case WM_COMMAND:
        if (lParam)
        {
            if (LOWORD(lParam) == ID_EDIT &&
                (HIWORD(wParam) == EN_ERRSPACE ||
                    HIWORD(wParam) == EN_MAXTEXT))
                MessageBox(hwnd, TEXT("Edit control out of space."),
                    szAppName, MB_OK | MB_ICONSTOP);
            return 0;
        }
        else switch (LOWORD(wParam))
        {
        case ID_FILE_NEW:
        case ID_FILE_OPEN:
        case ID_FILE_SAVE:
        case ID_FILE_SAVEAS:
        case ID_FILE_PRINT:
            MessageBeep(0);
            return 0;

        case ID_FILE_EXIT:
            SendMessage(hwnd, WM_CLOSE, 0, 0);
            return 0;

        case ID_EIDT_UNDO:
            SendMessage(hwndEdit, WM_UNDO, 0, 0);
            return 0;

        case ID_EIDT_CUT:
            SendMessage(hwndEdit, WM_CUT, 0, 0);
            return 0;

        case ID_EIDT_COPY:
            SendMessage(hwndEdit, WM_COPY, 0, 0);
            return 0;

        case ID_EIDT_PASTE:
            SendMessage(hwndEdit, WM_PASTE, 0, 0);
            return 0;

        case ID_EIDT_DELETE:
            SendMessage(hwndEdit, WM_CLEAR, 0, 0);
            return 0;

        case ID_EIDT_SELETEALL:
            SendMessage(hwndEdit, EM_SETSEL, 0, -1);
            return 0;

        case ID_HELP_HELP:
            MessageBox(hwnd, TEXT("Help not yet implemented!"),
                szAppName, MB_OK | MB_ICONEXCLAMATION);
            return 0;

        case ID_HELP_ABOUTPOPPAD2:
            MessageBox(hwnd, TEXT("POPPAD2 (c) Charles Petzold, 1998"),
                szAppName, MB_OK | MB_ICONINFORMATION);
            return 0;
        }
        break;

    case WM_CLOSE:
        if (IDYES == AskConfirmation(hwnd))
            DestroyWindow(hwnd);
        return 0;

    case WM_QUERYENDSESSION:
        if (IDYES == AskConfirmation(hwnd))
            return 1;
        else
            return 0;

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

十一、对话框

11.1.模态对话框

创建一个About1对话框

About1.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
ABOUT1                  ICON                    "About1.ico"


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

AboutBox DIALOGEX 32, 32, 211, 127
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,79,99,50,14
    LTEXT           "About1",IDC_STATIC,82,29,42,8
    LTEXT           "About Derek Demo",IDC_STATIC,70,50,101,15
    LTEXT           "derek 2022/12/12",IDC_STATIC,69,70,124,13
    ICON            "About1",IDC_STATIC,17,23,20,20
END


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU
BEGIN
    POPUP "Help"
    BEGIN
        MENUITEM "About About1...",             ID_HELP_ABOUTABOUT1
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    "AboutBox", DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 204
        TOPMARGIN, 7
        BOTTOMMARGIN, 120
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//

AboutBox AFX_DIALOG_LAYOUT
BEGIN
    0
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 About1.rc 使用
//
#define IDI_ICON1                       101
#define IDR_MENU1                       102
#define IDD_DIALOG1                     103
#define ID_HELP_ABOUTABOUT1             40001

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         40002
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

About1.c

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL    CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);

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

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

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    HMENU hMenu;
    hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));

    hwnd = CreateWindow(szAppName, TEXT("About Box Demo Program"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, hMenu, 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 HINSTANCE hInstance;

    switch (message)
    {
    case WM_CREATE:
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
        return 0;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case ID_HELP_ABOUTABOUT1:
            DialogBox(hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc);
            break;
        }
        return 0;

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

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
        case IDCANCEL:
            EndDialog(hDlg, 0);
            return TRUE;
        }
        break;
    }
    return FALSE;
}

11.2.更复杂的对话框

About2.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
ABOUT2               ICON                    "ABOUT2.ico"


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU
BEGIN
    POPUP "Help"
    BEGIN
        MENUITEM "About",                       ID_HELP_ABOUT
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

AboutBox DIALOGEX 0, 0, 359, 219
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,77,191,50,14
    PUSHBUTTON      "取消",IDCANCEL,155,188,50,14
    ICON            "About2",IDC_STATIC,18,19,21,20
    LTEXT           "About2",IDC_STATIC,102,15,81,17
    LTEXT           "About derek Demo Program",IDC_STATIC,67,32,184,10
    GROUPBOX        "Color",IDC_STATIC,36,52,81,127
    RADIOBUTTON     "Black",IDC_BLACK,44,63,32,10,WS_TABSTOP
    RADIOBUTTON     "Blue",IDC_BLUE,44,77,29,10
    RADIOBUTTON     "Green",IDC_GREEN,44,91,35,10
    RADIOBUTTON     "Cyan",IDC_CYAN,44,105,33,10
    RADIOBUTTON     "Red",IDC_RED,44,119,29,10
    RADIOBUTTON     "Magenta",IDC_MAGENTA,43,133,44,10
    RADIOBUTTON     "Yellow",IDC_YELLOW,43,147,36,10
    RADIOBUTTON     "White",IDC_WHITE,44,161,35,10
    GROUPBOX        "Figure",IDC_STATIC,209,126,83,49
    RADIOBUTTON     "Rectangle",IDC_RECT,215,140,48,10,WS_TABSTOP
    RADIOBUTTON     "Elipse",IDC_ELLIPSE,215,158,34,10
    LISTBOX         IDC_PAINT,191,52,116,63,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    AboutBox, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 352
        TOPMARGIN, 7
        BOTTOMMARGIN, 212
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//

AboutBox AFX_DIALOG_LAYOUT
BEGIN
    0
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 About2.rc 使用
//
#define IDI_ICON1                       101
#define IDR_MENU1                       102
#define IDD_DIALOG1                     105
#define IDC_BLACK                       1001
#define IDC_BLUE                        1002
#define IDC_GREEN                       1003
#define IDC_CYAN                        1004
#define IDC_RED                         1005
#define IDC_MAGENTA                     1006
#define IDC_YELLOW                      1007
#define IDC_WHITE                       1010
#define IDC_RECT                        1011
#define IDC_ELLIPSE                     1012
#define IDC_PAINT                       1013
#define ID_HELP_ABOUT                   40001

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        106
#define _APS_NEXT_COMMAND_VALUE         40002
#define _APS_NEXT_CONTROL_VALUE         1014
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

About2.c

/*------------------------------------------
   ABOUT2.C -- About Box Demo Program No. 2
               (c) Charles Petzold, 1998
  ------------------------------------------*/

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL    CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);

int iCurrentColor = IDC_BLACK,
iCurrentFigure = IDC_RECT;

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

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

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    HMENU hMenu;
    hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));

    hwnd = CreateWindow(szAppName, TEXT("About Box Demo Program"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, hMenu, hInstance, NULL);

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

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

void PaintWindow(HWND hwnd, int iColor, int iFigure)
{
    static COLORREF crColor[8] = { RGB(0,   0, 0), RGB(0,   0, 255),
                                   RGB(0, 255, 0), RGB(0, 255, 255),
                                   RGB(255,   0, 0), RGB(255,   0, 255),
                                   RGB(255, 255, 0), RGB(255, 255, 255) };

    HBRUSH          hBrush;
    HDC             hdc;
    RECT            rect;

    hdc = GetDC(hwnd);
    GetClientRect(hwnd, &rect);
    hBrush = CreateSolidBrush(crColor[iColor - IDC_BLACK]);
    hBrush = (HBRUSH)SelectObject(hdc, hBrush);

    if (iFigure == IDC_RECT)
        Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
    else
        Ellipse(hdc, rect.left, rect.top, rect.right, rect.bottom);

    DeleteObject(SelectObject(hdc, hBrush));
    ReleaseDC(hwnd, hdc);
}

void PaintTheBlock(HWND hCtrl, int iColor, int iFigure)
{
    InvalidateRect(hCtrl, NULL, TRUE);
    UpdateWindow(hCtrl);
    PaintWindow(hCtrl, iColor, iFigure);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HINSTANCE hInstance;
    PAINTSTRUCT      ps;

    switch (message)
    {
    case WM_CREATE:
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
        return 0;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case ID_HELP_ABOUT:
            if (DialogBox(hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc))
                InvalidateRect(hwnd, NULL, TRUE);
            return 0;
        }
        break;

    case WM_PAINT:
        BeginPaint(hwnd, &ps);
        EndPaint(hwnd, &ps);

        PaintWindow(hwnd, iCurrentColor, iCurrentFigure);
        return 0;

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

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    static HWND hCtrlBlock;
    static int  iColor, iFigure;

    switch (message)
    {
    case WM_INITDIALOG:
        iColor = iCurrentColor;
        iFigure = iCurrentFigure;

        CheckRadioButton(hDlg, IDC_BLACK, IDC_WHITE, iColor);
        CheckRadioButton(hDlg, IDC_RECT, IDC_ELLIPSE, iFigure);

        hCtrlBlock = GetDlgItem(hDlg, IDC_PAINT);

        SetFocus(GetDlgItem(hDlg, iColor));
        return FALSE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            iCurrentColor = iColor;
            iCurrentFigure = iFigure;
            EndDialog(hDlg, TRUE);
            return TRUE;

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return TRUE;

        case IDC_BLACK:
        case IDC_RED:
        case IDC_GREEN:
        case IDC_YELLOW:
        case IDC_BLUE:
        case IDC_MAGENTA:
        case IDC_CYAN:
        case IDC_WHITE:
            iColor = LOWORD(wParam);
            CheckRadioButton(hDlg, IDC_BLACK, IDC_WHITE, LOWORD(wParam));
            PaintTheBlock(hCtrlBlock, iColor, iFigure);
            return TRUE;

        case IDC_RECT:
        case IDC_ELLIPSE:
            iFigure = LOWORD(wParam);
            CheckRadioButton(hDlg, IDC_RECT, IDC_ELLIPSE, LOWORD(wParam));
            PaintTheBlock(hCtrlBlock, iColor, iFigure);
            return TRUE;
        }
        break;

    case WM_PAINT:
        PaintTheBlock(hCtrlBlock, iColor, iFigure);
        break;
    }
    return FALSE;
}

11.3.项目PopPad3

resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 PopPad3.rc 使用
//
//#define POPPAD                        101
//#define IDR_MENU1                     102
//#define IDR_ACCELERATOR1              103
#define IDD_DIALOG1                     104
#define About                           104
#define ID_FILE_NEW                     40001
#define ID_FILE_OPEN                    40002
#define ID_FILE_SAVE                    40003
#define ID_FILE_SAVEAS                  40004
#define ID_FILE_PRINT                   40005
#define ID_FILE_EXIT                    40006
#define ID_EDIT_UNDO                    40007
#define ID_EDIT_CUT                     40008
#define ID_EDIT_COPY                    40009
#define ID_EDIT_PASTE                   40010
#define ID_EDIT_DELETE                  40011
#define ID_EDIT_SELECTALL               40012
#define ID_SEARCH_FIND                  40013
#define ID_SEARCH_FINDNEXT              40014
#define ID_SEARCH_REPLACE               40015
#define ID_FORMAT_FONT                  40016
#define ID_HELP_HELP                    40017
#define ID_HELP_ABOUTPOPPAD             40018

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        106
#define _APS_NEXT_COMMAND_VALUE         40031
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

PopPad3.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
POPPAD                  ICON                    "poppad.ico"


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

POPPAD MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&New\tCtrl+N",                ID_FILE_NEW
        MENUITEM "&Open...\tCtrl+O",            ID_FILE_OPEN
        MENUITEM "&Save\tCtrl+S",               ID_FILE_SAVE
        MENUITEM "Save &As...",                 ID_FILE_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "&Print\tCtrl+P",              ID_FILE_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       ID_FILE_EXIT
    END
    POPUP "&Edit"
    BEGIN
        MENUITEM "&Undo\tCtrl+Z",               ID_EDIT_UNDO
        MENUITEM SEPARATOR
        MENUITEM "Cu&t\tCtrl+X",                ID_EDIT_CUT
        MENUITEM "&Copy\tCtrl+C",               ID_EDIT_COPY
        MENUITEM "&Paste\tCtrl+V",              ID_EDIT_PASTE
        MENUITEM "De&lete\tDel",                ID_EDIT_DELETE
        MENUITEM SEPARATOR
        MENUITEM "&Select All",                 ID_EDIT_SELECTALL
    END
    POPUP "&Search"
    BEGIN
        MENUITEM "&Find...\tCtrl+F",            ID_SEARCH_FIND
        MENUITEM "Find &Next\tF3",              ID_SEARCH_FINDNEXT
        MENUITEM "&Replace...\tCtrl+R",         ID_SEARCH_REPLACE
    END
    POPUP "F&ormat"
    BEGIN
        MENUITEM "&Font...",                    ID_FORMAT_FONT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&Help",                       ID_HELP_HELP
        MENUITEM "&About PopPad...",            ID_HELP_ABOUTPOPPAD
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

ABOUTBOX DIALOGEX 32, 32, 190, 129
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,65,92,50,14
    LTEXT           "PopPad",IDC_STATIC,72,18,57,15
    LTEXT           "Popup Editor for Windows",IDC_STATIC,48,42,104,14
    LTEXT           "by derek,20221214",IDC_STATIC,48,63,114,18
    ICON            "POPPAD",IDC_STATIC,17,17,20,20
END


/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//

POPPAD ACCELERATORS
BEGIN
    "^C",           ID_EDIT_COPY,           ASCII,  NOINVERT
    "^X",           ID_EDIT_CUT,            ASCII,  NOINVERT
    "^V",           ID_EDIT_PASTE,          ASCII,  NOINVERT
    "^Z",           ID_EDIT_UNDO,           ASCII,  NOINVERT
    "^N",           ID_FILE_NEW,            ASCII,  NOINVERT
    "^O",           ID_FILE_OPEN,           ASCII,  NOINVERT
    "^P",           ID_FILE_PRINT,          ASCII,  NOINVERT
    "^S",           ID_FILE_SAVE,           ASCII,  NOINVERT
    "^F",           ID_SEARCH_FIND,         ASCII,  NOINVERT
    "^R",           ID_SEARCH_REPLACE,      ASCII,  NOINVERT
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    "ABOUTBOX", DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 183
        TOPMARGIN, 7
        BOTTOMMARGIN, 122
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//

ABOUTBOX AFX_DIALOG_LAYOUT
BEGIN
    0
END

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


PopPad3.c

/*---------------------------------------
   POPPAD.C -- Popup Editor
               (c) Charles Petzold, 1998
  ---------------------------------------*/

#include <windows.h>
#include <commdlg.h>
#include "resource.h"

#define EDITID   1
#define UNTITLED TEXT ("(untitled)")

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL    CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);

// Functions in POPFILE.C

void PopFileInitialize(HWND);
BOOL PopFileOpenDlg(HWND, PTSTR, PTSTR);
BOOL PopFileSaveDlg(HWND, PTSTR, PTSTR);
BOOL PopFileRead(HWND, PTSTR);
BOOL PopFileWrite(HWND, PTSTR);

// Functions in POPFIND.C

HWND PopFindFindDlg(HWND);
HWND PopFindReplaceDlg(HWND);
BOOL PopFindFindText(HWND, int*, LPFINDREPLACE);
BOOL PopFindReplaceText(HWND, int*, LPFINDREPLACE);
BOOL PopFindNextText(HWND, int*);
BOOL PopFindValidFind(void);

// Functions in POPFONT.C

void PopFontInitialize(HWND);
BOOL PopFontChooseFont(HWND);
void PopFontSetFont(HWND);
void PopFontDeinitialize(void);

// Functions in POPPRNT.C

BOOL PopPrntPrintFile(HINSTANCE, HWND, HWND, PTSTR);

// Global variables

static HWND  hDlgModeless;
static TCHAR szAppName[] = TEXT("PopPad");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    MSG       msg;
    HWND      hwnd;
    HACCEL    hAccel;
    WNDCLASS  wndclass;

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

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName, NULL,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, szCmdLine);

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

    hAccel = LoadAccelerators(hInstance, szAppName);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (hDlgModeless == NULL || !IsDialogMessage(hDlgModeless, &msg))
        {
            if (!TranslateAccelerator(hwnd, hAccel, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }
    return msg.wParam;
}

void DoCaption(HWND hwnd, TCHAR* szTitleName)
{
    TCHAR szCaption[64 + MAX_PATH];

    wsprintf(szCaption, TEXT("%s - %s"), szAppName,
        szTitleName[0] ? szTitleName : UNTITLED);

    SetWindowText(hwnd, szCaption);
}

void OkMessage(HWND hwnd, TCHAR* szMessage, TCHAR* szTitleName)
{
    TCHAR szBuffer[64 + MAX_PATH];

    wsprintf(szBuffer, szMessage, szTitleName[0] ? szTitleName : UNTITLED);

    MessageBox(hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION);
}

short AskAboutSave(HWND hwnd, TCHAR* szTitleName)
{
    TCHAR szBuffer[64 + MAX_PATH];
    int   iReturn;

    wsprintf(szBuffer, TEXT("Save current changes in %s?"),
        szTitleName[0] ? szTitleName : UNTITLED);

    iReturn = MessageBox(hwnd, szBuffer, szAppName,
        MB_YESNOCANCEL | MB_ICONQUESTION);

    if (iReturn == IDYES)
        if (!SendMessage(hwnd, WM_COMMAND, ID_FILE_SAVE, 0))
            iReturn = IDCANCEL;

    return iReturn;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL      bNeedSave = FALSE;
    static HINSTANCE hInst;
    static HWND      hwndEdit;
    static int       iOffset;
    static TCHAR     szFileName[MAX_PATH], szTitleName[MAX_PATH];
    static UINT      messageFindReplace;
    int              iSelBeg, iSelEnd, iEnable;
    LPFINDREPLACE    pfr;

    switch (message)
    {
    case WM_CREATE:
        hInst = ((LPCREATESTRUCT)lParam)->hInstance;

        // Create the edit control child window

        hwndEdit = CreateWindow(TEXT("edit"), NULL,
            WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
            WS_BORDER | ES_LEFT | ES_MULTILINE |
            ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
            0, 0, 0, 0,
            hwnd, (HMENU)EDITID, hInst, NULL);

        SendMessage(hwndEdit, EM_LIMITTEXT, 32000, 0L);

        // Initialize common dialog box stuff

        PopFileInitialize(hwnd);
        PopFontInitialize(hwndEdit);

        messageFindReplace = RegisterWindowMessage(FINDMSGSTRING);

        DoCaption(hwnd, szTitleName);
        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_INITMENUPOPUP:
        switch (lParam)
        {
        case 1:             // Edit menu

                  // Enable Undo if edit control can do it

            EnableMenuItem((HMENU)wParam, ID_EDIT_UNDO,
                SendMessage(hwndEdit, EM_CANUNDO, 0, 0L) ?
                MF_ENABLED : MF_GRAYED);

            // Enable Paste if text is in the clipboard

            EnableMenuItem((HMENU)wParam, ID_EDIT_PASTE,
                IsClipboardFormatAvailable(CF_TEXT) ?
                MF_ENABLED : MF_GRAYED);

            // Enable Cut, Copy, and Del if text is selected

            SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&iSelBeg,
                (LPARAM)&iSelEnd);

            iEnable = iSelBeg != iSelEnd ? MF_ENABLED : MF_GRAYED;

            EnableMenuItem((HMENU)wParam, ID_EDIT_CUT, iEnable);
            EnableMenuItem((HMENU)wParam, ID_EDIT_COPY, iEnable);
            EnableMenuItem((HMENU)wParam, ID_EDIT_DELETE, iEnable);
            break;

        case 2:             // Search menu

             // Enable Find, Next, and Replace if modeless
             //   dialogs are not already active

            iEnable = hDlgModeless == NULL ?
                MF_ENABLED : MF_GRAYED;

            EnableMenuItem((HMENU)wParam, ID_SEARCH_FIND, iEnable);
            EnableMenuItem((HMENU)wParam, ID_SEARCH_FINDNEXT, iEnable);
            EnableMenuItem((HMENU)wParam, ID_SEARCH_REPLACE, iEnable);
            break;
        }
        return 0;

    case WM_COMMAND:
        // Messages from edit control

        if (lParam && LOWORD(wParam) == EDITID)
        {
            switch (HIWORD(wParam))
            {
            case EN_UPDATE:
                bNeedSave = TRUE;
                return 0;

            case EN_ERRSPACE:
            case EN_MAXTEXT:
                MessageBox(hwnd, TEXT("Edit control out of space."),
                    szAppName, MB_OK | MB_ICONSTOP);
                return 0;
            }
            break;
        }

        switch (LOWORD(wParam))
        {
            // Messages from File menu

        case ID_FILE_NEW:
            if (bNeedSave && IDCANCEL == AskAboutSave(hwnd, szTitleName))
                return 0;

            SetWindowText(hwndEdit, TEXT("\0"));
            szFileName[0] = '\0';
            szTitleName[0] = '\0';
            DoCaption(hwnd, szTitleName);
            bNeedSave = FALSE;
            return 0;

        case ID_FILE_OPEN:
            if (bNeedSave && IDCANCEL == AskAboutSave(hwnd, szTitleName))
                return 0;

            if (PopFileOpenDlg(hwnd, szFileName, szTitleName))
            {
                if (!PopFileRead(hwndEdit, szFileName))
                {
                    OkMessage(hwnd, TEXT("Could not read file %s!"),
                        szTitleName);
                    szFileName[0] = '\0';
                    szTitleName[0] = '\0';
                }
            }

            DoCaption(hwnd, szTitleName);
            bNeedSave = FALSE;
            return 0;

        case ID_FILE_SAVE:
            if (szFileName[0])
            {
                if (PopFileWrite(hwndEdit, szFileName))
                {
                    bNeedSave = FALSE;
                    return 1;
                }
                else
                {
                    OkMessage(hwnd, TEXT("Could not write file %s"),
                        szTitleName);
                    return 0;
                }
            }
            // fall through
        case ID_FILE_SAVEAS:
            if (PopFileSaveDlg(hwnd, szFileName, szTitleName))
            {
                DoCaption(hwnd, szTitleName);

                if (PopFileWrite(hwndEdit, szFileName))
                {
                    bNeedSave = FALSE;
                    return 1;
                }
                else
                {
                    OkMessage(hwnd, TEXT("Could not write file %s"),
                        szTitleName);
                    return 0;
                }
            }
            return 0;

        case ID_FILE_PRINT:
            if (!PopPrntPrintFile(hInst, hwnd, hwndEdit, szTitleName))
                OkMessage(hwnd, TEXT("Could not print file %s"),
                    szTitleName);
            return 0;

        case ID_FILE_EXIT:
            SendMessage(hwnd, WM_CLOSE, 0, 0);
            return 0;

            // Messages from Edit menu

        case ID_EDIT_UNDO:
            SendMessage(hwndEdit, WM_UNDO, 0, 0);
            return 0;

        case ID_EDIT_CUT:
            SendMessage(hwndEdit, WM_CUT, 0, 0);
            return 0;

        case ID_EDIT_COPY:
            SendMessage(hwndEdit, WM_COPY, 0, 0);
            return 0;

        case ID_EDIT_PASTE:
            SendMessage(hwndEdit, WM_PASTE, 0, 0);
            return 0;

        case ID_EDIT_DELETE:
            SendMessage(hwndEdit, WM_CLEAR, 0, 0);
            return 0;

        case ID_EDIT_SELECTALL:
            SendMessage(hwndEdit, EM_SETSEL, 0, -1);
            return 0;

            // Messages from Search menu

        case ID_SEARCH_FIND:
            SendMessage(hwndEdit, EM_GETSEL, 0, (LPARAM)&iOffset);
            hDlgModeless = PopFindFindDlg(hwnd);
            return 0;

        case ID_SEARCH_FINDNEXT:
            SendMessage(hwndEdit, EM_GETSEL, 0, (LPARAM)&iOffset);

            if (PopFindValidFind())
                PopFindNextText(hwndEdit, &iOffset);
            else
                hDlgModeless = PopFindFindDlg(hwnd);

            return 0;

        case ID_SEARCH_REPLACE:
            SendMessage(hwndEdit, EM_GETSEL, 0, (LPARAM)&iOffset);
            hDlgModeless = PopFindReplaceDlg(hwnd);
            return 0;

        case ID_FORMAT_FONT:
            if (PopFontChooseFont(hwnd))
                PopFontSetFont(hwndEdit);

            return 0;

            // Messages from Help menu

        case ID_HELP_HELP:
            OkMessage(hwnd, TEXT("Help not yet implemented!"),
                TEXT("\0"));
            return 0;

        case ID_HELP_ABOUTPOPPAD:
            DialogBox(hInst, TEXT("AboutBox"), hwnd, AboutDlgProc);
            return 0;
        }
        break;

    case WM_CLOSE:
        if (!bNeedSave || IDCANCEL != AskAboutSave(hwnd, szTitleName))
            DestroyWindow(hwnd);

        return 0;

    case WM_QUERYENDSESSION:
        if (!bNeedSave || IDCANCEL != AskAboutSave(hwnd, szTitleName))
            return 1;

        return 0;

    case WM_DESTROY:
        PopFontDeinitialize();
        PostQuitMessage(0);
        return 0;

    default:
        // Process "Find-Replace" messages

        if (message == messageFindReplace)
        {
            pfr = (LPFINDREPLACE)lParam;

            if (pfr->Flags & FR_DIALOGTERM)
                hDlgModeless = NULL;

            if (pfr->Flags & FR_FINDNEXT)
                if (!PopFindFindText(hwndEdit, &iOffset, pfr))
                    OkMessage(hwnd, TEXT("Text not found!"),
                        TEXT("\0"));

            if (pfr->Flags & FR_REPLACE || pfr->Flags & FR_REPLACEALL)
                if (!PopFindReplaceText(hwndEdit, &iOffset, pfr))
                    OkMessage(hwnd, TEXT("Text not found!"),
                        TEXT("\0"));

            if (pfr->Flags & FR_REPLACEALL)
                while (PopFindReplaceText(hwndEdit, &iOffset, pfr));

            return 0;
        }
        break;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            EndDialog(hDlg, 0);
            return TRUE;
        }
        break;
    }
    return FALSE;
}

PpopFile.c

/*------------------------------------------
   POPFILE.C -- Popup Editor File Functions
  ------------------------------------------*/
 
#include <windows.h>
#include <commdlg.h>

static OPENFILENAME ofn ;

void PopFileInitialize (HWND hwnd)
{
     static TCHAR szFilter[] = TEXT ("Text Files (*.TXT)\0*.txt\0")  \
                               TEXT ("ASCII Files (*.ASC)\0*.asc\0") \
                               TEXT ("All Files (*.*)\0*.*\0\0") ;
     
     ofn.lStructSize       = sizeof (OPENFILENAME) ;
     ofn.hwndOwner         = hwnd ;
     ofn.hInstance         = NULL ;
     ofn.lpstrFilter       = szFilter ;
     ofn.lpstrCustomFilter = NULL ;
     ofn.nMaxCustFilter    = 0 ;
     ofn.nFilterIndex      = 0 ;
     ofn.lpstrFile         = NULL ;          // Set in Open and Close functions
     ofn.nMaxFile          = MAX_PATH ;
     ofn.lpstrFileTitle    = NULL ;          // Set in Open and Close functions
     ofn.nMaxFileTitle     = MAX_PATH ;
     ofn.lpstrInitialDir   = NULL ;
     ofn.lpstrTitle        = NULL ;
     ofn.Flags             = 0 ;             // Set in Open and Close functions
     ofn.nFileOffset       = 0 ;
     ofn.nFileExtension    = 0 ;
     ofn.lpstrDefExt       = TEXT ("txt") ;
     ofn.lCustData         = 0L ;
     ofn.lpfnHook          = NULL ;
     ofn.lpTemplateName    = NULL ;
}

BOOL PopFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName)
{
     ofn.hwndOwner         = hwnd ;
     ofn.lpstrFile         = pstrFileName ;
     ofn.lpstrFileTitle    = pstrTitleName ;
     ofn.Flags             = OFN_HIDEREADONLY | OFN_CREATEPROMPT ;
     
     return GetOpenFileName (&ofn) ;
}

BOOL PopFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName)
{
     ofn.hwndOwner         = hwnd ;
     ofn.lpstrFile         = pstrFileName ;
     ofn.lpstrFileTitle    = pstrTitleName ;
     ofn.Flags             = OFN_OVERWRITEPROMPT ;
     
     return GetSaveFileName (&ofn) ;
}

BOOL PopFileRead (HWND hwndEdit, PTSTR pstrFileName)
{
     BYTE   bySwap ;
     DWORD  dwBytesRead ;
     HANDLE hFile ;
     int    i, iFileLength, iUniTest ;
     PBYTE  pBuffer, pText, pConv ;

          // Open the file.

     if (INVALID_HANDLE_VALUE == 
               (hFile = CreateFile (pstrFileName, GENERIC_READ, FILE_SHARE_READ,
                                    NULL, OPEN_EXISTING, 0, NULL)))
          return FALSE ;

          // Get file size in bytes and allocate memory for read.
          // Add an extra two bytes for zero termination.
                    
     iFileLength = GetFileSize (hFile, NULL) ; 
     pBuffer = malloc (iFileLength + 2) ;

          // Read file and put terminating zeros at end.
     
     ReadFile (hFile, pBuffer, iFileLength, &dwBytesRead, NULL) ;
     CloseHandle (hFile) ;
     pBuffer[iFileLength] = '\0' ;
     pBuffer[iFileLength + 1] = '\0' ;

          // Test to see if the text is Unicode

     iUniTest = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE ;
     
     if (IsTextUnicode (pBuffer, iFileLength, &iUniTest))
     {
          pText = pBuffer + 2 ;
          iFileLength -= 2 ;

          if (iUniTest & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
          {
               for (i = 0 ; i < iFileLength / 2 ; i++)
               {
                    bySwap = ((BYTE *) pText) [2 * i] ;
                    ((BYTE *) pText) [2 * i] = ((BYTE *) pText) [2 * i + 1] ;
                    ((BYTE *) pText) [2 * i + 1] = bySwap ;
               }
          }

               // Allocate memory for possibly converted string

          pConv = malloc (iFileLength + 2) ;

               // If the edit control is not Unicode, convert Unicode text to 
               // non-Unicode (ie, in general, wide character).

#ifndef UNICODE
          WideCharToMultiByte (CP_ACP, 0, (PWSTR) pText, -1, pConv, 
                               iFileLength + 2, NULL, NULL) ;

               // If the edit control is Unicode, just copy the string
#else
          lstrcpy ((PTSTR) pConv, (PTSTR) pText) ;
#endif

     }
     else      // the file is not Unicode
     {
          pText = pBuffer ;

               // Allocate memory for possibly converted string.

          pConv = malloc (2 * iFileLength + 2) ;

               // If the edit control is Unicode, convert ASCII text.

#ifdef UNICODE
          MultiByteToWideChar (CP_ACP, 0, pText, -1, (PTSTR) pConv, 
                               iFileLength + 1) ;

               // If not, just copy buffer
#else
          lstrcpy ((PTSTR) pConv, (PTSTR) pText) ;
#endif
     }
     
     SetWindowText (hwndEdit, (PTSTR) pConv) ;
     free (pBuffer) ;
     free (pConv) ;
   
     return TRUE ;
}

BOOL PopFileWrite (HWND hwndEdit, PTSTR pstrFileName)
{
     DWORD  dwBytesWritten ;
     HANDLE hFile ;
     int    iLength ;
     PTSTR  pstrBuffer ;
     WORD   wByteOrderMark = 0xFEFF ;

          // Open the file, creating it if necessary
     
     if (INVALID_HANDLE_VALUE == 
               (hFile = CreateFile (pstrFileName, GENERIC_WRITE, 0, 
                                    NULL, CREATE_ALWAYS, 0, NULL)))
          return FALSE ;

          // Get the number of characters in the edit control and allocate
          // memory for them.
     
     iLength = GetWindowTextLength (hwndEdit) ;
     pstrBuffer = (PTSTR) malloc ((iLength + 1) * sizeof (TCHAR)) ;
     
     if (!pstrBuffer)
     {
          CloseHandle (hFile) ;
          return FALSE ;
     }

          // If the edit control will return Unicode text, write the
          // byte order mark to the file.

#ifdef UNICODE
     WriteFile (hFile, &wByteOrderMark, 2, &dwBytesWritten, NULL) ;
#endif

          // Get the edit buffer and write that out to the file.
     
     GetWindowText (hwndEdit, pstrBuffer, iLength + 1) ;
     WriteFile (hFile, pstrBuffer, iLength * sizeof (TCHAR), 
                &dwBytesWritten, NULL) ;
     
     if ((iLength * sizeof (TCHAR)) != (int) dwBytesWritten)
     {
          CloseHandle (hFile) ;
          free (pstrBuffer) ;
          return FALSE ;
     }
     
     CloseHandle (hFile) ;
     free (pstrBuffer) ;
     
     return TRUE ;
}

PopFind.c

/*--------------------------------------------------------
   POPFIND.C -- Popup Editor Search and Replace Functions
  --------------------------------------------------------*/
 
#include <windows.h>
#include <commdlg.h>
#include <tchar.h>            // for _tcsstr (strstr for Unicode & non-Unicode)

#define MAX_STRING_LEN   256

static TCHAR szFindText [MAX_STRING_LEN] ;
static TCHAR szReplText [MAX_STRING_LEN] ;

HWND PopFindFindDlg (HWND hwnd)
{
     static FINDREPLACE fr ;       // must be static for modeless dialog!!!
     
     fr.lStructSize      = sizeof (FINDREPLACE) ;
     fr.hwndOwner        = hwnd ;
     fr.hInstance        = NULL ;
     fr.Flags            = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ;
     fr.lpstrFindWhat    = szFindText ;
     fr.lpstrReplaceWith = NULL ;
     fr.wFindWhatLen     = MAX_STRING_LEN ;
     fr.wReplaceWithLen  = 0 ;
     fr.lCustData        = 0 ;
     fr.lpfnHook         = NULL ;
     fr.lpTemplateName   = NULL ;
     
     return FindText (&fr) ;
}

HWND PopFindReplaceDlg (HWND hwnd)
{
     static FINDREPLACE fr ;       // must be static for modeless dialog!!!
     
     fr.lStructSize      = sizeof (FINDREPLACE) ;
     fr.hwndOwner        = hwnd ;
     fr.hInstance        = NULL ;
     fr.Flags            = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ;
     fr.lpstrFindWhat    = szFindText ;
     fr.lpstrReplaceWith = szReplText ;
     fr.wFindWhatLen     = MAX_STRING_LEN ;
     fr.wReplaceWithLen  = MAX_STRING_LEN ;
     fr.lCustData        = 0 ;
     fr.lpfnHook         = NULL ;
     fr.lpTemplateName   = NULL ;
     
     return ReplaceText (&fr) ;
}

BOOL PopFindFindText (HWND hwndEdit, int * piSearchOffset, LPFINDREPLACE pfr)
{
     int    iLength, iPos ;
     PTSTR  pstrDoc, pstrPos ;
     
          // Read in the edit document
     
     iLength = GetWindowTextLength (hwndEdit) ;
     
     if (NULL == (pstrDoc = (PTSTR) malloc ((iLength + 1) * sizeof (TCHAR))))
          return FALSE ;
     
     GetWindowText (hwndEdit, pstrDoc, iLength + 1) ;
     
          // Search the document for the find string
     
     pstrPos = _tcsstr (pstrDoc + * piSearchOffset, pfr->lpstrFindWhat) ;
     free (pstrDoc) ;
     
          // Return an error code if the string cannot be found
     
     if (pstrPos == NULL)
          return FALSE ;
     
          // Find the position in the document and the new start offset
     
     iPos = pstrPos - pstrDoc ;
     * piSearchOffset = iPos + lstrlen (pfr->lpstrFindWhat) ;
     
          // Select the found text
     
     SendMessage (hwndEdit, EM_SETSEL, iPos, * piSearchOffset) ;
     SendMessage (hwndEdit, EM_SCROLLCARET, 0, 0) ;
     
     return TRUE ;
}

BOOL PopFindNextText (HWND hwndEdit, int * piSearchOffset)
{
     FINDREPLACE fr ;
     
     fr.lpstrFindWhat = szFindText ;
     
     return PopFindFindText (hwndEdit, piSearchOffset, &fr) ;
}

BOOL PopFindReplaceText (HWND hwndEdit, int * piSearchOffset, LPFINDREPLACE pfr)
{
          // Find the text
     
     if (!PopFindFindText (hwndEdit, piSearchOffset, pfr))
          return FALSE ;
     
          // Replace it
     
     SendMessage (hwndEdit, EM_REPLACESEL, 0, (LPARAM) pfr->lpstrReplaceWith) ;
     
     return TRUE ;
}

BOOL PopFindValidFind (void)
{
     return * szFindText != '\0' ;
}

PopFont.c

/*------------------------------------------
   POPFONT.C -- Popup Editor Font Functions
  ------------------------------------------*/
 
#include <windows.h>
#include <commdlg.h>

static LOGFONT logfont ;
static HFONT   hFont ;

BOOL PopFontChooseFont (HWND hwnd)
{
     CHOOSEFONT cf ;
     
     cf.lStructSize    = sizeof (CHOOSEFONT) ;
     cf.hwndOwner      = hwnd ;
     cf.hDC            = NULL ;
     cf.lpLogFont      = &logfont ;
     cf.iPointSize     = 0 ;
     cf.Flags          = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS ;
     cf.rgbColors      = 0 ;
     cf.lCustData      = 0 ;
     cf.lpfnHook       = NULL ;
     cf.lpTemplateName = NULL ;
     cf.hInstance      = NULL ;
     cf.lpszStyle      = NULL ;
     cf.nFontType      = 0 ;               // Returned from ChooseFont
     cf.nSizeMin       = 0 ;
     cf.nSizeMax       = 0 ;
     
     return ChooseFont (&cf) ;
}

void PopFontInitialize (HWND hwndEdit)
{
     GetObject (GetStockObject (SYSTEM_FONT), sizeof (LOGFONT), 
                (PTSTR) &logfont) ;

     hFont = CreateFontIndirect (&logfont) ;
     SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFont, 0) ;
}

void PopFontSetFont (HWND hwndEdit)
{
     HFONT hFontNew ;
     RECT  rect ;
     
     hFontNew = CreateFontIndirect (&logfont) ;
     SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFontNew, 0) ;
     DeleteObject (hFont) ;
     hFont = hFontNew ;
     GetClientRect (hwndEdit, &rect) ;
     InvalidateRect (hwndEdit, &rect, TRUE) ;
}

void PopFontDeinitialize (void)
{
     DeleteObject (hFont) ;
}

PopPrnt0.c

/*---------------------------------------------------------------
   POPPRNT0.C -- Popup Editor Printing Functions (dummy version)
  ---------------------------------------------------------------*/
 
#include <windows.h>

BOOL PopPrntPrintFile (HINSTANCE hInst, HWND hwnd, HWND hwndEdit,
                       PTSTR pstrTitleName)
{
     return FALSE ;
}

posted on 2022-12-13 23:49  zhang_derek  阅读(107)  评论(0编辑  收藏  举报

导航