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.
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.
// 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:
You can't use winmdroot.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX
directly; instead, use Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX
.
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.
Replacing PInvoke with Microsoft.Windows.CsWin32 requires considerable effort.
文章评论