☰
VS2019 + MFC 开发销售管理系统实战指南
2026/10/13 13:18:41 网站建设 项目流程

简介:本资源是一个基于Visual Studio 2019与MFC框架开发的C++桌面端销售管理系统实战项目,面向C++初学者、MFC入门开发者及高校课程设计学生,聚焦企业进销存核心业务场景,提供可编译运行的完整工程实现。压缩包共111个文件,含15个头文件(.h)定义类结构与接口、12个源文件(.cpp)实现进货管理、出货登记、库存查询、用户权限控制等六大功能模块,以及2个可执行文件(.exe)、4个图标资源(.ico)和配套界面资源(.rc/.res),整体体积96.43MB,工程结构规范,适合作为MFC消息映射、数据库操作(CRecordset)、列表控件(CListCtrl)与对话框交互的综合学习范例。目前已有1328人学习下载,读者可直接编译调试,深入理解MFC文档/视图架构、UI事件响应机制及C++面向对象在业务系统中的落地实践。

1. 为什么还在用 VS2019 + MFC 做销售管理系统?不是怀旧,是稳得过头了

某公司三年前上线的销售管理系统,至今仍在主力运行——没有云原生、不接微服务、没上容器,核心界面仍是 MFC 对话框+ListCtrl+TreeCtrl 搭建的本地桌面程序,后端直连 SQL Server。它不炫技,但每天处理 3000+ 订单录入、500+ 客户档案变更、200+ 报表导出,零崩溃、零卡顿、零用户投诉。这不是技术倒退,而是对「交付确定性」的极致选择:VS2019 是微软最后一个完整支持 MFC 可视化设计、调试器深度集成、且长期稳定更新的 IDE;MFC 不是古董框架,而是 Windows 桌面业务系统里唯一能让你用拖拽控件+消息映射+资源脚本,在 2 小时内搭出可运行登录窗口、再用 1 天补完数据库连接和权限校验的成熟栈。它适合中小型企业内部系统、离线场景多、硬件配置老旧、IT 运维能力有限、且对 UI 动效无执念的落地场景。如果你正被 Electron 启动慢、WPF 部署报错、Blazor WebAssembly 兼容性问题反复折磨,这个组合不是妥协,是回归工程本质:用最短路径把功能做稳、做全、做可维护。


2. 从零搭建可运行骨架:VS2019 新建项目到主界面弹出

2.1 创建标准 MFC 应用程序项目(非 DLL/静态库)

打开 VS2019 → 新建项目 → 搜索 “MFC 应用程序” → 选择模板(注意:不要选 “MFC DLL” 或 “MFC ActiveX 控件”)→ 项目名设为SalesSystem→ 解决方案位置选空文件夹 → 点击“创建”。
在向导中关键设置:

  • 应用程序类型:选 “单文档”(SDI),理由:销售系统主界面天然适合一个主窗口承载菜单/工具栏/状态栏+中央客户列表+右侧订单详情,比 MDI 更轻量、比对话框应用更易扩展;
  • 复合文档支持:取消勾选(避免引入 OLE 复杂性);
  • 高级功能:仅勾选 “打印和打印预览”(报表导出刚需)、 “状态栏”(显示当前用户/连接状态)、 “工具栏”(快捷操作入口);
  • 生成的类:保持默认CSalesSystemApp/CMainFrame/CSalesSystemView,不改名——这是后续所有消息映射和资源引用的基础。

提示:VS2019 默认使用 Unicode 字符集(推荐),若需兼容老旧 GBK 编码的 Excel 导入,后续需在CFile或CStdioFile中显式指定编码,而非依赖系统 locale。

2.2 添加主界面核心控件:ListCtrl + TreeCtrl + Edit 组合

双击Resource View→Dialog→IDD_SALES_SYSTEM_FORM(主视图对话框资源)→ 进入可视化编辑器:

  • 从工具箱拖入List Control到窗口中央,ID 改为IDC_LIST_CUSTOMERS;
  • 拖入Tree Control到左侧,ID 改为IDC_TREE_CATEGORIES;
  • 拖入Edit Control到右上角,ID 改为IDC_EDIT_SEARCH;
  • 为ListCtrl设置属性:View选Report,Checkboxes勾选(支持多选操作);
  • 为TreeCtrl设置属性:Has Buttons和Has Lines勾选(显示折叠箭头和连线)。

