29、深入探索WSH技术:从脚本操作到远程执行

深入探索WSH技术:从脚本操作到远程执行

1. 脚本基础操作

在脚本编程中,有一些常见的操作,比如创建ZIP文件和终止脚本执行。以下是实现这些功能的代码示例:

Function CreateZipFile()  
    'Create the new ZIP file
    'WINZIP32 Command Syntax:
    'WINZIP32 [-min] action [options] filename[.zip] files
    ' -min   - Tells WinZip to run minimized
    ' action – Represents any one of the following arguments
    '         -a   Create new ZIP file
    '         -f   Refresh existing archive
    '         -u   Update an existing archive
    '         -m   Move archive to specified location
    ' options  - Optional arguments that include
    '         -r   Add files and folders when adding to ZIP file
    '         -p   Include information about any added folders
    ' filename[.zip]  – name of ZIP file to be created
    ' files  – names of file to be added to the ZIP file
    objWshShl.Run _
    "WINZIP32 -a C:\Temp\VBScripts.zip D:\VBScriptGames\*.vbs", 0, True
End Function

Function TerminateScriptExecution()  
    'Terminate the script’s execution
    WScript.Quit()
End Function

上述代码中, CreateZipFile 函数用于创建一个新的ZIP文件,它使用 WINZIP32 命令,将 D:\VBScriptGames\ 目录下所有的 .vbs 文件压缩到 C:\Temp\VBScripts.zip 中。 TerminateScriptExecution 函数则用于终止脚本的执行。

2. HTML应用程序(HTAs)

HTAs可以为脚本提供图形用户界面(GUI),使脚本的使用更加方便,即使是非技术人员也能轻松操作。下面介绍两个HTA的示例。

2.1 为WSH ping脚本添加GUI

以下是一个将 ping 命令封装在GUI中的HTA示例:

<head>
    <title>HTA Ping Application</title> 
    <HTA:APPLICATION ID="htaPingApp"
        APPLICATIONNAME="HTA Ping"
        BORDER="thin"
        BORDERSTYLE="normal"
        SINGLEINSTANCE="yes">
    <style>
        body {
            background-color:#AAAAAA;
            font:16px “Arial”
        }
        span {
            color:red;
            font-weight:bold
        }
    </style>
    <script type="text/vbscript">
        Sub Window_OnLoad
            self.resizeTo 750, 240
        End Sub
        Sub submitButton_onClick
            textOutput.innerHTML = ""
            self.resizeTo 750, 680
            strTempFile = "C:\Temp\PingFile.txt"
            Set WshFSO = CreateObject("Scripting.FileSystemObject")
            Set WshShell = CreateObject("Wscript.Shell")
            WshShell.Run "cmd.exe /c ping.exe " & textField.value & " > " & _
            strTempFile, 0, True
            Set objTextFile = WshFSO.OpenTextFile(strTempFile, 1)
            Do While objTextFile.AtEndOfStream <> True
                strOutput = strOutput & objTextFile.ReadLine & "<br>"
            Loop
            textOutput.innerHTML = strOutput
            objTextFile.Close
        End Sub
    </script>
</head>
<body>
    <p>IP Address/Hostname: <input type="text" name="textField" size="50">
    <input type="button" name="submitButton" value="Initiate Ping"></p>
    <div id="textOutput">Please be patient when waiting for results. The
    ping command can take a while to execute. </div>
</body>
</html>

这个HTA的操作步骤如下:
1. 打开HTA应用程序,会看到一个输入框和一个按钮。
2. 在输入框中输入要ping的IP地址或主机名。
3. 点击 “Initiate Ping” 按钮,程序会执行 ping 命令,并将结果显示在下方的区域。

2.2 自动化Windows关机

以下是一个自动化Windows关机的HTA示例:

