AppDomain 和动态加载

用Python进行GEO数据挖掘(学习笔记四):富集分析 该笔记详细解释了富集分析的工作原理,并简要地利用gseapy在python中进行富集分析的操作。 阅读详情

应用程序体系结构

在我专攻代码之前,我想谈谈我尝试做的事。您可能记得,SuperGraph 让您从函数列表中进行选择。我希望能够在具体的目录中放置外接程序程序集,让 SuperGraph 检测它们,加载它们,并找到它们中包含的所有函数。

如果 SuperGraph 自己能完成此操作则不需要单独的 AppDomain。Assembly.Load() 通常运行良好,但程序集无法独立卸载(只有 AppDomain 可以卸载)。这意味着如果您正在编写服务器,而且您希望用户无需启动和停止服务器即能更新他们的外接程序,那么您将无法使用默认的 AppDomain 实现此任务。

要实现此功能,我们将在一个独立的 AppDomain 中加载所有外接程序程序集。当添加或修改文件时,我们将卸载 AppDomain,创建新的 AppDomain,然后将当前文件加载到其中。这样,一切就都完美无缺了。

为了把这个讲得更明白一点,我创建了一个典型方案,如图 1 所示。

图 1:典型的 AppDomain 方案

在这个图表中,Loader 类创建一个名为 Functions 的新 AppDomain。创建 AppDomain 之后,Loader 在新的 AppDomain 中创建 RemoteLoader 的实例。

要加载程序集,请在 RemoteLoader 上调用加载函数。该函数打开新的程序集,找到程序集中的所有函数,将函数打包到 FunctionList 对象中,然后将该对象返回到 Loader。然后,就可以通过 Graph 函数使用此 FunctionList 中的 Function 对象。

创建 AppDomain

第一项任务是创建 AppDomain。要以正确的方式创建 AppDomain,我们需要向 AppDomain 传递一个 AppDomainSetup 对象。一旦您理解了这一切的工作原理,关于这些的文档就足够使用了,但是如果您正在试图理解其工作原理,那么这些文档的帮助并不大。当关于该主题的 Google 搜索将上个月的专栏作为较高的匹配之一返回时,我怀疑我可能有点麻烦了。

必须处理的基本问题是如何在运行时加载程序集。默认情况下,运行时将查看全局程序集缓存或当前应用程序目录树。而我们希望从完全不同的目录中加载我们的外接程序。

当您查看 AppDomainSetup 的文档时,您将发现可以把 ApplicationBase 属性设置为要搜索程序集的目录。然而,我们也需要参考原始的程序目录,因为那是 RemoteLoader 类存在的地方。

AppDomain 的创作者们理解这一点,因此他们已经提供了额外的位置,用于从中搜索程序集。我们将使用 ApplicationBase 引用外接程序目录,然后将 PrivateBinPath 设置为指向主应用程序目录。

下面是来自 Loader 类的代码,可实现此功能:

AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = functionDirectory;
setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
setup.ApplicationName = "Graph";
appDomain = AppDomain.CreateDomain("Functions", null, setup);

remoteLoader = (RemoteLoader)
appDomain.CreateInstanceFromAndUnwrap("SuperGraph.exe",
"SuperGraphInterface.RemoteLoader");

创建 AppDomain 之后,使用 CreateInstanceFromAndUnwrap() 函数在新的应用程序域中创建 RemoteLoader 类的实例。请注意,需要使用类所在的程序集的文件名以及类的全名。

当执行此调用时,我们返回如同 RemoteLoader 一样的实例。实际上,它是一个小型代理类,将所有调用转发到其他 AppDomain 中的 RemoteLoader 实例中。这和 .NET Remoting 使用的是同一种结构。

程序集绑定日志查看器

当您编写代码实现此功能时,您会产生错误。 本文档对如何调试应用程序并未提供什么建议,但是如果您知道该向谁询问,他们将告诉您有关程序集绑定日志查看器(名为“fuslogvw.exe”,因为 加载子系统称为“fusion”)的信息。运行查看器时,您可以要求它记录故障,然后当您运行的应用程序出现加载程序集的问题时,您可以刷新查看器,获得 当前情况的详细信息。

