aardio + .NET 快速开发独立 EXE 程序,可防 ILSpy 反编译
liuian 2024-12-10 18:06 63 浏览
简介
aardio 可以非常方便地调用 .NET( 不需要任何复杂的步骤 )。
.NET 在 aardio 中很好用,系统自带 .NET 组件以及各种开源 .NET 组件在 aardio 用户中也很受欢迎。
aardio + .NET 生成的 EXE —— 可避免被 ILSpy 直接反编译。
aardio 调用 .NET 示例:
//导入 .NET 支持库
import dotNet;
//导入.NET 程序集
dotNet.import("System");
//调用类的静态成员函数
var isValidHost = System.Uri.CheckHostName("www.aardio.com");
//构造 .NET 对象
var uri = System.Uri("https://www.aardio.com/test?q=aardio");
//读或写 .Net 对象的实例属性
var host = uri.Host;
//调用 .Net 对象实例的成员函数
var hash = uri.GetHashCode();aardio 语法与 JavaScript 接近,请参考:aardio 编程语言快速入门——语法速览
aardio + .Net 开发对 .NET 版本没有严格要求 —— 兼容流行 Windows 系统自带的不同版本 .NET。不但可以调用系统自带的大量 .NET 组件,也可以生成体积很小的 EXE 文件。
Win7 自带 .Net 3.5.1,支持 lambda
Win8 自带 .Net 3.5.1 + .Net 4.5
Win10 自带 .Net 4.6
Win10 1709 自带 .Net 4.7.1 ,支持 ValueTuple
Win11 自带 .NET 4.8
内存加载 .NET 程序集,生成独立 EXE
aardio 提供 dotNet.reference() 函数 —— 可以方便地通过内存数据加载 .NET 程序集,这样就可以生成独立 EXE 程序,不再需要带上一堆 DLL 文件。
将内存数据绑定为 .NET 程序集示例:
import dotNet;
dotNet.reference({
["test.mydll"] = #34;\test.mydll.dll";
["test.core"] = #34;\test.core.dll";
});dotNet.reference() 的第 1 个参数指定程序集名称,第 2 个参数指定实际要加载的程序集路径或内存数据,aardio 代码在文件路径前加 $ 号可将文件数据编译为二进制字符串( 发布后不再需要原文件 )。
然后就可以正常导入内存程序集了,代码如下:
dotNet.import("test.mydll");上面的代码导入 .NET 程序集,然后再将 .NET 名字空间导入 aardio ,dotNet.import() 函数的作用与下面的代码相同:
//导入 .NET 程序集
var assembly = dotNet.load("test.mydll");
//导入 .NET 名字空间
assembly.import("test.mydll");aardio 标准库提供的 .NET 库
aardio 标准库中已经提供了一些 .NET 库,例如 System ,调用示例:
//导入 .NET 名空间
import System;
//用 System 名字空间下面的类构造对象实例
var uri = System.Uri("https://www.aardio.com/test?q=aardio")
//读或写 .Net 对象的实例属性
var host = uri.Host ;aardio 代码一般使用小驼峰命名风格,但 .NET 名字空间或类名一般会大写首字母以示区别。
我们右键点 System ,在弹出菜单内点击「跳转到定义」看一下 System 库的源代码。
可以看到这个 Sytem 库的关键代码只有两句:
import dotNet;
dotNet.import("System")嵌入 .NET 窗口控件到 aardio 窗口
aardio 窗口嵌入 .NET DataGridView 控件范例的运行效果:
首先要了解 .Net 的所有控件都应当放在 .Net 创建的窗口里(也就是 System.Windows.Forms.Form 对象),窗口是管理控件的容器,不能直接把控件单独拧出来往 aardio 窗口里扔。
如果不想去弄个窗口,aardio 提供了一个更简单的方法 ,例如把 .Net 的 DataGridView 控件直接嵌入 aardio 窗口:
import System.Windows.Forms;
var Forms = System.Windows.Forms;
var dataGridView = Forms.CreateEmbed("DataGridView",winform.custom);非常简单。
好了,现在创建 DataTable 数据表,准备把他显示到控件里,先创建数据列,重点看怎么指定列字段使用的数据类型:
//添加数据列
var dataTable = System.Data.DataTable("DT");
dataTable.Columns.Add("名称");//添加列
dataTable.Columns.Add("计数",System.Type.GetType("System.Double")); //添加指定数据类型的列
dataTable.Columns.Add("选择",System.Type.GetType("System.Boolean")); //自动显示复选框 然后绑定数据源到视图:
//绑定数据源到视图
var dataView = System.Data.DataView(dataTable);
dataGridView.DataSource = dataView;
dataGridView.EditMode=2;好吧,想再加一个下拉框吗?!这个就略有些麻烦了,代码如下:
//先移除自动生成的列
dataGridView.Columns.Remove("名称");
//下面添加下拉框以替换上面移除的列
var cmbColumn = Forms.DataGridViewComboBoxColumn();
cmbColumn.Width = 120;
cmbColumn.Name = "Name";
cmbColumn.DataPropertyName = "名称";//对应上面 dataTable 里的字段名
cmbColumn.HeaderText = "名称"; //显示在列标题里的文本
//如果名称与显示值一样,那直接给 cmbColumn.DataSource 赋值一个数组就可以
//下面绑定下拉候选框的数据源,上面的 DataPropertyName 才是真正要读写的数据值。
cmbColumn.DisplayMember = "Name";//下拉框显示文本的属性名
cmbColumn.ValueMember = "Value"; //下拉框选项值的属性名
cmbColumn.DataSource = dotNet.createNameValueList(
{ "王五","张三"},
{ "WangWu","ZhangSan"}
);
//添加这个新的下拉框到数据视图
dataGridView.Columns.Add(cmbColumn);
//移动到第一列
dataGridView.Columns.Item["Name"].DisplayIndex = 0;然后添加下面的代码响应 .NET 控件的事件:
//添加事件(event)
dataTable.ColumnChanged = function(sender,eventArgs){
var columnName = eventArgs.Column.ColumnName;
var value = eventArgs.Row.getItem(columnName);
winform.edit.print("已改变列:",columnName," 已变更值:",value);
}然后读写数据:
//添加测试数据
var row = dataTable.NewRow();
row.ItemArray = {"WangWu",123, true}
dataTable.Rows.Add(row);
//读取数据
winform.button.oncommand = function(id,event){
for(i=1;dataTable.Rows.Count;1){
var arr = dataTable.Rows[i].ItemArray;
winform.edit.print( arr[1] ) ;
}
}以上完整范例源代码请参考 aardio 自带范例:
aardio 范例 / 调用其他语言 / .Net / 控件窗口 / 嵌入控件
可以看到 aardio 自带了大量调用 .NET 的范例。
在 aardio 中加载的 .NET 程序集如何调试
用下面的代码在 aardio 中加载 .NET 程序集的 pdb 调试文件:
dotNet.loadFile( "程序集路径" ,"pdb 调试文件路径" ); 然后用 VS 附加运行的 aardio 进程就可以调试了,懂 .NET 的都懂,这个不多说了。
用 aardio 在运行时编译 C# 源码
直接看 aardio 代码示例:
import dotNet;
//创建 C# 语言编译器
var compiler = dotNet.createCompiler("C#");
//DLL 程序集要提前引入,System.dll 默认已引入,注意这函数不支持内存 DLL
compiler.Reference("System.dll");
//设置待编译C#源码( 注释可赋值为字符串,注释标记首尾星号数目要一致 )
compiler.Source = /***
?>
/*
如果 C# 代码开始于 aardio 模板标记,则启用 aardio 模板语法。
参考:《aardio 使用手册 / aardio 语言 / 模板语法》
*/
namespace CSharpLibrary
{
public class Object
{
<? if _WINXP { ?>
public string Test(){
return "Windows XP";
}
<? } else { ?>
public string Test(){
return "<?= win.version.name ?>";
}
<? } ?>
}
}
***/
import win.version;
//编译并返回程序集,可选在参数中指定输出 DLL文件,不指定则编译为内存程序集。
var assembly = compiler.CompileOrFail(/*"/output.dll"*/);
//导入名字空间,也可以直接写 compiler.import("CSharpLibrary");
assembly.import("CSharpLibrary");
//使用 C# 编写的类构造对象实例
var netObj = CSharpLibrary.Object();
//调用实时编译的C#函数
var ret = netObj.Test();
import console;
console.log( ret );
console.pause();注意 aardio 中的注释可赋值为字符串,因为 aardio 要求段注释的首尾星号数目必须一致,所以不会与其他编程语言冲突,很适合用来放其他编程语言的源代码。
上面的 compiler.Source 可以用一个字符串指定 C# 源码,这个字符串支持类似 PHP 的模板语法,所以我们可以用 aardio 代码灵活地在运行时生态生成比较复杂的 C# 源代码,然后再用 .NET 编译为程序集。aardio 中的 dotNet.desktop 扩展库就使用了这种技术用很少的代码就实现了虚拟桌面管理支持库。
默认可以将 C# 源码编译为内存程序集,这样很适合生成独立 EXE 文件。
注意在 aardio 中编译 C#,调用的是 CLR,而 CLR 只有 2.0 / 4.0 的区别,运行时编译也只支持这两个版本的语法。例如安装了 .Net 3.5 但没有安装 .Net 4.x ,那么 CLR 2.0 下编译器不支持 var ,lambda 这些语法 (但是能运行编译后的 DLL,可以事先用 VS 编译 C# 代码生成 DLL 程序集)。
.NET 与 aardio 对象相互转换规则
aardio 会自动处理类型转换,调用 .NET 函数时如果参数类型不一致 —— aardio 也会尽最大可能地转换参数类型,用起来还是比较轻松的。但简单了解一下类型转换规则和原理是有必要的。
所有原生 .NET 对象在 aardio 中分为两类:
1、可自动转换的简单值类型
null值、数值、字符串、枚举、 System.Drawing.Color 等简单值类型,以及这些值类型的数组可以直接交换。aardio 中的 buffer 在 .NET 中对应字节数组。
2、在 aardio 中存为 COM 对象的 .NET 对象
其他原生 .NET 对象在 aardio 存为 com.NETObject 对象(对应 .NET 中的 System.__ComObject 类型),其中有些特殊的 .NET 对象(例如 struct,ValueTuple),在传入 aardio 时会封包为特殊的 DispatchableObject 对象。这些 .NET 对象在 aardio 中都会被封装为 dotNet.object 对象,在 aardio 中使用没有太大区别。
aardio 与 .NET 交互基于 COM 接口,所以遵守 aardio 的 COM 传参基本规则:
aardio 中的整数传入 .NET 默认为 int32,小数默认为 double 类型。aardio 数值数组传入 .NET 默认为 double 类型 COM 数组,纯字符串数组一律转为 BSTR 数组。其他数组转为 Variant 变体类型数组。
.NET 中的 enum 枚举会自动转换为 aardio 中的数值(双向自动转换),
.NET 中的 struct,tuple 由 .NET 对象 DispatchableObject 封包后再返回 aardio 。
aardio 函数则自动转换为委托、事件所需要的委托类型。
.NET 中的 System.IntPtr,System.UIntPtr 类型在 aardio 中会自动转换为整数值,
aardio 中的指针类型(pointer)必须使用 tonumber() 函数转换为数值才能传入 .NET。
窗口句柄( HWND ) 在 aardio 以整数值表示,可以直接传入 .NET。
System.Drawing.Color 在 aardio 则会自动转换为 ARGB 格式的颜色数值。
调用 .NET 时 ARGB 格式的颜色数值也能自动转换为 System.Drawing.Color 对象。
注意 GDI+ 使用 ARGB 格式颜色值,与 gdip库,plus 控件等兼容。
aardio 提供以下函数创建指定静态类型的 dotNet.object 对象:
dotNet.object(value,byRef) 转换为 .Net 对象。
dotNet.byte(value,byRef) 转换为 8 位整型数值。
dotNet.ubyte(value,byRef) 转换为 8 位无符号整型数值。
dotNet.word(value,byRef) 转换为 16 位整型数值。
dotNet.uword(value,byRef) 转换为 16 位无符号整型数值。
dotNet.int(value,byRef) 转换为 32 位整型数值。
dotNet.uint(value,byRef) 转换为 32 位无符号整型数值。
dotNet.long(value,byRef) 转换为 64 位整型数值。
dotNet.ulong(value,byRef) 转换为 64 位无符号整型数值。
dotNet.float(value,byRef) 转换为 32 位浮点数值。
dotNet.double(value,byRef) 转换为 64 位浮点数值
以上函数会将参数 1 存为 .NET 对象并封包为 DispatchableObject 对象后再返回 dotNet.object 对象,( 简单的值类型也会转换为 dotNet.object 对象 ),这可以让 aardio 直接引用 .NET 中的对象,方便实现 ref,out 等输出参数。
下面的 aardio 代码演示了 dotNet.object 的用法:
import dotNet;
var compiler = dotNet.createCompiler("C#");
//指定 C# 源代码
compiler.Source = /***
namespace CSharpLibrary
{
public class Object
{
public static void Test(ref double num,int [] arr){
num = 12.3;
arr[0] = 56;
}
}
}
***/
//编译程序集并导入名字空间
compiler.import("CSharpLibrary");
//创建 .Net 对象,启用引用传参。
var num = dotNet.double(12.5,true);
//创建 .Net 数组
var arr = dotNet.int({1,2,3});
//调用 .NET 函数。
CSharpLibrary.Object.Test(num,arr);
import console;
/*
dotNet.object 对象如果存储的是数组,
可用下标直接读写数组成员。
*/
console.log( arr[1] )
/*
dotNet.object 对象如果存储的是 Primitive,enum,string 类型
或这些类型的普通数组,则可使用 Value 属性读写原始值。
*/
console.log( num.Value );
//支持 tostring() 转换为字符串,tonumber() 转换为数值。
console.log(tostring(num),tonumber(num));
console.pause();下标
C# 中的 下标操作符[] 实际上会被自动转换为访问 Item[] 下标属性。
先看 aardio 代码示例:
import dotNet;
var compiler = dotNet.createCompiler("C#");
compiler.Source = /******
using System;
using System.Collections.Generic;
namespace CSharpLibrary
{
public class TestClass
{
private Object [] values = new Object [] {1,2,3,4,5,6,7,8,9};
public Object this [int index]
{
get { return values[index]; }
set { values[index] = value; }
}
public Dictionary<string,string> dict = new Dictionary<string,string> ();
}
}
******/
//编译并引入 C# 名字空间
compiler.import("CSharpLibrary");
//使用 C# 编写的类构造对象实例
var netObj = CSharpLibrary.TestClass();
//读下标属性,按 C# 规则起始下标为0,而非 aardio 中的起始下标为 1。
var item = netObj.Item[5];
var item = netObj.getItem(5); //这样读下标属性也可以,支持多参数
var item = netObj.Item(5); //get 前缀可以省略
//写下标属性
netObj.Item[5] = 123;
netObj.setItem(5); //这样写下标属性也可以,支持多参数
//如果.NET 对象的下标为数值,允许省略 Item,但这时候起始下标为 1。
var item = netObj[6];
//也可以下面这样赋值:
netObj[6] = 123;
import console;
console.log(netObj[6]);
//字典也可以这样访问
netObj.dict.Item["test"] = "abc";
console.log( netObj.dict.Item["test"] );
console.pause();要点:
1、在 aardio 中需要用 Item[] 访问 .NET 对象的 Item 属性。这时候要注意起始下标为 0 ( 遵守 C# 规则 )。
2、如果下标为数值可以省略 Item 直接写 [],但这时要起始下标为 1 (遵守 aardio 规则)。
调用 UWP
用下面的 aardio 代码创建支持调用 UWP 组件的 C# 编译器:
import dotNet.uwpCompiler
var uwpCompiler = dotNet.uwpCompiler( "\ocr.dll" )可选在 dotNet.uwpCompiler 的第 2 个参数指定 Windows.winmd 的路径,如果没有指定 aardio 会自动到下面的目录去查找最新版本 Windows 10 SDK 目录( 需要事先安装 ):
C:\Program Files (x86)\Windows Kits\10\UnionMetadata 然后在 SDK 目录下查找 Windows.winmd。我们只是在编译程序集时需要 Windows.winmd,运行时不需要它( 也不需要 Windows 10 SDK )。
例如 aardio 标准库 dotNet.ocr 包含的 ocr.dll 就是用下面的代码编译的:
import dotNet.uwpCompiler
var uwpCompiler = dotNet.uwpCompiler( "\ocr.dll" )
//启用编译优化
uwpCompiler.Parameters.CompilerOptions = "/optimize"
//设置待编译C#源码
uwpCompiler.Source = /******
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Runtime.InteropServices;
using Windows.Media.Ocr;
namespace aardio
{
public class UwpOcrResult
{
public UwpOcrResult(OcrResult ocrRet)
{
ocrResult = ocrRet;
}
public int LineCount()
{
return ocrResult.Lines.Count;
}
public string [] GetWords(int index)
{
ArrayList arr = new ArrayList();
foreach (var word in ocrResult.Lines[index].Words)
{
arr.Add(word.Text);
}
return (string[])arr.ToArray(typeof(string));
}
public object GetWordRects(int index)
{
ArrayList arr = new ArrayList();
foreach (var word in ocrResult.Lines[index].Words)
{
double[] rc = { word.BoundingRect.Left, word.BoundingRect.Top, word.BoundingRect.Right, word.BoundingRect.Bottom };
arr.Add(rc);
}
return (object)arr.ToArray(typeof(object));
}
private OcrResult ocrResult;
}
public class UwpOcrEngine
{
public string [] AvailableRecognizerLanguages(){
ArrayList arr = new ArrayList();
foreach (var lang in OcrEngine.AvailableRecognizerLanguages)
{
arr.Add(lang.LanguageTag);
}
return (string [])arr.ToArray(typeof( string));
}
public object IsLanguageSupported( string name ){
Windows.Globalization.Language lang = new Windows.Globalization.Language(name);
return OcrEngine.IsLanguageSupported(lang);
}
public UwpOcrResult Recognize(byte[] imgBuffer, string language){
return new UwpOcrResult( RecognizeAsync(imgBuffer, language).GetAwaiter().GetResult() );
}
async Task<OcrResult> RecognizeAsync(byte[] imgBuffer, string language)
{
var randomAccessStream = new InMemoryRandomAccessStream();
var outputStream = randomAccessStream.GetOutputStreamAt(0);
var dw = new DataWriter(outputStream);
var task = new Task(() => dw.WriteBytes(imgBuffer));
task.Start();
await task;
await dw.StoreAsync();
await outputStream.FlushAsync();
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(randomAccessStream);
SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
Windows.Globalization.Language lang = new Windows.Globalization.Language(language);
OcrEngine engine = OcrEngine.TryCreateFromLanguage(lang);
if (engine != null)
{
OcrResult ocrResult = await engine.RecognizeAsync(softwareBitmap);
return ocrResult;
}
return null;
}
}
}
******/
//编译并返回程序集
var assembly = uwpCompiler.CompileOrFail();
import console;
if(assembly) console.logPause("编译成功",uwpCompiler.Parameters.OutputAssembly);dotNet.ocr 支持库的体积很小,可以生成独立 EXE 文件,调用代码也非常简洁。下面是调用 示例:
更多
aardio 提供的 .NET 范例非常多,更多功能请参考 aardio 自带范例,如果大家有任何问题可以下面评论中留言,我会尽快解答。
相关推荐
- 三星固态驱动官网(三星固态官方软件)
-
三星手机序列号查询官网是http://www.samsung110.com/。手机序列号(S/N号)查询方法:设置-关于手机-状态-序列号(序号)。或通过以下方式查询:通过机器包装盒上的标贴查询用...
- 雨林木风u盘装机教程(雨林木风u盘装系统,步骤)
-
电脑系统安装步骤:1、用【u启动u盘启动盘制作工具】制作u启动盘,重启电脑等待出现开机画面按下启动快捷键,选择u盘启动进入到u启动主菜单,选取“【02】Win8PE装机维护版(新机器)”选项2、进...
- 无法连接到这个网络是怎么回事
-
有可能是网络本身有问题,需要联系运营商解决。也有可能是因为网卡驱动问题,首先鼠标右击开始按钮,然后点击设备管理器,双击网络适配器,最后查看网卡驱动有没有出现黄色的感叹号,如果有的话,右击选择更新驱动程...
- 刷机精灵怎么解除锁屏密码(刷机精灵怎么解除锁屏密码设置)
-
刷机精灵解锁手机锁屏密码方法:下载好刷机精灵。打开链接手机,之后在刷机精灵页面里能看到“实用工具”的选项。解除手机解锁图案要获取root权限,若没有获取的可以在这里点击获取root权限的选项。获取了...
- 联想云服务官网(联想云服务官网查找手机)
-
华为手机也是可以下载云服务软件安装然后使用联想账号登陆云服务的。部分云服务功能将无法使用。登录联想云服务方法:点开云服务软件,选择立即使用,即出现:手机号码登入,邮箱登入,第三方登入;手机号码登入,邮...
- 宏基笔记本系统重装快捷键(宏基笔记本重装系统步骤)
-
如果用系统u盘、光盘安装:1、需要在Bios中设置从u盘或光盘启动。2、启动电脑,dcer一般默认按Del键(有些型号F2、F12)进入Bios设置界面。F2键。宏碁笔记本重装系统按F2键,进入BIO...
- windows10官网打不开(win10系统官网打不开)
-
你可以通过以下步骤在Windows10官网上更新操作系统:1.打开windows官网,进入“下载和工具”页面。2.单击“立即下载工具”按钮,将下载“Windows10更新助手”。3.运行“...
- win7无线网卡插上没反应(win7无线网卡插上没反应怎么回事)
-
1、如果是路由器的问题,如果原来可以用,暂时不能用了,在有就是恢复出厂设置,从新设置就可以用了(这是在物理连接正确的前提下)。2、如果是宽带本身的问题,首先直接联接宽带网线测试,如果是宽带的问题,联系...
- 下载爱奇艺安装(下载爱奇艺安装包)
-
如果你的电脑无法安装爱奇艺,可能有以下原因,第一种原因可能是你的电脑系统版本太低,升级你的电脑操作系统,可以促进爱奇艺的下载,第二种情况是你下载的爱奇艺可能捆绑一些病毒软件,系统的杀毒软件识别有霸王软...
- 5000元左右的电脑配置单(5000左右的电脑配置推荐2021)
-
五千元至六千元价位电脑主机,如果组装机,可以配置配置很高的档次,电脑主机主板可以配置不低于十二代产品,可以设四个内存条插槽,相应的内存可以配置128GB内存条2至四根,电脑处理器也同样不低于十二代产品...
-
- 快速关机(快速关机按什么键)
-
1、我们直接长按手机右侧的电源键,大概5秒的时间,这时候手机页面会直接显示是否关机,选择关机就可以直接关机了。2、找到手机一侧的音量“+”键,再找到电源按键,之后只需同时按住音量“+”键和电源按钮,直到手机屏幕关闭即可强制关机。3、点击【设...
-
2025-12-25 08:05 liuian
- 云电脑免登录破解版(“云电脑破解版”)
-
虎牙YOWA云游戏平台便是一款完全免费的产品,只要玩家在自己的账号上购买过相关的产品即可通过云游戏平台直接登陆。但云游戏平台终归只是改变玩家的游戏方式,用户最终还是要回归于游戏中,如果难以保证游戏体验...
- 联想家庭版win7(联想家庭版笔记本电脑)
-
1、开机到欢迎界面时,按Ctrl+Alt+Delete,跳出帐号窗口,输入用户名:administrator,回车。2、如果这个帐号也有密码采用开机启动时按F8选“带命令行的安全模式”。3、选“Ad...
- 两台电脑怎么传文件最快(两台电脑怎么传文件比较快)
-
两台电脑之间传递文件可以有很多种方法。如果两台电脑同时在1栋楼或者一间办公室内,可以用U盘拷贝的方法传递文件。另外最快的方法还可以用通过邮箱、微信、QQ传送文件,那样速度更快,节省时间,又节省距离。将...
- win7计算机图标怎么弄出来(win7怎么设置计算机图标)
-
您好,如果您的Win7桌面图标不见了,可以尝试以下方法:1.右键点击桌面的空白处,点击查看之后点击显示桌面图标。2.如果第一种方法不起作用,可以使用组合键“ctrl键+alt键+delete键”,...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
