PInvoke.net 是官方用于操作 Win32 API 的库,但是已经归档了。也就是后续需要使用 Microsoft.Windows.CsWin32 来操作 win32 API。
但是 Microsoft.Windows.CsWin32 比较麻烦,因为 Microsoft.Windows.CsWin32 使用的是 Rolsyn 技术动态生成代码,并且只能针对当前项目。当然,优点是动态生成部分代码合并到项目中,项目打包发布的时候,不需要依赖 Microsoft.Windows.CsWin32 包了,体积会小一些。
但是既然要升级了,那就得踩坑,下面说说怎么使用 Microsoft.Windows.CsWin32。
首先第一步搜索安装 Microsoft.Windows.CsWin32。
然后打开 csproj 项目注释以下配置。
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
<!--<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
</PackageReference>
注释这两行是为了更加好地生成代码。
写完代码后,可以去掉这两行。这样在发布项目时,就不需要携带这个库。
然后配置项目允许使用不安全代码。
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
必须配置,否则不能使用,因为使用 win32 api,基本都会使用到指针的。
然后在当前项目下创建一个 NativeMethods.txt 文件,此文件用于配置你使用了哪些 win32 api。然后 Microsoft.Windows.CsWin32 框架会根据这个文件,通过 Roslyn 生成对应的代码。
创建 NativeMethods.txt 后,可以填写以下内容,这样会生成一些基本要用的东西,减少配置麻烦。
DwmRegisterThumbnail
DwmUpdateThumbnailProperties
DWM_TNP_*
然后在需要使用的地方引入命名空间:
using Windows.Win32;
如果提示命名空间不存在,没关系,可以先忽略。
下面来看看 PInvoke.net 和 Microsoft.Windows.CsWin32 的代码对比。
// PInvoke.net
var x = PInvoke.User32.GetSystemMetrics(PInvoke.User32.SystemMetric.SM_CXFULLSCREEN);
var y = PInvoke.User32.GetSystemMetrics(PInvoke.User32.SystemMetric.SM_CYFULLSCREEN);
//Microsoft.Windows.CsWin32
var x = PInvoke.GetSystemMetrics(Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX.SM_CXFULLSCREEN);
var y = PInvoke.GetSystemMetrics(Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX.SM_CYFULLSCREEN);
要使用 Microsoft.Windows.CsWin32 ,必须提前知道自己要使用哪个函数!会带来很大的使用难度。
比如是,要使用 USER32.dll
下的 GetSystemMetrics
函数,在 PInvoke.net 中,可以这样使用:
PInvoke.User32.GetSystemMetrics(PInvoke.User32.SystemMetric.SM_CXFULLSCREEN)
而在 Microsoft.Windows.CsWin32 中,需要先到 NativeMethods.txt 文件添加这个函数。
DwmRegisterThumbnail
DwmUpdateThumbnailProperties
DWM_TNP_*
GetSystemMetrics
然后引入 Windows.Win32
命名空间。
Microsoft.Windows.CsWin32 生成的函数其定义如下:
internal static partial class PInvoke
{
/// </remarks>
[DllImport("USER32.dll", ExactSpelling = true, SetLastError = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[SupportedOSPlatform("windows5.0")]
internal static extern int GetSystemMetrics(winmdroot.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX nIndex);
}
Microsoft.Windows.CsWin32 生成的函数不需要使用 PInvoke.User32.
,而是直接使用 PInvoke.
。
然后因为生成的代码都是 internal 作用域,因此,生成的代码只能在当前项目中使用,也就是说,如果不同项目需要使用 win32 ,必须在每个项目下定义一次!每个项目都需要引用 Microsoft.Windows.CsWin32 ,以及编写 NativeMethods.txt 文件。
然后函数参数,使用起来也有点麻烦。
比如:
实际上不能直接使用 winmdroot.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX
,而是使用 Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX
。
如果想保持参数名称一致,可以使用:
using winmdroot = global::Windows.Win32;
然后有一些 API 是不能直接平替的。使用 PInvoke.net 的代码如下:
PInvoke.User32.keybd_event(KEYE_CAPSLOCK, 0x45, PInvoke.User32.KEYEVENTF.KEYEVENTF_EXTENDED_KEY, UIntPtr.Zero.ToPointer());
PInvoke.User32.keybd_event(KEYE_CAPSLOCK, 0x45, PInvoke.User32.KEYEVENTF.KEYEVENTF_EXTENDED_KEY | PInvoke.User32.KEYEVENTF.KEYEVENTF_KEYUP, UIntPtr.Zero.ToPointer());
枚举类型的名称可能会变化,参数类型也可能会变化。
需要从 PInvoke 替换为 Microsoft.Windows.CsWin32 ,升级改造需要费不少功夫。
文章评论
这种自动生成没有智能感知吧,根本记不住函数名
@wx 确实是这样,没得办法