例如,您会发现 Assembly.Load() 的文件名末尾不需要 .dll,这一点非常有用。您可以从日志中获知这一点,因为它将告诉您它曾试图加载 f.dll.dll。

动态加载程序集

因此,既然我们已经创建了应用程序域,下一步应该搞清楚如何加载组件并从中提取函数。这需要两段相互独立的代码。第一段代码在目录中查找文件,然后加载找到的每个文件:

void LoadUserAssemblies()
{
availableFunctions = new FunctionList();
LoadBuiltInFunctions();

DirectoryInfo d = new DirectoryInfo(functionAssemblyDirectory);
foreach (FileInfo file in d.GetFiles("*.dll"))
{
string filename = file.Name.Replace(file.Extension, "");
FunctionList functionList = loader.LoadAssembly(filename);

availableFunctions.Merge(functionList);
}
}

Graph 类中的函数在外接程序目录中查找所有的 dll 文件,删除它们的扩展名,然后告诉加载程序加载它们。返回的函数列表将并入当前的函数列表。

第二段代码在 RemoteLoader 类中,它实际加载程序集并查找函数:

public FunctionList LoadAssembly(string filename)
{
FunctionList functionList = new FunctionList();
Assembly assembly = AppDomain.CurrentDomain.Load(filename);

foreach (Type t in assembly.GetTypes())
{
functionList.AddAllFromType(t);
}
return functionList;
}

这段代码只是对传入的文件名(实际是程序集名称)调用 Assembly.Load(),然后将所有有用的函数加载到 FunctionList 实例中返回给调用程序。

此时,应用程序可以启动,加载外接程序程序集,然后用户就可以引用它们。

重新加载程序集

下一项任务是能够按照需要重新加载这些程序集。最终,我们希望能够自动实现该任务,但是出于测试目的,我将 Reload 按钮添加到窗体中,以使程序集能够重新加载。该按钮的处理程序仅调用 Graph.Reload(),它需要执行以下操作:

    • 卸载 AppDomain。
    • 创建新的 AppDomain。
    • 在新的 AppDomain 中重新加载程序集。
    • 将图形线条挂钩到新创建的 AppDomain。

步骤 4 是必需的,因为 GraphLine 对象包含来自原 AppDomain 的 Function 对象。卸载 AppDomain 后,函数对象无法再被使用。

为解决此问题,HookupFunctions() 修改了 GraphLine 对象,使它们从当前应用程序域指向正确的函数。

代码如下:

loader.Unload();
loader = new Loader(functionAssemblyDirectory);
LoadUserAssemblies();
HookupFunctions();
reloadCount++;

if (this.ReloadCountChanged != null)
ReloadCountChanged(this, new ReloadEventArgs(reloadCount));

只要执行重新加载操作,最后两行将引发一个事件。其作用是更新窗体上的重新加载计数器。

检测新的程序集

下一步是能够检测在外接程序目录中显示的新的或修改过的程序集。该框架提供 FileSystemWatcher 类来实现此功能。下面是我添加到 Graph 类构造函数中的代码:

watcher = new FileSystemWatcher(functionAssemblyDirectory, "*.dll");
watcher.EnableRaisingEvents = true;
watcher.Changed += new FileSystemEventHandler(FunctionFileChanged);
watcher.Created += new FileSystemEventHandler(FunctionFileChanged);
watcher.Deleted += new FileSystemEventHandler(FunctionFileChanged);

当创建 FileSystemWatcher 类时,我们告诉它要在什么目录中查找,要跟踪哪些文件。EnableRaisingEvents 属性表示当它检测到更改时,我们是否需要它发送事件。最后 3 行将事件挂钩到类中的某个函数。该函数仅仅调用 Reload() 以重新加载程序集。

