From 9e4aeab1c74ce375e0e8503350873bf681f52122 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Sat, 29 Nov 2025 14:13:34 +0100 Subject: [PATCH] Feature: Add a startup progress display to the program --- server/2015Remote/2015Remote.cpp | 10 +- server/2015Remote/2015Remote.h | 48 ++-- server/2015Remote/2015RemoteDlg.cpp | 50 +++- server/2015Remote/2015RemoteDlg.h | 46 +-- server/2015Remote/2015Remote_vs2015.vcxproj | 2 + .../2015Remote_vs2015.vcxproj.filters | 6 + server/2015Remote/SplashDlg.cpp | 267 ++++++++++++++++++ server/2015Remote/SplashDlg.h | 53 ++++ 8 files changed, 436 insertions(+), 46 deletions(-) create mode 100644 server/2015Remote/SplashDlg.cpp create mode 100644 server/2015Remote/SplashDlg.h diff --git a/server/2015Remote/2015Remote.cpp b/server/2015Remote/2015Remote.cpp index ad9ca89..4af85d8 100644 --- a/server/2015Remote/2015Remote.cpp +++ b/server/2015Remote/2015Remote.cpp @@ -1,9 +1,10 @@ - + // 2015Remote.cpp : 定义应用程序的类行为。 // #include "stdafx.h" #include "2015Remote.h" +#include "SplashDlg.h" #include "2015RemoteDlg.h" #ifdef _DEBUG @@ -245,12 +246,19 @@ BOOL CMy2015RemoteApp::InitInstance() SetUnhandledExceptionFilter(&whenbuged); + // 创建并显示启动画面 + CSplashDlg* pSplash = new CSplashDlg(); + pSplash->Create(NULL); + pSplash->UpdateProgressDirect(5, _T("正在初始化系统图标...")); + SHFILEINFO sfi = {}; HIMAGELIST hImageList = (HIMAGELIST)SHGetFileInfo((LPCTSTR)_T(""), 0, &sfi, sizeof(SHFILEINFO), SHGFI_LARGEICON | SHGFI_SYSICONINDEX); m_pImageList_Large.Attach(hImageList); hImageList = (HIMAGELIST)SHGetFileInfo((LPCTSTR)_T(""), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SMALLICON | SHGFI_SYSICONINDEX); m_pImageList_Small.Attach(hImageList); + pSplash->UpdateProgressDirect(10, _T("正在初始化公共控件...")); + // 如果一个运行在 Windows XP 上的应用程序清单指定要 // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式, //则需要 InitCommonControlsEx()。否则,将无法创建窗口。 diff --git a/server/2015Remote/2015Remote.h b/server/2015Remote/2015Remote.h index df0cb83..40b5661 100644 --- a/server/2015Remote/2015Remote.h +++ b/server/2015Remote/2015Remote.h @@ -1,25 +1,25 @@ - -// 2015Remote.h : PROJECT_NAME Ӧóͷļ + +// 2015Remote.h : PROJECT_NAME 应用程序的主头文件 // #pragma once #ifndef __AFXWIN_H__ -#error "ڰļ֮ǰstdafx.h PCH ļ" +#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件" #endif -#include "resource.h" // +#include "resource.h" // 主符号 #include "common/iniFile.h" #include "IOCPServer.h" #include "IOCPUDPServer.h" #include "IOCPKCPServer.h" // CMy2015RemoteApp: -// йشʵ֣ 2015Remote.cpp +// 有关此类的实现,请参阅 2015Remote.cpp // // ServerPair: -// һSOCKETˣ˿: ͬʱTCPUDP. +// 一对SOCKET服务端,监听端口: 同时监听TCP和UDP. class ServerPair { private: @@ -38,11 +38,11 @@ public: BOOL StartServer(pfnNotifyProc NotifyProc, pfnOfflineProc OffProc, USHORT uPort) { UINT ret1 = m_tcpServer->StartServer(NotifyProc, OffProc, uPort); - if (ret1) MessageBox(NULL, CString("TCPʧ: ") + std::to_string(uPort).c_str() - + CString(": ") + std::to_string(ret1).c_str(), "ʾ", MB_ICONINFORMATION); + if (ret1) MessageBox(NULL, CString("启动TCP服务失败: ") + std::to_string(uPort).c_str() + + CString("。错误码: ") + std::to_string(ret1).c_str(), "提示", MB_ICONINFORMATION); UINT ret2 = m_udpServer->StartServer(NotifyProc, OffProc, uPort); - if (ret2) MessageBox(NULL, CString("UDPʧ: ") + std::to_string(uPort).c_str() - + CString(": ") + std::to_string(ret2).c_str(), "ʾ", MB_ICONINFORMATION); + if (ret2) MessageBox(NULL, CString("启动UDP服务失败: ") + std::to_string(uPort).c_str() + + CString("。错误码: ") + std::to_string(ret2).c_str(), "提示", MB_ICONINFORMATION); return (ret1 == 0 || ret2 == 0); } @@ -65,21 +65,29 @@ public: } }; +class CSplashDlg; // 前向声明 + class CMy2015RemoteApp : public CWinApp { private: - // ļȡ + // 配置文件读取器 config* m_iniFile = nullptr; - // б + // 服务端对象列表 std::vector m_iocpServer; - // + // 互斥量 HANDLE m_Mutex = nullptr; + // 启动画面 + CSplashDlg* m_pSplash = nullptr; public: CMy2015RemoteApp(); - CImageList m_pImageList_Large; //ϵͳͼ - CImageList m_pImageList_Small; //ϵͳСͼ + CImageList m_pImageList_Large; // 系统大图标 + CImageList m_pImageList_Small; // 系统小图标 + + // 获取启动画面指针 + CSplashDlg* GetSplash() const { return m_pSplash; } + void SetSplash(CSplashDlg* pSplash) { m_pSplash = pSplash; } virtual BOOL InitInstance(); @@ -88,8 +96,8 @@ public: return m_iniFile; } - // ˣɹ0 - // nPortʾ: 6543;7543 + // 启动多个服务端,成功返回0 + // nPort示例: 6543;7543 UINT StartServer(pfnNotifyProc NotifyProc, pfnOfflineProc OffProc, const std::string& uPort, int maxConn, const std::string& method) { bool succeed = false; @@ -111,7 +119,7 @@ public: return succeed ? 0 : -1; } - // ͷŷ SOCKET + // 释放服务端 SOCKET void Destroy() { for (int i=0; i #include "common/file_upload.h" +#include "SplashDlg.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -1000,13 +1001,37 @@ bool IsFunctionReallyHooked(const char* dllName, const char* funcName) return false; // 未发现 Hook } +// 关闭启动画面的辅助函数 +static void CloseSplash() +{ + CSplashDlg* pSplash = THIS_APP->GetSplash(); + if (pSplash) { + THIS_APP->SetSplash(nullptr); + if (pSplash->GetSafeHwnd()) { + pSplash->DestroyWindow(); + } + delete pSplash; + } +} + BOOL CMy2015RemoteDlg::OnInitDialog() { +#define UPDATE_SPLASH(percent, text) \ + do { \ + CSplashDlg* pSplash = THIS_APP->GetSplash(); \ + if (pSplash && pSplash->GetSafeHwnd()) { \ + pSplash->UpdateProgressDirect(percent, _T(text)); \ + } \ + } while(0) + AUTO_TICK(500); CDialogEx::OnInitDialog(); + + UPDATE_SPLASH(20, "正在初始化文件上传模块..."); int ret = InitFileUpload(GetHMAC()); g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, AfxGetInstanceHandle(), 0); + UPDATE_SPLASH(25, "正在初始化视频墙..."); m_GroupList = {"default"}; // Grid 容器 int size = THIS_CFG.GetInt("settings", "VideoWallSize"); @@ -1018,6 +1043,7 @@ BOOL CMy2015RemoteDlg::OnInitDialog() m_gridDlg->SetGrid(size, size); } + UPDATE_SPLASH(30, "正在验证配置..."); if (!IsPwdHashValid()) { THIS_CFG.SetStr("settings", "superAdmin", ""); THIS_CFG.SetStr("settings", "Password", ""); @@ -1033,7 +1059,8 @@ BOOL CMy2015RemoteDlg::OnInitDialog() } } - // 将“关于...”菜单项添加到系统菜单中。 + UPDATE_SPLASH(35, "正在加载客户端数据库..."); + // 将"关于..."菜单项添加到系统菜单中。 SetWindowText(_T("Yama")); LoadFromFile(m_ClientMap, GetDbPath()); @@ -1052,6 +1079,8 @@ BOOL CMy2015RemoteDlg::OnInitDialog() pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } + + UPDATE_SPLASH(40, "正在加载授权模块..."); // 主控程序公网IP std::string ip = THIS_CFG.GetStr("settings", "master", ""); int port = THIS_CFG.Get1Int("settings", "ghost", ';', 6543); @@ -1084,10 +1113,18 @@ BOOL CMy2015RemoteDlg::OnInitDialog() SAFE_DELETE_ARRAY(data); } } + + UPDATE_SPLASH(50, "正在加载内核模块 (x86 DLL)..."); g_2015RemoteDlg = this; m_ServerDLL[PAYLOAD_DLL_X86] = ReadKernelDll(false, true, master); + + UPDATE_SPLASH(55, "正在加载内核模块 (x64 DLL)..."); m_ServerDLL[PAYLOAD_DLL_X64] = ReadKernelDll(true, true, master); + + UPDATE_SPLASH(60, "正在加载内核模块 (x86 Bin)..."); m_ServerBin[PAYLOAD_DLL_X86] = ReadKernelDll(false, false, master); + + UPDATE_SPLASH(65, "正在加载内核模块 (x64 Bin)..."); m_ServerBin[PAYLOAD_DLL_X64] = ReadKernelDll(true, false, master); // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 @@ -1095,18 +1132,19 @@ BOOL CMy2015RemoteDlg::OnInitDialog() SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 + UPDATE_SPLASH(70, "正在创建工具栏..."); // TODO: 在此添加额外的初始化代码 isClosed = FALSE; CreateToolBar(); InitControl(); + UPDATE_SPLASH(75, "正在创建界面组件..."); CreatStatusBar(); - CreateNotifyBar(); - CreateSolidMenu(); + UPDATE_SPLASH(80, "正在加载配置..."); std::string nPort = THIS_CFG.GetStr("settings", "ghost", "6543"); m_nMaxConnection = 2; std::string pwd = THIS_CFG.GetStr("settings", "Password"); @@ -1148,10 +1186,13 @@ BOOL CMy2015RemoteDlg::OnInitDialog() SetTimer(TIMER_CHECK, max(1, tm) * 60 * 1000, NULL); #endif + UPDATE_SPLASH(85, "正在启动FRP代理..."); m_hFRPThread = CreateThread(NULL, 0, StartFrpClient, this, NULL, NULL); + UPDATE_SPLASH(90, "正在启动网络服务..."); // 最后启动SOCKET if (!Activate(nPort, m_nMaxConnection, method)) { + CloseSplash(); OnCancel(); return FALSE; } @@ -1160,6 +1201,9 @@ BOOL CMy2015RemoteDlg::OnInitDialog() THIS_CFG.SetStr("settings", "PwdHash", GetPwdHash()); THIS_CFG.SetStr("settings", "MasterHash", GetMasterHash()); + UPDATE_SPLASH(100, "启动完成!"); + CloseSplash(); + return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } diff --git a/server/2015Remote/2015RemoteDlg.h b/server/2015Remote/2015RemoteDlg.h index dceb9e8..b56656f 100644 --- a/server/2015Remote/2015RemoteDlg.h +++ b/server/2015Remote/2015RemoteDlg.h @@ -1,5 +1,5 @@ - -// 2015RemoteDlg.h : ͷļ + +// 2015RemoteDlg.h : 头文件 // #pragma once @@ -8,9 +8,9 @@ #include "IOCPServer.h" ////////////////////////////////////////////////////////////////////////// -// Ϊʹ +// 以下为特殊需求使用 -// Ƿ˳ضʱҲ˳ͻ +// 是否在退出主控端时也退出客户端 #define CLIENT_EXIT_WITH_SERVER 0 typedef struct DllInfo { @@ -40,7 +40,7 @@ struct _ClientValue { char Note[64]; char Location[64]; char Level; - char Reserved[127]; // Ԥ + char Reserved[127]; // 预留 _ClientValue() { memset(this, 0, sizeof(_ClientValue)); @@ -87,21 +87,23 @@ typedef _ClientValue ClientValue; typedef std::unordered_map ComputerNoteMap; -// unordered_map ļ +// 保存 unordered_map 到文件 void SaveToFile(const ComputerNoteMap& data, const std::string& filename); -// ļȡ unordered_map +// 从文件读取 unordered_map 数据 void LoadFromFile(ComputerNoteMap& data, const std::string& filename); ////////////////////////////////////////////////////////////////////////// enum { - PAYLOAD_DLL_X86 = 0, // 32λ DLL - PAYLOAD_DLL_X64 = 1, // 64λ DLL + PAYLOAD_DLL_X86 = 0, // 32位 DLL + PAYLOAD_DLL_X64 = 1, // 64位 DLL PAYLOAD_MAXTYPE }; -// CMy2015RemoteDlg Ի +class CSplashDlg; // 前向声明 + +// CMy2015RemoteDlg 对话框 class CMy2015RemoteDlg : public CDialogEx { protected: @@ -141,21 +143,21 @@ protected: } LeaveCriticalSection(&m_cs); } - // + // 构造 public: - CMy2015RemoteDlg(CWnd* pParent = NULL); // ׼캯 + CMy2015RemoteDlg(CWnd* pParent = NULL); // 标准构造函数 ~CMy2015RemoteDlg(); - // Ի + // 对话框数据 enum { IDD = IDD_MY2015REMOTE_DIALOG }; protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV ֧ - // ʵ + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 + // 实现 protected: HICON m_hIcon; void* m_tinyDLL; std::string m_superPass; - // ɵϢӳ亯 + // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); @@ -176,7 +178,7 @@ public: if (ok) { m_gridDlg->AddChild((CDialog*)Dlg); LONG style = ::GetWindowLong(Dlg->GetSafeHwnd(), GWL_STYLE); - style &= ~(WS_CAPTION | WS_SIZEBOX); // ȥ͵С + style &= ~(WS_CAPTION | WS_SIZEBOX); // 去掉标题栏和调整大小 ::SetWindowLong(Dlg->GetSafeHwnd(), GWL_STYLE, style); ::SetWindowPos(Dlg->GetSafeHwnd(), nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); @@ -190,8 +192,8 @@ public: return 0; } - VOID InitControl(); //ʼؼ - VOID TestOnline(); //Ժ + VOID InitControl(); //初始控件 + VOID TestOnline(); //测试函数 VOID AddList(CString strIP, CString strAddr, CString strPCName, CString strOS, CString strCPU, CString strVideo, CString strPing, CString ver, CString startTime, const std::vector& v, CONTEXT_OBJECT* ContextObject); VOID ShowMessage(CString strType, CString strMsg); @@ -212,7 +214,7 @@ public: VOID MessageHandle(CONTEXT_OBJECT* ContextObject); VOID SendSelectedCommand(PBYTE szBuffer, ULONG ulLength); VOID SendAllCommand(PBYTE szBuffer, ULONG ulLength); - // ʾûϢ + // 显示用户上线信息 CWnd* m_pFloatingTip = nullptr; CListCtrl m_CList_Online; CListCtrl m_CList_Message; @@ -222,7 +224,7 @@ public: void LoadListData(const std::string& group); void DeletePopupWindow(); - CStatusBar m_StatusBar; //״̬ + CStatusBar m_StatusBar; //状态条 CTrueColorToolBar m_ToolBar; CGridDialog * m_gridDlg = NULL; std::vector m_DllList; @@ -238,7 +240,7 @@ public: std::map m_RemoteWnds; CDialogBase* GetRemoteWindow(HWND hWnd); void RemoveRemoteWindow(HWND wnd); - CDialogBase* m_pActiveSession = nullptr; // ǰỰָ / NULL ʾ + CDialogBase* m_pActiveSession = nullptr; // 当前活动会话窗口指针 / NULL 表示无 afx_msg LRESULT OnSessionActivatedMsg(WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); HHOOK g_hKeyboardHook = NULL; diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj b/server/2015Remote/2015Remote_vs2015.vcxproj index 0c7b1b6..b0f3311 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj +++ b/server/2015Remote/2015Remote_vs2015.vcxproj @@ -304,6 +304,7 @@ + @@ -386,6 +387,7 @@ + Create Create diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj.filters b/server/2015Remote/2015Remote_vs2015.vcxproj.filters index ea19847..ff5666e 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj.filters +++ b/server/2015Remote/2015Remote_vs2015.vcxproj.filters @@ -57,6 +57,9 @@ + + + @@ -127,6 +130,9 @@ + + + diff --git a/server/2015Remote/SplashDlg.cpp b/server/2015Remote/SplashDlg.cpp new file mode 100644 index 0000000..31024ea --- /dev/null +++ b/server/2015Remote/SplashDlg.cpp @@ -0,0 +1,267 @@ +#include "stdafx.h" +#include "SplashDlg.h" +#include "resource.h" +#include "2015Remote.h" + +BEGIN_MESSAGE_MAP(CSplashDlg, CWnd) + ON_WM_PAINT() + ON_MESSAGE(WM_USER + 5000, OnUpdateProgress) // WM_SPLASH_PROGRESS + ON_MESSAGE(WM_USER + 5001, OnUpdateStatus) // WM_SPLASH_STATUS + ON_MESSAGE(WM_USER + 5002, OnCloseSplash) // WM_SPLASH_CLOSE +END_MESSAGE_MAP() + +CSplashDlg::CSplashDlg() + : m_nProgress(0) + , m_strStatus(_T("正在初始化...")) + , m_hIcon(NULL) +{ +} + +CSplashDlg::~CSplashDlg() +{ + if (m_fontTitle.m_hObject) + m_fontTitle.DeleteObject(); + if (m_fontStatus.m_hObject) + m_fontStatus.DeleteObject(); +} + +BOOL CSplashDlg::Create(CWnd* pParent) +{ + // 注册窗口类 + CString strClassName = AfxRegisterWndClass( + CS_HREDRAW | CS_VREDRAW, + ::LoadCursor(NULL, IDC_ARROW), + (HBRUSH)(COLOR_WINDOW + 1), + NULL); + + // 计算居中位置 + int screenWidth = GetSystemMetrics(SM_CXSCREEN); + int screenHeight = GetSystemMetrics(SM_CYSCREEN); + int x = (screenWidth - SPLASH_WIDTH) / 2; + int y = (screenHeight - SPLASH_HEIGHT) / 2; + + // 创建无边框窗口 + DWORD dwStyle = WS_POPUP | WS_VISIBLE; + DWORD dwExStyle = WS_EX_TOPMOST | WS_EX_TOOLWINDOW; + + if (!CreateEx(dwExStyle, strClassName, _T(""), dwStyle, + x, y, SPLASH_WIDTH, SPLASH_HEIGHT, + pParent ? pParent->GetSafeHwnd() : NULL, NULL)) + { + return FALSE; + } + + // 创建字体 + m_fontTitle.CreateFont( + 28, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Microsoft YaHei")); + + m_fontStatus.CreateFont( + 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Microsoft YaHei")); + + // 加载图标 + m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); + + // 注册到应用程序 + THIS_APP->SetSplash(this); + + ShowWindow(SW_SHOW); + UpdateWindow(); + + return TRUE; +} + +void CSplashDlg::SetProgress(int nPercent) +{ + if (nPercent < 0) nPercent = 0; + if (nPercent > 100) nPercent = 100; + + if (GetSafeHwnd()) + { + PostMessage(WM_SPLASH_PROGRESS, nPercent, 0); + } +} + +void CSplashDlg::SetStatusText(const CString& strText) +{ + if (GetSafeHwnd()) + { + CString* pText = new CString(strText); + PostMessage(WM_SPLASH_STATUS, (WPARAM)pText, 0); + } +} + +void CSplashDlg::UpdateProgressDirect(int nPercent, const CString& strText) +{ + if (nPercent < 0) nPercent = 0; + if (nPercent > 100) nPercent = 100; + + m_nProgress = nPercent; + m_strStatus = strText; + + if (GetSafeHwnd()) + { + InvalidateRect(NULL, FALSE); + UpdateWindow(); + + // 处理待处理的消息,确保界面响应 + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +void CSplashDlg::Close() +{ + if (GetSafeHwnd()) + { + PostMessage(WM_SPLASH_CLOSE, 0, 0); + } +} + +LRESULT CSplashDlg::OnUpdateProgress(WPARAM wParam, LPARAM lParam) +{ + m_nProgress = (int)wParam; + InvalidateRect(NULL, FALSE); + UpdateWindow(); + return 0; +} + +LRESULT CSplashDlg::OnUpdateStatus(WPARAM wParam, LPARAM lParam) +{ + CString* pText = (CString*)wParam; + if (pText) + { + m_strStatus = *pText; + delete pText; + InvalidateRect(NULL, FALSE); + UpdateWindow(); + } + return 0; +} + +LRESULT CSplashDlg::OnCloseSplash(WPARAM wParam, LPARAM lParam) +{ + THIS_APP->SetSplash(nullptr); + DestroyWindow(); + return 0; +} + +void CSplashDlg::OnPaint() +{ + CPaintDC dc(this); + CRect rect; + GetClientRect(&rect); + + // 双缓冲绘制 + CDC memDC; + memDC.CreateCompatibleDC(&dc); + CBitmap memBitmap; + memBitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()); + CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap); + + // 绘制背景 - 渐变效果 + for (int y = 0; y < rect.Height(); y++) + { + int r = 45 + (y * 20 / rect.Height()); + int g = 45 + (y * 20 / rect.Height()); + int b = 55 + (y * 25 / rect.Height()); + CPen pen(PS_SOLID, 1, RGB(r, g, b)); + CPen* pOldPen = memDC.SelectObject(&pen); + memDC.MoveTo(0, y); + memDC.LineTo(rect.Width(), y); + memDC.SelectObject(pOldPen); + } + + // 绘制边框 + CPen borderPen(PS_SOLID, 2, RGB(100, 100, 120)); + CPen* pOldPen = memDC.SelectObject(&borderPen); + memDC.SelectStockObject(NULL_BRUSH); + memDC.Rectangle(rect); + memDC.SelectObject(pOldPen); + + // 绘制图标 + if (m_hIcon) + { + DrawIconEx(memDC.GetSafeHdc(), 30, 30, m_hIcon, 48, 48, 0, NULL, DI_NORMAL); + } + + // 绘制标题 + memDC.SetBkMode(TRANSPARENT); + memDC.SetTextColor(RGB(255, 255, 255)); + CFont* pOldFont = memDC.SelectObject(&m_fontTitle); + memDC.TextOut(95, 35, _T("YAMA")); + memDC.SelectObject(pOldFont); + + // 绘制副标题 + memDC.SetTextColor(RGB(180, 180, 190)); + pOldFont = memDC.SelectObject(&m_fontStatus); + memDC.TextOut(95, 70, _T("Remote Administration Tool")); + memDC.SelectObject(pOldFont); + + // 绘制进度条背景 + CRect progressRect(30, 120, rect.Width() - 30, 140); + DrawProgressBar(&memDC, progressRect); + + // 绘制状态文本 + memDC.SetTextColor(RGB(200, 200, 210)); + pOldFont = memDC.SelectObject(&m_fontStatus); + CRect textRect(30, 150, rect.Width() - 30, 180); + memDC.DrawText(m_strStatus, textRect, DT_LEFT | DT_SINGLELINE | DT_VCENTER); + memDC.SelectObject(pOldFont); + + // 绘制进度百分比 + CString strPercent; + strPercent.Format(_T("%d%%"), m_nProgress); + memDC.SetTextColor(RGB(100, 200, 255)); + textRect = CRect(rect.Width() - 80, 150, rect.Width() - 30, 180); + memDC.DrawText(strPercent, textRect, DT_RIGHT | DT_SINGLELINE | DT_VCENTER); + + // 复制到屏幕 + dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); + + memDC.SelectObject(pOldBitmap); +} + +void CSplashDlg::DrawProgressBar(CDC* pDC, const CRect& rect) +{ + // 进度条背景 + CBrush bgBrush(RGB(30, 30, 40)); + pDC->FillRect(rect, &bgBrush); + + // 进度条边框 + CPen borderPen(PS_SOLID, 1, RGB(80, 80, 100)); + CPen* pOldPen = pDC->SelectObject(&borderPen); + pDC->SelectStockObject(NULL_BRUSH); + pDC->Rectangle(rect); + pDC->SelectObject(pOldPen); + + // 计算进度条填充区域 + if (m_nProgress > 0) + { + CRect fillRect = rect; + fillRect.DeflateRect(2, 2); + fillRect.right = fillRect.left + (fillRect.Width() * m_nProgress / 100); + + // 渐变填充 + for (int x = fillRect.left; x < fillRect.right; x++) + { + float ratio = (float)(x - fillRect.left) / (float)fillRect.Width(); + int r = (int)(50 + ratio * 50); + int g = (int)(150 + ratio * 50); + int b = (int)(200 + ratio * 55); + CPen pen(PS_SOLID, 1, RGB(r, g, b)); + CPen* pOld = pDC->SelectObject(&pen); + pDC->MoveTo(x, fillRect.top); + pDC->LineTo(x, fillRect.bottom); + pDC->SelectObject(pOld); + } + } +} + diff --git a/server/2015Remote/SplashDlg.h b/server/2015Remote/SplashDlg.h new file mode 100644 index 0000000..90ac41a --- /dev/null +++ b/server/2015Remote/SplashDlg.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +// 启动画面对话框 - 显示加载进度 +class CSplashDlg : public CWnd +{ +public: + CSplashDlg(); + virtual ~CSplashDlg(); + + // 创建并显示启动画面 + BOOL Create(CWnd* pParent = NULL); + + // 更新进度 (0-100) - 通过消息队列(用于跨线程) + void SetProgress(int nPercent); + + // 更新状态文本 - 通过消息队列(用于跨线程) + void SetStatusText(const CString& strText); + + // 直接更新进度和状态(同一线程使用,立即刷新) + void UpdateProgressDirect(int nPercent, const CString& strText); + + // 关闭启动画面 + void Close(); + +protected: + DECLARE_MESSAGE_MAP() + + afx_msg void OnPaint(); + afx_msg LRESULT OnUpdateProgress(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnUpdateStatus(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnCloseSplash(WPARAM wParam, LPARAM lParam); + +private: + int m_nProgress; // 当前进度 0-100 + CString m_strStatus; // 状态文本 + CFont m_fontTitle; // 标题字体 + CFont m_fontStatus; // 状态文本字体 + HICON m_hIcon; // 图标 + + // 窗口尺寸 + static const int SPLASH_WIDTH = 400; + static const int SPLASH_HEIGHT = 200; + + // 自定义消息 (使用较大的偏移避免与其他消息冲突) + static const UINT WM_SPLASH_PROGRESS = WM_USER + 5000; + static const UINT WM_SPLASH_STATUS = WM_USER + 5001; + static const UINT WM_SPLASH_CLOSE = WM_USER + 5002; + + // 绘制相关 + void DrawProgressBar(CDC* pDC, const CRect& rect); +};