VC客户端调用VB DLL的实践教程

部署运行你感兴趣的模型镜像

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:在VC客户端调用VB的DLL是一种跨语言编程的应用,涉及到C++和Visual Basic之间的交互,使用DLL封装可重用的代码。本文提供了如何在VC客户端应用程序中调用VB编写的DLL函数的详细步骤和示例代码。具体包括创建VB DLL、在VC中引用和调用VB DLL、处理调用约定和类型匹配问题、错误处理和资源释放等关键部分。通过这些步骤和示例,开发者可以学习如何实现VC与VB代码的有效整合。
在VC客户端调用vb的dll.rar_VB VC DLL_VB调用DLL_VB调用VC_vc 调用 dll_vc调用vb的DLL

1. 跨语言编程简介

1.1 跨语言编程的必要性

在当今的软件开发领域,不同的编程语言往往以其独特的优点而被应用于不同的场景。比如,Python以其简洁易学的特性广泛应用于数据科学领域,Java则因其跨平台特性而成为企业级应用的首选。然而,在开发过程中,为了满足特定的功能需求、优化性能或是集成现有代码,开发人员常常需要将不同的编程语言结合起来,这就形成了跨语言编程的需求。跨语言编程能够在保持各自语言特性的同时,实现互补和资源共享,从而提升开发效率和软件质量。

1.1.1 不同编程语言的优势分析

不同的编程语言各有所长,选择合适的语言能够极大地提高开发效率。例如:

  • C/C++ :适合系统编程和性能敏感型应用,因其接近硬件和高效的性能。
  • Java :因其“一次编写,到处运行”的特性,非常适合大型企业级应用和跨平台应用。
  • Python :具有丰富的库支持,尤其在数据处理和人工智能领域具有明显优势。
  • JavaScript :在前端开发和服务器端(Node.js)的应用日益广泛。

1.1.2 跨语言编程在软件开发中的作用

跨语言编程的实践能够帮助开发团队:

  • 整合现有资源 :利用不同语言的优势,集成已有的代码库。
  • 性能优化 :针对特定的操作选择最适合的编程语言,比如数值计算使用C/C++,逻辑处理使用Python。
  • 功能拓展 :在需要特定功能(如第三方库)而没有现成的解决方案时,可以通过跨语言编程实现。

跨语言编程技术的发展使得软件开发更加灵活多样,为解决复杂问题提供了更多可能性。在后续章节中,我们将深入探讨跨语言编程的具体实现方式,以及VB与VC这两种编程语言在其中扮演的角色。

2. DLL在Windows系统中的作用

2.1 DLL的基本概念和功能

2.1.1 DLL的定义和工作原理

动态链接库(DLL)是Windows操作系统中一种实现代码复用和模块化程序设计的重要方式。DLL提供了一种方法,使得程序可以共享一段代码,从而提高程序的性能并减少内存的使用。DLL文件包含了代码和数据,它们可以被多个程序同时访问,但只有一个实例在内存中被加载和维护。

工作原理上,当一个应用程序需要使用DLL中的函数时,它会在运行时动态链接到DLL。这个链接过程分为加载(Loading)、绑定(Binding)和调用(Calling)三个阶段。加载是指将DLL映射到调用进程的地址空间中;绑定则是在加载时确定函数的入口点,以便程序知道如何调用DLL中的函数;调用时实际将控制权转交给DLL中的函数。

2.1.2 DLL在系统中的作用和优势

DLL的作用包括但不限于:

  • 代码重用 :多个应用程序可以共享同一个DLL,减少了代码重复。
  • 资源管理 :DLL可以管理共享资源,如图形、字体等,避免资源浪费。
  • 模块化设计 :开发者可以将功能独立封装到DLL中,使得程序结构清晰。
  • 易于更新 :当DLL被更新时,使用它的程序无需重新编译,易于维护。

DLL的优势主要在于其动态链接的特性,允许应用程序在不重新链接和重新加载整个程序的情况下添加、删除或替换DLL。这大大提高了程序的灵活性和可维护性。

2.2 DLL与Windows编程模型

2.2.1 Windows编程模型概述

