C#导出本机Win32native dll
C# 使用 "3f/DllExport" 工具导出C风格的本机函数
[文 / 张赐荣]
首先,让我们来了解一下什么是争渡读屏软件,以及什么是争渡文本预处理API。争渡读屏软件是一款屏幕朗读软件,用于协助视力障碍人士操作电脑。
争渡文本预处理API是一种让插件可以修改读屏朗读的文本的接口,它是一个32位的动态链接库(DLL),文件名为ZDTextPreprocess.dll,放在"争渡读屏安装目录\addins"下。读屏会优先加载这个DLL文件,然后调用其中的两个函数:Init和TextPreprocess。
"Init"函数是用来初始化插件的,它没有参数,只返回一个整数值,表示当前插件的版本号。这个版本号应该和文档中的版本号一致,目前是1。
"TextPreprocess"函数是用来对文本进行预处理的,它有两个参数:oldString和newString。oldString是一个宽字符指针,指向读屏即将朗读的原始文本。newString也是一个宽字符指针,指向插件修改后的新文本,它的缓冲区大小为40960字节,如果超出了这个大小,需要截断多余的部分。这个函数返回一个整数值,表示新文本的长度。如果发生了错误,或者没有替换文本,就返回0。
插件开发者可以根据自己的需求,编写TextPreprocess函数的逻辑,实现对读屏朗读文本的定制化修改。例如,可以替换一些特殊符号,或者添加一些注释等。
接下来,让我们来看看如何用C#来编写争渡文本预处理插件。我们需要使用一个叫"3f/DllExport"的工具,它可以让.NET程序集导出本机风格的函数。这样我们就可以用C#来编写类似于C语言生成的DLL一样的插件了。
"3f/DllExport"是一个开源项目,在GitHub上可以找到它的源代码和文档。它是在"Unmanaged Exports"基础上发展而来的。"Unmanaged Exports"是一个早期的尝试,它也可以让.NET程序集导出函数,但是它有很多问题,而且已经很久没有更新了。
"3f/DllExport"的工作原理是修改了.NET生成的DLL,在其中插入了本机DLL的头以及导出函数表、重定位函数表信息等。这样,它就像一个普通的本机DLL一样,可以被任何支持调用本机DLL的语言或平台使用。当然,它本质上并不是真正的本机DLL,它的执行代码依然是IL(中间语言)而不是本机二进制代码,所以本身还是要依赖.NET运行时。只是因为现在有了导出函数表,并且加入了类似于普通DLL的头信息,看起来就与普通C语言导出的DLL相似。
这与最新的.NET 7 Native AOT不一样,那个是真正地编译为了非托管本机代码,可以脱离.NET运行时。但是.NET 7 Native AOT不支持X86(32位),而易语言只支持X86的DLL。所以对于我们来说,并没有什么用处。
我不知道为什么微软官方没有去做这样的支持,反而还需要第三方工具来修改。
不过,既然有了这样一个好用的工具,就不用再抱怨了。我们可以利用它来实现我们想要的功能,而不用再去学习和使用C++或其他语言。可以用C#的强大功能和丰富的库,来编写更多的文本预处理效果。
那么"3f/DllExport"怎么使用呢?下面我就以编写争渡文本预处理插件为例,给大家做一个简单的教程。
首先,我们需要下载"3f/DllExport"这个工具。我们可以前往它的GitHub页面,找到它的最新版本,目前是"v1.7.4 (Latest on Jan 3, 2021)",文件名应该是"offline.DllExport.1.7.4.29858.c1cc52f.zip"。我们下载并解压这个文件,可以看到里面有很多文件和文件夹。
接着,我们需要打开Visual Studio 2019,新建一个.NET Framework类库项目,名字叫"TextPreprocessAddon"。然后关闭Visual Studio,将刚才解压的文件全部复制到我们的解决方案文件夹下(也就是与*.sln同目录的文件夹)。然后运行目录中的"DllExport.bat"进行配置。打开后有很多配置和选项,我们暂时不用管,勾选"Installed"复选框,然后点击"Apply"应用设置。它会修改我们的项目配置文件,让它支持导出函数。
配置DllExport就基本完成了,接下去就是编写代码了。导出本机函数的方法是在代码中,使用[DllExport]特性来标记静态方法。"DllExport"特性支持自定义导出函数的名字、调用约定等。与["DllImport"]特性有些类似。注意,如果不指定调用约定,默认导出的是"__cdecl",由于Windows API和易语言只支持stdcall方式导出,所以调用约定我们选择"WINAPI"。DllExport也支持"MarshalAs"特性,可以用这个特性告诉.NET如何将.NET托管类型转换为本机非托管类型。
C#代码:
----------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace TextPreprocessAddon
{
public static class TextPreprocessAddon
{
[DllExport("Init",CallingConvention.Winapi)]
public static int Init ()
{
return (1);
}
[DllExport("TextPreprocess",CallingConvention.Winapi)]
public static int TextPreprocess (IntPtr oldTxtPtr,IntPtr newTxtPtr)
{
try
{
string oldText = Marshal.PtrToStringUni(oldTxtPtr);
string newText = oldText.Replace("女孩","男孩");
byte[] tempBytes = Encoding.Unicode.GetBytes(newText+"\0");
Marshal.Copy(tempBytes,0,newTxtPtr,tempBytes.Length);
return (newText.Length);
}
catch (Exception ex)
{
return (0);
}
}
}
}
----------
上面的代码是用C#编写的争渡文本预处理插件,它可以将原始文本中的"女孩"替换成"男孩"。导出函数的实现过程如下:
使用[DllExport]特性来标记静态方法,指定导出函数的名称和调用约定。
使用Marshal类中的方法来转换指针和字符串类型,以及复制字节数组到内存空间。
使用字符串类型中的方法来替换文本中的内容,以及添加空字符。
返回新文本的长度,或者在发生错误时返回0。
然后编译这个项目,可以在"release"文件夹中找到X86的文件夹,进去就有导出的DLL了。
这样,我们就用C#编写了一个争渡文本预处理插件,它可以将原始文本中的"女孩"替换成"男孩",并将修改后的文本送给读屏朗读。我们可以将这个DLL文件放在争渡读屏安装目录\addins\下,然后启动争渡读屏软件,选择我们的插件,就可以听到效果了。
使用Python调用上面导出的DLL,实现Python调用C#代码。
import ctypes
# 加载 dll(完整路径)
my_lib = ctypes.WinDLL('d:\TextPreprocessAddon.dll')
# 定义函数原型
my_lib.Init.restype = ctypes.c_int
# 调用函数
int_result = my_lib.Init()
print("Result:", int_result)
# 调用 TextPreprocess 函数,并获取返回值
old_string = "这个小女孩长得很漂亮。"
new_string = ctypes.create_unicode_buffer(65536) # 创建一个 Unicode 缓冲区
result = my_lib.TextPreprocess(ctypes.c_wchar_p(old_string), new_string)
if result > 0:
print("New String:", new_string.value)
else:
print("Old String:", old_string)
input()
首先,我们需要导入ctypes模块,它是Python的一个标准库,可以让我们调用本机DLL中的函数。
然后,使用ctypes.WinDLL函数,加载我们的DLL文件,注意要指定完整的路径。这个函数会返回一个对象,我们把它赋值给my_lib变量,以便后面使用。
接着,使用my_lib.Init.restype属性,定义Init函数的返回类型为整数类型。这样我们就可以正确地接收Init函数的返回值了。
然后,直接调用my_lib.Init()函数,它会返回一个整数值,表示当前插件的版本号。我们把它赋值给int_result变量,并打印出来。
接着,我们准备调用TextPreprocess函数,它有两个参数:oldTxtPtr和newTxtPtr。oldTxtPtr是一个指向读屏即将朗读的原始文本的指针,newTxtPtr是一个指向插件修改后的新文本的指针。
为了传递这两个参数,我们需要做一些转换。首先,我们定义一个字符串变量old_string,赋值为"这个小女孩长得很漂亮。"。这是我们想要修改的原始文本。
然后,使用ctypes.c_wchar_p函数,将old_string转换为一个宽字符指针类型的对象。这样就可以作为oldTxtPtr参数传递给TextPreprocess函数了。
接着,我们使用ctypes.create_unicode_buffer函数,创建一个Unicode缓冲区类型的对象。这个函数需要一个参数,表示缓冲区的大小。我们给它传递65536,表示缓冲区可以存储65536个字节。这个缓冲区就可以作为newTxtPtr参数传递给TextPreprocess函数了。
然后,调用my_lib.TextPreprocess函数,并传递oldTxtPtr和newTxtPtr两个参数。这个函数会返回一个整数值,表示新文本的长度。我们把它赋值给result变量,并判断是否大于0。
如果result大于0,表示插件成功地修改了文本,并将新文本存储在newTxtPtr指向的缓冲区中。我们可以使用new_string.value属性,获取缓冲区中的字符串值,并打印出来。
如果result等于0,表示插件没有修改文本,或者发生了错误。我们就直接打印old_string变量的值。
通过这段Python代码,我们就可以看到争渡文本预处理插件的效果了。它会将原始文本中的"女孩"替换成"男孩",并将修改后的文本送给读屏朗读。
你看,用C#导出本机DLL给Python是多么美好的事情啊!我们不仅可以利用C#的强大功能和丰富的库来编写更多的文本预处理效果,还可以让Python等其他语言方便地调用我们的插件。这样就实现了跨语言和跨平台的互操作性。
这只是一个简单的导出函数例子。
"3f/DllExport"的最大优点是可以让C#也能导出本机风格的函数,供其他语言或平台调用,从而实现跨语言和跨平台的互操作性。这个库的出现,解决了C#的一大短板,就是不能像C++一样生成普通的动态链接库。
"3f/DllExport"工具非常强大,支持与"ilmerge"配合使用,如果你的程序集引用了第三方库,它可以配合ILMerge将其合并为一个DLL。也支持风送复杂类型,比如struct、array等。进阶玩法大家就自己去研究吧,本文就写到这里。
参考资料:
https://github.com/3F/DllExport/
https://github.com/dotnet/ILMerge/
https://en.wikipedia.org/wiki/Assembly_(CLI)
https://en.wikipedia.org/wiki/Dynamic-link_library
相关文章:
C#导出本机Win32native dll
C# 使用 "3f/DllExport" 工具导出C风格的本机函数 [文 / 张赐荣] 首先,让我们来了解一下什么是争渡读屏软件,以及什么是争渡文本预处理API。争渡读屏软件是一款屏幕朗读软件,用于协助视力障碍人士操作电脑。 争渡文本预处理API是一…...

