SDK 资源

目录

资源的使用

带资源的.exe文件的编译方式

向窗口发送消息

菜单

加载菜单

菜单消息

图标

光标

快捷键

字符串


资源的使用

在VS2019中,点击视图下的其他窗口,资源视图,就可以看到本项目的所有资源

SDK 资源_第1张图片

鼠标右键添加-资源,即可添加各种类型的资源

SDK 资源_第2张图片

带资源的.exe文件的编译方式

SDK 资源_第3张图片

不带资源的文件是如何编译的?

SDK 资源_第4张图片

带资源的编译链接过程:

SDK 资源_第5张图片

SDK 资源_第6张图片

可以适应Editor查看到可执行文件的最后面就是.h的二进制文件

向窗口发送消息

SendMessage:将指定的消息发送到一个或多个窗口,它调用指定窗口的窗口过程,知道该窗口过程处理完该消息后才返回

LRESULT SendMessage(
  [in] HWND   hWnd,
  [in] UINT   Msg,
  [in] WPARAM wParam,
  [in] LPARAM lParam
);

PostMessage:将消息投递到消息队列

BOOL PostMessageA(
  [in, optional] HWND   hWnd,
  [in]           UINT   Msg,
  [in]           WPARAM wParam,
  [in]           LPARAM lParam
);

区别:

  • PostMessage :发送的消息进消息队列(阻塞式)
  • SendMessage:发送的消息不进入消息队列,直接调用窗口过程函数。

参数说明:

  • 参数1:指定窗口,是一个句柄
  • 参数2:发送的消息
  • 参数3:其他的消息特定信息。
  • 参数4:其他的消息特定信息。
HWND hCalc = FindWindow("Notepad", NULL);
if (hCalc == NULL) {
	return FALSE;
}
PostMessage(hCalc,WM_QUIT,0,NULL);

HWND hNotepad = FindWindow("Notepad", NULL);
if (hNotepad == NULL) {
	return FALSE;
}
HWND hEdit = GetWindow(hNotepad, GW_CHILD);
PostMessage(hEdit, WM_KEYDOWN, 'A', 0);
PostMessage(hEdit, WM_KEYUP, 'B', 0);
PostMessage(hEdit, WM_KEYDOWN, 'C', 0);

HDC hdc = GetDC(hEdit);
while (TRUE) {
	SetTextColor(hdc,RGB(255,0,0));
	TextOut(hdc, 0, 0, "SB", 2);
}
ReleaseDC(hEdit, hdc);

菜单

SDK 资源_第7张图片

加载菜单

方式一:通过代码创建菜单,子菜单

    // 创建菜单资源
	HMENU HMenu = CreateMenu();

	// 给窗口添加菜单,向菜单中添加菜单项
	BOOL ret;
	ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("文件(&F)"));
	ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("编辑(&E)"));
	
    // 把菜单加载给窗口
    SetMenu(hWnd, HMenu);

	// 添加子菜单
	HMENU hSubMenu = GetSubMenu(HMenu, 0);
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, _T("打开(&O)"));
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, _T("保存(&O)"));
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, _T("退出(&O)"));
    
    // 把菜单加载给窗口
	SetMenu(hWnd, HMenu);

AppendMenu()函数:将新项追加到指定菜单栏、下拉菜单、子菜单或快捷菜单的末尾。 可以使用此函数指定菜单项的内容、外观和行为。

BOOL AppendMenuA(
  [in]           HMENU    hMenu,
  [in]           UINT     uFlags,
  [in]           UINT_PTR uIDNewItem,
  [in, optional] LPCSTR   lpNewItem
);

参数说明:

  • 参数1:菜单资源的句柄
  • 参数2:控制新菜单项的外观和行为。MF_STRING:指定菜单项为文本字符串;MF_POPUP:指定菜单项打开下拉菜单或子菜单。 
  • 参数3:如果 uFlags 参数设置为 MF_POPUP,则为下拉菜单或子菜单的句柄。
  • 参数4:如果有包含MF_STRING,指向以 null 结尾的字符串的指针。

返回值:如果该函数成功,则返回值为非零值。 如果函数失败,则返回值为零。 

SetMenu()函数:将新菜单分配给指定的窗口。

BOOL SetMenu(
  [in]           HWND  hWnd,
  [in, optional] HMENU hMenu
);

参数说明:

  • 参数1:要为其分配菜单的窗口的句柄。
  • 参数2:新菜单的句柄

