SDK 资源

news2024/12/23 10:18:59

目录

资源的使用

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

向窗口发送消息

菜单

加载菜单

菜单消息

图标

光标

快捷键

字符串


资源的使用

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

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

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

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

带资源的编译链接过程:

可以适应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);

菜单

加载菜单

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

    // 创建菜单资源
	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的

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

加载菜单

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),加载资源

子窗口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

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

图标

图标资源

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);

光标

光标资源

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);

快捷键

方式一:创建快捷键表

方式二:创建快捷键表

	// 申请堆控件,因为快捷键需要全局访问
	// 申请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);
			}
		}
	}

字符串

字符串资源:

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 <windows.h>
#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 <windows.h>
#include <tchar.h>
#include <fstream>
#include <string>

#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 <windows.h>
#include <tchar.h>
#include <fstream>
#include <string>

#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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1144675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据特征工程 | 主成分分析(Python)

特征抽取(feature extraction)和特征选择(feature selection)不一样,特征抽取是从原特征集中推导出有用的信息构成新的特征集。特征选择是从原特征集中选择一部分子集作为训练特征。 特征抽取将数据集从一个特征空间投影到了一个更低维度的特征空间。 主成分分析(princ…

C++设计模式_16_Adapter 适配器

Adapter 适配器也是属于“接口隔离”模式&#xff0c;也是间接思想在某一个层面的应用。 文章目录 1. 动机( Motivation)2. 模式定义3. 结构 (Structure)4. Adapter 适配器的代码实现5. 要点总结6. 其他参考 1. 动机( Motivation) 在软件系统中&#xff0c;由于应用环境的变化…

python:使用Scikit-image对遥感影像进行傅里叶变换特征提取(fourier)

作者:CSDN @ _养乐多_ 在本博客中,我们将介绍如何使用Scikit-Image来进行傅里叶变换特征提取(fourier),并且提供一个示例代码,演示了如何在单波段遥感图像上应用这些方法。 傅里叶变换特征提取是一种数学工具,用于将图像中的细节、纹理和边缘信息以不同频率的方式呈现…

[SpringCloud] Nacos 简介

目录 一、Nacos&#xff0c;启动&#xff01; 1、安装 Nacos 2、运行 Nacos 3、Nacos 服务注册 二、Nacos 服务多级存储模型 1、服务跨集群分配 2、NacosRule 负载均衡&#xff08;优先本地&#xff09; 3、服务实例的权重设置 4、环境隔离 三、Nacos 注册中心细节分…

私有云:【2】AD域的安装

私有云&#xff1a;【2】AD域的安装 1、使用vmwork创建虚拟机2、启动配置虚拟机3、安装域服务4、配置域服务器 1、使用vmwork创建虚拟机 新建虚拟机 稍后安装操作系统 选择win2012&#xff0c;如下图 设置名称及路径 分配硬盘大小&#xff0c;默认60G即可 镜像选择win2012的is…

【数据结构】数组和字符串(九):稀疏矩阵的链接存储:十字链表的插入、查找、删除操作

文章目录 4.2.1 矩阵的数组表示4.2.2 特殊矩阵的压缩存储a. 对角矩阵的压缩存储b~c. 三角、对称矩阵的压缩存储d. 稀疏矩阵的压缩存储——三元组表4.2.3三元组表的转置、加法、乘法、操作4.2.4十字链表0. 十字链表的创建、遍历打印、销毁1. 插入2. 查找3. 删除4. 主函数5. 代码…

C++学习day--22 宏和结构体

1、宏 1.1 为什么要使用宏 1、提高代码的可读性和可维护性 2、避免函数调用&#xff0c;提高程序效率 1.2 什么是宏 它是一种预处理器指令&#xff0c;在预编译阶段将宏名替换为后面的替换体 。 1.3 宏的定义 由三部分组成&#xff1a; #define WIDTH 960 三个部分分别是&…

Python 作用域:局部作用域、全局作用域和使用 global 关键字

变量只在创建它的区域内可用。这被称为作用域。 局部作用域 在函数内部创建的变量属于该函数的局部作用域&#xff0c;并且只能在该函数内部使用。 示例&#xff1a;在函数内部创建的变量在该函数内部可用&#xff1a; def myfunc():x 300print(x)myfunc()函数内部的函数 …

C++——类和对象(中)(2)尚未完结

拷贝构造 概念 在现实生活中&#xff0c;可能存在一个与你一样的自己&#xff0c;我们称其为双胞胎。 那在创建对象时&#xff0c;可否创建一个与已存在对象一某一样的新对象呢? 拷贝构造函数: 只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&…

模块化编程

1、函数单独写在一个文件中.c&#xff0c;然后声明也写在一个文件中.h,在mian.c中引用 2、安装目录下面的文件夹用<>,在自己文件夹目录下就是"" 3、创建自己的&#xff08;先把函数放在c文件&#xff0c;再创建头h文件&#xff09;

ruoyi-plus创建模块、自动生成代码

ruoyi-plus自动生成代码 1、创建模块 复制其他部分的resouce过来 修改yml文件 2 修改Nacos 2.1 修改数据库文件 复制其他数据库的链接 &#xff0c;改为自己新建的数据库名字 修改为自己要生成的数据库 新建数据库的yaml文件 3 重启docker的ruoyi-gen服务 docker re…

数据结构【DS】B树

m阶B树的核心特性: Q&#xff1a;根节点的子树数范围是多少&#xff1f;关键字数的范围是多少&#xff1f; A&#xff1a;根节点的子树数∈[2, m],关键字数∈[1, m-1]。 Q&#xff1a;其他结点的子树数范围是多少&#xff1f;关键字数范围是多少&#xff1f; Q&#xff1a;对任…

SSD1306 oled显示屏的驱动SPI接口

有IIC接口 和SPI接口 还有8080,6080接口等 arduino SPI接口 直接使用u8g2库实现 //U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock*/ 13, /* data*/ 11, /* cs*/ 10, /* dc*/ 9, /* reset*/ 8); asrpro(SPI接口按下方修改&#xff0c;IIC接口官方有驱动&…

环形链表(C++解法)

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#…

Vmware下的虚拟机NAT连接后仍然木有网络

问题描述 出现在主机能ping通&#xff0c;互联网ping不通的情况。 废话 假设已经设置了网络配置文件IPADDR。 那么&#xff0c;NAT后可以访问互联网的前提是&#xff1a;这个IPADDR的网段在Vmware软件设置的网段内。 解决 在Vmware虚拟网络设置选项卡中&#xff0c;进NAT配…

10000字!图解机器学习特征工程

文章目录 引言特征工程1.特征类型1.1 结构化 vs 非结构化数据1.2 定量 vs 定性数据 2.数据清洗2.1 数据对齐2.2 缺失值处理 原文链接&#xff1a;https://www.showmeai.tech/article-detail/208 作者&#xff1a;showmeAI 引言 上图为大家熟悉的机器学习建模流程图&#xff0c;…

TYWZOJ 礼品配对包装 题解

文章目录 题目描述输入格式输出格式样例样例输入样例输出 数据范围与提示思路与部分实现完整代码 题目描述 爱与愁大神在这家目标店买了 2 x 2x 2x 份礼物&#xff0c;打算分给班级同学。其中有 x x x 份黑礼品&#xff0c; x x x 份白礼品&#xff0c; 2 x 2 2x2 2x2 个空…

计网小题题库整理第一轮(面向期末基础)(3)

基础选择题的最后一章更新&#xff0c;看完期末75至少没问题~ 前情提要&#xff1a; 计网小题题库整理第一轮&#xff08;12期&#xff09; 一.选择题 1、 目前,最流行的以太网组网的拓扑结构是&#xff08; C &#xff09;。 A&#xff09; 总线结构 B&#xff09; 环…

如何能够在发现问题和提问的时候一并带出自己的解决方案

1. 充分理解问题&#xff1a; 在提出问题之前&#xff0c;确保你已经完全理解了问题的本质。从不同的角度分析问题&#xff0c;确保没有遗漏任何重要的信息或者上下文。 2. 进行自我调查和研究&#xff1a; 在向他人寻求帮助之前&#xff0c;尝试自己解决问题。利用网络资源…

Go学习第十三章——Gin(入门与路由)

Go web框架——Gin&#xff08;入门与路由&#xff09; 1 Gin框架介绍1.1 基础介绍1.2 安装Gin1.3 快速使用 2 路由2.1 基本路由GET请求POST请求 2.2 路由参数2.3 路由分组基本分组带中间件的分组 2.4 重定向 1 Gin框架介绍 github链接&#xff1a;https://github.com/gin-gon…