express-generator快速构建node后端项目
express-generator是express官方团队开发者准备的一个快速生成工具,可以非常快速的生成一个基于express开发的框架基础应用。 npm安装 npm install express-generator -g初始化应用 express my_node_test 创建了一个名为 my_node_test 的express骨架项目通过 Exp…...

视频监控系统/视频汇聚平台EasyCVR如何反向代理进行后端保活?
安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…...

金融信创黄金三年:小程序生态+跨端技术框架构建
小程序应用场景生态的发展,受益于开源技术的发展,以及响应快速开发的实际业务需求,一些跨端框架如:Electron、wxPython、FinClip、Tauri、Flutter等发展也非常迅速,小程序生态跨端技术框架,不仅能满足自有超…...

这短短 6 行代码你能数出几个bug?
前言:本文仅仅只是分享笔者一年前见到的诡异代码,大家可以看看乐子,随便数一数一共有多少个bug,这数bug多少还是要点水平的 在初学编程的时候,写的第一个代码大多都是 hello world,可是就算是 hello world…...

【毕设选题】深度学习 机器视觉 车位识别车道线检测 - python opencv
0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过…...

不同数据类型在单片机内存中占多少字节?
文章目录 前言一、不同编译器二、C51* 指针型 三、sizeof结构体联合体 前言 在C语言中,数据类型指的是用于声明不同类型的变量或者函数的一个广泛的系统。变量的类型决定了变量存储占用的空间 一、不同编译器 类型16位编译器大小32位编译器大小64位编译器大小char…...
安卓LinearLayout让控件居中的办法
在控件属性上,是处理不了的。必须是在LinearLayout处理: 垂直居中 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width&…...
uniapp下拉刷新
为什么要使用uniapp的下拉刷新呢 跨平台兼容性: Uniapp 允许你一次编写代码,然后在多个平台(如微信小程序、H5、iOS 和 Android 等)上运行。使用 Uniapp 的下拉刷新功能,可以确保在不同平台上都能提供一致的用户体验&a…...
【工作记录】css3 grid布局笔记
概述 Grid 布局即网格布局,是一种新的 CSS 布局模型,比较擅长将一个页面划分为几个主要区域,以及定义这些区域的大小、位置、层次等关系。号称是最强大的的 CSS 布局方案,是目前唯一一种 CSS 二维布局 参数配置说明 属性说明可…...

