Win32-鼠标-定时器-菜单
鼠标&定时器&菜单
// 鼠标消息 - 窗口绘制 - 窗口拖动重影 // WinMouse.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "stdio.h" HINSTANCE g_hInst = NULL; HANDLE g_hStdOut = NULL; int g_nXPos = 0; int g_nYPos = 0; void PrintLog( LPSTR pszLog ) { WriteConsole( g_hStdOut, pszLog, strlen(pszLog), NULL, NULL ); } void OnPaint( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps = { 0 }; HDC hDC = BeginPaint( hWnd, &ps ); CHAR szText[] = "Hello Mouse"; TextOut( hDC, g_nXPos, g_nYPos, szText, strlen(szText) ); EndPaint( hWnd, &ps ); } LRESULT CALLBACK WndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { switch( nMsg ) { case WM_PAINT: OnPaint( hWnd, nMsg, wParam, lParam ); break; case WM_LBUTTONDOWN: PrintLog( "WM_LBUTTONDOWN\n" ); break; case WM_LBUTTONUP: PrintLog( "WM_LBUTTONUP\n" ); break; case WM_RBUTTONDOWN: PrintLog( "WM_RBUTTONDOWN\n" ); break; case WM_RBUTTONUP: PrintLog( "WM_RBUTTONUP\n" ); break; case WM_MOUSEMOVE: { int nX = LOWORD(lParam); int nY = HIWORD(lParam); POINT ptScreen = { 0 }; ptScreen.x = nX; ptScreen.y = nY; ClientToScreen( hWnd, &ptScreen ); CHAR szText[260] = { 0 }; sprintf( szText, "WM_MOUSEMOVE: X=%d(%d),Y=%d(%d)\n", nX, ptScreen.x, nY, ptScreen.y ); PrintLog( szText ); if( wParam & MK_CONTROL ) { PrintLog( "WM_MOUSEMOVE: MK_CONTROL\n" ); } if( wParam & MK_LBUTTON ) { PrintLog( "WM_MOUSEMOVE: MK_LBUTTON\n" ); } g_nXPos = LOWORD(lParam); g_nYPos = HIWORD(lParam); InvalidateRect( hWnd, NULL, FALSE ); } break; case WM_DESTROY: PostQuitMessage( 0 ); break; } return DefWindowProc( hWnd, nMsg, wParam, lParam ); } BOOL RegisterWnd( LPSTR pszClassName ) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof( wce ); wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hbrBackground = HBRUSH(COLOR_WINDOW); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInst; wce.lpfnWndProc = WndProc; wce.lpszClassName = pszClassName; wce.lpszMenuName = NULL; wce.style = CS_HREDRAW|CS_VREDRAW; ATOM nAtom = RegisterClassEx( &wce ); if( 0 == nAtom ) { return FALSE; } return TRUE; } HWND CreateWnd( LPSTR pszClassName ) { HWND hWnd = CreateWindowEx( 0, pszClassName, "MyWnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInst, NULL ); return hWnd; } void DisplayWnd( HWND hWnd ) { ShowWindow( hWnd, SW_SHOW ); UpdateWindow( hWnd ); } void Message( ) { MSG msg = { 0 }; while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } void NewConsole( ) { AllocConsole( ); g_hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { NewConsole( ); g_hInst = hInstance; RegisterWnd( "MyWnd" ); HWND hWnd = CreateWnd( "MyWnd" ); DisplayWnd( hWnd ); Message( ); return 0; } -------------------------------------------------------------------------------- // 窗口绘制 - 点击左键不放画框框,取消鼠标拖动显示很多重影 // 并显示x和y坐标 #include "stdafx.h" #include "stdio.h" HINSTANCE g_hInst = NULL; HANDLE g_hStdOut = NULL; int g_nXPos = 0; int g_nYPos = 0; int g_nX1Rect = 0; int g_nY1Rect = 0; int g_nX2Rect = 0; int g_nY2Rect = 0; void PrintLog( LPSTR pszLog ) { WriteConsole( g_hStdOut, pszLog, strlen(pszLog), NULL, NULL ); } void OnPaint( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps = { 0 }; HDC hDC = BeginPaint( hWnd, &ps ); CHAR szText[] = "Hello Mouse"; TextOut( hDC, g_nXPos, g_nYPos, szText, strlen(szText) ); Rectangle( hDC, g_nX1Rect, g_nY1Rect, g_nX2Rect, g_nY2Rect ); EndPaint( hWnd, &ps ); } LRESULT CALLBACK WndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { switch( nMsg ) { case WM_PAINT: OnPaint( hWnd, nMsg, wParam, lParam ); break; case WM_LBUTTONDOWN: { PrintLog( "WM_LBUTTONDOWN\n" ); g_nX1Rect = LOWORD( lParam ); g_nY1Rect = HIWORD( lParam ); } break; case WM_LBUTTONUP: { PrintLog( "WM_LBUTTONUP\n" ); g_nX2Rect = LOWORD( lParam ); g_nY2Rect = HIWORD( lParam ); InvalidateRect( hWnd, NULL, TRUE ); } break; case WM_RBUTTONDOWN: PrintLog( "WM_RBUTTONDOWN\n" ); break; case WM_RBUTTONUP: PrintLog( "WM_RBUTTONUP\n" ); break; case WM_MOUSEMOVE: { int nX = LOWORD(lParam); int nY = HIWORD(lParam); POINT ptScreen = { 0 }; ptScreen.x = nX; ptScreen.y = nY; ClientToScreen( hWnd, &ptScreen ); CHAR szText[260] = { 0 }; sprintf( szText, "WM_MOUSEMOVE: X=%d(%d),Y=%d(%d)\n", nX, ptScreen.x, nY, ptScreen.y ); PrintLog( szText ); if( wParam & MK_CONTROL ) { PrintLog( "WM_MOUSEMOVE: MK_CONTROL\n" ); } if( wParam & MK_LBUTTON ) { PrintLog( "WM_MOUSEMOVE: MK_LBUTTON\n" ); } g_nXPos = LOWORD(lParam); g_nYPos = HIWORD(lParam); InvalidateRect( hWnd, NULL, TRUE ); } break; case WM_LBUTTONDBLCLK: PrintLog( "WM_LBUTTONDBLCLK\n" ); break; case WM_RBUTTONDBLCLK: PrintLog( "WM_RBUTTONDBLCLK\n" ); break; case WM_MOUSEWHEEL: { short nDetla = HIWORD( wParam ); int nX = LOWORD( lParam ); int nY = HIWORD( lParam ); CHAR szText[260] = { 0 }; sprintf( szText, "WM_MOUSEWHEEL: Detla=%d, X=%d,Y=%d\n", nDetla, nX, nY ); PrintLog( szText ); } break; case WM_DESTROY: PostQuitMessage( 0 ); break; } return DefWindowProc( hWnd, nMsg, wParam, lParam ); } BOOL RegisterWnd( LPSTR pszClassName ) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof( wce ); wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hbrBackground = HBRUSH(COLOR_WINDOW); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInst; wce.lpfnWndProc = WndProc; wce.lpszClassName = pszClassName; wce.lpszMenuName = NULL; wce.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS; ATOM nAtom = RegisterClassEx( &wce ); if( 0 == nAtom ) { return FALSE; } return TRUE; } HWND CreateWnd( LPSTR pszClassName ) { HWND hWnd = CreateWindowEx( 0, pszClassName, "MyWnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInst, NULL ); return hWnd; } void DisplayWnd( HWND hWnd ) { ShowWindow( hWnd, SW_SHOW ); UpdateWindow( hWnd ); } void Message( ) { MSG msg = { 0 }; while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } void NewConsole( ) { AllocConsole( ); g_hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { NewConsole( ); g_hInst = hInstance; RegisterWnd( "MyWnd" ); HWND hWnd = CreateWnd( "MyWnd" ); DisplayWnd( hWnd ); Message( ); return 0; } -------------------------------------------------------------------------------- // 定时器消息 - 设置定时时间显示事件消息 // WinTimer.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "stdio.h" HINSTANCE g_hInst = NULL; HANDLE g_hStdOut = NULL; UINT g_nTimerID1 = 0; void CALLBACK TimerProc1( HWND hWnd, UINT nMsg, UINT idEvent, DWORD dwTime ) { CHAR szText[] = "TimerProc1: Hello Timer\n"; WriteConsole( g_hStdOut, szText, strlen(szText), NULL, NULL ); } void OnCreate( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { //使用窗口处理函数,创建2个定时器 SetTimer( hWnd, 1000, 3 * 1000, NULL ); SetTimer( hWnd, 1001, 5 * 1000, NULL ); //使用窗口处理函数, 未指明定时器ID g_nTimerID1 = SetTimer( hWnd, 0, 1 * 1000, NULL ); //使用TimerProc处理函数创建定时器 SetTimer( hWnd, 1002, 7 * 1000, TimerProc1 ); } void OnTimer( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { switch( wParam ) { case 1000: { CHAR szText[] = "1000: Hello Timer\n"; WriteConsole( g_hStdOut, szText, strlen(szText), NULL, NULL ); } break; case 1001: { CHAR szText[] = "1001: Hello Timer\n"; WriteConsole( g_hStdOut, szText, strlen(szText), NULL, NULL ); } break; default: { CHAR szText[260] = {0}; sprintf( szText, "%d: Hello Timer\n", g_nTimerID1 ); WriteConsole( g_hStdOut, szText, strlen(szText), NULL, NULL ); } break; } } LRESULT CALLBACK WndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { switch( nMsg ) { case WM_CREATE: OnCreate( hWnd, nMsg, wParam, lParam ); break; case WM_TIMER: OnTimer( hWnd, nMsg, wParam, lParam ); break; case WM_DESTROY: KillTimer( hWnd, 1000 ); PostQuitMessage( 0 ); return 0; } return DefWindowProc( hWnd, nMsg, wParam, lParam ); } BOOL RegisterWnd( LPSTR pszClassName ) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof( wce ); wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hbrBackground = HBRUSH(COLOR_WINDOW); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInst; wce.lpfnWndProc = WndProc; wce.lpszClassName = pszClassName; wce.lpszMenuName = NULL; wce.style = CS_VREDRAW|CS_HREDRAW; ATOM nAtom = RegisterClassEx( &wce ); if( 0 == nAtom ) { return FALSE; } return TRUE; } HWND CreateWnd( LPSTR pszClassName ) { HWND hWnd = CreateWindowEx( 0, pszClassName, "MyWnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInst, 0 ); return hWnd; } void DisplayWnd( HWND hWnd ) { ShowWindow( hWnd, SW_SHOW ); UpdateWindow( hWnd ); } void Message( ) { MSG msg = {0}; while ( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } void NewConsole( ) { AllocConsole( ); g_hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { NewConsole( ); g_hInst = hInstance; RegisterWnd( "MYWND" ); HWND hWnd = CreateWnd( "MYWND" ); DisplayWnd( hWnd ); Message( ); return 0; } -------------------------------------------------------------------------------- // 菜单 - 显示菜单消息 // WinMenu.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "stdio.h" HINSTANCE g_hInst = NULL; HANDLE g_hStdOut = NULL; void OnCreate( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { //创建主菜单 HMENU hMainMenu = CreateMenu( ); //创建子菜单 HMENU hFileMenu = CreatePopupMenu( ); //增加菜单项 AppendMenu( hFileMenu, MF_STRING|MF_CHECKED, 1001, "新建(&N)"); AppendMenu( hFileMenu, MF_SEPARATOR, 0, NULL ); AppendMenu( hFileMenu, MF_STRING, 1002, "退出(&X)"); AppendMenu( hMainMenu, MF_STRING|MF_POPUP, (UINT)hFileMenu, "文件(&F)"); HMENU hEditMenu = CreatePopupMenu( ); AppendMenu( hEditMenu, MF_STRING, 1003, "剪切(&T)" ); AppendMenu( hEditMenu, MF_STRING, 1004, "拷贝(&C)" ); AppendMenu( hEditMenu, MF_STRING, 1005, "粘贴(&P)" ); AppendMenu( hMainMenu, MF_STRING|MF_POPUP, (UINT)hEditMenu, "编辑(&E)"); HMENU hHelpMenu = CreatePopupMenu( ); AppendMenu( hHelpMenu, MF_STRING, 1006, "帮助(&H)" ); AppendMenu( hHelpMenu, MF_STRING, 1007, "关于(&A)" ); AppendMenu( hMainMenu, MF_STRING|MF_POPUP, (UINT)hHelpMenu, "帮助(&H)"); //给窗口设置主菜单 SetMenu( hWnd, hMainMenu ); } void OnCommand( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { UINT nID = LOWORD( wParam ); CHAR szText[260] = {0}; sprintf( szText, "OnCommand: %d\n", nID ); WriteConsole( g_hStdOut, szText, strlen(szText), NULL, NULL ); switch( nID ) { case 1002: PostQuitMessage( 0 ); break; } } LRESULT CALLBACK WndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { switch( nMsg ) { case WM_CREATE: OnCreate( hWnd, nMsg, wParam, lParam ); break; case WM_COMMAND: OnCommand( hWnd, nMsg, wParam, lParam ); break; case WM_DESTROY: PostQuitMessage( 0 ); return 0; } return DefWindowProc( hWnd, nMsg, wParam, lParam ); } BOOL RegisterWnd( LPSTR pszClassName ) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof( wce ); wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hbrBackground = HBRUSH(COLOR_WINDOW); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInst; wce.lpfnWndProc = WndProc; wce.lpszClassName = pszClassName; wce.lpszMenuName = NULL; wce.style = CS_VREDRAW|CS_HREDRAW; ATOM nAtom = RegisterClassEx( &wce ); if( 0 == nAtom ) { return FALSE; } return TRUE; } HWND CreateWnd( LPSTR pszClassName ) { HWND hWnd = CreateWindowEx( 0, pszClassName, "MyWnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInst, 0 ); return hWnd; } void DisplayWnd( HWND hWnd ) { ShowWindow( hWnd, SW_SHOW ); UpdateWindow( hWnd ); } void Message( ) { MSG msg = {0}; while ( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } void NewConsole( ) { AllocConsole( ); g_hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { NewConsole( ); g_hInst = hInstance; RegisterWnd( "MYWND" ); HWND hWnd = CreateWnd( "MYWND" ); DisplayWnd( hWnd ); Message( ); return 0; } -------------------------------------------------------------------------------- // 显示子菜单的WPARAM和LPARAM参数 // WinMenu.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "stdio.h" HINSTANCE g_hInst = NULL; HANDLE g_hStdOut = NULL; BOOL g_bCheckCut = FALSE; void OnCreate( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { //创建主菜单 HMENU hMainMenu = CreateMenu( ); //创建子菜单 HMENU hFileMenu = CreatePopupMenu( ); //增加菜单项 AppendMenu( hFileMenu, MF_STRING|MF_CHECKED, 1001, "新建(&N)"); AppendMenu( hFileMenu, MF_SEPARATOR, 0, NULL ); AppendMenu( hFileMenu, MF_STRING, 1002, "退出(&X)"); AppendMenu( hMainMenu, MF_STRING|MF_POPUP, (UINT)hFileMenu, "文件(&F)"); HMENU hEditMenu = CreatePopupMenu( ); AppendMenu( hEditMenu, MF_STRING, 1003, "剪切(&T)" ); AppendMenu( hEditMenu, MF_STRING, 1004, "拷贝(&C)" ); AppendMenu( hEditMenu, MF_STRING, 1005, "粘贴(&P)" ); AppendMenu( hMainMenu, MF_STRING|MF_POPUP, (UINT)hEditMenu, "编辑(&E)"); HMENU hHelpMenu = CreatePopupMenu( ); AppendMenu( hHelpMenu, MF_STRING, 1006, "帮助(&H)" ); AppendMenu( hHelpMenu, MF_STRING, 1007, "关于(&A)" ); AppendMenu( hMainMenu, MF_STRING|MF_POPUP, (UINT)hHelpMenu, "帮助(&H)"); //给窗口设置主菜单 SetMenu( hWnd, hMainMenu ); } void OnCommand( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { UINT nID = LOWORD( wParam ); CHAR szText[260] = {0}; sprintf( szText, "OnCommand: %d\n", nID ); WriteConsole( g_hStdOut, szText, strlen(szText), NULL, NULL ); switch( nID ) { case 1002: PostQuitMessage( 0 ); break; case 1003: g_bCheckCut = !g_bCheckCut; break; } } void OnInitMenuPopup( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { CHAR szText[260] = { 0 }; sprintf( szText, "OnInitMenuPopup: WPARAM=%08X, LPARAM=%08X\n", wParam, lParam ); WriteConsole( g_hStdOut, szText, strlen(szText), NULL, NULL ); HMENU hMenu = (HMENU)wParam; if( TRUE == g_bCheckCut ) { CheckMenuItem( hMenu, 1003, MF_CHECKED|MF_BYCOMMAND ); } else { CheckMenuItem( hMenu, 1003, MF_UNCHECKED|MF_BYCOMMAND ); } } LRESULT CALLBACK WndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { switch( nMsg ) { case WM_CREATE: OnCreate( hWnd, nMsg, wParam, lParam ); break; case WM_COMMAND: OnCommand( hWnd, nMsg, wParam, lParam ); break; case WM_INITMENUPOPUP: OnInitMenuPopup( hWnd, nMsg, wParam, lParam ); break; case WM_DESTROY: PostQuitMessage( 0 ); return 0; } return DefWindowProc( hWnd, nMsg, wParam, lParam ); } BOOL RegisterWnd( LPSTR pszClassName ) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof( wce ); wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hbrBackground = HBRUSH(COLOR_WINDOW); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInst; wce.lpfnWndProc = WndProc; wce.lpszClassName = pszClassName; wce.lpszMenuName = NULL; wce.style = CS_VREDRAW|CS_HREDRAW; ATOM nAtom = RegisterClassEx( &wce ); if( 0 == nAtom ) { return FALSE; } return TRUE; } HWND CreateWnd( LPSTR pszClassName ) { HWND hWnd = CreateWindowEx( 0, pszClassName, "MyWnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInst, 0 ); return hWnd; } void DisplayWnd( HWND hWnd ) { ShowWindow( hWnd, SW_SHOW ); UpdateWindow( hWnd ); } void Message( ) { MSG msg = {0}; while ( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } void NewConsole( ) { AllocConsole( ); g_hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { NewConsole( ); g_hInst = hInstance; RegisterWnd( "MYWND" ); HWND hWnd = CreateWnd( "MYWND" ); DisplayWnd( hWnd ); Message( ); return 0; }
效果展示
鼠标消息
定时器
菜单
迷茫的人生,需要不断努力,才能看清远方模糊的志向!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?