返回值:如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。

这种方式还需要手动声明宏定义

#define IDM_OPEN 102
#define IDM_SAVE 103
#define IDM_EXIT 104

方式二:通过编辑菜单资源,给主窗口设计一个菜单,推荐使用

File菜单的属性中是没有ID的

SDK 资源_第8张图片

下面的子菜单是有ID,可以通过快捷键 CTRL+O 打开

SDK 资源_第9张图片

加载菜单

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

LoadMenu函数:获取菜单资源的句柄

HMENU LoadMenuA(
  [in, optional] HINSTANCE hInstance,
  [in]           LPCSTR    lpMenuName
);

参数说明:

  • 参数1:该进程的实例句柄
  • 参数2:MAKEINTRESOURCE(IDR_MENU1),加载资源

SDK 资源_第10张图片

子窗口Open的ID是:ID_OPEN;子窗口Save的ID是:ID_SAVE

这种方式下,系统会在.h文件给出对应的宏定义,我们不需要管

菜单消息

WM_COMMAND:当点击菜单的时候,会响系统发送一条WM_COMMAND消息。当消息处理函数接受到WM_COMMAND消息的时候处理菜单选项内容。

作用:用于处理菜单或快捷键 都会发送COMMAND消息,有窗口过程函数处理,该消息的参数:

  • 参数1:窗口句柄
  • 参数2:消息ID,识别消息是来自于哪一个
  • 参数3:低字可以拿到ID,高字可以拿到菜单的来源
  • 参数4:控件句柄

处理WM_COMMAND消息实例

	switch (uMsg) {
	case WM_COMMAND:
		if (LOWORD(wParam) == ID_SAVE) {

		}
		else if (LOWORD(wParam) == IDB_LOGIN) {

		}
		break;

通过LOWOED()拿到wParam参数的低字,这里面信息就是ID值,前面定义的ID_SAVE,ID_OPEN

这样可以根据不同的菜单选择给出对应的处理方式。

图标

图标资源

SDK 资源_第11张图片

LoadIcon函数:获取图标资源的句柄

HICON LoadIconA(
  [in, optional] HINSTANCE hInstance,
  [in]           LPCSTR    lpIconName
);

参数说明:

  • 参数1:DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的图标的文件。可以使用GetHandle();若要加载预定义的系统图标,请将此参数设置为 NULL
  • 参数2:

    如果 hInstance 为非 NULL, 则 lpIconName 按名称或序号指定图标资源。 必须使用 MAKEINTRESOURCE 宏打包此序号;如果 hInstance 为 NULL, 则 lpIconName 将指定标识符

代码如下:

    ws.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(MN_ICON));
    wc.hIcon = LoadIcon(NULL, IDI_HAND);

光标

光标资源

SDK 资源_第12张图片

LoadCursor:获取光标资源的句柄

HCURSOR LoadCursorA(
  [in, optional] HINSTANCE hInstance,
  [in]           LPCSTR    lpCursorName
);

参数说明:

  • 参数1:DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的游标的文件。使用GetMoudleHandle;请若要加载预定义的系统游标,请将此参数设置为 NULL
  • 参数2:如果 hInstance 为非 NULL, 则 lpCursorName 按名称或序号指定游标资源。 必须使用 MAKEINTRESOURCE 宏打包此序号;如果 hInstance 为 NULL, 则 lpCursorName 将指定标识符

返回值:如果函数成功,则返回值是新加载的游标的句柄。如果函数失败,则返回值为 NULL

代码如下:

    ws.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_POINTER));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

快捷键

方式一:创建快捷键表

SDK 资源_第13张图片