区块链技术-比特币数据结构
背景 随着近几年区块链技术的迅速发展,越来越多的行业正在将区块链技术应用到实际中去。例如,金融、物流、交易所等行业都开始尝试使用区块链技术来替代传统技术。伴随着区块链迅速发展的期间,诞生了比特币(BTC)、以太…...

SpringBoot结合dev-tool 实现IDEA项目热部署
什么是热部署? 应用正在运行的时候升级功能, 不需要重新启动应用对于Java应用程序来说, 热部署就是在运行时更新Java类文件 通俗的来讲,应用在运行状态下,修改项目源码后,不用重启应用,会把编译的内容部署到服务器上…...
flink中使用外部定时器实现定时刷新
背景: 我们经常会使用到比如数据库中的配置表信息,而我们不希望每次都去查询db,那么我们就想定时把db配置表的数据定时加载到flink的本地内存中,那么如何实现呢? 外部定时器定时加载实现 1.在open函数中进行定时器的…...

Spring Cloud Pipelines 入门实践
文章目录 1. 前言2. Spring Cloud Pipelines 是做什么的2.1. 预定义的流程2.2. 集成测试和契约测试2.3.部署策略 4. Spring Cloud Pipelines的使用示例4.1. 创建一个Spring Boot应用4.2. 将代码托管到GitHub仓库4.3. 添加Spring Cloud Pipelines依赖4.4. 配置Spring Cloud Pipe…...