这种方法有一些累赘的地方。在更新程序集时,我们必须卸载程序集才能够加载新的版本,但是添加或删除文件时不需要卸载程序集。在这种情况下,对所有更改执行此操作的成本并不是很高,而且它使代码更简单。

在构造此代码之后,我们运行该应用程序,然后尝试把新的程序集复制到外接程序目录中。正如我们所希望的那样,我们获得了文件更改事件,当重新加载完毕时,新的函数就可供使用。

然而,当我们试图更新现有的程序集时,我们遇到了一个问题。运行时已经锁定该文件,这意味着我们无法将新的程序集复制到外接程序目录中,我们收到一个错误。

AppDomain 类的设计人员意识到这是一个问题,因此他们提供一种不错的解决方法。当 ShadowCopyFiles 属性设置为 true(字符串 true,不是布尔常数 true。不要问我为什么……)时,运行时将把程序集复制到缓存目录中,然后打开该程序集。这样,原文件就不会被锁定,我们也就能更新正在使用的程序集。ASP.NET 使用了这种机制。

为了启用此功能,我在 Loader 类的构造函数中添加了以下行:

setup.ShadowCopyFiles = "true";

然后我重新生成了该应用程序,并得到相同的错误。我查看了 ShadowCopyDirectories 属性的文档,该文档明确指出 PrivateBinPath 指定的所有目录(包括 ApplicationBase 指定的目录)是阴影复制的(如果未设置此属性)。记得我是如何说该文档在这个方面不是很好的吗?

有关此属性的文档肯定是错了。我没有验证确切的表现方式,但是我可以告诉您 ApplicationBase 目录中的文件在默认情况下并不是阴影复制的。明确指定目录可以解决此问题:

setup.ShadowCopyDirectories = functionDirectory;

搞明白这一点至少花了我半个小时。

现在我们可以更新现有文件并将其正确地加载进去。可我刚把这个理顺,又遇到了另外一个小的问题。当我们从窗体的按钮上运行重新加载函数时,重新加载总是和绘制发生在同一个线程中,这意味着在重新加载过程中我们不可能尝试绘制直线。

既然我们已经切换到文件更改事件,那么在卸载 AppDomain 之后和加载新的 AppDomain 之前,有可能会进行绘制。如果发生这种情况,我们会得到一个异常。

这是传统的多线程编程问题,使用 C# lock 语句很容易处理。我在绘图函数和重新加载函数中添加了 lock 语句,这就确保了它们不会同时发生。这就解决了该问题,添加程序集的更新版本将使程序自动切换到函数的新版本。这相当不错。

还有一个奇怪的现象。原来用于检测文件更改的 Win32® 函数发送的更改数量很大,因此对文件做一次更新将发送五个更改事件,程序集也将被重新加载五次。解决方法是编写更智能的、可以将这些操作组合在一起的 FileSystemWatcher,但是此版本中没有提供这种解决方法。

拖放

将文件复制到目录中不是很方便,因此我决定在该应用程序中添加拖放功能。实现该任务的第一步是把窗体的 AllowDrop 属性设置为 true,这将打开拖放功能。下一步,我将一个例程挂钩到 DragEnter 事件。当光标在对象上移动进行拖放操作以确定当前对象是否接受拖放时,将调用该事件。

private void Form1_DragEnter(
object sender, System.Windows.Forms.DragEventArgs e)
{
object o = e.Data.GetData(DataFormats.FileDrop);
if (o != null)
{
e.Effect = DragDropEffects.Copy;
}
string[] formats = e.Data.GetFormats();
}

在此处理程序中,我查看是否有可用的 FileDrop 数据(也就是说,文件被拖放到窗口中)。如果有,我把效果设置为“复制”,这将相应地设置光标,并且如果用户释放鼠标按钮,将发送 DragDrop 事件。该函数中的最后一行完全是出于调试目的,用于查看操作中有哪些可用信息。

下一项任务是为 DragDrop 事件编写处理程序:

private void Form1_DragDrop(
object sender, System.Windows.Forms.DragEventArgs e)
{
string[] filenames = (string[]) e.Data.GetData(DataFormats.FileDrop);
graph.CopyFiles(filenames);
}

此例程获得与此操作关联的数据(文件名数组),将其传递到图形函数,然后图形函数把文件复制到外接程序目录中,触发文件更改事件以便重新加载它们。

状态

此时,您可以运行该应用程序,把新的程序集拖到程序上,程序将很快加载它们并保持运行。这相当不错。

 
总结目前开源的视频生成/视频编辑大模型 随着Sora的爆火,视频生成和视频编辑受到了越来越多的关注。这篇博客总结一下目前开源的视频生成和视频编辑的大模型,并分析他们各自的优缺点,以及在自己进行科研任务或者工作中应该如何选用对应的开源模型,希望能给从事这一领域研究和使用的小伙伴们帮助。 阅读详情

相关推荐

Claude Code插件安装与VSCode深度集成实战指南

Claude Code是一款专为IDE设计的AI编程助手插件,其核心价值在于将自然语言意图转化为可运行代码,从而优化开发者认知带宽分配。它依赖Node.js运行时、git版本上下文和VSCode编辑器API三者协同工作,技术原理涉及本地进程通信、权限策略绕过、上下文感知补全等关键机制。该工具显著提升工程效率,适用于CLI开发、EXIF元数据处理、异步逻辑生成等典型JavaScript/TypeScript应用场景。本文聚焦Claude Code插件在VSCode中的真实落地链路,覆盖Windows/macO

weixin_34006468的博客 475

应用程序体系结构 AppDomain 和动态加载 ShadowCopyFiles

 应用程序体系结构   在我专攻代码之前,我想谈谈我尝试做的事。您可能记得,SuperGraph 让您从函数列表中进行选择。我希望能够在具体的目录中放置外接程序程序集,让 SuperGraph 检测它们,加载它们,并找到它们中包含的所有函数。   如果 SuperGraph 自己能完成此操作则不需要单独的 AppDomain。Assembly.Load() 通常运行良好,但程序集无法独立

MinSen 3468

西门子828D/840DSL数控机床产量数据采集实战:一个C#程序员的踩坑与填坑记录

本文详细记录了C#程序员在西门子828D/840DSL数控机床产量数据采集实战中的经验与教训。从硬件连接到软件配置,再到数据读取与异常处理,文章分享了工业现场编程的特殊挑战和解决方案,特别是针对数控系统的TCP/IP通信、PLC变量地址映射及数据解析等关键技术点。

weixin_42741930的博客 596

AppDomain实现【插件式】开发

前言:技术资讯 www.secn.net  近期项目中需要实现“热插拔”式的插件程序,例如:定义一个插件接口;由不同开发人员实现具体的插件功能类库;并最终在应用中调用具体插件功能。  此时需要考虑:插件执行的安全性(隔离运行)和插件可卸载升级。说到隔离运行和可卸载首先想到的是AppDomain。  那么AppDomain是什么呢? 一、AppDomain介绍  AppDomain是.Net平台里一个很重要的特性,在.Net以前,每个程序是"封装"在不同的进程中的,这样导致的结果就造就占用资源大,可复用性低.

nidongla的博客 533

基于ENVI5.6与SARscape的哨兵一号时序InSAR实战:从数据准备到PS/SBAS结果解译

本文详细介绍了基于ENVI5.6与SARscape软件,利用哨兵一号(Sentinel-1)卫星数据,进行PS-InSAR与SBAS-InSAR时序分析,以监测地表形变的完整实战流程。内容涵盖从数据下载、软件配置、预处理、核心处理步骤到结果解译与质量评估,重点解析了PS与SBAS方法的关键参数设置、常见问题及解决方案,旨在帮助用户掌握从数据准备到成果分析的完整技术链条。

oo7890的博客 634

Agent测试框架Harbor指南

