很多开发者,包括开发老司机们,在碰到需要调用 Win32 函数时,都有一个困扰,那就是我应该如何去调用。有两个主要的选项,第一就是自己写 PInvoke 代码,第二就是使用其他大佬给许多 Win32 函数封装好的库。然而这两个方法都有各有各的缺点,第一个方法缺点是可能工作量会很大,需要写方法,写结构体等等。第二个方法缺点是大佬封装的库,虽然全,但可惜里面有很多我用不着的函数,有些浪费。本文将来和大家介绍一个宝藏库,可以很好解决此问题

这是由微软官方发布的库,基于 SourceGenerator 源代码生成技术实现的库。核心原理和工作方式就是,通过源代码生成的方法,生成你项目所需的 Win32 函数。自动生成的 Win32 函数调用封装,可以省去很多开发成本。尽管对于一些特殊一点的 Win32 函数,默认的自动实现也许带坑,但是对于极大多数情况来说,自动生成的都是挺好的,至少好过自己随便去网上抄的代码。由于只生成项目所使用到的 Win32 函数的 PInvoke 代码,此库可以做到极少的代码浪费。相对比引用其他大佬对 Win32 函数进行封装的库来说,使用 CsWin32 库的优点在于可以不需要多依赖程序集,不需要多依赖程序集可以提升应用启动性能,且 CsWin32 只包含项目所需的 Win32 函数的 PInvoke 代码,生成的体积更小

下面来让我介绍一下 CsWin32 库的使用方法

这是一个使用 SourceGenerator 源代码生成技术,生成对 Win32 函数的 PInvoke 封装的库,也就是说这个库是没有最终需要发布的 DLL 的存在的,而是将 Win32 函数的 PInvoke 封装写入到自己的项目里面。按照惯例,使用的第一步就是通过 NuGet 包进行安装。对于 SDK 风格的 csproj 项目文件格式来说,可以编辑 csproj 项目文件,添加以下代码用来安装 Microsoft.Windows.CsWin32 库

  <ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" PrivateAssets="all" Version="0.2.63-beta" />
</ItemGroup>

添加之后的 csproj 项目文件的代码大概如下

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" PrivateAssets="all" Version="0.2.63-beta" />
</ItemGroup>
</Project>

此 Microsoft.Windows.CsWin32 当前最低支持到 .NET Framework 4.5 的版本。有一些旧的项目,采用的 csproj 项目文件格式还不是 SDK 风格的,推荐先改造此 csproj 文件,修改为 SDK 风格的。修改为 SDK 风格的 csproj 能有更好的可读性,而且可以减少多人协作时,编辑 csproj 带来的冲突。如何从旧的项目格式文件升级到 SDK 风格的,其实只需要两句命令行,请参阅 从以前的项目格式迁移到 VS2017 新项目格式

这里需要敲一下黑板,此 Microsoft.Windows.CsWin32 库使用到 SourceGenerator 技术,要求采用 VisualStudio 2022 较新版本才能支持。是 VisualStudio 2022 较新版本,不仅仅是 VisualStudio 2022 哦。如果你的 VisualStudio 2022 的版本比较落后了,那这个库使用的时候,也许会提示很多诡异的错误,比如找不到方法,或者是构建找到重复的文件

安装完成之后,就可以开始编写代码了。如上文说的,这个 Microsoft.Windows.CsWin32 库是只生成项目所需要的 Win32 函数的 PInvoke 封装,那么咱需要解决一个问题,如何让 Microsoft.Windows.CsWin32 库知道咱项目里需要哪些 Win32 函数

做法就是新建一个叫 NativeMethods.txt 的文件,将此文件放入到项目的根路径里面,也就是不要将这个文件藏在项目的其他文件夹里面。如果依然不知道怎么放,那就到本文末尾获取本文的源代码,看看例子

在 NativeMethods.txt 文件里面,一行一个 Win32 方法名,只需要写入方法名,就会自动生成对此方法的封装

下面是在 NativeMethods.txt 文件写的例子

GetModuleHandle
RegisterWaitUntilOOBECompleted

我写上了两个函数名,然后交给 Microsoft.Windows.CsWin32 生成这两个 Win32 函数的封装,以及这两个 Win32 函数用到的参数类型,和一些辅助代码,如下图

生成的代码都是可以直接调用的