方式二:创建快捷键表

	// 申请堆控件,因为快捷键需要全局访问
	// 申请100字节的局部堆地址空间
	// 这样设计的原因,内存空间申请释放,产生了很多碎片,通过把这些碎片整理得到一个更大的内存空间,这是地址变化了
	
	// 申请局部堆地址
	HLOCAL hMenu = LocalAlloc(LHND,100);
	LPVOID lpMemory = LocalLock(hMenu);
	LocalFree(hMenu);


	// 申请全局堆地址
	// 原因:16位系统,有全局堆和局部堆;32位,两者合并,只有一个堆
	GlobalAlloc(LHND, 100);


	// 申请堆空间
	// 新的API,返回值是一个地址,前两个函数的底层是调用这个
	//HeapAlloc();
	
	// 申请堆地址空间,可以指定内存属性
	//VirtualAlloc();

	ACCEL *pAccelNews = (ACCEL*)HeapAlloc(GetProcessHeap(), 0, sizeof(ACCEL)*2);

	if (pAccelNews == nullptr) {
		ShowErrorMsg();
		return 0;
	}

	// 创建快捷键表
	HACCEL hAccel =  CreateAcceleratorTable(pAccelNews, 2);
	if (hAccel == NULL) {
		ShowErrorMsg();
		return 0;
	}

	pAccelNews[0].fVirt = FALT | FCONTROL | FVIRTKEY;
	pAccelNews[0].key = 'A';
	pAccelNews[0].cmd = WM_COMMAND;

	pAccelNews[1].fVirt = FALT | FCONTROL | FVIRTKEY;
	pAccelNews[1].key = 'B';
	pAccelNews[1].cmd = WM_COMMAND;

加载快捷键:

LoadAccelerators():加载指定的快捷键表。

HACCEL LoadAcceleratorsW(
  [in, optional] HINSTANCE hInstance,
  [in]           LPCWSTR   lpTableName
);

参数说明:

  • 参数1:模块的句柄,其可执行文件包含要加载的加速器表。
  • 参数2:要加载的快捷键表的名称。 或者,此参数可以在低序字中指定快捷键表资源的资源标识符,在高序字中指定零。 若要创建此值,请使用 MAKEINTRESOURCE 宏。

返回值:如果函数成功,则返回值是加载的加速器表的句柄;如果函数失败,则返回值为 NULL。

TranslateAccelerator():把按键消息转换成WM_COMMAND消息,处理菜单命令的快捷键。 如果指定快捷键表中) 键有条目,函数会将WM_KEYDOWN或WM_SYSKEYDOWN ( 消息转换为WM_COMMAND或WM_SYSCOMMAND消息,然后将WM_COMMANDWM_SYSCOMMAND消息直接发送到指定的窗口过程。 在窗口过程处理完消息之前,TranslateAccelerator 不会返回 

int TranslateAcceleratorA(
  [in] HWND   hWnd,
  [in] HACCEL hAccTable,
  [in] LPMSG  lpMsg
);

参数说明:

  • 参数1:要转换其消息的窗口的句柄。
  • 参数2:快捷键表的句柄。 加速键表必须已通过对 LoadAccelerators 函数的调用加载或通过调用 CreateAcceleratorTable 函数创建。
  • 参数3:指向 MSG 结构的指针,该结构包含使用 GetMessage 或 PeekMessage 函数从调用线程的消息队列检索到的消息信息。

返回值:如果该函数成功,则返回值为非零值;如果函数失败,则返回值为零。

代码如下:

HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));

	BOOL bRet;
	MSG msg;
	// 消息循环是以线程为单位的
	while ((bRet = GetMessage(&msg,NULL,0,0)) != 0) {
		if (bRet == -1) {
			break;
		}
		else {
			if (!TranslateAccelerator(hDlg, hAccel, &msg)) {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
	}

字符串

字符串资源:

SDK 资源_第14张图片

LoadString():加载字符串资源

int LoadStringA(
  [in, optional] HINSTANCE hInstance,
  [in]           UINT      uID,
  [out]          LPSTR     lpBuffer,
  [in]           int       cchBufferMax
);

参数说明:

  • 参数1:其可执行文件包含字符串资源的模块实例的句柄。 若要获取应用程序本身的句柄,请使用 NULL 调用 GetModuleHandle 函数。
  • 参数2:要加载的字符串的标识符。
  • 参数3:如果 cchBufferMax 为非零) ,则接收字符串的缓冲区 (;如果 cchBufferMax 为零) ,则为指向字符串资源本身 (只读指针。 必须具有足够的长度,才能将指针保留 (8 个字节) 。
  • 参数4:缓冲区的大小(以字符为单位)。 如果字符串的长度超过指定的字符数,则字符串将被截断并以 null 结尾。 如果此参数为 0,则 lpBuffer 会收到指向字符串资源本身的只读指针。

示例代码

TCHAR szBuf[MAXBYTE];
TCHAR szTitle[MAXWORD];
LoadString(g_hInstance, IDS_SAVE, szBuf, sizeof(szBuf));
LoadString(g_hInstance, IDS_Title, szTitle, sizeof(szTitle));
MessageBox(hwndDlg, szBuf,szTitle, MB_OK);

总代码-1

#include 
#include "resource.h"

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

LRESULT OnMenu(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static char szAppName[] = "Hello Win";

    //设计窗口类
    WNDCLASS ws;
    ws.style = CS_HREDRAW | CS_VREDRAW;
    ws.lpfnWndProc = &MyWndProc;
    ws.cbClsExtra = NULL;
    ws.cbWndExtra = NULL;
    ws.hInstance = hInstance;
    ws.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(MN_ICON));
    ws.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_POINTER));
    ws.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    ws.lpszMenuName = NULL;//MAKEINTRESOURCE(MN_MAIN);
    ws.lpszClassName = szAppName;

    //注册窗口类
    if (!RegisterClass(&ws))
    {
        MessageBox(NULL, "注册窗口失败", "错误提示:", MB_OK);
        return 0;
    }

    //创建窗口
    HWND HelloHwnd = CreateWindow(
        szAppName,
        "The Hello Program",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        LoadMenu(hInstance,MAKEINTRESOURCE(MN_MAIN)),   //拿到资源的句柄
        hInstance,
        NULL,
        );
     
    //显示窗口
    ShowWindow(HelloHwnd, iCmdShow);

    //更新窗口,调用Begin使得画面有效
    UpdateWindow(HelloHwnd);
    SetTimer(HelloHwnd, 1, 100, NULL);

    //加载快捷键资源
    HACCEL haccel = LoadAccelerators(hInstance,MAKEINTRESOURCE(ACCEL_TEXT));

    //建立消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(HelloHwnd,haccel,&msg))
        { 
            TranslateMessage(&msg); //将msg结构传给Windows,进行一些键盘转换
            DispatchMessage(&msg);  //使得自动调用消息窗口句柄所属的窗口过程函数
        }
    }

    return msg.wParam;  //结构的wParam字段是传递给PostQuitMessage函数的值(通常是0)。然后return叙述将退出WinMain并终止程序。
}