保存后,右键IDC_LIST_CUSTOMERS→ “添加变量” → 类型选CListCtrl,变量名m_listCustomers;同理为TreeCtrl添加m_treeCategories,为Edit添加m_editSearch。这一步生成DDX_Control绑定代码,是 MFC 控件操作的基石。

2.3 初始化树形分类与列表列头(OnInitDialog / OnInitialUpdate)

在CSalesSystemView.cpp的OnInitialUpdate()函数中(SDI 视图初始化入口),插入初始化逻辑:

void CSalesSystemView::OnInitialUpdate() { CFormView::OnInitialUpdate(); // 1. 初始化客户分类树(模拟:一级为地区,二级为行业) HTREEITEM hRoot = m_treeCategories.InsertItem(_T("全部客户"), TVI_ROOT, TVI_LAST); HTREEITEM hNorth = m_treeCategories.InsertItem(_T("华北区"), hRoot, TVI_LAST); m_treeCategories.InsertItem(_T("制造业"), hNorth, TVI_LAST); m_treeCategories.InsertItem(_T("零售业"), hNorth, TVI_LAST); // 2. 初始化客户列表列头 m_listCustomers.InsertColumn(0, _T("客户编号"), LVCFMT_LEFT, 100); m_listCustomers.InsertColumn(1, _T("客户名称"), LVCFMT_LEFT, 150); m_listCustomers.InsertColumn(2, _T("联系人"), LVCFMT_LEFT, 100); m_listCustomers.InsertColumn(3, _T("电话"), LVCFMT_LEFT, 120); m_listCustomers.InsertColumn(4, _T("状态"), LVCFMT_CENTER, 80); // 3. 加载首屏数据(此处先填测试数据) LoadSampleCustomers(); }

LoadSampleCustomers()是自定义函数,用于填充测试行(后续替换为数据库查询):

void CSalesSystemView::LoadSampleCustomers() { m_listCustomers.DeleteAllItems(); // 清空旧数据 int nItem = m_listCustomers.InsertItem(0, _T("C001")); m_listCustomers.SetItemText(nItem, 1, _T("北京智联科技")); m_listCustomers.SetItemText(nItem, 2, _T("张经理")); m_listCustomers.SetItemText(nItem, 3, _T("010-88889999")); m_listCustomers.SetItemText(nItem, 4, _T("活跃")); nItem = m_listCustomers.InsertItem(1, _T("C002")); m_listCustomers.SetItemText(nItem, 1, _T("上海云启信息")); m_listCustomers.SetItemText(nItem, 2, _T("李总监")); m_listCustomers.SetItemText(nItem, 3, _T("021-66667777")); m_listCustomers.SetItemText(nItem, 4, _T("暂停")); }

参数说明:InsertItem()返回行索引,SetItemText()按列索引写入内容;LVCFMT_LEFT/CENTER控制对齐;100/150是像素宽度,需根据实际字段长度调整,避免截断。


3. 数据持久化:ADO 连接 SQL Server 并实现增删改查

3.1 配置 ADO 工程依赖与头文件

MFC 默认不启用 ADO,需手动配置:

  • 右键项目 → “属性” → “配置属性” → “常规” → “使用 ATL” 设为 “否”;
  • “C/C++” → “常规” → “附加包含目录” 添加:$(VCInstallDir)atlmfc\include;
  • “链接器” → “输入” → “附加依赖项” 添加:msado15.lib;
  • 在stdafx.h末尾添加:
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \ no_namespace rename("EOF", "EndOfFile")

注意:msado15.dll路径需与本机一致(Win10/11 通常在此),若报错找不到,用regsvr32 msado15.dll注册一次即可。此步是 ADO 能用的前提,跳过则后续所有数据库操作编译失败。

3.2 封装数据库连接类CDBConnection

新建头文件DBConnection.h:

#pragma once #include "stdafx.h" #include <comdef.h> class CDBConnection { public: CDBConnection(); ~CDBConnection(); bool Connect(const CString& strConn); // 连接字符串示例:"Provider=SQLOLEDB;Data Source=.;Initial Catalog=SalesDB;Integrated Security=SSPI;" void Disconnect(); _ConnectionPtr GetConnection() { return m_pConnection; } private: _ConnectionPtr m_pConnection; };

实现文件DBConnection.cpp:

#include "DBConnection.h" CDBConnection::CDBConnection() { ::CoInitialize(NULL); // COM 初始化,必须在连接前调用 m_pConnection.CreateInstance(__uuidof(Connection)); } CDBConnection::~CDBConnection() { if (m_pConnection && m_pConnection->State == adStateOpen) m_pConnection->Close(); ::CoUninitialize(); } bool CDBConnection::Connect(const CString& strConn) { try { m_pConnection->Open(_bstr_t(strConn), "", "", adConnectUnspecified); return true; } catch (_com_error& e) { AfxMessageBox(_T("数据库连接失败:") + CString(e.ErrorMessage())); return false; } } void CDBConnection::Disconnect() { if (m_pConnection && m_pConnection->State == adStateOpen) m_pConnection->Close(); }

关键点:CoInitialize(NULL)必须在主线程首次使用 ADO 前调用;adConnectUnspecified是安全连接模式;异常捕获用_com_error,直接弹窗提示比静默失败更利于调试。

3.3 在视图中调用数据库加载客户数据

在CSalesSystemView.h中添加成员变量:

private: CDBConnection m_dbConn; CString m_strConn; // 连接字符串,建议存于配置文件或注册表

在CSalesSystemView.cpp的OnInitialUpdate()中,将LoadSampleCustomers()替换为:

void CSalesSystemView::OnInitialUpdate() { CFormView::OnInitialUpdate(); // 初始化连接字符串(生产环境应从配置文件读取) m_strConn = _T("Provider=SQLOLEDB;Data Source=.;Initial Catalog=SalesDB;Integrated Security=SSPI;"); // 尝试连接数据库 if (!m_dbConn.Connect(m_strConn)) { AfxMessageBox(_T("无法连接数据库,请检查服务是否启动")); return; // 连接失败则不加载数据 } LoadCustomersFromDB(); // 替换原测试数据加载 }

LoadCustomersFromDB()实现:

void CSalesSystemView::LoadCustomersFromDB() { m_listCustomers.DeleteAllItems(); _RecordsetPtr pRs; try { pRs.CreateInstance(__uuidof(Recordset)); pRs->Open(_bstr_t("SELECT CustID, CustName, Contact, Phone, Status FROM Customers ORDER BY CustID"), m_dbConn.GetConnection(), adOpenStatic, adLockReadOnly, adCmdText); int nRow = 0; while (!pRs->EndOfFile) { CString strID = (LPCTSTR)(_bstr_t)pRs->Fields->GetItem("CustID")->Value; CString strName = (LPCTSTR)(_bstr_t)pRs->Fields->GetItem("CustName")->Value; CString strContact = (LPCTSTR)(_bstr_t)pRs->Fields->GetItem("Contact")->Value; CString strPhone = (LPCTSTR)(_bstr_t)pRs->Fields->GetItem("Phone")->Value; CString strStatus = (LPCTSTR)(_bstr_t)pRs->Fields->GetItem("Status")->Value; int nItem = m_listCustomers.InsertItem(nRow, strID); m_listCustomers.SetItemText(nItem, 1, strName); m_listCustomers.SetItemText(nItem, 2, strContact); m_listCustomers.SetItemText(nItem, 3, strPhone); m_listCustomers.SetItemText(nItem, 4, strStatus); nRow++; pRs->MoveNext(); } } catch (_com_error& e) { AfxMessageBox(_T("查询客户数据失败:") + CString(e.ErrorMessage())); } }

参数说明:adOpenStatic提供静态快照,适合只读列表;adLockReadOnly防止意外修改;MoveNext()是遍历核心;类型转换(LPCTSTR)(_bstr_t)是 ADO 字段值转 CString 的标准写法,缺一不可。


4. 避坑:MFC 销售系统开发中 5 个高频翻车点

4.1 现象:程序启动后 ListCtrl 显示空白,但调试发现数据已加载

原因:OnInitialUpdate()中调用LoadCustomersFromDB()时,m_listCustomers控件尚未完成窗口句柄创建(m_hWnd为空),导致InsertItem失败但无报错。
解决:在CSalesSystemView.h中重载OnActivateView(),确保控件就绪后再加载数据:

// 在头文件中声明 protected: virtual void OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView); // 在 CPP 中实现 void CSalesSystemView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView) { CFormView::OnActivateView(bActivate, pActivateView, pDeactiveView); if (bActivate && !m_bDataLoaded) { LoadCustomersFromDB(); m_bDataLoaded = TRUE; } }

并在类中添加BOOL m_bDataLoaded = FALSE;成员变量。

4.2 现象:TreeCtrl 点击节点无响应,NM_CLICK消息未触发

原因:MFC 树控件默认不发送NM_CLICK,需手动启用通知:在资源编辑器中选中TreeCtrl→ 属性 → “Notify” 设为 “True”。
验证:在类向导中为IDC_TREE_CATEGORIES添加NM_CLICK消息处理函数,若向导中无此选项,即 Notify 未开启。

4.3 现象:中文字段从数据库读出后显示为乱码(如 “北京” 变成 “鍖椾含”)

原因:SQL Server 数据库排序规则为Chinese_PRC_CI_AS,但 ADO 连接字符串未指定字符集,ADO 默认用 ANSI 编码解析 Unicode 字段。
解决:在连接字符串末尾添加;Charset=utf-8(SQL Server 2019+)或改用Provider=SQLNCLI11(Native Client)并确保数据库字段为NVARCHAR类型。更稳妥做法是在LoadCustomersFromDB()中强制转换:

CString ConvertBSTRToCString(_bstr_t bstr) { if (bstr.length() == 0) return _T(""); return CString((LPCWSTR)bstr); } // 使用:CString strName = ConvertBSTRToCString(pRs->Fields->GetItem("CustName")->Value);

4.4 现象:点击菜单“新增客户”后弹出对话框,但输入中文后保存到数据库变成问号(?)

原因:CDialog中CEdit控件获取文本用GetWindowTextA()(ANSI 版),而数据库字段为NVARCHAR,需用 Unicode 版本。
解决:确保所有文本获取用GetWindowText()(Unicode 版),并在对话框类中确认字符集为 Unicode(项目属性 → 常规 → 字符集 = 使用 Unicode 字符集)。

4.5 现象:程序退出时崩溃,调用堆栈指向CoUninitialize()

原因:CDBConnection析构函数中CoUninitialize()被多次调用(如多个连接实例共用同一 COM 初始化)。
解决:改为全局 COM 管理——在SalesSystem.cpp的InitInstance()开头加::CoInitialize(NULL),在ExitInstance()结尾加::CoUninitialize(),CDBConnection类中移除所有CoInitialize/CoUninitialize调用。


5. 权限控制与报表导出:让系统真正可用

5.1 基于角色的菜单/按钮可见性控制

销售系统需区分管理员、销售员、客服三类角色。在CMainFrame.cpp的OnCreate()中动态隐藏菜单项:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // 从登录模块获取当前用户角色(假设存于全局变量 g_nUserRole) switch (g_nUserRole) { case ROLE_ADMIN: // 所有菜单可见 break; case ROLE_SALES: // 隐藏“系统设置”、“用户管理”菜单 CMenu* pMenu = GetMenu(); if (pMenu) { pMenu->EnableMenuItem(ID_SYSTEM_SETTINGS, MF_BYCOMMAND | MF_GRAYED); pMenu->EnableMenuItem(ID_USER_MANAGEMENT, MF_BYCOMMAND | MF_GRAYED); } break; case ROLE_SERVICE: // 仅显示“客户查询”、“工单处理” CMenu* pMain = GetMenu(); if (pMain) { pMain->EnableMenuItem(ID_CUSTOMER_ADD, MF_BYCOMMAND | MF_GRAYED); pMain->EnableMenuItem(ID_ORDER_PROCESS, MF_BYCOMMAND | MF_ENABLED); } break; } return 0; }

提示:MF_GRAYED置灰但保留菜单项,MF_REMOVE彻底删除。生产环境建议将权限配置存入数据库,启动时加载到内存,避免硬编码。

5.2 使用 Excel 2003 格式导出客户列表(兼容 Win7/Win10 离线环境)

MFC 自带COleDocument导出复杂,直接调用 Excel 早期版本 COM 接口更可靠:

void CSalesSystemView::ExportToExcel() { _ApplicationPtr pExcel; _WorkbookPtr pWorkbook; _WorksheetPtr pSheet; try { pExcel.CreateInstance(__uuidof(Application)); pExcel->Visible = FALSE; pWorkbook = pExcel->Workbooks->Add(); pSheet = pWorkbook->ActiveSheet; // 写入列头 pSheet->Cells->Item[1][1] = _T("客户编号"); pSheet->Cells->Item[1][2] = _T("客户名称"); pSheet->Cells->Item[1][3] = _T("联系人"); pSheet->Cells->Item[1][4] = _T("电话"); pSheet->Cells->Item[1][5] = _T("状态"); // 写入数据(遍历 ListCtrl) for (int i = 0; i < m_listCustomers.GetItemCount(); i++) { pSheet->Cells->Item[i + 2][1] = m_listCustomers.GetItemText(i, 0); pSheet->Cells->Item[i + 2][2] = m_listCustomers.GetItemText(i, 1); pSheet->Cells->Item[i + 2][3] = m_listCustomers.GetItemText(i, 2); pSheet->Cells->Item[i + 2][4] = m_listCustomers.GetItemText(i, 3); pSheet->Cells->Item[i + 2][5] = m_listCustomers.GetItemText(i, 4); } // 保存文件 CString strPath = GetDesktopPath() + _T("\\客户列表_") + GetCurrentDateTime() + _T(".xls"); pWorkbook->SaveAs(_variant_t(strPath), xlExcel8, vtMissing, vtMissing, vtMissing, vtMissing, xlNoChange, xlNoChange, vtMissing, vtMissing); AfxMessageBox(_T("导出成功!文件已保存至桌面。")); } catch (_com_error& e) { AfxMessageBox(_T("Excel 导出失败:") + CString(e.ErrorMessage())); } // 必须释放 COM 对象,否则 Excel 进程残留 if (pWorkbook) pWorkbook->Close(vtFalse, vtMissing, vtMissing); if (pExcel) pExcel->Quit(); }

关键点:xlExcel8指定 Excel 97-2003 格式,兼容性最好;vtMissing是 COM 调用必需的占位参数;pExcel->Quit()必须调用,否则后台 Excel 进程不退出,用户任务管理器里会堆积僵尸进程。

5.3 打印预览与定制报表(基于 MFC 内置打印框架)

利用 MFCOnPreparePrinting/OnPrint实现客户列表打印:

// 在 CSalesSystemView.h 中声明 protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnPrint(CDC* pDC, CPrintInfo* pInfo); // 实现 BOOL CSalesSystemView::OnPreparePrinting(CPrintInfo* pInfo) { pInfo->SetMaxPage(1); // 简单列表,单页足矣 return DoPreparePrinting(pInfo); } void CSalesSystemView::OnPrint(CDC* pDC, CPrintInfo* pInfo) { // 设置字体 CFont font; font.CreatePointFont(100, _T("宋体")); // 10pt CFont* pOldFont = pDC->SelectObject(&font); // 打印标题 pDC->TextOut(100, 100, _T("客户信息列表")); pDC->TextOut(100, 150, _T("打印时间:") + GetCurrentDateTime()); // 打印列头(位置单位为逻辑英寸,需换算) int x = 100, y = 250; pDC->TextOut(x, y, _T("客户编号")); pDC->TextOut(x + 800, y, _T("客户名称")); pDC->TextOut(x + 2000, y, _T("联系人")); pDC->TextOut(x + 3000, y, _T("电话")); pDC->TextOut(x + 4200, y, _T("状态")); // 打印数据行 y += 300; for (int i = 0; i < m_listCustomers.GetItemCount(); i++) { pDC->TextOut(x, y, m_listCustomers.GetItemText(i, 0)); pDC->TextOut(x + 800, y, m_listCustomers.GetItemText(i, 1)); pDC->TextOut(x + 2000, y, m_listCustomers.GetItemText(i, 2)); pDC->TextOut(x + 3000, y, m_listCustomers.GetItemText(i, 3)); pDC->TextOut(x + 4200, y, m_listCustomers.GetItemText(i, 4)); y += 300; } pDC->SelectObject(pOldFont); }

参数说明:100单位是 0.01 英寸(即 1 逻辑英寸 = 100 单位),TextOut坐标需按此换算;CreatePointFont(100, ...)创建 10pt 字体;打印区域需预留页边距,此处简化处理,生产环境应计算pDC->GetDeviceCaps(LOGPIXELSX)动态适配。


6. 部署与维护:让系统在客户电脑上真正跑起来

6.1 生成可分发安装包(Inno Setup 最小化配置)

VS2019 编译的 MFC 程序依赖vcruntime140.dll、msvcp140.dll等 VC 运行库,不能直接拷贝运行。推荐用 Inno Setup 打包:

  • 下载 Inno Setup Compiler(免费);
  • 创建脚本setup.iss:
[Setup] AppName=销售管理系统 AppVersion=1.0 DefaultDirName={autopf}\SalesSystem OutputBaseFilename=SalesSystem_Setup [Files] Source: "Release\SalesSystem.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\Windows\System32\vcruntime140.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\Windows\System32\msvcp140.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\Windows\System32\concrt140.dll"; DestDir: "{app}"; Flags: ignoreversion [Run] Filename: "{app}\SalesSystem.exe"; Description: "运行销售管理系统"

注意:vcruntime140.dll等文件需从本机C:\Windows\System32复制(VS2019 对应 VC++ 14.29),严禁从网络下载,版本不匹配会导致启动黑屏。

6.2 数据库部署脚本(SQL Server Express 免费版适配)

为降低客户部署门槛,提供一键建库脚本init_db.sql:

-- 检查并创建数据库 IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = N'SalesDB') CREATE DATABASE SalesDB COLLATE Chinese_PRC_CI_AS; GO USE SalesDB; GO -- 创建客户表 IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Customers' AND xtype='U') CREATE TABLE Customers ( CustID NVARCHAR(20) PRIMARY KEY, CustName NVARCHAR(100) NOT NULL, Contact NVARCHAR(50), Phone NVARCHAR(20), Status NVARCHAR(20) DEFAULT N'活跃', CreatedDate DATETIME DEFAULT GETDATE() ); GO -- 插入测试数据 INSERT INTO Customers (CustID, CustName, Contact, Phone, Status) VALUES ('C001', N'北京智联科技', N'张经理', '010-88889999', N'活跃'), ('C002', N'上海云启信息', N'李总监', '021-66667777', N'暂停'); GO

客户只需:1)安装 SQL Server Express;2)用 SSMS 连接后执行此脚本;3)修改程序连接字符串中的Data Source为本机实例名(如.\SQLEXPRESS)。

6.3 日志与错误追踪:在 Release 版中保留关键诊断能力

Release 版关闭调试输出,但需保留错误日志:

// 在 SalesSystem.cpp 中添加全局日志函数 void WriteLog(const CString& strMsg) { static CString strLogPath = GetModulePath() + _T("\\SalesSystem.log"); CStdioFile file; if (file.Open(strLogPath, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite)) { file.SeekToEnd(); file.WriteString(_T("[") + GetCurrentDateTime() + _T("] ") + strMsg + _T("\r\n")); file.Close(); } } // 在数据库操作异常处调用 catch (_com_error& e) { CString strErr = _T("数据库错误:") + CString(e.ErrorMessage()); WriteLog(strErr); AfxMessageBox(strErr); }

血泪经验:某次客户现场部署后程序闪退,无任何提示。打开日志发现CoInitialize failed—— 原因是客户电脑禁用了 COM 组件策略。有了日志,3 分钟定位,否则至少 2 小时远程排查。

我坚持在每个新项目里保留WriteLog和GetCurrentDateTime()(封装为CTime::GetCurrentTime().Format(_T("%Y-%m-%d %H:%M:%S"))),不是为了炫技,是给未来的自己留一条退路。当客户凌晨三点打电话说“系统打不开了”,你打开日志看到第一行[2024-06-15 03:12:04] 数据库连接超时,那种踏实感,胜过所有架构图。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询