来看看其中的 Windows.Win32.PInvoke.KERNEL32.dll.g.cs 文件里对 GetModuleHandle 方法的生成代码

		/// <inheritdoc cref="GetModuleHandle(winmdroot.Foundation.PCWSTR)"/>
[SupportedOSPlatform("windows5.1.2600")]
internal static unsafe FreeLibrarySafeHandle GetModuleHandle(string lpModuleName)
{
fixed (char* lpModuleNameLocal = lpModuleName)
{
winmdroot.Foundation.HINSTANCE __result = PInvoke.GetModuleHandle(lpModuleNameLocal);
return new FreeLibrarySafeHandle(__result, ownsHandle: false);
}
} /// <summary>Retrieves a module handle for the specified module. The module must have been loaded by the calling process.</summary>
/// <param name="lpModuleName">
/// <para>The name of the loaded module (either a .dll or .exe file). If the file name extension is omitted, the default library extension .dll is appended. The file name string can include a trailing point character (.) to indicate that the module name has no extension. The string does not have to specify a path. When specifying a path, be sure to use backslashes (\\), not forward slashes (/). The name is compared (case independently) to the names of modules currently mapped into the address space of the calling process.</para>
/// <para>If this parameter is NULL, <b>GetModuleHandle</b> returns a handle to the file used to create the calling process (.exe file). The <b>GetModuleHandle</b> function does not retrieve handles for modules that were loaded using the <b>LOAD_LIBRARY_AS_DATAFILE</b> flag. For more information, see <a href="https://docs.microsoft.com/windows/desktop/api/libloaderapi/nf-libloaderapi-loadlibraryexa">LoadLibraryEx</a>.</para>
/// <para><see href="https://docs.microsoft.com/windows/win32/api//libloaderapi/nf-libloaderapi-getmodulehandlew#parameters">Read more on docs.microsoft.com</see>.</para>
/// </param>
/// <returns>
/// <para>If the function succeeds, the return value is a handle to the specified module. If the function fails, the return value is NULL. To get extended error information, call <a href="/windows/desktop/api/errhandlingapi/nf-errhandlingapi-getlasterror">GetLastError</a>.</para>
/// </returns>
/// <remarks>
/// <para><see href="https://docs.microsoft.com/windows/win32/api//libloaderapi/nf-libloaderapi-getmodulehandlew">Learn more about this API from docs.microsoft.com</see>.</para>
/// </remarks>
[DllImport("KERNEL32.dll", ExactSpelling = true, EntryPoint = "GetModuleHandleW", SetLastError = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[SupportedOSPlatform("windows5.1.2600")]
internal static extern winmdroot.Foundation.HINSTANCE GetModuleHandle(winmdroot.Foundation.PCWSTR lpModuleName);

可以看到生成的 Win32 函数的封装的代码的质量还是不错的,写的十分标准,包含了入口点,和对字符串的处理,加上设置 LastError 和 DLL 寻找地方以及对应的系统版本,更重要的是还能自动拷贝注释过来

本文的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin ce7ae7a347546b8234bfa7da5d30b284366a7656

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin ce7ae7a347546b8234bfa7da5d30b284366a7656

获取代码之后,进入 KedemhawgerkearfiHeewadainear 文件夹

更多编译器、代码分析、代码生成相关博客,请参阅我的 博客导航

dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑的更多相关文章

  1. 基于Struts2框架实现登录案例 之 使用Struts2标签库简化表单+继承ActionSupport完成输入交验

    一,使用Struts2标签库简化表单 在文章[基于Struts2框架实现登录案例]的基础上,通过使用Struts标签库可以简化登录页面login2.jsp <%@ page language=& ...

  2. 使用HANDLE_MSG宏简化Win32应用的开发

    http://blog.csdn.net/daiyutage/article/details/17241161 Win32应用中的回调函数WndProc用于接收Windows向应用程序直接发送的消息, ...

  3. [dotnet core]使用Peach简化Socket网络通讯协议开发

    Peach是基于DotNetty的Socket网络通讯帮助类库,可以帮助开发者简化使用DotNetty,关于DotNetty可参考我之前的这篇文章. Peach内置实现了一个基于文本协议的Comman ...

  4. libcurl开源库在Win32程序中使用下载文件显示进度条实例

    一.配置工程引用libcurl库 #define CURL_STATICLIB #include "curl/curl.h" #ifdef _DEBUG #pragma comme ...

  5. javaWeb_使用标签库简化jsp

    jsp标签库.也叫自己定义标签. 应用范围 jsp标签.主要应用于前台页面.在jsp中.假设jsp中存在<% %> 等 java代码.那么对前台开发者来说.就须要了解 java代码. 怎样 ...

  6. postgresql从库搭建--逻辑复制

    1 物理复制及逻辑复制对比 前文做了PostgreSQL物理复制的部署,其有如下主要优点 物理层面完全一致,是主要的复制方式,其类似于Oracle的DG 延迟低,事务执行过程中产生REDO recor ...

  7. MySQL锁(一)全局锁:如何做全库的逻辑备份?

    数据库锁设计的初衷是处理并发问题,这也是数据库与文件系统的最大区别. 根据加锁的范围,MySQL里大致可以分为三种锁:全局锁.表锁和行锁.接下来我们会分三讲来介绍这三种锁,今天要讲的是全局锁. 全局锁 ...

  8. C++ 系列:静态库与动态库

    转载自http://www.cnblogs.com/skynet/p/3372855.html 这次分享的宗旨是——让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择 ...

  9. C++静态库与动态库

    C++静态库与动态库 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学,推荐一 ...

  10. c/c++:动态库 静态库 linux/windows 例子 (转)

    作者:吴秦出处:http://www.cnblogs.com/skynet/本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接). C++静 ...

随机推荐

  1. 记录--记一次前端CSS升级

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 目前平台前端使用的是原生CSS+BEM命名,在多人协作的模式下,容易出现样式冲突.为了减少这一类的问题,提升研效,我调研了业界上主流的7种 ...

  2. Saltstack 最大打开文件数问题之奇怪的 8192

    哈喽大家好,我是咸鱼. 今天分享一个在压测过程中遇到的问题,当时排查这个问题费了我们好大的劲,所以我觉得有必要写一篇文章来记录一下. 问题出现 周末在进行压测的时候,测试和开发的同事反映压测有问题,请 ...

  3. vue中elementui组件el-dialog拖拽(已处理边界情况)

    全局注册 Vue.directive("elDialogDrag", (el) => { const header = el.querySelector(".el- ...

  4. 7.3万字肝爆Java8新特性,我不信你能看完!(建议收藏)

    大家好,我是冰河~~ 说实话,肝这篇文章花了我一个月的时间,关于Java8的新特性全在这儿了,建议先收藏后阅读. Java8有哪些新特性? 简单来说,Java8新特性如下所示: Lambda表达式 函 ...

  5. linux scp 学习

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令, scp传输是加密的,可能会稍微影响一下速度.另外,scp还非常不占资源,不会提高多少系统负荷,在这一点上,rsync就 ...

  6. SQLite数据库(来自菜鸟教程)

    SQLite是什么?为什么要用SQLite?SQLite有什么特点? 答:下面请听小猪娓娓道来: ①SQLite是一个轻量级的关系型数据库,运算速度快,占用资源少,很适合在移动设备上使用, 不仅支持标 ...

  7. MySQL分组数据和子查询

    分组数据 创建分组 mysql> SELECT vend_id,COUNT(*) AS num_prods FROM products GROUP BY vend_id; +---------+ ...

  8. Postman模拟浏览器网页请求并获取网页数据

      本文介绍在浏览器中,获取网页中的某一个请求信息,并将其导入到Postman软件,并进行API请求测试的方法.   Postman是一款流行的API开发和测试工具,它提供了一个用户友好的界面,用于创 ...

  9. 5 HTML表单标签

    5 表单标签 表单主要是用来收集客户端提供的相关信息,提供了用户数据录入的方式,有多选.单选.单行文本.下拉列表等输入框,便于网站管理员收集用户的数据,是Web浏览器和Web服务器之间实现信息交流和数 ...

  10. #二进制拆分,矩阵乘法#洛谷 6569 [NOI Online #3 提高组] 魔法值

    题目 分析 考虑一个点的权值能被统计到答案当且仅当其到1号点的路径条数为奇数条. 那么设 \(dp[i][x][y]\) 表示从 \(x\) 到 \(y\) 走 \(i\) 步路径条数的奇偶性, 这个 ...