//实现消息过程函数
LRESULT CALLBACK MyWndProc(
    HWND hwnd,      //
    UINT message,   //
    WPARAM wParam,  //参数wParam表明窗口是非最小化还是非最大化,是最小化、最大化,还是隐藏
    LPARAM lParam   //lParam参数包含了新窗口的大小,新宽度和新高度均为16位值,合在一起成为32位的lParam。
)
{
    switch (message)
    {
    case WM_CREATE:
        return 0;
    case WM_COMMAND:
        return OnMenu(hwnd, message, wParam, lParam);
    case WM_DESTROY:
        PostQuitMessage(0); //发送WM_QUIT以结束消息循环
        return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);    //操作系统待处理我们不处理的消息
}

//wParam 高位代表消息来源,地位代表ID
LRESULT OnMenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    //判断是否来自于菜单
    if (HIWORD(wParam) == 0)
    {
        switch (LOWORD(wParam))
        {
        case MN_FILE_NEW:
            MessageBox(NULL, "MN_FILE_NEW", "菜单选项:", MB_OK);
            break;
        case MN_FILE_OPEN:
            MessageBox(NULL, "MN_FILE_OPEN", "菜单选项:", MB_OK);
            break;
        case MN_FILE_SAVE:
            MessageBox(NULL, "MN_FILE_SAVE", "菜单选项:", MB_OK);
            break;
        case MN_EDT_REDO:
            MessageBox(NULL, "MN_EDT_REDO", "菜单选项:", MB_OK);
            break;
        case MN_EDT_UNDO:
            MessageBox(NULL, "MN_EDT_UNDO", "菜单选项:", MB_OK);
            break;
        default:
            break;
        }
    }
    //
    else if (HIWORD(wParam) == 1)
    {
        switch (LOWORD(wParam))
        {
        case ACCEL_CTRL_A:
            MessageBox(NULL, "ACCEL_CTRL_A", "菜单选项:", MB_OK);
            break;
        case ACCEL_CTRL_ALT_A:
            MessageBox(NULL, "ACCEL_CTRL_ALT_A", "菜单选项:", MB_OK);
            break;
        default:
            break;
        }
    }
    return 0;
}

总代码-2

#include 
#include 
#include 
#include 

#define IDM_OPEN 102
#define IDM_SAVE 103
#define IDM_EXIT 104

using namespace std;

string g_Text;
TEXTMETRIC g_tm; // 字体信息