Windows编程模型是基于消息驱动和事件驱动的机制。在这种模型下,应用程序通过接收和处理消息来响应用户的操作或系统事件。DLL作为模型的一部分,可以提供额外的功能,或者封装那些对多个应用程序都有用的代码。

DLL可以作为应用程序的扩展,提供如自定义控件、数据处理或特定业务逻辑等额外功能。同时,DLL内部的代码可以被独立于任何特定应用程序进行更新和维护,这也使得DLL在大型软件开发中成为一种有效的组织方式。

2.2.2 DLL在Windows编程模型中的角色

在Windows编程模型中,DLL可以扮演以下角色:

  • 提供API :DLL可以导出函数供其他应用程序或DLL调用,形成应用程序编程接口(API)。
  • 实现插件系统 :应用程序可以通过加载不同的DLL来扩展其功能。
  • 封装核心功能 :将复杂功能如数据库访问、网络通信等封装进DLL,实现核心功能模块化。

通过以上角色,DLL不仅是代码复用的工具,更是灵活扩展和功能模块化的解决方案,这对于提升大型软件系统的可维护性及降低开发复杂性至关重要。

3. VB DLL的创建与导出函数

在现代软件开发中,跨语言编程已经成为一种普遍的需求,能够将不同语言编写的模块集成在一起,提高软件的可维护性和扩展性。Visual Basic (VB) 和 Visual C++ (VC) 作为微软的两大编程语言,它们在历史长河中各自发展出独特的特点,同时在某些应用场景下需要协同工作。VB以其简洁的语法和快速开发的能力而备受青睐,而VC则在系统级编程和性能优化方面具有优势。为了实现两种语言编写的代码模块间的调用,我们可以通过创建VB编写的DLL (Dynamic Link Library) 并在VC中引用它来完成跨语言的函数调用。

3.1 VB DLL的创建过程

3.1.1 VB项目设置与DLL模板选择

在开始创建VB DLL之前,需要进行基本的项目设置。这一步骤主要是在Visual Basic的开发环境中配置好新项目的类型。选择创建一个标准的VB项目,并在项目类型中选择DLL模板。通常情况下,选择“ActiveX DLL”类型较为常见,因为这种类型的DLL可以被其他组件所引用,并且能够支持COM (Component Object Model) 接口,这对于在多种编程语言之间共享和调用功能提供了便利。

以下是选择项目模板时的操作步骤:

  1. 打开Visual Basic开发环境。
  2. 在“文件”菜单中选择“新建项目”。
  3. 在项目类型中,选择“Visual Basic项目”。
  4. 在模板列表中,选择“ActiveX DLL”。
  5. 给项目命名并确定存放位置。

选择完项目模板之后,Visual Basic会自动生成一个包含必要代码的DLL框架。开发者可以在该框架基础上添加自己的功能代码。

3.1.2 VB中创建DLL的步骤

创建VB DLL项目后,接下来就是添加和实现导出函数的具体步骤。在VB中创建DLL的步骤涉及到几个关键的代码实现部分,包括导出函数的编写、模块的配置以及最终的编译生成过程。

' 例如,创建一个简单的Add函数,用于加法运算
' Add.vb
Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
    Return x + y
End Function

在上述代码中,我们定义了一个名为Add的函数,它接受两个整型参数并返回它们的和。这只是函数的一个简单示例,实际开发中可以根据需要添加复杂的逻辑和功能。

为了让这个函数可以被其他语言的代码调用,需要使用VB提供的“Public”关键字将其标记为公共接口。在创建完所有需要导出的函数后,开发者可以使用VB的项目编译功能来生成DLL文件。

编译过程中的主要步骤如下:

  1. 在Visual Basic中,点击“文件”菜单,选择“保存全部”。
  2. 点击“构建”菜单,选择“构建解决方案”。
  3. 如果没有编译错误,编译器会生成DLL文件,通常存放在项目的“bin\debug”或“bin\release”文件夹下。

3.2 VB DLL函数的导出

在VB中导出函数是实现跨语言功能调用的关键一步,它涉及到将VB编写的函数暴露给外部引用,使得其他语言的程序也能访问这些函数。