<html>
    <title>SHUTDOWN IN PROGRESS</title> 
    <HTA:APPLICATION
        ID="htaShutdownApp"
        APPLICATIONNAME="Shutdown Demo"
        SCROLL="auto"
        SINGLEINSTANCE="yes">
    <head>
        <style>
            body {
                font-family: Arial;
                text-align: center;
            }
            #msgTitle {
                margin: 12px 0 0;
            }
            #msgCountdown {
                color: #FF00FF;
            }
        </style>
        <script type="text/vbscript">
            Sub Window_OnLoad
                self.resizeTo 700, 300
            End Sub
            Sub SystemShutdown
                Set objSystemSet = GetObject _
                ("winmgmts:{impersonationLevel=impersonate, (Shutdown )}")_
               .InstancesOf("Win32_OperatingSystem")
                For Each objSystem In objSystemSet
                    objSystem.Win32Shutdown 5
                Next
            End Sub
            Sub StopCountdown
                Set WshShell = CreateObject("WScript.Shell")
                Window.Close
            End Sub
        </script>
    </head>
    <body>
        <h1 id="msgTitle">Automated System Shutdown</h1>
        <p id="msgText">A shutdown of this system has been initiated.
        Click on Cancel to override.</p>
        <div id="msgCountdown"></div>
        <p>
            <input type="button" value="Cancel" name="btnCancel"
            onclick="StopCountdown">
        </p>
        <script type="text/vbscript">
            noOfIterations = 60
            SixtySecondCountdown()
            Sub SixtySecondCountdown
                timerProcess = _
                window.setTimeout("SixtySecondCountdown", 1000, "VBScript")
                noOfIterations = noOfIterations - 1
                document.getElementById("msgCountdown").innerHTML = noOfIterations
                If noOfIterations = 0 Then
                    window.clearTimeout(timerProcess)
                    SystemShutdown()
                End If
            End Sub
        </script>
    </body>
</html>

这个HTA的操作步骤如下:
1. 打开HTA应用程序,会看到一个提示框,显示系统即将关机,并开始60秒的倒计时。
2. 如果不想关机,可以点击 “Cancel” 按钮,终止关机操作。
3. 如果不进行干预,倒计时结束后,系统将自动关机。

3. 远程WSH技术

Remote WSH是一项在WSH 5.6中引入的新技术,它允许在网络上远程启动和监控脚本的执行。

3.1 使用Remote WSH的要求

要使用Remote WSH,需要满足以下条件:
- 本地计算机必须运行WSH 5.6或更高版本。
- 远程计算机必须运行WSH 5.6或更高版本。
- 本地和远程计算机都必须运行Windows 2000、XP、2003、Vista、2008、2012、7或8。
- 必须在远程计算机上具有管理员权限。

3.2 启用Remote WSH

要在计算机上启用Remote WSH,需要进行以下操作:
1. 在注册表 HKCU\Software\Microsoft\Windows Script Host\Settings\ 中添加一个名为 Remote 的新值,并将其设置为 1
2. 在远程Windows计算机上执行 WScript –regserver 命令,将 WScript.exe 执行主机注册为远程COM服务器。

3.3 Remote WSH的架构

Remote WSH由三个对象组成,它们分别支持各种方法、属性和事件。以下是这些对象的关系图:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    A(WshController):::process --> B(WshRemote):::process
    B --> C(WshRemoteError):::process
  • WshController 对象是最高级的对象,用于创建 WshRemote 对象。
  • WshRemote 对象用于远程执行脚本和终止脚本的执行。
  • WshRemoteError 对象用于获取远程脚本执行时的错误信息。
3.4 执行Remote WSH方法

以下是一个远程执行脚本的示例:

Set WshControl = CreateObject("WshController")
Set RemoteScript = WshControl.CreateScript("Test.vbs", "\\DSKTP001")
RemoteScript.Execute

上述代码中,首先创建了一个 WshController 对象,然后使用 CreateScript 方法创建了一个 WshRemote 对象,并指定要执行的脚本和远程计算机的名称。最后,使用 Execute 方法启动远程脚本的执行。

如果要终止远程脚本的执行,可以使用 Terminate 方法:

RemoteScript.Terminate
3.5 响应WSH远程事件

远程执行的脚本会产生三种不同的事件:
| 事件 | 描述 |
| ---- | ---- |
| Start | 远程脚本开始执行时触发 |
| End | 远程脚本停止执行时触发 |
| Error | 远程脚本发生错误时触发 |

要捕获这些事件,可以使用 WScript 对象的 ConnectObject 方法:

