PInvoke.net 替换为 CsWin32

2023年11月22日 1036点热度 0人点赞 2条评论
内容纲要

PInvoke.net 是官方用于操作 Win32 API 的库,但是已经归档了。也就是后续需要使用 Microsoft.Windows.CsWin32 来操作 win32 API。

file
file

但是 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 的代码对比。

file

// 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 文件。

然后函数参数,使用起来也有点麻烦。
比如:

file

实际上不能直接使用 winmdroot.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX,而是使用 Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX
file

如果想保持参数名称一致,可以使用:

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());

枚举类型的名称可能会变化,参数类型也可能会变化。
file
file

需要从 PInvoke 替换为 Microsoft.Windows.CsWin32 ,升级改造需要费不少功夫。

痴者工良

高级程序员劝退师

文章评论

  • wx

    这种自动生成没有智能感知吧,根本记不住函数名

    2024年4月3日
    • 痴者工良

      @wx 确实是这样,没得办法

      2024年4月11日