3.2.1 使用Public关键字导出函数

在VB中,通过使用Public关键字将模块或类中的函数定义为公共成员。在DLL项目中,所有的公共函数都可被其他项目引用。这一步骤不仅需要在函数声明时添加Public关键字,还需要在模块的声明中使用“ClassInterface”属性来指明接口类型,常见的选择是“ClassInterfaceType.None”,这样可以保证接口的稳定性和兼容性。

3.2.2 使用模块级变量和函数

除了使用Public关键字导出函数之外,VB还允许开发者通过模块级的变量和函数来实现某些特定的功能。模块级的变量或函数对于整个模块都是可见的,并且可以在该模块的任何地方被调用。

例如:

' Module1.vb
Public Module Module1
    Public Function SayHello() As String
        Return "Hello, World!"
    End Function
End Module

在这个例子中,SayHello函数被定义为模块级函数,可以通过DLL的外部引用被调用。

3.2.3 VB DLL函数的声明和注册

为了使VB DLL中的函数可以在其他程序中使用,需要对该DLL进行注册。注册DLL通常有以下几种方法:

  1. 使用Windows注册表:可以手动或通过脚本编辑注册表,将DLL的路径添加到注册表中。
  2. 使用Regsvr32工具:通过命令行使用这个工具注册或注销DLL。
  3. 在程序中动态加载:在需要使用DLL的程序代码中动态加载DLL,并使用LoadLibrary和GetProcAddress函数获取函数地址。

在VB中,注册DLL通常会在项目属性中设置。开发者可以使用“Register for COM Interop”选项,这样VB编译器会自动为DLL生成必要的COM注册信息。

' 项目属性设置中的“Register for COM Interop”选项
' 通常在Visual Studio的项目属性对话框的“Compile”选项卡下,勾选“Register for COM interop”。

注册之后,其他支持COM的编程语言,包括VC++,就可以像引用普通COM对象一样引用VB编写的DLL了。至此,一个VB DLL的创建与导出过程基本完成,接下来就是如何在VC++中引用并调用这个VB DLL了。

4. VC中引用VB DLL的过程

4.1 VC环境下的DLL引用准备

在使用Visual C++ (VC) 引用Visual Basic (VB) 编写的DLL之前,需要进行一系列的准备工作。这包括项目设置和理解DLL的导入机制。本节将详细探讨这两个方面,以确保顺利进行跨语言编程。

4.1.1 VC项目设置与DLL引用的前期准备

在VC中引用VB DLL,首先需要配置项目以支持COM组件。这通常涉及到以下几个步骤:

  1. 打开VC项目,并进入项目属性设置。
  2. 在“配置属性” -> “C/C++” -> “预处理器”选项卡中,添加 _WIN32 预处理器定义,确保代码编译器能正确处理Windows特定的API。
  3. 在“配置属性” -> “链接器” -> “输入”选项卡中,添加要链接的VB生成的DLL文件。
  4. 设置包含目录,以包含VB DLL导出的头文件,这些头文件通常包含了函数声明和类型定义。
4.1.2 使用#import指令引入VB DLL

在Visual C++中,可以通过 #import 指令自动从类型库生成C++类包装器,以便于使用VB DLL中的接口。具体操作步骤如下:

  1. 在项目的头文件或源文件中,添加 #import 指令,指定VB DLL的类型库文件。例如:
    cpp #import "C:\path\to\your\vbDLL.tlb" no_namespace named_guids
    在这个例子中, no_namespace 用于告诉编译器不要创建一个额外的命名空间,而 named_guids 指示编译器使用名称而非GUID作为枚举值。

  2. 使用导入的类和方法时,可以像使用普通的C++类一样。编译器会处理所有COM相关的细节,如引用计数和接口查询。

4.2 VC中调用VB DLL的具体步骤

引用了VB DLL之后,我们进入实际的调用阶段。以下是调用VB DLL中的函数或方法的详细步骤,包括创建COM包装类、编译和链接过程注意事项以及调试和异常处理。

4.2.1 创建对应的COM包装类