Harbor是一个AI Agent单元测试框架,用于自动化测试AI代理在容器环境中的任务执行能力。它支持18+种AI代理,通过沙箱隔离和自动验证机制评估任务完成情况。任务必须具有可编程验证的输出结果,适合代码、数据、系统等可量化任务,不适合创意写作等主观任务。Harbor采用CLI操作方式,核心架构包含Job管理、Trial执行和验证器三个层级,执行流程包括环境启动、Agent执行和结果验证。任务目录包含指令文件、配置文件、Docker环境定义和测试脚本等必需组件,通过测试脚本自动判断任务完成质量。

loo_Charles_ool的博客 1410

c# 用代码来设置程序的PrivatePath

有时候我们想让程序的exe文件和dll文件分开在不同目录,可这时候wufazhixing

织梦行云 1万+

动态时间规整(Dynamic Time Warping,DTW):让时间序列分析不再枯燥和代码全分析

动态时间规整(DTW)是一种用于比较时间序列相似性的算法,特别适用于处理长度不同或时间轴不对齐的序列。本文介绍了DTW的核心概念,包括其通过非线性对齐解决时间扭曲问题的能力,以及在语音识别、金融分析等领域的应用。通过Python代码示例,演示了如何计算两个模拟时间序列的DTW距离,并可视化最优对齐路径。结果显示,DTW能够有效捕捉序列间的相似模式,即使它们在时间轴上存在偏移或伸缩。该算法通过最小化累积距离来评估相似性,得分越低表明序列间相似度越高。

此星光明博客 163

代码控制PrivateBinPath和ConfigurationFile的位置

.Net的WinForm程序有的时候让人很烦的是,在执行目录下总是一大堆的DLL,配置文件,最少则是个以下,多的时候怕有四五十个吧……,自己程序中的类库,第三方的类库……加载一起让人感觉乱糟糟的,非常不爽。在下虽然在个人卫生上没有什么洁癖,可是对于应用程序的这个样子确实没有一点容忍力的,是可忍孰不可忍啊! 处理这些DLL还是比较简单的,Configuration文件里就可以配置了。...

iteye_2169的博客 255

全球SAR卫星大盘点与回波数据处理专栏目录

本专栏将详细介绍各国各地区的SAR卫星的特点和性能,以及SAR回波数据的处理方法和技巧。主要介绍民用和商用SAR卫星,帮助你了解商业SAR卫星的最新动态和技术,MATLAB代码将全部开源。以下是专栏目录,持续更新中。

3165

YOLOv13添加DRB模块map暴涨12.33!准确率提升8.56!召回率提升15.66

本文介绍了在YOLOv13模型中添加DRB模块的改进方法。实验结果显示,添加DRB模块后模型性能显著提升,mAP暴涨12.33%,准确率提升8.56%。文章详细说明了移植过程,包括创建yolov13-DRB.yaml配置文件、修改tasks.py和__init__.py文件,以及引入UniRepLKNet模块的具体实现。其中重点描述了GRNwithNHWC、NCHWtoNHWC等核心组件的实现细节,并提供了完整的模型结构配置代码。这些改进使得YOLOv13模型在保持原有架构优势的同时,获得了更强大的特征提取

FJN110的博客 2822

使用AppDomain进行动态加载和卸载dll

加载dll最简单的写法 Assembly.Load("<dll路径>")    但这样写这个dll就被程序占用不可删除,即不可以卸载。 通过AppDomain加代理的方式进行加载则可以实现卸载,从而实现程序不被占用并被删除。 第一步,创建AssemblyProxy类 public class AssemblyProxy : Marsha...

weixin_30532759的博客 712

2026 年 3 月青少年软编等考 C/C++ 三级测试题解析

摘要 本文包含三部分内容: 单选题(10题):考察C++基础语法和数据结构知识,涉及vector、栈、队列的操作和特性。 判断题(10题):测试对动态数组、栈、队列、字符串操作的理解,包括内存管理、访问方式和函数返回值等。 编程题(1题):模拟火车站售票窗口排队场景,要求根据输入输出各窗口的排队情况,重点考查队列的实际应用和边界条件处理。 答案解析详细,涵盖常见编程考点,适合检验基础知识的掌握程度。

