VB网页编辑器源码:designMode与execCommand链路
发布时间:2026/10/30 7:16:10 锦皓数字建站

简介网页编辑器VB版是一套面向Visual Basic开发者的富文本编辑控件及配套示例用于在桌面应用中嵌入类似在线记事本的HTML创建、编辑与格式化能力。它基于DHTML技术提供字体、字号、文本颜色、对齐、插入图片与链接等工具栏功能可服务于内容管理系统、论坛、博客等需要用户输入富文本的场景。压缩包为rar格式共99个文件约197KB主要由gif界面素材、cs源代码、dll组件以及xml、js、css、aspx等配置和网页资源构成兼顾示例运行与二次开发参考。已有1046人学习下载。其中的WebTextPaneDemo演示项目展示了控件初始化、HTML输入处理及内容保存或发送等环节便于开发者理解DHTML控件与IE渲染引擎的配合方式并据此在VB程序中快速搭建可交互的网页编辑界面。1. 一个能点进去打字的空白页才是 VB 网页编辑器的入口很多人拿到 VB 网页编辑器源码的第一反应是双击工程文件窗体确实弹出来了编辑区却像一张冻结的图点不进去光标不闪工具栏按钮按下去毫无反应。这时候很容易误判成代码不全或控件没注册。它本质上不是浏览器外壳而是把 IE 的编辑模式借出来让你像用文字处理软件一样排 HTML 的桌面工具——加粗斜体、插入图片和表格、切换源码、保存成 .html功能全藏在工具栏那几十个按钮后面。适合两类人手上还有 VB6 或 WinForms 老项目、需要给内部系统塞一个富文本编辑能力的维护者以及想弄明白 designMode、execCommand 这套老接口到底怎么跑起来的开发者。把它跑起来之后你会发现按钮点下去真正生效的代码往往只有一行。2. 技术选型与工程结构WebBrowser 控件加 MSHTML 的这套组合2.1 为什么是 WebBrowser 而不是 RichTextBoxVB 里能编辑文本的控件不多RichTextBox 算一个但它活在 RTF 世界。存出来的内容是{\rtf1\ansi...}这种结构想变成 HTML 得再挂一个转换器转换出来的标签又长又脏span style...能套三四层后续想用 CSS 统一改样式几乎无从下手。WebBrowser 走的是另一条路。它本质上是 IE 的渲染引擎加一份 COM 自动化接口mshtml页面加载完之后把document.designMode设成On整个文档立刻变成可编辑区域光标、选区、撤销栈、拖拽选字全都是浏览器原生给的不用自己实现一遍。工具栏上的按钮要做的事也只是调document.execCommand一行命令对应一个动作。这个选择的代价也很明确渲染内核老。默认状态下 WebBrowser 控件会把自己伪装成 IE7flex、grid、部分 CSS3 选择器全不认页面上出现莫名其妙的间距时不要先怀疑自己的样式写错了。解决办法是给进程写FEATURE_BROWSER_EMULATION注册表值把内核拉到 IE11 模式。这段代码要放在主窗体创建、任何 WebBrowser 实例出现之前。 VB.NET启动时调用一次位置放在 Sub Main 或主窗体 Load 的第一行 Private Sub EnableModernIE() Dim exeName As String Process.GetCurrentProcess().ProcessName .exe Using key As Microsoft.Win32.RegistryKey Microsoft.Win32.Registry.CurrentUser.CreateSubKey( Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION) 11001 IE11 边缘模式11000 跟随页面里的 X-UA-Compatible key.SetValue(exeName, 11001, Microsoft.Win32.RegistryValueKind.DWord) End Using End Sub参数说明写的是 HKCU不需要管理员权限键名必须和 exe 文件名完全一致带.exe后缀。在 IDE 里调试时进程名是vb6.exe或devenv.exe这个值对调试进程同样生效所以如果你在 IDE 里改了这一项又发现别的 VB 程序排版变样记得回来删掉。VB6 那边没有Process类一般用GetModuleFileName拿到自身路径再拆出文件名然后走RegSetValueEx逻辑一致。还有一种思路是干脆在 WebBrowser 里加载一个现成的 JS 富文本组件让组件去管工具栏和 HTML 生成。这条路适合已经有前端资源的团队但对纯 VB 项目来说等于引入了第二套技术栈调试时要在 VB 和 JS 之间来回看出问题定位成本翻倍。这份源码选的是原生 execCommand 路线代码量小、依赖少缺点是可定制的粒度粗后面二次开发那一章会讲怎么在它上面补。2.2 工程文件清单与各自职责VB6 和 VB.NET 两个版本的目录组织差不多下面按 VB6 的 .vbp 工程列括号里是 VB.NET 版本的对应文件。文件类型职责ProjEditor.vbp.vbproj工程文件引用列表、启动对象、编译配置frmMain.frm / .frxForm1.vb窗体工具栏、WebBrowser 编辑区、状态栏、源码文本框modEditor.basEditorModule.vb标准模块execCommand 统一封装、选区操作、HTML 片段处理modFile.basFileModule.vb标准模块文件读写、编码转换、图片落盘与相对路径clsDoc.clsDocWrapper.vb类模块对 IHTMLDocument2 的薄封装收拢 COM 调用template/default.html资源新建文档的初始模板含基础 CSSres/toolbar.bmp资源工具栏按钮图ImageList 使用config.ini配置默认保存目录、模板路径、默认字体字号关键引用有三个Microsoft Internet ControlsWebBrowser 控件本体、Microsoft HTML Object Librarymshtml提供 IHTMLDocument2、IHTMLTxtRange 这些接口、Microsoft Windows Common Controls 6.0工具栏和状态栏。少勾一个代码里Dim doc As IHTMLDocument2立刻报用户定义类型未定义。VB.NET 版本对应的是在项目属性里勾 COM 引用等价于在 .vbproj 里加COMReference IncludeMSHTML /。clsDoc 这个类模块看着多余实际是把散落在各个按钮事件里的WebBrowser1.Document.body.innerHTML收拢成Doc.BodyHtml这样的属性。改到第三天你就会感谢这个决定因为一旦要加编辑前先做一次脏标签清理只需要改一个方法不用去工具栏的二十个 Click 事件里翻。原始工程里如果没这个类二次开发时补一个也不亏。资源文件的位置有个常见坑template/default.html和config.ini一般用App.Path拼路径读取在 IDE 里跑时App.Path指向工程目录编译后指向 exe 目录。第一次编译出来发现模板读不到、编辑区空白八成是忘了把资源目录一起复制到输出目录或者干脆写了一个绝对路径换机器就崩。2.3 编译与第一次启动VB6 直接打开 .vbp把 frmMain 设为启动对象F5 就能跑。VB.NET 版本用 msbuild 或者 IDE 编译平台目标建议设成 x86# 32 位输出更稳少数 64 位环境下 mshtml 的 COM 互操作会抛类型不匹配 msbuild ProjEditor.vbproj /p:ConfigurationRelease /p:Platformx86编译成功后输出目录里应该能看到 ProjEditor.exe、template 目录和 config.ini。双击 exe窗口出现后停一两秒编辑区里会出现模板里的占位文字鼠标点进去能直接打字按钮区变灰的那几个说明命令封装里还没接上对应方法。判断是否真的进入编辑模式有个非常快的土办法在编辑区里按 CtrlA 再按 Delete如果内容被清空且光标还在说明 designMode 生效了如果整个页面被选中、光标消失说明还在只读模式去看DocumentComplete有没有执行到设置 designMode 那一行。这个动作后面排查章节还会提到因为它是区分初始化失败和命令失败的分水岭。3. 核心链路拆解从 designMode 打开到保存出第一份 HTML3.1 初始化把一个只读页面变成可编辑文档整个程序最关键的一次状态切换就在这两行代码里位置在窗体的 DocumentComplete 事件。注意这个事件会因为 iframe、about:blank的多次导航而被触发不止一次所以判断条件要写全。 frmMain.frm —— 窗体加载模板先读进内存 Private m_strTemplate As String Private m_blnInited As Boolean Private Sub Form_Load() m_strTemplate LoadHtml(App.Path \template\default.html) about:blank 是最轻的初始文档不产生网络请求 WebBrowser1.Navigate about:blank End Sub Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant) 只处理顶层文档页面里的 iframe 完成时也会触发这个事件 If Not (pDisp Is WebBrowser1.Object) Then Exit Sub If m_blnInited Then Exit Sub m_blnInited True Dim doc As MSHTML.IHTMLDocument2 Set doc WebBrowser1.Document 这一行之前文档是只读的之后才有光标和选区 doc.designMode On 用 innerHTML 灌内容而不是 document.write避免再触发一次导航 If Len(m_strTemplate) 0 Then doc.body.innerHTML m_strTemplate End If 默认样式挂在 body 上编辑时新插入节点的继承来源就在这里 doc.body.style.fontFamily 微软雅黑, sans-serif doc.body.style.fontSize 14px doc.body.style.padding 12px 没有这一句工具栏第一个命令很可能静默失败 WebBrowser1.Document.body.focus End Sub参数与逻辑说明designMode有三个取值On、Off、Inherit设置成Inherit时会跟随父文档在顶层文档里等同于 Off别被这个名字骗了。pDisp Is WebBrowser1.Object判断的是这次完成事件是不是顶层浏览器对象发出的漏掉这一句页面里只要有个 iframe初始化逻辑就会跑两次第二次会把用户刚输入的内容覆盖成模板。m_blnInited是第二道保险因为某些 IE 内核版本对about:blank会连发两次完成事件。Microsoft HTML Object Library里focus方法挂在 body 元素上对 IHTMLDocument2 本身调用没有意义。这一句看起来可有可无实际是后面 execCommand 能不能生效的前置条件——焦点在工具栏按钮上时命令会作用到空选区表现就是点了没反应也不报错。VB.NET 版本的事件签名不同判断方式换成If sender IsNot WebBrowser1 Then Exit Sub其余逻辑一样。如果工程里把 WebBrowser 放在 TabControl 或 Panel 里且默认隐藏DocumentComplete 可能在控件还没显示时就触发这时doc.body会是 Nothing加一句If doc Is Nothing Then Exit Sub兜底。3.2 命令层每个按钮背后都是一条 execCommand工具栏的每个按钮最终都落到同一个封装方法上这样做的好处是错误处理、焦点检查只写一遍。 modEditor.bas —— 所有无参数命令的公共入口 Public Sub ExecCmd(ByVal strCmd As String) On Error GoTo EH 命令发出去之前先确认编辑区有焦点 frmMain.WebBrowser1.SetFocus If Not frmMain.WebBrowser1.Document Is Nothing Then frmMain.WebBrowser1.Document.body.focus End If 第三个参数必须占位部分命令对参数个数敏感 frmMain.WebBrowser1.Document.execCommand strCmd, False, vbNullString Exit Sub EH: MsgBox 命令执行失败 strCmd vbCrLf Err.Description, vbExclamation End Sub 带参数的命令单独开一个入口避免调用方传错位置 Public Sub ExecCmdEx(ByVal strCmd As String, ByVal varValue As Variant) On Error GoTo EH frmMain.WebBrowser1.SetFocus frmMain.WebBrowser1.Document.body.focus frmMain.WebBrowser1.Document.execCommand strCmd, False, varValue Exit Sub EH: MsgBox 命令执行失败 strCmd vbCrLf Err.Description, vbExclamation End Sub按钮事件因此变得极薄加一个按钮只需要一行Private Sub cmdBold_Click() ExecCmd Bold End Sub Private Sub cmdItalic_Click() ExecCmd Italic End Sub Private Sub cmdUnderline_Click() ExecCmd Underline End Sub常用命令和它们的产出标签对照如下处理保存内容和做 HTML 清洗时按这张表来认标签最省事。按钮execCommand第三个参数典型产出加粗Bold空b或strong斜体Italic空i下划线Underline空u超链接CreateLinkURL 字符串a href...插入图片InsertImage图片路径img src...插入表格InsertTable空table行列数需自行补有序列表InsertOrderedList空olli水平线InsertHorizontalRule空hr字号FontSize1 到 7 的字符串font sizen前景色ForeColor#RRGGBBfont color...格式块FormatBlock带尖括号的标签名h2、pre等这张表里藏了两个容易翻车的点。第一个是字号FontSize只吃 1 到 7 这七个刻度产出的是font size标签不是像素值。想要 14px、16px 这种精确字号得先打开 CSS 输出模式 让后续命令优先产出 style 属性而不是 font 这类表现层标签 Private Sub cmdUseCss_Click() frmMain.WebBrowser1.Document.execCommand StyleWithCSS, False, True End Sub打开之后ForeColor产出的是span stylecolor:...而不再是font color页面更干净但标签嵌套层级会变多。这个开关是进程内状态切换源码视图、重新 write 文档之后会复位需要在初始化里重新打开一次。第二个坑是FormatBlock的参数格式标签名要带尖括号写成h2才生效写成h2命令返回 False 且没有任何提示。这类返回 False 但不报错的情况在 execCommand 里很常见调试阶段建议在封装里加一句Debug.Print strCmd, 返回值把返回值打出来。命令是否真的执行了、选区是否为空、标签名是否合法全靠这个布尔值分辨光看界面不变根本分不清是哪一种。还有一个视觉细节要注意连续点击加粗两次是打开再关闭的语义不是叠加两层b。如果用户先选中一段已经加粗的文字再点加粗浏览器会拆掉标签。做撤销功能时不能用自己记录的点击次数来推算状态得用document.queryCommandState(Bold)去查当前选区的真实状态按钮的高亮状态也应该跟着这个方法走。3.3 源码视图innerHTML、outerHTML 与 document.write 的取舍源码视图的切换看着简单实际是整份代码里最容易出玄学行为的地方因为三种取内容的方式返回的东西完全不同。 编辑区片段适合做自动保存草稿、字数统计 Public Function GetBodyHtml() As String GetBodyHtml frmMain.WebBrowser1.Document.body.innerHTML End Function 完整文档保存成独立 .html 文件时用这个 Public Function GetFullHtml() As String Dim doc As MSHTML.IHTMLDocument2 Set doc frmMain.WebBrowser1.Document GetFullHtml !DOCTYPE html vbCrLf doc.documentElement.outerHTML End Functionbody.innerHTML不含head、不含样式声明、不含文档类型拿它保存出来的文件在浏览器里打开会丢掉所有页面级样式。documentElement.outerHTML会带上html和head但 IE 内核输出的属性大小写、自闭合标签写法和现代浏览器有差异br可能写成br/属性可能全大写。做源码视图就展示这个内容用户改完再灌回去MSHTML 还会按自己的规则再修正一遍改乱的标签会被它悄悄整理成合法结构。这是它的黑匣子特性之一也是很多人对着源码视图改了半天发现保存出来不一样的原因。从源码回灌设计视图不要用body.innerHTML 新内容那只会替换 body 内部head 里的样式改动全部丢失。正确做法是重建整个文档Private Sub ApplySource() Dim doc As MSHTML.IHTMLDocument2 Set doc frmMain.WebBrowser1.Document open/write/close 会重建文档designMode 会被重置必须重新打开 doc.open doc.write txtSource.Text doc.close doc.designMode On write 之后可能再触发 DocumentComplete用标志位挡住模板灌入 m_blnInited True WebBrowser1.Document.body.focus End Subdoc.open、doc.write、doc.close这三步是 IE 时代的文档重建三件套效果等同于导航到一个新页面所以designMode必然复位不补那一行用户回到设计视图就点不进去了。doc.close之后有可能再触发一次 DocumentComplete如果不把m_blnInited置回 True初始化逻辑会把模板又灌一遍用户在源码里改的内容直接蒸发。这个现象一开始表现为切回设计视图内容变成默认模板查起来很费时间。如果源码里包含scriptdocument.write会真的执行它。做单机编辑器问题不大但如果这个工具将来要打开别人发来的 HTML建议在 write 之前把 script 标签剥掉或者把编辑区的安全级别调高。这是最容易被忽略的一处因为编辑自己的文件时永远不会触发只有在打开外部文件时才会暴露。3.4 保存与读取UTF-8 编码和 meta 声明VB6 自带的Open ... For Output加Print #是按系统 ANSI 代码页写文件的中文在 UTF-8 声明的页面里打开就是一片乱码。这份源码用的是 ADODB.Stream虽然绕但能精确控制编码。 modFile.bas —— 统一用 ADODB.Stream 处理文本读写 Public Sub SaveHtml(ByVal strPath As String, ByVal strHtml As String) Dim stm As Object Set stm CreateObject(ADODB.Stream) stm.Type 2 adTypeText文本模式 stm.Charset utf-8 必须在 Open 之前设好 stm.Open stm.WriteText strHtml stm.SaveToFile strPath, 2 2 adSaveCreateOverWrite覆盖已有文件 stm.Close Set stm Nothing End Sub Public Function LoadHtml(ByVal strPath As String) As String Dim stm As Object Set stm CreateObject(ADODB.Stream) stm.Type 2 stm.Charset utf-8 stm.Open stm.LoadFromFile strPath LoadHtml stm.ReadText stm.Close Set stm Nothing End Function参数说明Type 2是文本模式1是二进制模式二进制模式下Charset无效。SaveToFile的第二个参数2表示覆盖1表示文件已存在就报错。这套 API 在 VB6 和 VBScript 里都能用不需要额外引用靠 CreateObject 后期绑定即可代价是没有智能提示、拼错属性名只在运行时暴露。只改写入编码还不够文件里必须有对应的 meta 声明否则浏览器拿到 UTF-8 字节流也会按本地编码猜。保存前补一道检查Public Function EnsureCharset(ByVal strHtml As String) As String If InStr(1, strHtml, charset, vbTextCompare) 0 Then EnsureCharset strHtml Exit Function End If 只在第一个 head 后面插一次避免重复 EnsureCharset Replace(strHtml, head, _ head vbCrLf _ meta http-equivContent-Type contenttext/html; charsetutf-8, _ 1, 1, vbTextCompare) End FunctionVB.NET 版本不需要这么绕File.WriteAllText(strPath, html, New UTF8Encoding(False))一行搞定False表示不写 BOM。不带 BOM 更干净因为某些老版本浏览器会把 BOM 当成正文内容页面顶部莫名多出一行空白查半天以为是 CSS 的锅。VB6 那边 ADODB.Stream 写 UTF-8 默认也不带 BOM两边行为一致。保存前还有一道容易被跳过的清理把编辑过程中产生的空span、空p以及 MSHTML 插入的nbsp;占位符处理掉。不清也能用但文件会越来越大而且用户从 Word 粘贴进来的内容会带一大堆mso-开头的样式直接保存出去在页面上看是正常的实际源码里全是冗余。清理逻辑可以写在 GetFullHtml 之后、SaveHtml 之前用正则逐条替换这也是二次开发里最容易加值的一个位置。4. 避坑与排查五条反复出现的故障4.1 初始化跑了两遍模板把用户输入覆盖了现象程序启动后编辑区正常但只要页面里加载过任何带 iframe 的模板用户打了几个字之后内容突然跳回初始模板或者切换源码视图再切回来修改全部丢失。原因DocumentComplete是给每个框架文档都发一次的iframe 加载完成同样触发而初始化代码里没有判断事件来源模板被重复灌入。about:blank在某些内核版本下也会触发两次。解决用pDisp Is WebBrowser1.Object判断顶层文档再配一个m_blnInited布尔标志做二次拦截。所有会重建文档的操作document.write、重新 Navigate、切换源码回灌执行完之后都要把标志位重新置为 True否则下一次事件又会走一遍初始化。4.2 保存出来的中文在浏览器里是乱码现象编辑器里显示正常保存成 .html 双击打开中文变成问号、方块或者一段乱码。用记事本打开另存为 UTF-8 之后再打开就正常了。原因VB6 的Print #按系统 ANSI 代码页写字节而页面里声明的是 utf-8浏览器按声明解码就错位。或者反过来字节是 UTF-8 的但 head 里没有 charset 声明浏览器按本地编码猜。解决写入统一走 ADODB.Stream 并设Charset utf-8保存前用EnsureCharset补 meta。验证方法是保存后用十六进制查看器看文件头几个字节中文的 UTF-8 编码是三字节序列看到E4 B8 AD这类组合就对了。4.3 工具栏按钮点了完全没反应也不报错现象按钮能点、有按下效果编辑区毫无变化没有任何异常弹窗。原因焦点不在编辑文档的 body 上execCommand 作用到了空选区或者当前处于源码视图WebBrowser 已经隐藏命令发给了不可见文档又或者命令名拼错、参数格式不对execCommand 返回 False 但代码没检查返回值。解决所有命令入口统一调用SetFocus加body.focus()在源码视图下把工具栏按钮整体禁用而不是让它们继续发命令封装里加Debug.Print输出命令名和返回值先确认返回 True 再谈界面效果。FormatBlock这类需要带尖括号参数的命令参数写错是最常见的 False 来源。4.4 插入的图片换一台机器就变成红叉现象本机编辑、本机打开一切正常把 html 发给同事或者复制到另一台机器图片位置全是红叉右键查看属性显示的是C:\Users\xxx\Desktop\xxx.png这种本地绝对路径。原因InsertImage命令把传入的字符串原样写进src传的是本地完整路径文件一离开这台机器就找不到。解决插入前把图片复制到工作目录下的images子目录src写相对路径。文件名加时间戳前缀防重名同一张图重复插入也不会互相覆盖。Public Function CopyImageToWorkDir(ByVal strSrc As String) As String Dim fso As Object, strName As String Set fso CreateObject(Scripting.FileSystemObject) strName fso.GetFileName(strSrc) 时间戳前缀避免不同目录下同名文件互相覆盖 strName Format$(Now, yyyymmddhhnnss) _ strName If Not fso.FolderExists(m_strWorkDir \images) Then fso.CreateFolder m_strWorkDir \images End If fso.CopyFile strSrc, m_strWorkDir \images\ strName, True CopyImageToWorkDir images/ strName End Function注意返回值里用的是正斜杠浏览器对反斜杠的容忍度因内核版本而异正斜杠是稳的。CopyFile的第三个参数 True 表示覆盖配合时间戳前缀基本不会撞上。4.5 换到 64 位机器排版全乱或者启动直接报类型不匹配现象在老机器上排版正常的页面新机器上 flex 不生效、圆角消失、字体大小不对或者程序启动时抛无法将类型为 COM 对象的实例强制转换为接口类型。原因WebBrowser 默认按 IE7 兼容模式渲染不认现代 CSS64 位进程加载 32 位 COM 组件或引用平台不匹配时mshtml 的接口转换会失败。解决编译目标设成 x86同时写入FEATURE_BROWSER_EMULATION注册表值 11001页面里再加一句meta http-equivX-UA-Compatible contentIEedge作为兜底。三项做完之后同一份 HTML 在新旧机器上的渲染结果基本一致。另外卸载或迁移程序时记得把注册表值一起清理否则同名 exe 会一直沿用旧设置。5. 二次开发加一个代码块按钮把验证流程固定下来工具栏上的按钮都是选中文字点一下包一层标签的模式照着现有代码抄一个就能跑。最容易下手的三个扩展点如下。扩展点位置改动量适合做什么工具栏按钮frmMain 的 Toolbar 控件加按钮写 Click 事件小新增格式化命令、插入固定内容命令封装modEditor.bas 的 ExecCmd 系列小统一加日志、加权限判断、加二次确认保存前后处理GetFullHtml 与 SaveHtml 之间中HTML 清洗、图片归档、模板注入以把选中文字包成代码块为例FormatBlock能直接产出pre但它会把整段替换成 pre对行内选区不友好。用 MSHTML 的选区对象包一层更可控 把当前选区包进带 class 的 pre方便页面里统一挂样式 Public Sub WrapSelectionAsCode() Dim doc As MSHTML.IHTMLDocument2 Dim rng As MSHTML.IHTMLTxtRange Set doc frmMain.WebBrowser1.Document selection 在文档没有焦点时是空的这里必须先兜一次 frmMain.WebBrowser1.SetFocus Set rng doc.selection.createRange() If Len(rng.text) 0 Then MsgBox 先选中一段文字, vbInformation Exit Sub End If pasteHTML 会替换选区内容所以要把原内容拼回去 rng.pasteHTML pre classcode rng.htmlText /pre End Subrng.htmlText拿到的是选区内的原始 HTML不是纯文本这样用户选中的加粗、链接都会保留在代码块里。pasteHTML会先删掉选区再插入新内容所以必须把原内容拼进去直接传一个空pre会把用户的文字吃掉。样式可以写在template/default.html的style里.code { background:#f6f8fa; padding:8px; }这类声明会在新文档里自动生效不用在 VB 代码里拼内联样式。改造之后的验证流程我一般固定走四步缺一步都可能留下看着好了、保存出来不对的隐患。第一步按钮点下去立刻在封装里看 execCommand 的返回值False 就不用往下测了先查命令名、参数和焦点。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。