G1 GC详解及设置
一、概述 G1 GC,全称Garbage-First Garbage Collector,在JDK1.7中引入了G1 GC,从JAVA 9开始,G1 GC是默认的GC算法。通过-XX:UseG1GC参数来启用。G1收集器是工作在堆内不同分区上的收集器,分区既可以是年轻代也可以是老…...
GitHub详细教程
将代码推送到GitHub仓库涉及一系列的步骤。以下是详细的步骤说明: 创建一个新的仓库(如果还没有的话): 访问 GitHub。登录您的帐户。点击页面右上角的图标,然后选择“New repository”。填写仓库名称、描述等信息&…...

【小沐学Python】Python实现Web图表功能(Dash)
文章目录 1、简介2、安装3、功能示例3.1 Hello World3.2 连接到数据3.3 可视化数据3.4 控件和回调3.5 设置应用的样式3.5.1 HTML and CSS3.5.2 Dash Design Kit (DDK)3.5.3 Dash Bootstrap Components3.5.4 Dash Mantine Components 4、更多示例4.1 Basic Dashboard4.2 Using C…...

【RabbitMQ】docker rabbitmq集群 docker搭建rabbitmq集群
docker rabbitmq集群 docker搭建rabbitmq集群 RabbitMQ提供了两种常用的集群模式 1.普通集群模式 2.镜像集群模式 普通集群模式只能同步主节点上的交换机和队列信息,但对于队列中的消息不做同步,主节点宕机也不能进行切换(故障转移ÿ…...
Linux 网络驱动实验
本文章对Linux 网络驱动实验中的设备树进行介绍,Linux网络驱动程序比较复杂,只要学会应用。 1、I.MX6ULL 网络外设设备树 I.MX6ULL 有两个 10/100M 的网络 MAC 外设,因此 I.MX6ULL 网络驱动主要就是这两个网络 MAC 外设的驱动。 fec1…...

访问Apache Tomcat的虚拟主机管理页面
介绍 通过Tomcat Host Manager应用可以创建、删除、管理Tomcat内的虚拟主机(virtual hosts)。该应用是Tomcat安装的一部分,默认在<Tomcat安装目录>/webapps/host-manager: 配置用户名、密码、角色 要访问Host Manager应…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...