void ShowErrorMsg() {
	LPVOID lpMsgBuf;
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&lpMsgBuf,
		0,
		NULL
	);
	MessageBox(NULL, (LPCTSTR)lpMsgBuf, _T("ERROR"), MB_OK | MB_ICONINFORMATION);
	LocalFree(lpMsgBuf);
}


LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
	OutputDebugString(_T("[51asm]: WM_Create\n"));

	HDC hdc = GetDC(hwnd);
	SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
	GetTextMetrics(hdc, &g_tm);
	ReleaseDC(hwnd, hdc);

	return TRUE;
}


LRESULT OnClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
	OutputDebugString(_T("[51asm]: WM_Close\n"));
	DestroyWindow(hwnd);           
	return FALSE;
}


LRESULT OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
	OutputDebugString(_T("[51asm]: WM_Destory\n"));
	PostMessage(hwnd, WM_QUIT, 0, NULL);
	return TRUE;
}



LRESULT OnChar(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
	TCHAR szBuf[MAXBYTE];

	if ((char)wParam == '\r') {
		g_Text += (char)wParam;
		g_Text += '\n';
	}
	else if ((char)wParam == '\b') {
		if (!g_Text.empty()) {
			g_Text.pop_back();
		}
	}
	else {
		g_Text += (char)wParam;
	}



	wsprintf(szBuf, _T("[51asm] OnChar %s\n"), g_Text.data());
	OutputDebugString(szBuf);

	// 	// 获取窗口HDC,用API时一定要阅读文档
	// 	// 获取一个新的句柄时,往往是需要释放的,否则该进程的内存会越来愈大
	// 	//HDC hdc = GetWindowDC(hwnd);   // 非客户区域
	// 
	// 	HDC hdc = GetDC(hwnd);
	// 
	// 	//TextOut(hdc, 0, 0, g_Text.data(), g_Text.length());
	// 	// 获取窗口客户区域大小
	// 	RECT rc;
	// 	GetClientRect(hwnd, &rc);
	// 
	// 	// 创建一个白色的刷子
	// 	HGDIOBJ hBrushOld;
	// 	HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
	// 	//check,可能会失败,需要GetLastError
	// 
	// 
	// 	// DC选择刷子
	// 	hBrushOld = SelectObject(hdc, hBrush);
	// 	//check
	// 
	// 	// 绘制背景
	// 	FillRect(hdc, &rc, hBrush);
	// 	//check
	// 
	// 	// 绘制文本
	// 	DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
	// 	//check
	// 
	// 	// 还原默认刷子
	// 	SelectObject(hdc, hBrushOld);
	// 	//check
	// 
	// 	// 释放刷子
	// 	DeleteObject(hBrush);
	// 	//check
	// 
	// 	// 释放DC
	// 	ReleaseDC(hwnd, hdc);
	// 	//check
	// 
	// 	SetCaretPos(g_tm.tmAveCharWidth * g_Text.length(), 0);
	// 	ShowCaret(hwnd);

		// 采用方式2:
	RECT rc;
	GetClientRect(hwnd, &rc);

	// 把整个窗口设置为无效区域
	InvalidateRect(hwnd, NULL, TRUE);

	// 每当写入后就会产生WM_PAINT消息

	return TRUE;
}


LRESULT OnSetFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm] OnSetFocus\n"));

	CreateCaret(hwnd, (HBITMAP)NULL, 1, g_tm.tmHeight);
	SetCaretPos(0, 0);
	ShowCaret(hwnd);

	return TRUE;
}


LRESULT OnKillFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm] OnKillFocus\n"));

	DestroyCaret();

	return TRUE;
}


// 绘制
LRESULT OnPaint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm] OnPaint\n"));

	//  // 方式1
	// 	HDC hdc = GetDC(hwnd);
	// 
	// 	// 获取窗口客户区域大小
	// 	RECT rc;
	// 	GetClientRect(hwnd, &rc);
	// 
	// 	// 绘制文本
	// 	DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
	// 	//check
	// 
	// 	// 释放DC
	// 	ReleaseDC(hwnd, hdc);
	// 	//check
	// 
	// 	// 将无效区域设置为有效区域
	// 	ValidateRect(hwnd,&rc);




		// 方式2:推荐
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hwnd, &ps);

	RECT rc;
	GetClientRect(hwnd, &rc);

	DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);

	EndPaint(hwnd, &ps);  // 自动把无效区域设置为有效区域
	return TRUE;


	// 无效区域,有变化的区域,系统需要重新绘制,WM_PAINT来了
	// 有效区域,不需要变化,系统不需要冲洗绘制
	// 设置无效区域为有效区域属于GDI的API

	return TRUE;
}


