重点建设专业 专题网站,网上做任务网站有哪些,网站开发与spark,惠州做棋牌网站建设找哪家效益快C# 使用 3f/DllExport 工具导出C风格的本机函数 [文 / 张赐荣]
首先#xff0c;让我们来了解一下什么是争渡读屏软件#xff0c;以及什么是争渡文本预处理API。争渡读屏软件是一款屏幕朗读软件#xff0c;用于协助视力障碍人士操作电脑。 争渡文本预处理API是一… 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不支持X8632位而易语言只支持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