news 2026/7/18 15:35:33

VC++ 2010 Express实战指南:从环境搭建到MFC图形界面开发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VC++ 2010 Express实战指南:从环境搭建到MFC图形界面开发

1. 项目概述:为什么今天还要聊VC++ 2010 Express?

如果你在2025年还在搜索“Visual C++ 2010 Express中文版”,大概率不是想用它开发下一个商业级应用。这个十几年前的免费IDE,在今天看来界面老旧、编译器标准落后,甚至安装包都很难在微软官网找到。但恰恰是这些“缺点”,让它成为了无数C++初学者、高校学生,甚至是需要维护遗留代码的开发者绕不开的一个起点。我当年就是用这套工具啃下了第一行Windows GUI代码,踩过无数坑,也真切体会到了从控制台“黑窗口”到画出第一个窗口的那种成就感。

Visual C++ 2010 Express的本质,是一个高度集成且“五脏俱全”的Windows原生开发沙盒。它免费、轻量(相比几个G的完整版VS)、完全中文界面,并且捆绑了那个时代最经典的C++开发工具链:v100编译器、MFC/ATL库、以及一个虽然简陋但足够强大的调试器。对于学习C++核心语法、理解Windows程序的消息机制、甚至初窥面向对象和图形界面编程,它提供了一个几乎没有干扰的纯净环境。你不会被现代IDE五花八门的插件、云同步、AI代码补全分散注意力,所有的焦点都集中在代码、编译、链接、运行这个最原始也最本质的循环上。

这篇文章,就是为你还原这个环境,并带你走通从安装配置到写出一个真正可用的、带图形界面的小程序的全过程。我会基于我当年大量的实操经验,告诉你哪些设置是坑必须避开,哪些技巧能极大提升在这个“老家伙”上的开发效率。我们的目标不是怀旧,而是通过这个经典的平台,扎实地掌握那些至今依然通用的C++/Windows开发核心概念。

2. 环境搭建与避坑指南:让老工具跑起来

万事开头难,而用VC++ 2010 Express开头,难在第一步:安装和配置。网上的安装包鱼龙混杂,直接运行很可能遇到“.NET Framework初始化错误”或者干脆装不上。下面是我验证过最稳妥的路径。

2.1 获取与安装:找到正确的“遗产”

首先,放弃在微软官网直接搜索的念头。最可靠的来源是像MSDN I Tell You这样的第三方存档站,或者一些技术论坛的精华帖。你需要寻找的是名为vc_express.exe或类似的可执行文件,大小约几百MB。下载后,别急着双击。

关键步骤一:以管理员身份运行安装程序。这是老版本Windows安装程序的通病,权限不足会导致写入系统目录失败。

关键步骤二:自定义安装路径。强烈建议不要安装在默认的C:\Program Files (x86)\...下。选择一个简单的、没有空格和中文的路径,比如D:\VC2010。这能避免后续一系列因路径问题导致的编译和调试诡异错误。

关键步骤三:只选必要的。安装过程中,选择“自定义”安装。对于纯C++学习,你只需要勾选“Visual C++”核心组件和“Microsoft Foundation Classes (MFC)”。像“Microsoft SQL Server 2008 Express”这类数据库组件完全可以不装,节省磁盘空间。

安装完成后,第一次启动可能会提示你注册。Express版是免费的,你可以选择跳过注册,功能不受任何影响。

2.2 第一个项目:从“Hello, World”到理解解决方案

打开VC++ 2010,界面是经典的蓝灰色调。我们通过“文件”->“新建”->“项目”来开始。

在这里你会遇到第一个重要概念:项目类型。对于初学者,我建议按这个顺序尝试:

  1. Win32控制台应用程序:这是起点。创建一个空项目,你会得到一个main函数。在这里学习基础语法、输入输出、流程控制。
  2. Win32项目:选择“空项目”,然后手动添加WinMain函数。这是理解Windows应用程序入口和消息循环的绝佳方式。
  3. MFC应用程序:当你对C++语法和Windows编程有基本概念后,可以用向导生成一个带窗口的程序骨架,快速看到图形界面。

创建项目时,注意对话框底部的“解决方案名称”和“项目名称”。在VC++ 2010中,解决方案(.sln文件)是一个容器,可以包含一个或多个项目(.vcxproj文件)。对于学习阶段,通常一个解决方案只放一个项目,两者同名即可。