当VC通过 #import 指令引入VB DLL后,它会自动为DLL中定义的每个类型生成包装类。这些包装类隐藏了COM细节,使得C++代码能够以面向对象的方式调用VB DLL中的方法。例如:

#include <MyVBLibrary.tlb>
MyVBLibrary::MyObjectPtr obj(__uuidof(MyObject)); // 创建VB对象的实例
obj->MyMethod(); // 调用VB对象的方法

在这个例子中, MyObjectPtr #import 生成的智能指针类,它帮助管理VB对象的生命周期,包括创建和销毁VB对象实例。

4.2.2 编译和链接过程注意事项

在编译和链接过程中,特别要注意以下几点,以确保跨语言编程的顺利进行:

  1. 确保项目设置正确,所有依赖关系都已正确配置。
  2. 查看编译器和链接器的输出,确保没有遗漏的依赖或符号未解析的问题。
  3. 在多线程或需要异常处理的应用程序中,检查是否有额外的库或标志需要添加到链接器设置中。
4.2.3 调试与异常处理

由于涉及跨语言,调试可能比单一语言编程复杂。VC提供了一些工具来协助调试:

  • 使用 try-catch 块捕获跨语言调用过程中可能抛出的异常。
  • 利用调试器的断点功能,设置在VB DLL的函数入口处,以便于跟踪执行流程和参数变化。
  • 使用调试器的内存和变量视图,检查跨语言调用中对象的内部状态。

代码块和逻辑分析

以一个简单函数为例,该函数将来自VB DLL的字符串返回。以下是完整的代码块和逻辑分析:

// 假设有一个VB DLL导出的函数,它接受字符串参数并返回字符串结果
void CallVBFunction() {
    // 使用#import生成的智能指针创建VB对象实例
    VBExampleLib::VBClassPtr vbClass(__uuidof(VBClass));
    try {
        // 调用VB类的方法
        BSTR result = vbClass->EchoString(L"Hello from VC!");
        // 输出返回的字符串
        wprintf(L"Result from VB: %ls\n", result);
        // 使用完毕,释放分配的BSTR内存
        SysFreeString(result);
    } 
    catch (_com_error &e) {
        // 捕获和报告COM异常
        wprintf(L"COM error caught: %s\n", e.ErrorMessage());
    }
}

在上述代码块中, EchoString 是一个假设的VB DLL导出函数,它接收一个 BSTR 类型的字符串,并返回一个处理后的字符串。我们使用 try-catch 块捕获可能的COM异常,并通过 _com_error 对象获取错误信息。注意,通过VB DLL返回的 BSTR 字符串需要使用 SysFreeString 函数释放,否则会导致内存泄漏。

通过这个过程,我们能够看到如何在VC中引用和使用VB编写的DLL,以及如何处理跨语言编程中出现的异常。这些步骤对于在Windows平台下进行混合编程的开发人员是必须了解和掌握的。

总结

通过本章节的介绍,我们已经详细阐述了在VC环境中引用VB DLL的完整过程。从前期的项目配置,到通过 #import 指令自动创建包装类,再到实际函数调用和异常处理,每一步都伴随着代码示例和逻辑分析。这些内容对于期望在不同编程语言间共享和复用代码的开发者来说,是至关重要的。

5. 函数调用接口与调用约定

5.1 函数调用接口的设计原则

5.1.1 接口设计的一般规则

在设计函数调用接口时,首先需要考虑接口的清晰性和简洁性。一个好的接口设计应该直观易懂,参数类型和返回类型应该明确,以减少使用者的学习成本。另外,接口应该能够适应未来的需求变更,具有一定的扩展性。

5.1.2 兼容性考虑和接口的扩展性

在实现跨语言的函数调用接口时,兼容性尤其重要。需要确保在不同的语言环境中,接口的调用行为保持一致。此外,设计接口时应考虑到可能的未来变更,例如增加新功能或参数,而不影响现有的调用代码。扩展性可以通过设计灵活的参数类型(如变体类型)和定义清晰的接口版本来实现。

5.2 调用约定的选择与实现

5.2.1 调用约定的种类及特点

