caimouse
在IT行业有20多年的经验。拥有20多年的C和C++开发经验,5年以上Python开发经验,资深数据库开发、上百G数据库优化经验。曾经任职嵌入式工程师、P2P开发工程师、银行信用卡交易系统工程师、全自动化电池测试部门经理。
展开
-
C#里使用libxl的数字格式
还有科学计算表示的数字使用小数点位数与普通货币也不一样。比如表示货币数字时,与表示普通序号的数字就不一样。由于EXCEL里可以表示不同的数字格式,原创 2025-03-22 20:11:32 · 198 阅读 · 0 评论 -
C#里使用libxl来插入行或列
在这里使用函数InsertRow和InsertCol函数。原创 2025-03-22 19:32:32 · 223 阅读 · 0 评论 -
C#里使用libxl来对列或行进行分组显示
这时候需要使用函数GroupCols和GroupRows来对这些列或行进行分组。需要把某些行进行隐藏起来,那么就需要使用到行或列进行隐藏的操作。分组不能出现交叉的情况,否则会抛出异常。有时候由于EXCEL里的行数很多,原创 2025-03-22 19:23:11 · 320 阅读 · 0 评论 -
C#里使用libxl来合并单元格的例子
通过不同的单元格的合并,可以生成不同的表格。操作EXCEL的文件格式是常用的功能,原创 2025-03-21 23:57:08 · 145 阅读 · 0 评论 -
C#代码经过编译生成CIL代码
这张图体现了 .NET 平台的核心特性:通过 CIL 实现不同语言的统一编译输出,再借助 JIT 编译器在运行时将 CIL 转为机器码执行,最终达成跨语言开发和跨平台运行的能力。////解释:在上述微软中间语言(MSIL)中,存在长度为一或两个字节的操作码。所有其他类所继承的基类声明包含在(微软核心库动态链接库)中。在Main()方法里,ldstr指令将字符串 “GeeksforGeeks” 加载到栈上。接着调用静态的函数,字符串从栈中弹出。最后,ret指令标志着函数调用结束。原创 2025-03-19 11:04:06 · 35 阅读 · 0 评论 -
C# 模块里cctor函数: mono_runtime_run_module_cctor
静态构造函数用于初始化类的静态成员,它在类的任何静态成员被引用之前或创建类的第一个实例之前自动执行,并且每个类只能有一个静态构造函数。静态构造函数没有访问修饰符,也没有参数。静态构造函数不能直接调用,而是由.NET 运行时自动调用。静态构造函数在类被加载时执行一次,且仅执行一次。原创 2025-03-15 11:04:06 · 149 阅读 · 0 评论 -
C#里最快添加log4net的日志输出
在C#里添加日志支持最快的方法,就是使用log4net。因为它是软件发布之后时行跟踪,调试和改进的关键数据。如果没有日志的软件,必然是一个不成熟的软件。因为它没有办法进行软件的质量改进。日志输出是每一个软件必须的功能,这样就可以有运行的库了。原创 2025-03-07 15:34:26 · 169 阅读 · 0 评论 -
C#里定义对象序列化保存的例子
C#里定义对象序列化保存的例子由于很多软件有自定义参数,不同的时间使用不同的参数,不同的用户使用不同的参数,这些参数都需要保存到磁盘,以便软件关闭之后再加载出来。面对这样的需求,就需要把软件里参数进行序列化保存,当软件启动时,再把这些参数恢复过来。下面的例子就是使用SerializationInfo 来实现: 代码解释:定义可序列化类:序列化过程:反序列化过程:输出结果:通过这个示例,你可以看到如何使用 类来实现自定义类的序列化和反序列化。原创 2025-03-03 09:34:25 · 165 阅读 · 0 评论 -
Mono里运行C#脚本44—System.Console.WriteLine()函数的生成过程
类构造函数(静态构造函数)主要用于初始化类的静态成员,它会在类的任何静态成员被引用或者类的实例被创建之前自动执行,并且每个类的静态构造函数只会执行一次。它是从函数mono_runtime_class_init_full开始,它是对整个类的运行时进行初始化,以便这个类可以运行其中的函数。前面已经分析WriteLine() 的调用过程,接着就会找到对应的类Console,然后再找到此类的信息,生成一个类的。要想扫行WriteLine函数,就需要先初始化类的运行环境。获取.cctor的方法名称。原创 2025-03-02 12:51:53 · 200 阅读 · 0 评论 -
C#里创建异步管道服务器通讯
就是当连接过来的管道没有关闭时,服务端也不能退出程序,它会一直等到客户端关闭为止。在C#里使用同步的管道服务,有一个问题很难处理,因此,使用异步的方法就可以避免这个问题。也没有别的办法去停止这个等待的过程。原创 2025-02-28 14:26:15 · 304 阅读 · 0 评论 -
C#里使用MSMQ来实现跨进程通讯
消息数量统计:使用 queue.GetMessageEnumerator2() 方法获取队列中消息的枚举器,然后通过 foreach 循环遍历枚举器,每遍历到一条消息,messageCount 计数器就加 1。队列路径定义:queuePath 变量指定了要查看的消息队列的路径,这里使用的是本地私有队列的路径格式。性能影响:遍历队列中的所有消息来统计数量可能会对性能产生一定影响,尤其是在队列中消息数量较多的情况下。一是可以提高并发开发的速度,二是减少一个软件的复杂度。结果输出:最后输出队列中的消息数量。原创 2025-02-27 14:11:04 · 206 阅读 · 0 评论 -
C#里计算坐标轴的刻度步长的方法
ZedGraph 可以根据曲线中数据值的范围自动选择合适的坐标轴刻度最小值、最大值和步长值。或者,你也可以手动设置其中任意一个或所有这些值,之后刻度选择逻辑会尝试为那些仍处于自动模式的剩余参数选取合适的值。这个数量级会被转换为最高有效数字,然后将其调整为 1、2 或 5,以使刻度成为 10 的整数约数。刻度选择逻辑基于这样一个假设:最符合人类认知习惯的步长应该是 10 的整数约数。也就是说,步长应该是 1、2 或 5 乘以 10 的某个幂次方。通过上面的计算,就可以计算出来以1、2、5为刻度的坐标轴刻度。原创 2025-02-25 13:32:46 · 159 阅读 · 0 评论 -
C#里使用消息进行进程间通讯
在窗口应用程序之间进行进程通讯,最为方便的方法就是使用WM_COPYDATA消息。当软件开发越来越多功能时,就会导致每个软件代码急剧上升,从而把系统变得越来越复杂。把程序功能分开之后,这样就可以把程序的代码控制在合适的范围内。但是程序与程序之间,还需要协同工作,那么就需要相互进行通讯。这时,我们应对的办法,就是把程序功能拆分。提高了开发人员的效率,减少了系统的复杂度。下面就是使用消息通讯的例子,原创 2025-02-24 09:47:10 · 199 阅读 · 0 评论 -
Mono里运行C#脚本43—System.Console.WriteLine()函数的生成过程
上面这段代码只是给出一个调用方法void class [mscorlib]System.Console::WriteLine(string)的JIT过程,并没有给方法WriteLine(string)生成代码,这个需要后面将要运行之后,才会找到相应的代码进行编译,才会生成X86-64的代码。要找到这个代码,就需要从文件mscorlib里查找,分别找到命名空间System,类名Console,然后再找到方法WriteLine(string)。在这里调用的代码是库里代码,与前面内部嵌入的函数会不一样。原创 2025-02-22 12:34:12 · 126 阅读 · 0 评论 -
Mono里运行C#脚本42—mono_arch_create_generic_trampoline函数的生成的汇编代码
下面的代码要与mono_magic_trampoline函数的代码一行一行地对比地查看。才能知道跳板代码怎么样调用mono_magic_trampoline函数。为了更详细地分析跳板代码的运行机制,所以需要查看这个跳板生成的代码,原创 2025-02-09 22:09:44 · 180 阅读 · 0 评论 -
Mono里运行C#脚本41—编译MonoEmbed::gimme()调用的过程
也就是说在Main()函数进行JIT阶段,就会对函数 MonoEmbed::gimme()进行编译,但不会在此阶段生成它的JIT代码。所以函数mono_lookup_internal_call会找此函数的地址,这样就可以调用函数进行运行了。当Main函数进行JIT时遇到这行代码,就会在函数mono_method_to_ir里面处理。所以在编译这个函数时,就会对函数 MonoEmbed::gimme()进行编译。当运行到这行代码时,就会查找调用方法的属性,也就是方法的唯一签名。原创 2025-02-06 11:34:53 · 203 阅读 · 0 评论 -
Mono里运行C#脚本40—mono_magic_trampoline函数的参数设置
由于 REX 前缀占 1 字节,操作码占 1 字节,而 mono_atomic_xchg_ptr 函数要操作的是这个 64 位立即数(8 字节)的存储位置,所以从 orig_code 往前偏移 1 + 1 + 8 = 10 字节是 64 位立即数的起始位置,但由于地址计算是从当前位置开始偏移,所以实际偏移量为 11 字节(考虑到指针运算的起始点),即 orig_code - 11 指向的就是需要修改的 64 位立即数的起始地址。因此也会把这些参数传送给函数common_call_trampoline,原创 2025-02-04 12:55:40 · 185 阅读 · 0 评论 -
Mono里运行C#脚本39—mono_jit_runtime_invoke函数
当脚本MonoEmbed里的Main ()函数JIT编译完成之后,那么就需要在C代码里运行受托管的代码,即是C#的代码。要运行托管的代码,这是需要初始化一个运行环境,以便把参数从C代码传送给托管代码,又需要从托管代码返回值传送回到C代码。create_runtime_invoke_info函数会根据传入参数和返回值参数类型来创建此函数名称。在这里是通过函数mono_jit_runtime_invoke来实现这个过程的。这样就创建一个从C调用托管代码的函数返回。这样就可以进入运行Main函数的过程。原创 2025-02-01 12:44:55 · 261 阅读 · 0 评论 -
Mono里运行C#脚本38—MonoEmbed入口点函数Main的三个阶段的代码对比
可见后面的机器码最长,也最为复杂,却是机器可以执行的代码。这段代码是即将可以运行的代码。原创 2025-01-30 17:24:26 · 83 阅读 · 0 评论 -
Mono里运行C#脚本37—mono_compile_create_vars函数
它根据给定的类型和操作码,分配适当的寄存器,并调用 mono_compile_create_var_for_vreg 函数来完成变量的创建。在上面的代码里,先调用mono_method_signature_internal函数,在此函数里会使用一个签名的结构MonoMethodSignature。在这里调用函数mono_compile_create_var,它的指令操作码是OP_ARG,表明生成一条返回值的参数指令。然后根据返回值的类型来创建控制流图的返回值语句cfg->ret。保存在cfg->ret里。原创 2025-01-30 17:13:34 · 458 阅读 · 0 评论 -
Mono里运行C#脚本36—加载C#类定义的成员变量和方法的数量
还有一种通用类,也就是泛型类型,例如,.NET 类型 System.Collections.Generic.List<T> 具有一个类型参数,它按照惯例被命名为 T。成员klass是保存类相关的信息,first_method_idx是保存方法开始位置,method_count是保存方法的数量,first_field_idx是保存成员变量的开始位置, field_count是保存成员变量的数量。因为我们知道C#语言定义一个类,主要就是定义成员变量,以及那些对此成员变量进行操作的方法,原创 2025-01-25 17:31:56 · 381 阅读 · 0 评论 -
Mono里运行C#脚本35—加载C#语言基类的过程
先拿到一堆属性,并且是一个类型定义,它的名称是MonoEmbed,并且获取扩展的属性,以及继承的基类名称。但是还有很多功能没有解析的,就是C#语言相关最多的,就是类的加载,以及类语言设计的实现属性,然后到方法的编译,机器代码的生成,再到函数调用的跳板转换,进而解析递归地实现JIT。前面大体地分析了整个Mono运行过程,主要从文件的加载,再到EXE文件的入口点,因此这个类会有特殊的处理,这个类的定义是由入口点函数来带动类定义的过程的。比如类的继承,类的方法的多态,类的虚拟方法,还有类的成员变量等等。原创 2025-01-25 16:10:27 · 515 阅读 · 0 评论 -
Mono里运行C#脚本34—内部函数调用的过程
它会依据方法的属性和类型,采取不同的编译策略,最终返回编译后的代码指针。这样就可以把调用内部函数的查找任务落实到mono_lookup_internal_call_full_with_flags函数上了。在这里看到查看HASH表icall_hash了,说明前面注册的函数名称,通过这里就可以找到相关的函数。由于调用的是内部函数,不会生成代码,所以就需要调用特别的处理函数compile_special。这个函数提供了对内部注册过的函数进行查找,其实就是对HASH表icall_hash进行检索。原创 2025-01-24 09:54:06 · 211 阅读 · 0 评论 -
Mono里运行C#脚本33—跳板代码处理调用过程
common_call_trampoline 是一个静态函数,其作用在于处理普通、虚方法以及接口方法的调用与跳转,这些调用和跳转既可能源于即时编译(JIT)代码,也可能来自 LLVM 编译后的代码。当运行前面函数mono_arch_create_generic_trampoline实现的手写汇编代码时,host_mgreg_t *regs:指向主机寄存器状态的指针,用于传递调用时的寄存器信息。周而复此,就可以把整个调用链进行编译并运行,没有调用到的函数是没有进行编译的过程的。原创 2025-01-23 14:22:47 · 228 阅读 · 0 评论 -
Mono里运行C#脚本32—IL代码中调用指令call的处理过程
若mono_aot_only为真,调用mono_aot_create_specific_trampoline;上面代码接着会调用mono_create_jit_trampoline (domain, patch_info->data.method, error)来创建跳板的代码。mono_create_jit_trampoline函数与前面的跳板初始化函数关联在一起,就可以实现跳板进行调用过程处理。mono_arch_create_specific_trampoline函数是根据不同的CPU架构来实现的,原创 2025-01-23 11:06:45 · 78 阅读 · 0 评论 -
Mono里运行C#脚本31—mono_arch_create_generic_trampoline
mono_arch_create_generic_trampoline` 函数的主要功能是创建一个通用的跳板(trampoline)代码。此函数根据传入的跳板类型 `tramp_type`、是否为 AOT(Ahead Of Time)编译模式 `aot` 等参数,生成相应的汇编代码,并返回生成的代码缓冲区指针。- 使用 `amd64_pop_reg`、`amd64_push_reg`、`amd64_mov_reg_reg` 等函数生成汇编代码,完成栈帧的设置、寄存器的保存和恢复等操作。原创 2025-01-19 11:48:02 · 354 阅读 · 0 评论 -
Mono里运行C#脚本30—create_trampoline_code
比如先调用JIT生成Main函数的代码,这时就会发现里面调用其它的函数,这时又需要调用JIT来对后面发现的函数进行JIT编译。// 调用 mono_arch_create_generic_trampoline 函数来创建通用跳板代码。因此就需要使用mono_arch_create_generic_trampoline来初始化通用的跳板代码,// 传入 tramp_type 作为跳板类型,info 的地址用于存储生成的跳板信息。所谓的跳板代码,就是从JIT编译出来的代码对下一个函数的调用中转站。原创 2025-01-19 11:26:24 · 136 阅读 · 0 评论 -
Mono里运行C#脚本29—mono_trampolines_init
这里调用 `create_trampoline_code` 函数为不同类型的 `trampoline` 创建代码,并将生成的代码存储在 `mono_trampoline_code` 数组中,不同的 `MONO_TRAMPOLINE_*` 是一些预定义的枚举或宏,代表不同类型的 `trampoline`。- `create_trampoline_code`:这是一个自定义的函数,它接收一个表示 `trampoline` 类型的参数,并生成相应的 `trampoline` 代码。原创 2025-01-19 10:40:09 · 286 阅读 · 0 评论 -
Mono里运行C#脚本28—mono_create_jit_trampoline
它的主要作用是为指定的 C 函数创建调用代码,接收一个描述 C 函数的参数和返回类型的monomethod,并创建调用该函数的代码。- 它们调用 `mono_class_init_trampoline()` C 函数,该函数执行作为跳板参数传递的类的类初始化器,然后将调用类初始化跳板的代码替换为 `NOP`(无操作)指令,以便不再执行该代码。- 当跳板被调用时,它会调用 `mono_magic_trampoline()` 函数,该函数会编译目标方法,并将已编译代码的地址返回给跳板,然后跳板会跳转到该地址。原创 2025-01-19 10:20:18 · 156 阅读 · 0 评论 -
C#里await Task.Run死锁的分析与解决
需要再次使用int bytesLoaded = await downloading,也就是调用await获取结果再次异步,在这段代码里,GetResAsync()函数是使用await Task.Run来等线程任务执行返回一个字符串结果。第二种是返回结果不要直接等结果,采用再一次异步等结果,即是await res.Result。执行res.Result时,需要等线程执行结果,它采用一个wait函数等线程完成。因为在前面await Task.Run已经在等线程去执行,这时就会造成死锁。这样返回字节的结果。原创 2025-01-17 15:13:08 · 302 阅读 · 0 评论 -
Mono里运行C#脚本27—X86_64指令寄存器初步了解mono_arch_regname
Mono里运行C#脚本27—X86_64指令寄存器初步了解mono_arch_regname通过前面的分析,我们知道一个程序要运行,要么解释执行,要么编译后执行。JIT是采用即时编译的技术,显然是把程序转换为机器码再运行的。要实现从C#代码到机器码的转换,就需要了解X86_64的指令格式。当然,如果你想转换C#程序到鸿蒙系统上运行,那么就需要了解ARM64的指令格式。如果想转换到龙芯系统上运行,就需要了解龙芯架构指令。因此,JIT是基于不同的CPU架构指令来实现的。原创 2025-01-14 11:03:19 · 377 阅读 · 0 评论 -
C#里使用libxl里演示输出日期和读取日期数据的例子
在许多西方国家,日期的表示顺序遵循“日-月-年”的规则,即“Day-Month-Year”,例如:12th January 2023。而在某些亚洲国家,如日本和韩国,日期则遵循“月-日-年”的顺序,即“Month-Day-Year”,例如“1月12日,2023年”。总结来说,马来西亚的日期格式遵循“日-月-年”的顺序,其中“日”表示日期,“Month”表示月份,“Year”表示年份。所以EXCEL里处理的日期格式和显示,需要获取不同时区,再根据时区,或者操作系统的格式来判断输出EXCEL的日期格式。原创 2025-01-11 00:09:21 · 206 阅读 · 0 评论 -
C#里使用libxl设置EXCEL里公式计算的例子
因为公式是一种自动化计算工具,并且可以固化人类的智慧,相当于把复杂的计算功能嵌入到固定的数据处理了。比如一个经验丰富的财务人员,可以编制一个复杂公式计算的表格,只要一个不懂财务的人员,输入每个人的工资,就可以自动计算出来五险一金来了。• 在写入公式时,注意公式字符串中的分隔符应与 Excel 的区域设置一致。因此在很多情况下,需要开发人员把公式固化在EXCEL文件中,这样导出的数据处理,就会自动处理统计的数据。所以EXCEL中的公式是一种非常实用的工具,是一种把复杂计算变成简单处理的工具。原创 2025-01-10 23:33:51 · 629 阅读 · 0 评论 -
C#里使用libxl读取EXCEL文件里的图片并保存出来
在这里通过GetPicture方法,就可以获取图片数据和图片数据大小,这样可以在内存里处理,也可以把这些数据再次保存成文件,就可以打开了。如果需要把这些图片抽取出来,再保存到系统里,就需要读取这些图片数据,生成合适的文件再保存。因为很多用户喜欢使用图片保存在EXCEL里,比如用户保存一些现场整改的图片。有时候需要读取EXCEL里的图片文件,这样就可以生成一个图片文件保存出来了。在libxl里也提供了这样的方法,原创 2025-01-10 22:38:57 · 516 阅读 · 0 评论 -
C#里使用libxl读取EXCEL文件的例子
因为所有数据都是保存在EXCEL文件里,这时我们能够打开EXCEL文件,并读取。这样就可以把里面的数据取出来了,再通过SQL语句就可以把数据进入到数据库。现在我们有一个任务,就是把里面的数据全部读取出来,然后插入到进销存系统。当你面对这样的需求时,就需要去读取EXCEL文件了。需要使用读取EXCEL文件的API。原创 2025-01-10 22:00:59 · 469 阅读 · 0 评论 -
C#调用MyLibxl来生成EXCEL的订货清单
• 特别地,日期格式使用了自定义的日期格式字符串,电话格式使用了自定义的电话号码格式字符串,美元格式使用了自定义的货币格式字符串。这段代码的主要功能是生成一个包含销售收据信息的 Excel 文件,格式化良好,包含公司信息、客户信息、商品列表和总计等部分。• 设置列宽和行高,写入标题、公司信息、日期、收据编号、客户信息等。原创 2025-01-10 10:59:56 · 460 阅读 · 0 评论 -
C#里导出数据为EXCEL文件
主要封装一个方便C#使用的库来导出EXCEL文件,它的最大优势就是不需要安装OFFICE,就可以导出数据,就可以读取数据。因为购买一套OFFICE的成本太高了,不可能每一台电脑都购买一套。并且这台电脑只是导出数据,并不进行查看EXCEL文件。这样使用libxl库就完全避免了上面的问题。在这里是使用libxl库来导出数据,引用文件MyLibxl.dll文件。原创 2025-01-09 16:10:06 · 170 阅读 · 0 评论 -
Mono里运行C#脚本26—CEE_ADD/MONO_CEE_ADD/OP_IADD/X86_ADD的转换过程
接着会经过mono_method_to_ir函数处理,就会把MONO_CEE_ADD转换为更进一步细分的指令码OP_IADD。跟着后面就是mono_codegen处理已经转换为OP_IADD指令码,这时就会调用不同的CPU指令来生成执行代码。Mono里运行C#脚本26—CEE_ADD/MONO_CEE_ADD/OP_IADD/X86_ADD的转换过程。其它指令也是通过上面的过程处理,就可以生成整个函数的代码,后面就可以调用函数的方式来执行此代码了。首先C#代码被编译后,会产生CIL指令的代码,原创 2025-01-08 18:59:34 · 216 阅读 · 0 评论 -
C#里对已经存在的文件进行压缩生成ZIP文件
因为.net 后面实现都是使用ziplib.dll来压缩了,这样就是使用C语言编译的库来实现压缩的,所以速度飞快,比使用C#实现的库要快很多。这样就有需要压缩的文件列表List,包含所有要压缩的文件名称。这段代码,就遍历所有文件列表,然后一个一个文件保存在创建的压缩包里。后面就可以把这些文件打包到一个ZIP文件。这段代码层次比较多,但是比较实现。原创 2025-01-07 17:44:45 · 367 阅读 · 0 评论 -
Mono里运行C#脚本25—mono_codegen
也就是说它是假的代码,不是现实世界存在的机器的代码,因此不能直接执行,必须经过后端编译器的再次编译才能真正运行它。这行代码对基本块里所有的代码进行拆解,因为虚拟机的指令代码还是比较高级,在机器指令里可能表示不了,需要把一条指令分解成两条或两条以上的指令表示。前面会把CIL代码都进行拆分,拆分的原则就是把顺序执行的指令全部放到一个基本块里,把分支跳转指令放到一个基本块,保证每个基本块只有最后一条指令是离开基本块的。这段代码里,每一个调用的函数都是比较复杂的处理,特别是寄存器分配算法,就会比较复杂。原创 2025-01-05 22:06:06 · 299 阅读 · 0 评论