// 擦除背景
LRESULT OnEraseBackground(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm] OnEraseBackground\n"));

	DestroyCaret();

	return TRUE;
}


LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm] OnCommand\n"));
	WORD wID = LOWORD(wParam);
	switch (wID) {
	case IDM_OPEN:
		MessageBox(NULL, "打开", "51asm", MB_OK);
		break;
	case IDM_EXIT:
		PostQuitMessage(0); // 给自己投递QUIT消息
		break;
	}
	return TRUE;
}





// 消息处理
// 可以下断点debug调试分析消息,在监视这里可以 uMsg.wm 可以以看到
// 先创建非客户区,再创建客户区,还有创建窗口等很多消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	LRESULT lResult = FALSE;

	switch (uMsg) {
	case WM_CREATE:
		lResult = OnCreate(hwnd, uMsg, wParam, lParam); 
		break;
	case WM_CLOSE:
		lResult = OnClose(hwnd, uMsg, wParam, lParam);
		break;
	case WM_DESTROY:
		lResult = OnDestroy(hwnd, uMsg, wParam, lParam);
		break;
	case WM_CHAR:
		lResult = OnChar(hwnd, uMsg, wParam, lParam);
		break;
	case WM_SETFOCUS:
		lResult = OnSetFocus(hwnd, uMsg, wParam, lParam);
		break;
	case WM_KILLFOCUS:
		lResult = OnKillFocus(hwnd, uMsg, wParam, lParam);
		break;
	case WM_ERASEBKGND:  // 刷背景,最大化时候会刷背景,最小化不会刷
		lResult = OnEraseBackground(hwnd, uMsg, wParam, lParam);
		break;
	case WM_PAINT:      //绘制消息,窗口(界面)发生变化就会产生这个消息
		lResult = OnPaint(hwnd, uMsg, wParam, lParam);
		break;
	case WM_COMMAND:      //绘制消息,窗口(界面)发生变化就会产生这个消息
		lResult = OnCommand(hwnd, uMsg, wParam, lParam);
		break;
	}

	if (!lResult) {
		return DefWindowProc(hwnd, uMsg, wParam, lParam); // 默认窗口过程处理函数,包括销毁窗口
	}
	return lResult;
}



int WINAPI wWinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPWSTR lpCmdLine,
	int nShowCmd
) {
	// 骚操作,在本窗口给别的窗口发消息
	// 启动一个记事本,发送退出消息
// 	HWND hCalc = FindWindow("Notepad", NULL);
// 	if (hCalc == NULL) {
// 		return FALSE;
// 	}
// 	// 需要让GetMassage()拿到这个消息
// 	//SendMessage(hCalc,WM_QUIT,0,NULL);  调用对方过程函数,消息没进消息队列,处理不到
// 	//PostMessage();
// 	PostMessage(hCalc,WM_QUIT,0,NULL);

// 	HWND hNotepad = FindWindow("Notepad", NULL);
// 	if (hNotepad == NULL) {
// 		return FALSE;
// 	}
// 	HWND hEdit = GetWindow(hNotepad, GW_CHILD);
// 	PostMessage(hEdit, WM_KEYDOWN, 'A', 0);
// 	PostMessage(hEdit, WM_KEYUP, 'B', 0);
// 	PostMessage(hEdit, WM_KEYDOWN, 'C', 0);
// 	
// 	HDC hdc = GetDC(hEdit);
// 	while (TRUE) {
// 		SetTextColor(hdc,RGB(255,0,0));
// 		TextOut(hdc, 0, 0, "SB", 2);
// 	}
// 	ReleaseDC(hEdit, hdc);



	// 创建窗口实例
	TCHAR szWndClassName[] = { _T("CR41WndClassName") };

	WNDCLASSEX wc = { 0 };
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_HAND);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
	wc.lpszMenuName = NULL;
	wc.lpszClassName = szWndClassName;

	// 注册窗口
	if (RegisterClassEx(&wc) == 0) {
		ShowErrorMsg();
		return 0;
	}


	// 创建窗口
	TCHAR szWndName[] = { _T("51asm") };
	HWND hWnd = CreateWindowEx(0,
		szWndClassName,
		szWndName,
		WS_OVERLAPPEDWINDOW,  //组合属性,可拉伸窗口
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		NULL,
		NULL,
		hInstance,
		NULL);



	if (hWnd == NULL) {
		ShowErrorMsg();
		return 0;
	}

	// 菜单
	HMENU hMenu = CreateMenu();

	// 弹出菜单
	BOOL ret;
	ret = AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hMenu, "文件(&F)");
	ret = AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hMenu, "编辑(&E)");
	SetMenu(hWnd, hMenu);

	// 添加子菜单
	HMENU hSubMenu = GetSubMenu(hMenu, 0);
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, "打开(&O)");
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, "保存(&O)");
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, "退出(&O)");

	SetMenu(hWnd, hMenu);


	RECT rc;
	GetClientRect(hWnd, &rc);

	// 控件:带有特殊功能的窗口
	// 按钮 编辑框