调用约定是编译器在调用函数时所遵循的一系列规则,包括参数传递的顺序、堆栈的清理责任等。常见的调用约定有C调用约定(__cdecl)、快速调用约定(__stdcall)、Windows API调用约定(__stdcall,也称为WINAPI)等。每种调用约定都针对不同的性能和兼容性需求进行了优化。

5.2.2 VB与VC默认调用约定的差异

VB(Visual Basic)通常使用 __stdcall 调用约定,而VC(Visual C++)在默认情况下使用 __cdecl 调用约定。因此,在调用VB创建的DLL函数时,VC编译器需要使用正确的调用约定,否则会出现参数传递错误、内存泄漏等问题。

5.2.3 实现跨语言调用的调用约定

为了实现跨语言调用,需要在编写DLL接口时明确指定调用约定,并确保在调用端使用相同的约定。例如,在VB中使用 Declare 关键字来声明DLL函数时,必须指定调用约定,而VC中则可能需要在函数声明时显式地添加 __stdcall

5.3 类型匹配和数据类型转换

5.3.1 跨语言数据类型的匹配问题

在跨语言编程中,由于不同语言支持的数据类型可能不一致,因此数据类型匹配成为一个重要问题。例如,VB中的 Long 类型在VC中通常对应为 int long 类型。在调用DLL函数时,必须确保参数类型完全匹配。

5.3.2 数据类型转换的方法和工具

为了解决数据类型不匹配问题,开发者通常会使用语言提供的类型转换方法,或者在DLL中编写适配层来处理数据类型的转换。此外,还可以使用一些中间工具或库来辅助完成类型转换,如使用COM接口进行自动化类型处理。

5.3.3 转换中常见的错误和解决方案

在进行跨语言的数据类型转换时,常见的错误包括内存泄漏、类型不匹配、缓冲区溢出等。为了解决这些问题,开发者可以采取以下措施:
- 在编写DLL时,进行充分的类型检查和边界检查;
- 使用工具或库进行类型转换时,确保了解其转换机制和限制;
- 在调试阶段仔细检查所有的数据传递和转换操作;
- 在代码中实现明确的错误处理逻辑,对可能的异常进行捕获和处理。

5.4 错误处理与资源释放

5.4.1 跨语言调用中的错误处理机制

在跨语言调用中,错误处理机制是保证程序稳定运行的关键。不同编程语言有不同的错误处理方式,比如VB中通常使用错误代码和 Err 对象,而VC中则使用异常(如 try-catch )。为了实现统一的错误处理机制,可能需要在DLL中定义统一的错误代码,并在调用端进行相应的处理。

5.4.2 资源管理与释放的策略

在跨语言编程中,资源管理也是一个不可忽视的方面。正确的资源管理可以避免内存泄漏和资源占用问题。开发者需要确保在不再需要资源时,能够及时释放它们。通常的做法是在DLL中实现资源的引用计数管理,或者由调用方明确地释放资源。

5.4.3 代码示例:VB DLL中的资源管理

以下是一个VB DLL中的资源管理示例代码,展示了如何使用引用计数来管理资源:

' 定义一个带有引用计数的资源管理类
Public Type MyResource
    refCount As Long
    ' 其他资源相关字段
End Type

Global hResource As Long

Public Function AcquireResource() As Long
    ' 创建资源,并初始化引用计数
    hResource = CreateResource()
    If hResource <> 0 Then
        Call InterlockedIncrement(hResource)
    End If
    AcquireResource = hResource
End Function

Public Function ReleaseResource(resID As Long) As Boolean
    ' 减少引用计数,并在引用计数为零时释放资源
    If Call InterlockedDecrement(resID) = 0 Then
        Call DestroyResource(resID)
        ReleaseResource = True
    Else
        ReleaseResource = False
    End If
End Function

Private Function CreateResource() As Long
    ' 创建资源并初始化引用计数
    Dim resource As MyResource
    resource.refCount = 1
    ' 其他初始化代码
    CreateResource = CastLongPointerToLong(resource)
End Function

Private Sub DestroyResource(resID As Long)
    ' 销毁资源,释放所有相关资源
    ' 销毁相关代码