WScript.ConnectObject RemoteScript, "RemoteScript_"

Function RemoteScript_Start()
    'Add statements here to process the start event.
End Function

上述代码中,使用 ConnectObject 方法将 RemoteScript 对象的事件连接到以 RemoteScript_ 为前缀的子程序或函数。然后,创建了一个 RemoteScript_Start 函数,用于处理远程脚本开始执行的事件。

3.6 访问WSH远程属性

如果远程脚本执行时发生错误,可以使用 WshRemote 对象的 Error 属性访问 WshRemoteError 对象,从而获取详细的错误信息。以下是 WshRemoteError 对象的属性列表:
| 属性 | 描述 |
| ---- | ---- |
| Description | 错误的描述 |
| Number | 错误的数字代码 |
| Line | 错误发生的行号 |
| Source | 报告错误的对象 |
| SourceText | 产生错误的代码行 |
| Character | 代码行中错误发生的字符位置 |

此外, WshRemote 对象还有一个 Status 属性,用于跟踪远程脚本的执行状态:
| 值 | 描述 |
| ---- | ---- |
| 0 | 远程脚本尚未开始执行 |
| 1 | 远程脚本正在执行 |
| 2 | 远程脚本已执行完毕 |

3.7 远程WSH示例

以下是一个完整的远程执行脚本的示例:

'*************************************************************************
'Script Name: WSHRemoteDemo.vbs
'Author: Jerry Ford
'Created: 03/14/14
'Description: This script controls the remote execute of a VBScript named 
'             Test.vbs on a computer named DSKTP001.
'*************************************************************************
'Initialization Section
Option Explicit
Set wshController = CreateObject("WshController") 
Set wshRemote = wshController.CreateScript("Test.vbs", "\\DSKTP001")
'Main Processing Section
WScript.ConnectObject wshRemote, "RemoteScript_"
wshRemote.Execute
Do Until wshRemote.Status = 2
    WScript.Sleep 1000
Loop
'Procedure Section
Sub RemoteScript_Start()
    MsgBox "Test.vbs has started!"
End Sub
Sub RemoteScript_End()
    MsgBox "Test.vbs has terminated!"
End Sub

这个脚本的执行步骤如下:
1. 创建 WshController 对象和 WshRemote 对象,并将 Test.vbs 脚本复制到远程计算机 DSKTP001 上。
2. 使用 ConnectObject 方法定义事件前缀,以便捕获远程脚本执行时的事件。
3. 使用 Execute 方法启动远程脚本的执行。
4. 进入一个循环,每隔1秒检查一次 WshRemote 对象的 Status 属性,直到脚本执行完毕。
5. 定义两个子程序 RemoteScript_Start RemoteScript_End ,分别处理远程脚本开始和结束执行的事件。

4. WSH核心对象模型

WSH核心对象模型提供了对Windows资源的编程访问,它包含14个对象,每个对象都提供了对特定类别的Windows资源的访问。

4.1 WSH核心对象的层次结构

在WSH核心对象模型的顶层是 WScript 对象,它是WSH的根对象,所有其他对象都从这个对象实例化而来。 WScript 对象在执行主机启动时自动建立,因此可以在脚本中直接引用,无需先进行实例化。除了 WScript 对象外,还有 WshController WshShell WshNetwork 这三个公共对象,它们需要使用 WScript 对象的 CreateObject() 方法进行实例化。其他对象则需要通过与 WScript WshController WshShell WshNetwork 对象相关的属性或方法来实例化。以下是这些对象及其实例化方法的列表:
| 对象 | 实例化方法 |
| ---- | ---- |
| WshArguments | WScript.Arguments |
| WshNamed | WScript.Arguments.Named |
| WshUnnamed | WScript.Arguments.Unnamed |
| WshRemote | WshController.CreateScript() |
| WshRemoteError | WshRemote.Error |
| WshShortcut | WshShell.CreateShortcut() |
| WshUrlShortcut | WshShell.CreateShortcut() |
| WshEnvironment | WshShell.Environment |
| WshSpecialFolders | WshShell.SpecialFolders |
| WshScriptExec | WshShell.Exec() |