创建成功后,解决方案资源管理器(通常位于界面右侧)会显示你的项目结构。右键点击“源文件”筛选器,选择“添加”->“新建项”,添加一个.cpp文件,比如main.cpp

现在,输入最经典的代码:

#include <iostream> int main() { std::cout << "Hello, VC++ 2010!" << std::endl; return 0; }

按下F5(开始调试)或Ctrl+F5(开始执行(不调试))。如果一切顺利,你会看到一个黑色的控制台窗口弹出并显示那句话。恭喜,你的环境跑通了。

实操心得:如果按F5后窗口一闪而过,有两种方法:一是在return 0;前加上system(“pause”);(需要#include <cstdlib>);二是在项目属性中配置:右键项目 -> 属性 -> 配置属性 -> 链接器 -> 系统 -> 子系统,选择“控制台(/SUBSYSTEM:CONSOLE)”。后者是更规范的做法。

2.3 关键配置调优:告别默认的“坑”

默认配置是为了兼容性,但不利于学习和调试。以下几个设置必须调整:

1. 关闭“预编译头”:对于小型学习项目,预编译头(stdafx.h)不仅没用,还容易引发编译错误。关闭方法:项目属性 -> 配置属性 -> C/C++ -> 预编译头 -> 选择“不使用预编译头”。同时,在解决方案资源管理器中删除stdafx.hstdafx.cpp文件。

2. 调整警告等级和调试信息:项目属性 -> 配置属性 -> C/C++ -> 常规 -> 警告等级,设置为“等级3(/W3)”或“等级4(/W4)”。这能让编译器告诉你更多潜在的代码问题。 项目属性 -> 配置属性 -> 链接器 -> 调试 -> 生成调试信息,确保是“是(/DEBUG)”。这是使用调试器的前提。

3. 输出目录规范化:默认的编译输出(.exe, .obj文件)和源代码混在一起,很乱。建议设置:

  • 输出目录:$(SolutionDir)bin\$(Platform)\$(Configuration)\
  • 中间目录:$(SolutionDir)temp\$(Platform)\$(Configuration)\这样,你的项目文件夹会保持干净,所有生成文件都归类到bintemp子目录下。

4. 字符集设置:老项目和新系统的兼容性问题,很多出在字符集上。VC++ 2010默认使用“多字节字符集”,但现代Windows API更推荐Unicode。对于新项目,我建议在项目属性 -> 配置属性 -> 常规 -> 字符集中,设置为“使用Unicode字符集”。这会将TCHAR映射为wchar_t,相关API如MessageBox会使用MessageBoxW版本。编写字符串时使用_T(“文本”)L”文本”来兼容。

3. 核心开发技能实战:不止于黑窗口

掌握了环境,我们就进入核心环节。VC++ 2010虽然老,但用它学习C++和Windows编程的核心思想,一点都不过时。

3.1 深入理解编译与链接:.cpp如何变成.exe

按下F7(生成解决方案)后,IDE背后发生了什么?理解这个过程,是解决“无法解析的外部符号”、“LNK错误”等问题的关键。

编译阶段:你的每个.cpp文件都是一个独立的“编译单元”。编译器(cl.exe)会单独处理它:

  1. 预处理:处理所有#开头的指令。比如#include就是把头文件内容复制进来;#define进行宏替换。你可以通过项目属性 -> C/C++ -> 预处理器 -> 生成预处理文件,来查看预处理后的代码,这对调试宏错误非常有用。
  2. 编译:将C++代码翻译成机器相关的汇编代码,再进一步生成目标文件(.obj)。这个.obj文件包含了你的函数和变量的二进制代码,但其中引用自其他.cpp文件或库的函数地址还是“空壳”(未解决的外部符号)。

链接阶段:链接器(link.exe)登场,它的任务是把所有.obj文件,以及你指定的库文件(.lib)“缝合”成一个可执行文件(.exe)或动态链接库(.dll)。

  1. 符号解析:链接器扫描所有.obj,建立一个符号表。对于每个“未解决的外部符号”,它去其他.obj.lib中寻找其定义。
  2. 地址重定位:确定每个函数和变量在最终内存镜像中的实际地址,并修正.obj中的引用。
  3. 生成PE文件:生成符合Windows可执行文件格式(PE格式)的最终文件。

一个典型错误分析:error LNK2001: 无法解析的外部符号 “void __cdecl MyFunc(int)” (?MyFunc@@YAXH@Z)这个错误告诉你:链接器在某个.obj里看到了对MyFunc的调用,但在所有.obj和库文件里都没找到它的实现。原因可能是:

  • 声明了函数但没定义(只有void MyFunc(int);,没有void MyFunc(int x) { … })。
  • 定义函数的.cpp文件没有被加入项目参与编译。
  • 函数名在C++中被名字修饰了(就是那个?MyFunc@@YAXH@Z),如果你在C语言项目(或用extern “C”声明的函数)中错误地链接了C++编译的.obj,就会因名字修饰不匹配而找不到。

避坑技巧:遇到链接错误,首先检查函数签名(返回值、参数类型、命名空间)是否在声明和定义处完全一致。对于涉及C/C++混合编程的情况,正确使用extern “C”包裹C语言函数声明。

3.2 使用STL:在VC++ 2010中用好标准库

Visual C++ 2010的STL实现基于C++03标准,不支持C++11的auto、范围for、智能指针等现代特性,但这不影响我们学习容器和算法的核心思想。

容器选择与内存管理:std::vector是你的首选序列容器。它连续存储,访问速度快。但要注意它的扩容策略:当push_back导致容量不足时,它会分配一块新的更大的内存(通常是原容量的1.5倍),然后把所有元素拷贝或移动过去。这是一个O(n)操作。

std::vector<int> vec; // 糟糕的做法:反复push_back导致多次扩容和数据拷贝 for(int i=0; i<10000; ++i) { vec.push_back(i); } // 推荐的做法:如果知道大致大小,预先分配 vec.reserve(10000); // 只分配内存,不改变size for(int i=0; i<10000; ++i) { vec.push_back(i); // 此时不会触发扩容 }

std::list是双向链表,任意位置插入删除是O(1),但访问是O(n),且内存不连续,缓存不友好。除非你需要频繁在中间插入删除,否则vector通常是更好的选择。

迭代器失效陷阱:这是STL新手最容易踩的坑。在修改容器时,指向其元素的迭代器、指针或引用可能会失效。

  • 对于vectordeque:任何可能引起内存重新分配的操作(如insert,push_back导致扩容),都会使所有迭代器失效。erase操作会使被删除元素及其之后元素的迭代器失效。
  • 对于list,map,set等关联容器:插入操作不会使任何迭代器失效(除了指向被删除元素的迭代器)。erase操作仅使指向被删除元素的迭代器失效。

安全遍历与删除:错误示例(在循环中删除vector元素):

std::vector<int> v = {1,2,3,4,5}; for(auto it = v.begin(); it != v.end(); ++it) { if(*it % 2 == 0) { v.erase(it); // 错误!erase后it失效,后续的++it行为未定义 } }

正确做法(利用erase返回值):

for(auto it = v.begin(); it != v.end(); ) { // 注意这里没有++it if(*it % 2 == 0) { it = v.erase(it); // erase返回被删除元素之后元素的有效迭代器 } else { ++it; } }

对于listmaperase也会返回下一个有效迭代器,用法相同。

3.3 踏入图形界面:MFC入门第一课

MFC(Microsoft Foundation Classes)是VC++ 2010内置的C++类库,用于快速开发Windows GUI程序。它用面向对象的方式封装了原始的Win32 API,虽然庞大且有些设计已显陈旧,但学习它能让你深刻理解Windows消息机制。

创建第一个MFC对话框程序:

  1. 新建项目 -> Visual C++ -> MFC -> MFC应用程序。
  2. 在“应用程序类型”中选择“基于对话框”,取消“使用Unicode库”(为简化初学,我们先使用多字节字符集)。
  3. 完成向导后,你会得到一个对话框资源和一个CXXXDlg类。

理解消息映射:MFC的核心机制是消息映射,它把Windows消息(如鼠标点击、键盘输入)映射到类的成员函数上。这比原始的WndProc回调函数清晰得多。 在对话框类的头文件(.h)中,你会看到:

afx_msg void OnBnClickedButton1(); // 这是一个消息处理函数声明 DECLARE_MESSAGE_MAP()

在实现文件(.cpp)中,会有:

BEGIN_MESSAGE_MAP(CMyDlg, CDialogEx) ON_BN_CLICKED(IDC_BUTTON1, &CMyDlg::OnBnClickedButton1) END_MESSAGE_MAP() void CMyDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 MessageBox(_T("按钮被点击了!")); }

ON_BN_CLICKED就是一个消息映射宏,它将按钮(ID为IDC_BUTTON1)的点击消息,与CMyDlg::OnBnClickedButton1函数绑定。当用户点击按钮时,MFC框架会自动调用这个函数。

与控件交互:在对话框编辑器中拖放一个编辑框(Edit Control)和一个按钮。为编辑框添加一个控件变量

  1. 右键编辑框 -> 添加变量。
  2. 变量类型选择“值”,类别选择“CString”,并命名(如m_strInput)。
  3. 这样,你就可以在按钮点击事件中,通过UpdateData(TRUE)将界面数据更新到变量,或通过UpdateData(FALSE)将变量值更新到界面。
void CMyDlg::OnBnClickedButtonOk() { UpdateData(TRUE); // 将对话框控件内容同步到关联的成员变量(如m_strInput) CString strMsg; strMsg.Format(_T(“你输入了:%s”), m_strInput); MessageBox(strMsg); UpdateData(FALSE); // 如果需要清空输入框,可以设置m_strInput为空,然后调用此函数 }

UpdateData是MFC对话框数据交换(DDX)机制的核心,它简化了控件和变量之间的数据同步。

4. 调试技巧与性能优化:像侦探一样工作

调试是程序员的核心技能。VC++ 2010的调试器功能强大,用好它事半功倍。

4.1 断点的艺术:不只是F9

除了行断点(F9),你更应该掌握:

  • 条件断点:在循环中,你只想在i == 500时中断。右键断点 -> 条件,输入i == 500。这能避免你手动按几百次F10。
  • 命中次数断点:右键断点 -> 命中次数。你可以设置“中断,当命中次数等于100时”。用于定位那些偶尔才出现的bug。
  • 数据断点(仅x86):当某个变量被改变时中断。这在追踪谁修改了你的全局变量时非常有用。通过“调试” -> “窗口” -> “断点”打开断点窗口,点击“新建” -> “新建数据断点”,输入&变量名

4.2 监视与内存窗口:洞察程序状态

调试时,自动窗口局部变量窗口会显示当前上下文中的变量。但监视窗口更强大,你可以输入任何合法的C++表达式,比如array[10]ptr->memberstr.GetLength()

当指针出现问题(如访问违规)时,内存窗口是终极武器。在地址栏输入指针的值(如0x0012FF34),你可以直接查看该地址开始的一片内存区域。结合你对数据结构布局的了解,可以判断是缓冲区溢出、野指针还是内存被意外覆盖。

一个实战案例:你的程序在某个函数中崩溃,错误是“写入位置 0x00000000 时发生访问冲突”。这明显是一个向空指针(NULL)写入的操作。

  1. 在可能出错的代码行前设置断点。
  2. 运行到断点,打开监视窗口
  3. 找到可疑的指针变量,查看其值。如果是0x00000000,那么它就是空指针。
  4. 向上回溯,看这个指针是在哪里被初始化的,为什么没有被正确赋值。

4.3 调用堆栈与异常定位:找到问题根源

程序崩溃或抛出异常时,调用堆栈窗口是你的地图。它显示了从当前出错点一直到mainWinMain的函数调用序列。双击堆栈中的任意一行,IDE会带你跳转到对应的源代码位置。这能帮你快速定位是哪个函数调用链导致了问题。

对于C++标准异常(如std::out_of_range),调试器通常能捕获并停在throw语句处。对于Windows结构化异常(如访问违规、除零错误),你需要确保“调试” -> “异常”对话框中,对应的异常类型(如“Win32 Exceptions”下的c0000005访问冲突)是被勾选“引发”的,这样调试器才会在异常发生时中断,而不是直接终止程序。

5. 项目实战:打造一个简易通讯录管理程序

现在,我们把所有知识串联起来,用VC++ 2010开发一个具有图形界面、能持久化存储的简易通讯录程序。我们将采用基于对话框的MFC应用程序。

5.1 需求分析与界面设计

功能需求:

  1. 显示通讯录列表(姓名、电话)。
  2. 支持添加、删除、修改联系人。
  3. 将数据保存到本地文件,下次启动可加载。

界面设计:

  1. 创建一个MFC对话框项目ContactBook
  2. 在对话框上放置一个List Control控件,设置其View属性为Report(报表视图),用于显示列表。
  3. 添加几个Edit Control用于输入姓名、电话。
  4. 添加“添加”、“修改”、“删除”、“保存”按钮。
  5. 添加一个Static Text显示状态。

5.2 数据结构与文件操作

我们使用std::vector来管理内存中的联系人列表,每个联系人是一个结构体。 在对话框类的头文件中定义:

struct Contact { CString name; CString phone; }; class CContactBookDlg : public CDialogEx { // ... private: std::vector<Contact> m_contacts; CListCtrl m_listCtrl; // 控件变量,关联到List Control CString m_strName; // 值变量,关联到姓名编辑框 CString m_strPhone; // 值变量,关联到电话编辑框 // ... };

文件持久化我们使用简单的文本格式。在对话框初始化时(OnInitDialog)加载,在程序退出时保存。

// 加载文件 void CContactBookDlg::LoadFromFile(const CString& filename) { CStdioFile file; if(file.Open(filename, CFile::modeRead)) { CString line; m_contacts.clear(); while(file.ReadString(line)) { int commaPos = line.Find(_T(‘,’)); if(commaPos != -1) { Contact c; c.name = line.Left(commaPos); c.phone = line.Mid(commaPos + 1); m_contacts.push_back(c); } } file.Close(); RefreshList(); // 更新列表显示 } } // 保存文件 void CContactBookDlg::SaveToFile(const CString& filename) { CStdioFile file; if(file.Open(filename, CFile::modeCreate | CFile::modeWrite)) { for(const auto& contact : m_contacts) { CString line = contact.name + _T(“,”) + contact.phone + _T(“\n”); file.WriteString(line); } file.Close(); } }

5.3 核心功能实现与消息响应

初始化列表控件:OnInitDialog函数中,我们需要为List Control插入列。

BOOL CContactBookDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置列表风格 m_listCtrl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); // 插入列 m_listCtrl.InsertColumn(0, _T(“姓名”), LVCFMT_LEFT, 100); m_listCtrl.InsertColumn(1, _T(“电话”), LVCFMT_LEFT, 150); // 加载数据 LoadFromFile(_T(“contacts.txt”)); return TRUE; }

刷新列表显示:这是一个通用函数,在加载、添加、删除后调用。

void CContactBookDlg::RefreshList() { m_listCtrl.DeleteAllItems(); // 清空现有项 for(int i = 0; i < m_contacts.size(); ++i) { m_listCtrl.InsertItem(i, m_contacts[i].name); // 插入行,并设置第一列 m_listCtrl.SetItemText(i, 1, m_contacts[i].phone); // 设置第二列 } }

“添加”按钮响应:

void CContactBookDlg::OnBnClickedButtonAdd() { UpdateData(TRUE); // 从界面获取数据到m_strName, m_strPhone if(m_strName.IsEmpty() || m_strPhone.IsEmpty()) { MessageBox(_T(“姓名和电话不能为空!”), _T(“提示”)); return; } Contact newContact = {m_strName, m_strPhone}; m_contacts.push_back(newContact); RefreshList(); // 清空输入框 m_strName.Empty(); m_strPhone.Empty(); UpdateData(FALSE); // 将空值更新回界面 }

“删除”按钮响应:需要获取列表当前选中的行。

void CContactBookDlg::OnBnClickedButtonDelete() { int nSelected = m_listCtrl.GetSelectionMark(); if(nSelected >= 0 && nSelected < m_contacts.size()) { // 安全删除vector元素 m_contacts.erase(m_contacts.begin() + nSelected); RefreshList(); } else { MessageBox(_T(“请先选中要删除的联系人!”), _T(“提示”)); } }

“保存”和“退出”响应:

void CContactBookDlg::OnBnClickedButtonSave() { SaveToFile(_T(“contacts.txt”)); MessageBox(_T(“保存成功!”), _T(“提示”)); } void CContactBookDlg::OnOK() { // 响应对话框的“确定”或“关闭” SaveToFile(_T(“contacts.txt”)); // 退出前自动保存 CDialogEx::OnOK(); }

5.4 项目总结与扩展思考

通过这个项目,你实践了:

  • MFC对话框应用程序的创建。
  • 控件(列表、编辑框、按钮)的使用和数据交换(DDX/DDV)。
  • std::vector容器的增删改查。
  • MFC文件类(CStdioFile)进行简单的文本I/O操作。
  • 基本的错误处理(如空输入判断)。

可以扩展的方向:

  1. 数据验证:为电话号码添加格式验证(如纯数字)。
  2. 查找功能:添加一个编辑框和“查找”按钮,遍历vector找到匹配的联系人并高亮显示。
  3. 使用std::liststd::map:尝试用std::map<CString, Contact>以姓名为键来存储,提升查找效率。
  4. 更优雅的持久化:使用XML或JSON格式保存,提高可读性和扩展性。虽然VC++ 2010没有内置库,可以寻找像TinyXML这样的第三方库集成。
  5. 多文档界面:挑战一下,将程序改为MDI(多文档界面),每个联系人列表是一个独立的子窗口。

这个项目麻雀虽小,五脏俱全。它涵盖了从界面到数据,从内存管理到文件存储的完整链条。当你亲手把它调试通过,看着自己写的程序能真正保存下数据时,你对编程的理解会深一个层次。VC++ 2010 Express这个老工具,也就在这个过程中完成了它的使命——带你入门,并让你有能力走向更广阔的世界。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 15:33:48

C++实现LZW压缩算法:从原理到工程实践详解

1. 项目概述&#xff1a;为什么今天还要手搓LZW&#xff1f; 如果你在C的学习路上已经摸爬滚打了一段时间&#xff0c;刷过一些算法题&#xff0c;也写过几个小项目&#xff0c;可能会觉得基础的数据结构都玩得差不多了。这时候&#xff0c;一个“压缩算法”的项目标题跳出来&a…

作者头像 李华
网站建设 2026/7/18 15:33:43

C++并发编程实战:锁机制详解与性能优化指南

1. 项目概述&#xff1a;为什么C程序员必须懂锁&#xff1f; 如果你写过C多线程程序&#xff0c;大概率遇到过数据竞争、段错误或者一些“灵异”的、难以复现的bug。我职业生涯早期的一个项目&#xff0c;一个看似简单的日志系统&#xff0c;在多线程高并发下偶尔会丢失几条日志…

作者头像 李华
网站建设 2026/7/18 15:33:35

基于MediaPipe手势识别实现体感切水果游戏:Python+OpenCV实战

1. 项目概述&#xff1a;隔空切水果的乐趣与技术实现最近在琢磨怎么把一些前沿的计算机视觉技术玩出点新花样&#xff0c;正好看到MediaPipe这个工具包&#xff0c;它提供了一套非常成熟的手势和姿态识别方案。我就想&#xff0c;能不能用它来做个不用手柄、不用触摸屏&#xf…

作者头像 李华
网站建设 2026/7/18 15:32:36

185、注意力机制进化:SAN的二阶通道注意力与RCAN的残差通道注意力对比

185、注意力机制进化:SAN的二阶通道注意力与RCAN的残差通道注意力对比 从一次模型退化说起 去年有个项目,用EDSR做4倍超分,训练到第200个epoch时PSNR死活卡在32.1dB不动了。我盯着loss曲线看了半小时,发现它像条死鱼一样平躺——梯度还在更新,但指标纹丝不动。当时第一反…

作者头像 李华
网站建设 2026/7/18 15:31:31

OpenClaw 对接 DeepSeek 大模型完整配置实操步骤

OpenClaw 连接 DeepSeek 图文教程 前置准备 已安装并可以正常打开 OpenClaw Windows。 OpenClaw 顶部 Gateway 状态保持在线。 电脑已联网&#xff0c;可正常访问 DeepSeek 开放平台。 已准备可接收验证码的手机号或微信账号&#xff0c;用于登录平台。 DeepSeek 开放平台…

作者头像 李华