//  	HWND hEdit = CreateWindowEx(0,
//  		_T("Edit"),
//  		NULL,                                               
//  		WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE, //组合属性,可拉伸窗口  最后一个表示支持多行
//  		0,
//  		0,
//  		rc.right - rc.left,
//  		rc.bottom - rc.top, 
//  		hWnd,
//  		NULL,
//  		hInstance,
//  		NULL);



		// 显示,更新窗口
	ShowWindow(hWnd, SW_SHOWNORMAL); // 调用Show时候父子窗口都会被调用
	//ShowWindow(hChild, SW_SHOWNORMAL);  非子窗口需要单独show
	UpdateWindow(hWnd);  // 产生WM_PAINT

	SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));




	// 消息循环
	BOOL bRet;
	MSG msg;
	while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
	{
		if (bRet == -1) {
			break;
		}
		else {
			TranslateMessage(&msg);// 转换键盘消息
			DispatchMessage(&msg); // 派发消息
		}
	}


	return msg.wParam;
}

总代码-3:

#include 
#include 
#include 
#include 

#define IDM_OPEN 102
#define IDM_SAVE 103
#define IDM_EXIT 104

using namespace std;

string g_Text;
TEXTMETRIC g_tm; // 字体信息


void ShowErrorMsg() {
	LPVOID lpMsgBuf;
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&lpMsgBuf,
		0,
		NULL
	);
	MessageBox(NULL, (LPCTSTR)lpMsgBuf, _T("ERROR"), MB_OK | MB_ICONINFORMATION);
	LocalFree(lpMsgBuf);
}


LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm]: WM_Create\n"));
	return TRUE;
}

// 当你关闭窗口点击确定后就会向窗口发送WM_Destroy消息,就意味要销毁窗口,
LRESULT OnClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm]: WM_Close\n"));
	return FALSE;
}

// 这个工作可以交给系统处理,也可以自己处理
LRESULT OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm]: WM_Destory\n"));
	return TRUE;
}



LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm] OnCommand\n"));
	WORD wID = LOWORD(wParam);
	switch (wID) {
	case IDM_OPEN:
		MessageBox(NULL, _T("打开"), _T("51asm"), MB_OK);
		break;
	case IDM_EXIT:
		PostQuitMessage(0); // 给自己投递QUIT消息
		break;
	}
	return TRUE;
}



LRESULT OnKeyDown(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm]: OnKeyDown\n"));
	return TRUE;
}



LRESULT OnKeyUp(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	OutputDebugString(_T("[51asm]: OnKeyUp\n"));
	return TRUE;
}



// 消息处理
// 可以下断点debug调试分析消息,在监视这里可以 uMsg.wm 可以以看到
// 先创建非客户区,再创建客户区,还有创建窗口等很多消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	LRESULT lResult = FALSE;

	switch (uMsg) {
	case WM_CREATE:
		lResult = OnCreate(hwnd, uMsg, wParam, lParam); // 表示处理WM_CREATE消息的函数
		break;
	case WM_CLOSE:
		lResult = OnClose(hwnd, uMsg, wParam, lParam);
		break;
	case WM_DESTROY:
		lResult = OnDestroy(hwnd, uMsg, wParam, lParam);
		break;
	case WM_COMMAND:      //绘制消息,窗口(界面)发生变化就会产生这个消息
		lResult = OnCommand(hwnd, uMsg, wParam, lParam);
		break;
	case WM_KEYDOWN:      //绘制消息,窗口(界面)发生变化就会产生这个消息
		lResult = OnKeyDown(hwnd, uMsg, wParam, lParam);
		break;
	case WM_KEYUP:      //绘制消息,窗口(界面)发生变化就会产生这个消息
		lResult = OnKeyUp(hwnd, uMsg, wParam, lParam);
		break;
	}

	if (!lResult) {
		return DefWindowProc(hwnd, uMsg, wParam, lParam); // 默认窗口过程处理函数,包括销毁窗口
	}
	return lResult;
}