End Sub

在上述代码中, CreateResource 函数用于创建资源并返回资源的句柄,资源的句柄实际上是一个内存地址,它被封装成了一个长整型数值。引用计数通过 InterlockedIncrement InterlockedDecrement 进行线程安全的自增和自减操作。当引用计数减少到零时, ReleaseResource 函数调用 DestroyResource 来实际释放资源。

通过这种方式,可以有效地管理跨语言调用中的资源,确保资源得到适当的清理,避免内存泄漏等问题。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:在VC客户端调用VB的DLL是一种跨语言编程的应用,涉及到C++和Visual Basic之间的交互,使用DLL封装可重用的代码。本文提供了如何在VC客户端应用程序中调用VB编写的DLL函数的详细步骤和示例代码。具体包括创建VB DLL、在VC中引用和调用VB DLL、处理调用约定和类型匹配问题、错误处理和资源释放等关键部分。通过这些步骤和示例,开发者可以学习如何实现VC与VB代码的有效整合。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

内容概要:本文详细介绍了一个基于Java和Vue的联邦学习隐私保护推荐系统的设计与实现。系统采用联邦学习架构,使用户数据在本地完成模型训练,仅上传加密后的模型参数或梯度,通过中心服务器进行联邦平均聚合,从而实现数据隐私保护与协同建模的双重目标。项目涵盖完整的系统架构设计,包括本地模型训练、中心参数聚合、安全通信、前后端解耦、推荐算法插件化等模块,并结合差分隐私与同态加密等技术强化安全性。同时,系统通过Vue前端实现用户行为采集与个性化推荐展示,Java后端支撑高并发服务与日志处理,形成“本地训练—参数上传—全局聚合—模型下发—个性化微调”的完整闭环。文中还提供了关键模块的代码示例,如特征提取、模型聚合、加密上传等,增强了项目的可实施性与工程参考价值。 适合人群:具备一定Java和Vue开发基础,熟悉Spring Boot、RESTful API、分布式系统或机器学习相关技术,从事推荐系统、隐私计算或全栈开发方向的研发人员。 使用场景及目标:①学习联邦学习在推荐系统中的工程落地方法;②掌握隐私保护机制(如加密传输、差分隐私)与模型聚合技术的集成;③构建高安全、可扩展的分布式推荐系统原型;④实现前后端协同的个性化推荐闭环系统。 阅读建议:建议结合代码示例深入理解联邦学习流程,重点关注本地训练与全局聚合的协同逻辑,同时可基于项目架构进行算法替换与功能扩展,适用于科研验证与工业级系统原型开发。
源码来自:https://pan.quark.cn/s/a4b39357ea24 遗传算法 - 简书 遗传算法的理论是根据达尔文进化论而设计出来的算法: 人类是朝着好的方向(最优解)进化,进化过程中,会自动选择优良基因,淘汰劣等基因。 遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。 进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择、杂交等。 搜索算法的共同特征为: 首先组成一组候选解 依据某些适应性条件测算这些候选解的适应度 根据适应度保留某些候选解,放弃其他候选解 对保留的候选解进行某些操作,生成新的候选解 遗传算法流程 遗传算法的一般步骤 my_fitness函数 评估每条染色体所对应个体的适应度 升序排列适应度评估值,选出 前 parent_number 个 个体作为 待选 parent 种群(适应度函数的值越小越好) 从 待选 parent 种群 中随机选择 2 个个体作为父方和母方。 抽取父母双方的染色体,进行交叉,产生 2 个子代。 (交叉概率) 对子代(parent + 生成的 child)的染色体进行变异。 (变异概率) 重复3,4,5步骤,直到新种群(parentnumber + childnumber)的产生。 循环以上步骤直至找到满意的解。 名词解释 交叉概率:两个个体进行交配的概率。 例如,交配概率为0.8,则80%的“夫妻”会生育后代。 变异概率:所有的基因中发生变异的占总体的比例。 GA函数 适应度函数 适应度函数由解决的问题决定。 举一个平方和的例子。 简单的平方和问题 求函数的最小值,其中每个变量的取值区间都是 [-1, ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值