4.2 WSH核心对象的属性和方法

每个WSH核心对象都提供了对特定Windows功能子集的访问,以下是这些对象的详细描述、属性和方法列表:
| 对象 | 描述 | 属性 | 方法 |
| ---- | ---- | ---- | ---- |
| WScript | 这是WSH的根对象,提供对许多有用属性和方法的访问,也提供对WSH核心对象模型中其他对象的访问 | Arguments, FullName, Interactive, Name, Path, ScriptFullName, ScriptName, StdErr, StdIn, StdOut, Version | ConnectObject(), CreateObject(), DisconnectObject(), Echo(), GetObject(), Quit(), Sleep() |
| WshArguments | 该对象允许访问脚本执行时传递的命令行参数 | Count, Item, Length, Named, Unnamed | Count(), ShowUsage() |
| WshNamed | 该对象提供对一组命名命令行参数的访问 | Item, Length | Count(), Exists() |
| WshUnnamed | 该对象提供对一组未命名命令行参数的访问 | Item, Length | Count() |
| WshController | 该对象提供创建远程脚本进程的能力 | None | CreateScript() |
| WshRemote | 该对象提供通过网络使用脚本管理远程计算机系统的能力 | Status, Error | Execute(), Terminate() |
| WshRemoteError | 该对象提供对远程脚本产生的错误信息的访问 | Description, Line, Character, SourceText, Source, Number | None |
| WshNetwork | 该对象提供对许多不同网络资源(如网络打印机和驱动器)的访问 | ComputerName, UserDomain, UserName | AddWindowsPrinterConnection(), AddPrinterConnection(), EnumNetworkDrives(), EnumPrinterConnection(), MapNetworkDrive(), RemoveNetworkDrive(), RemovePrinterConnection(), SetDefaultPrinter() |
| WshShell | 该对象提供对Windows注册表、事件日志、环境变量、快捷方式和应用程序的访问 | CurrentDirectory, Environment, SpecialFolders | AppActivate(), CreateShortcut(), ExpandEnvironmentStrings(), LogEvent(), Popup(), RegDelete(), RegRead(), RegWrite(), Run(), SendKeys(), Exec() |
| WshShortcut | 该对象为脚本提供创建和操作Windows快捷方式的方法和属性 | Arguments, Description, FullName, HotKey, IconLocation, TargetPath, WindowStyle, WorkingDirectory | Save() |
| WshUrlShortcut | 该对象为脚本提供创建和操作URL快捷方式的方法和属性 | FullName, TargetPath | Save() |
| WshEnvironment | 该对象提供对Windows环境变量的访问 | Item, Length | Remove(), Count() |
| WshSpecialFolders | 该对象提供对特殊Windows文件夹(如开始菜单和桌面文件夹)的访问 | Item | Count() |
| WshScriptExec | 该对象提供对使用Exec方法运行的脚本的错误信息的访问 | Status, StdOut, StdIn, StdErr | Terminate() |

4.3 检查对象属性