/*
	客户区(用户区)与非客户区(系统的) NC


*/

int WINAPI wWinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPWSTR lpCmdLine,
	int nShowCmd
) {
	// 申请堆控件,因为快捷键需要全局访问
	// 申请100字节的局部堆地址空间
	// 这样设计的原因,内存空间申请释放,产生了很多碎片,通过把这些碎片整理得到一个更大的内存空间,这是地址变化了
	
	// 申请局部堆地址
	HLOCAL hMenu = LocalAlloc(LHND,100);
	LPVOID lpMemory = LocalLock(hMenu);
	LocalFree(hMenu);


	// 申请全局堆地址
	// 原因:16位系统,有全局堆和局部堆;32位,两者合并,只有一个堆
	GlobalAlloc(LHND, 100);


	// 申请堆空间
	// 新的API,返回值是一个地址,前两个函数的底层是调用这个
	//HeapAlloc();
	
	// 申请堆地址空间,可以指定内存属性
	//VirtualAlloc();

	ACCEL *pAccelNews = (ACCEL*)HeapAlloc(GetProcessHeap(), 0, sizeof(ACCEL)*2);

	if (pAccelNews == nullptr) {
		ShowErrorMsg();
		return 0;
	}

	// 创建快捷键表
	HACCEL hAccel =  CreateAcceleratorTable(pAccelNews, 2);
	if (hAccel == NULL) {
		ShowErrorMsg();
		return 0;
	}

	pAccelNews[0].fVirt = FALT | FCONTROL | FVIRTKEY;
	pAccelNews[0].key = 'A';
	pAccelNews[0].cmd = WM_COMMAND;

	pAccelNews[1].fVirt = FALT | FCONTROL | FVIRTKEY;
	pAccelNews[1].key = 'B';
	pAccelNews[1].cmd = WM_COMMAND;





	// 创建窗口实例
	TCHAR szWndClassName[] = { _T("CR41WndClassName") };

	WNDCLASSEX wc = { 0 };
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_HAND);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
	wc.lpszMenuName = NULL;
	wc.lpszClassName = szWndClassName;

	// 注册窗口
	if (RegisterClassEx(&wc) == 0) {
		ShowErrorMsg();
		return 0;
	}


	// 创建窗口
	TCHAR szWndName[] = { _T("51asm") };
	HWND hWnd = CreateWindowEx(0,
		szWndClassName,
		szWndName,
		WS_OVERLAPPEDWINDOW,  //组合属性,可拉伸窗口
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		NULL,
		NULL,
		hInstance,
		NULL);



	if (hWnd == NULL) {
		ShowErrorMsg();
		return 0;
	}

	// 菜单
	HMENU HMenu = CreateMenu();

	// 弹出菜单
	BOOL ret;
	ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("文件(&F)"));
	ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("编辑(&E)"));
	SetMenu(hWnd, HMenu);

	// 添加子菜单
	HMENU hSubMenu = GetSubMenu(HMenu, 0);
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, _T("打开(&O)"));
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, _T("保存(&O)"));
	ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, _T("退出(&O)"));

	SetMenu(hWnd, HMenu);


// 	RECT rc;
// 	GetClientRect(hWnd, &rc);


	// 显示,更新窗口
	ShowWindow(hWnd, SW_SHOWNORMAL); // 调用Show时候父子窗口都会被调用
	//ShowWindow(hChild, SW_SHOWNORMAL);  非子窗口需要单独show
	UpdateWindow(hWnd);  // 产生WM_PAINT

	SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));




	// 消息循环
	BOOL bRet;
	MSG msg;
	while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
	{
		if (bRet == -1) {
			break;
		}
		else {
			// 转换快捷键消息 WM_COMMAND
			if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
				TranslateMessage(&msg);// 转换键盘消息
				DispatchMessage(&msg); // 派发消息
			}
		}
	}

	// 删除快捷键表
	DestroyAcceleratorTable(hAccel);
	HeapFree(GetProcessHeap(), 0, pAccelNews);

	return msg.wParam;
}

你可能感兴趣的:(SDK,C++,SDK)