Replace PInvoke.net with CsWin32

2023年11月22日 94点热度 0人点赞 2条评论
内容目录

PInvoke.net is the official library for interacting with the Win32 API, but it has been archived. This means that the Microsoft.Windows.CsWin32 package should now be used to interact with the Win32 API.

file
file

However, Microsoft.Windows.CsWin32 is a bit cumbersome because it utilizes Roslyn technology to generate code dynamically and can only target the current project. The advantage is that the dynamically generated code is merged into the project, so when the project is packaged for release, it doesn't require the Microsoft.Windows.CsWin32 package, which makes the project size slightly smaller.

Since we need to upgrade, there will be some pitfalls. Below is a guide on how to use Microsoft.Windows.CsWin32.

First, search for and install Microsoft.Windows.CsWin32.
Then, open the csproj project and comment out the following configuration.

		<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
			<!--<PrivateAssets>all</PrivateAssets>
		  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
		</PackageReference>

Commenting these two lines is meant to better facilitate code generation.
After writing the code, you can remove these lines. This way, the library does not need to be included during project release.

Then, configure the project to allow unsafe code.

		<AllowUnsafeBlocks>True</AllowUnsafeBlocks>

This configuration is mandatory; otherwise, you cannot use it, as using the Win32 API typically involves pointers.

Next, create a NativeMethods.txt file in the current project; this file is used to configure which Win32 APIs you are using. The Microsoft.Windows.CsWin32 framework will generate the corresponding code based on this file.
After creating NativeMethods.txt, you can fill in the following contents to generate some essential components, thereby reducing configuration hassle.

DwmRegisterThumbnail
DwmUpdateThumbnailProperties
DWM_TNP_*

Then include the namespace in the places where you need to use it:

using Windows.Win32;

If it prompts that the namespace does not exist, don’t worry; you can ignore it for now.

Now let's look at the code comparison between PInvoke.net and 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);

To use Microsoft.Windows.CsWin32, you must know in advance which functions you want to use! This adds significant usability difficulties.
For instance, if you want to use the GetSystemMetrics function from USER32.dll, you can use it like this in PInvoke.net:

PInvoke.User32.GetSystemMetrics(PInvoke.User32.SystemMetric.SM_CXFULLSCREEN);

In contrast, with Microsoft.Windows.CsWin32, you need to add this function to the NativeMethods.txt file first.

DwmRegisterThumbnail
DwmUpdateThumbnailProperties
DWM_TNP_*
GetSystemMetrics

Then include the Windows.Win32 namespace.

The function generated by Microsoft.Windows.CsWin32 has the following definition:

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

Functions generated by Microsoft.Windows.CsWin32 do not require PInvoke.User32.; you can use them directly with PInvoke..
Additionally, as the generated code has internal scope, it can only be used within the current project. This means that if different projects need to use Win32, each project must define it once! Each project must also reference Microsoft.Windows.CsWin32 and create a NativeMethods.txt file.

Moreover, using function parameters can be somewhat cumbersome.
For example:

file

You can't use winmdroot.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX directly; instead, use Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX.
file

If you want to keep the parameter name consistent, you can use:

using winmdroot = global::Windows.Win32;

Additionally, some APIs cannot be directly replaced. The code using PInvoke.net looks like this:

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

The names of enumeration types and parameter types may vary.
file
file

Replacing PInvoke with Microsoft.Windows.CsWin32 requires considerable effort.

痴者工良

高级程序员劝退师

文章评论