通过访问对象属性,脚本在执行时可以收集各种信息。例如,使用 WshNetwork 对象的属性,脚本可以收集运行脚本的用户所登录的Windows域、计算机名称和用户名等信息。这些信息可以用于防止脚本在某些域或计算机上执行。以下是一些常见的WSH对象属性及其描述:
| 属性 | 描述 |
| ---- | ---- |
| Arguments | 设置指向 WshArguments 集合的指针引用 |
| AtEndOfLine | 根据流中是否到达行尾标记返回 true false |
| AtEndOfStream | 根据输入流是否到达末尾返回 true false |
| Character | 标识代码行中发生错误的特定字符 |
| Column | 返回输入流中的当前列位置 |
| ComputerName | 获取计算机的名称 |
| CurrentDirectory | 设置或获取脚本的当前工作目录 |
| Description | 获取指定快捷方式的描述 |
| Environment | 设置指向 WshEnvironment 的指针引用 |
| Error | 提供访问 WshRemoteError 对象的能力 |
| ExitCode | 返回使用 Exec() 启动的脚本的退出代码 |
| FullName | 获取快捷方式或可执行程序的路径 |
| HotKey | 获取指定快捷方式关联的热键 |
| IconLocation | 获取图标的位置 |
| Interactive | 提供以编程方式设置脚本模式的能力 |
| Item | 从集合中检索指定的项或提供对 WshNamed 对象中存储的项的访问 |
| Length | 获取枚举项的计数 |
| Line | 返回输入流中当前行的行号或标识脚本中发生错误的行号 |
| Name | 返回表示 WScript 对象名称的字符串 |
| Number | 提供访问错误编号的能力 |
| Path | 返回 CScript.exe WScript.exe 执行主机所在文件夹的位置 |
| ProcessID | 获取使用 WshScriptExec 对象启动的进程的进程ID(PID) |
| ScriptFullName | 返回正在执行的脚本的路径 |
| ScriptName | 返回正在执行的脚本的名称 |
| Source | 获取导致脚本错误的对象的标识 |
| SourceText | 获取产生错误的源代码 |
| SpecialFolders | 提供对Windows开始菜单和桌面文件夹的访问 |
| Status | 提供有关远程执行的脚本或使用 Exec() 启动的脚本的状态信息 |
| StdErr | 使脚本能够写入错误输出流或提供对 Exec 对象的只读错误输出的访问 |
| StdIn | 提供对输入流的读取访问或提供对 Exec 对象的只写输入流的访问 |
| StdOut | 提供对输出流的写入访问或提供对 Exec 对象的只写输出流的访问 |
| TargetPath | 获取快捷方式关联对象的路径 |
| UserDomain | 获取域名 |
| UserName | 获取当前登录用户的名称 |
| Version | 获取WSH版本号 |
| WindowStyle | 获取快捷方式的窗口样式 |
| WorkingDirectory | 返回与指定快捷方式关联的工作目录 |

以下是一个访问 WshNetwork 对象属性的示例脚本:

Set WshNtwk = WScript.CreateObject("WScript.Network")
PropertyInfo = "User Domain" & vbTab & "= " & WshNtwk.UserDomain & _
vbCrLf & "Computer Name" & vbTab & "= " & WshNtwk.ComputerName & _
vbCrLf & "User Name" & vbTab & "= " & WshNtwk.UserName & vbCrLf
MsgBox PropertyInfo, vbOkOnly , "WshNtwk Properties Example"

这个脚本首先创建了一个 WshNetwork 对象的实例,然后将该对象的 UserDomain ComputerName UserName 属性的值组合成一个字符串,并使用 MsgBox 函数显示在一个弹出对话框中。

4.4 检查对象方法

WSH还提供了大量的对象方法,通过在VBScript中使用这些方法,可以操作与对象关联的Windows资源。例如,使用 WshShell 对象的 RegRead() RegWrite() RegDelete() 方法,可以创建访问和操作Windows注册表内容的脚本。以下是一些常见的WSH对象方法及其描述:
| 方法 | 描述 |
| ---- | ---- |
| AddPrinterConnection() | 创建打印机映射 |
| AddWindowsPrinterConnection() | 创建新的打印机连接 |
| AppActivate() | 激活目标应用程序窗口 |
| Close() | 终止或结束打开的数据流 |
| ConnectObject() | 建立与对象的连接 |
| Count | 获取 WshNamed WshUnnamed 对象中找到的开关数量 |
| CreateObject() | 创建对象的新实例 |
| CreateScript() | 实例化一个表示远程运行脚本的 WshRemote 对象 |
| CreateShortcut() | 创建Windows快捷方式 |
| DisconnectObject() | 终止与对象的连接 |
| Echo() | 显示文本消息 |
| EnumNetworkDrives() | 允许访问网络驱动器 |
| EnumPrinterConnections() | 允许访问网络打印机 |
| Exec() | 在子命令shell中执行应用程序并提供对环境变量的访问 |
| Execute() | 启动远程脚本对象的执行 |
| Exists() | 确定指定的键是否存在于 WshNamed 对象中 |
| ExpandEnvironmentStrings() | 获取表示 Process 环境变量内容的字符串 |
| GetObject() | 获取自动化对象 |
| GetResource() | 获取由 <resource> 标签指定的资源的值 |
| LogEvent() | 在Windows事件日志中写入消息 |
| MapNetworkDrive() | 创建网络驱动器映射 |
| Popup() | 在弹出对话框中显示文本消息 |
| Quit() | 终止脚本的执行 |
| Read() | 从输入流中检索一串字符 |
| ReadAll() | 检索由输入流中的字符组成的字符串 |
| ReadLine() | 从输入流中检索包含整行数据的字符串 |
| RegDelete() | 删除注册表键或值 |
| RegRead() | 检索注册表键或值 |
| RegWrite() | 创建注册表键或值 |
| Remove() | 删除指定的环境变量 |
| RemoveNetworkDrive() | 删除与指定网络驱动器的连接 |
| RemovePrinterConnection() | 删除与指定网络打印机的连接 |
| Run() | 启动新进程 |
| Save() | 保存快捷方式 |
| SendKeys() | 模拟按键并将输入的数据发送到指定窗口 |
| SetDefaultPrinter() | 建立默认的Windows打印机 |
| ShowUsage() | 获取有关脚本应该如何执行的信息 |
| Skip() | 从输入流中读取时跳过 x 个字符 |
| SkipLine() | 从输入流中读取时跳过整行 |
| Sleep() | 暂停脚本执行 x 秒 |
| Terminate() | 停止由 Exec() 启动的进程 |
| Write() | 将字符串放入输出流中 |
| WriteBlankLines() | 在输出流中放置空白行 |
| WriteLine() | 将字符串放入输出流中 |