发布少儿编程考级的真题和优秀竞赛题解析,以及一些讲义。 252

AppDomain与Assembly的动态加载与卸载

为了将问题描述清楚,我们先来看一个例子。在这个例子中,WinForm上有一个按钮,当用户点击这个按钮后,就会装载一个已经存在的Assembly,并且在界面的Label控件上显示出这个Assembly的FullName。对Reflection稍微熟悉一点的朋友都知道,这是非常简单的事情,只需要用Assembly.LoadFile方法获得Assembly,然后用FullName属性来显示即可,比如下面

无知人生,记录点滴 1万+

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

AppDomain二——C#中动态加载和卸载DLL

该demo实现动态加载TestDll的功能 首先定义一个TestDll库 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestDll { pub...

sinat_31608641的博客 1484

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

关于C#中动态加载AppDomain的问题

在操作系统中,利用进程可以对正在运行的应用程序进行隔离,每个应用程序被加载到单独的进程中,并为其分配虚拟内存,进程无法直接访问物理内存,只能通过操作系统将虚拟内存映射到物理内存中,并保证进程之间的物理内存不会重叠,但是进程最大的缺点就是效率问题,尤其是进程的切换开销很大,而进程间不能共享内存,所以不可能从一个进程通过传递指针给另一个进程。 在...

weixin_30449239的博客 1015

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

AppDomain和动态加载 转

AppDomain和动态加载下载 supergraphfiles.exe 示例文件。 应用程序体系结构 在我专攻代码之前,我想谈谈我尝试做的事。您可能记得,SuperGraph 让您从函数列表中进行选择。我希望能够在具体的目录中放置外接程序程序集,让 SuperGraph 检测它们,加载它们,并找到它们中包含的所有函数。 如果 SuperGraph 自己能完成此操作则不需要单独的

Yellow 痕迹 591

转 C#中运用AppDomain动态加载程序集详解

在.net中有了一个AppDomain——应用程序域 的新概念,.NET程序通过AppDomain这个媒介来运行在进程中。   我们运行一个.NET应用程序或者一个运行库宿主时,OS会首先建立一个进程,然后会在进程中加载CLR(这个加载一般是通过调用 _CorExeMain或者_CorBindToRuntimeEx方法来实现),在加载CLR时会创建一个默认的AppDomain,它是CLR...

yfdong21_2009的博客 534

C#中运用AppDomain动态加载程序集详解

在.net中有了一个AppDomain——应用程序域的新概念,.NET程序通过AppDomain这个媒介来运行在进程中。   我们运行一个.NET应用程序或者一个运行库宿主时,OS会首先建立一个进程,然后会在进程中加载CLR(这个加载一般是通过调用_CorExeMain或者_C

Steven Lei的博客 6093

AppDomain与动态加载程序集

AppDomain与动态加载程序集   我们首先来了解下AppDomain 和应用程序池,我们知道在IIS上部署一个Web应用程序时,会有一个让你选择应用程序池的选择项,我们通常都喜欢用默认值,即选择 DefaultApppool。我们也可以新创建个应用程序池,将需要部署的Web应用程序放到新建的应用程序池中。其实每个应用程序池就对应着一个任务 管理器中的W3WP.exe进程,当你新建了N个应...

weixin_30877227的博客 140

(转)AppDomain 和动态加载

http://www.microsoft.com/china/msdn/archives/library/dncscol/html/csharp05162002.aspEric GunnersonMicrosoft Corporation 2002 年 5 月 17 日 请从 MSDN Online Code Center 下载 supergraphfiles.exe(英文)。 这个...

dench2001的博客 245

C#中动态加载卸载Appdomain

原文地址:http://developer.51cto.com/art/200906/131416.htm

Hello World 2075
上一篇: C#与DLL和COM的混合编程(1)-C#调用C++写的非托管的DLL中导出的函数
下一篇: AppDomain
Steven_god
博客等级 码龄22年 6粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值