dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑
很多开发者,包括开发老司机们,在碰到需要调用 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 寻找地方以及对应的系统版本,更重要的是还能自动拷贝注释过来
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 函数调用逻辑的更多相关文章
- 基于Struts2框架实现登录案例 之 使用Struts2标签库简化表单+继承ActionSupport完成输入交验
一,使用Struts2标签库简化表单 在文章[基于Struts2框架实现登录案例]的基础上,通过使用Struts标签库可以简化登录页面login2.jsp <%@ page language=& ...
- 使用HANDLE_MSG宏简化Win32应用的开发
http://blog.csdn.net/daiyutage/article/details/17241161 Win32应用中的回调函数WndProc用于接收Windows向应用程序直接发送的消息, ...
- [dotnet core]使用Peach简化Socket网络通讯协议开发
Peach是基于DotNetty的Socket网络通讯帮助类库,可以帮助开发者简化使用DotNetty,关于DotNetty可参考我之前的这篇文章. Peach内置实现了一个基于文本协议的Comman ...
- libcurl开源库在Win32程序中使用下载文件显示进度条实例
一.配置工程引用libcurl库 #define CURL_STATICLIB #include "curl/curl.h" #ifdef _DEBUG #pragma comme ...
- javaWeb_使用标签库简化jsp
jsp标签库.也叫自己定义标签. 应用范围 jsp标签.主要应用于前台页面.在jsp中.假设jsp中存在<% %> 等 java代码.那么对前台开发者来说.就须要了解 java代码. 怎样 ...
- postgresql从库搭建--逻辑复制
1 物理复制及逻辑复制对比 前文做了PostgreSQL物理复制的部署,其有如下主要优点 物理层面完全一致,是主要的复制方式,其类似于Oracle的DG 延迟低,事务执行过程中产生REDO recor ...
- MySQL锁(一)全局锁:如何做全库的逻辑备份?
数据库锁设计的初衷是处理并发问题,这也是数据库与文件系统的最大区别. 根据加锁的范围,MySQL里大致可以分为三种锁:全局锁.表锁和行锁.接下来我们会分三讲来介绍这三种锁,今天要讲的是全局锁. 全局锁 ...
- C++ 系列:静态库与动态库
转载自http://www.cnblogs.com/skynet/p/3372855.html 这次分享的宗旨是——让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择 ...
- C++静态库与动态库
C++静态库与动态库 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学,推荐一 ...
- c/c++:动态库 静态库 linux/windows 例子 (转)
作者:吴秦出处:http://www.cnblogs.com/skynet/本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接). C++静 ...
随机推荐
- dynatrace统计sql执行时间要考虑网络延时
对一个系统的功能环境做压测,响应时间特别慢,开发环境却很快. 原因是,开发的应用服务器在北方,功能的应用服务器在南方,数据库服务器共用一个,在北方. 北方的应用调北方的数据库,响应时间2s,互相pin ...
- execute immediate 用法小结
1.常规用法 v_sql varchar2(1000); v_sql := 'update Test set name= ''lw112190'' where id= 1'; execute imme ...
- KingbaseES V8R6 创建索引create index concurrently被阻塞
前言 CREATE INDEX CONCURRENTLY(CIC)是DBA们最常用的语句之一,它的好处是不阻塞DML语句. 但在大事务.长事务较多的系统,它可能被阻塞得很久. 本篇就从这个阻塞的案例开 ...
- SQLServer递归触发器在KES中的一次改造分析
文章概要: 某项目将数据从 SQLSERVER 迁移到 KES.其中SQLSERVER中触发器用到了 TRIGGER_NESTLEVEL() 函数,KES并不能直接支持该函数. 起初在分析该问题时想复 ...
- windows系统cmd切换盘符路径命令失效
问题描述:比如当我在C盘想切换到D盘的某个文件夹路径下时 只是输出了那个路径 但是并没有真的切换 这时候需要再多操作一步就会成功了
- Python爬虫爬取爱奇艺电影片库首页
1 import time 2 import traceback 3 import requests 4 from lxml import etree 5 import re 6 from bs4 i ...
- 18 JavaScript中的三元运算
18 JavaScript中的三元运算 先来看一个例子: let a = 10; let b = 20; let d = a > b? a: b console.log(d); // 20 三元 ...
- C++ 多线程编程和同步机制:详解和实例演示
C++中的多线程编程和同步机制使得程序员可以利用计算机的多核心来提高程序的运行效率和性能.本文将介绍多线程编程和同步机制的基本概念和使用方法. 多线程编程基础 在C++中,使用<thread&g ...
- Seaborn时间线图和热图
lineplot() 绘制与时间相关性的线图. sns.lineplot( x=None, y=None, hue=None, size=None, style=None, data=None, pa ...
- Qt数据结构-QString一:常用方法
一.拼接字符串 拼接字符串有两种方法: += . append QString s; s = "hello"; s = s + " "; s += &quo ...