以下是一个使用 WshShell 对象的 LogEvent() 方法向Windows事件日志写入消息的示例脚本:

Set WshShl = WScript.CreateObject("WScript.Shell")
WshShl.LogEvent 0, "EventLogger.vbs - Beginning script execution."

这个脚本首先创建了一个 WshShell 对象的实例,然后使用 LogEvent() 方法向事件日志写入一条消息。要查看写入的消息,可以在Windows 8.1中点击开始按钮,输入 “View Event Logs” 并按回车键,然后点击 “View Event Logs” 打开事件查看器控制台。在事件查看器中,展开 “Windows Logs” 节点并点击 “Application” 日志,即可查看应用程序事件日志中的事件。

总结

通过本文的介绍,我们了解了脚本编程中的一些基础操作,如创建ZIP文件和终止脚本执行。同时,还学习了如何使用HTML应用程序(HTAs)为脚本添加图形用户界面,使脚本的使用更加方便。此外,我们深入探讨了远程WSH技术,包括其使用要求、启用方法、架构、执行方法、事件响应和属性访问等方面。最后,我们详细介绍了WSH核心对象模型,包括对象的层次结构、属性和方法,以及如何通过访问对象属性和方法来操作Windows资源。这些知识将有助于我们更好地进行Windows脚本编程,提高工作效率。

【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开研究,重点探讨其系统建模与控制策略,结合Matlab代码与Simulink仿真实现。文章详细分析了无人机的动力学模型,特别是引入螺旋桨倾斜机构后带来的全驱动特性,使其在姿态与位置控制上具备更强的机动性与自由度。研究涵盖了非线性系统建模、控制器设计(如PID、MPC、非线性控制等)、仿真验证及动态响应分析,旨在提升无人机在复杂环境下的稳定性和控制精度。同时,文中提供的Matlab/Simulink资源便于读者复现实验并进一步优化控制算法。; 适合人群:具备一定控制理论基础和Matlab/Simulink仿真经验的研究生、科研人员及无人机控制系统开发工程师,尤其适合从事飞行器建模与先进控制算法研究的专业人员。; 使用场景及目标:①用于全驱动四旋翼无人机的动力学建模与仿真平台搭建;②研究先进控制算法(如模型预测控制、非线性控制)在无人机系统中的应用;③支持科研论文复现、课程设计或毕业课题开发,推动无人机高机动控制技术的研究进展。; 阅读建议:建议读者结合文档提供的Matlab代码与Simulink模型,逐步实现建模与控制算法,重点关注坐标系定义、力矩分配逻辑及控制闭环的设计细节,同时可通过修改参数和添加扰动来验证系统的鲁棒性与适应性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值