Writing PInvoke code is not trivial. Most of the time you need to find the method signature from the documentation or the header files. This takes time and is error-prone. Also, you can't find a NuGet package that wraps all Win32 methods because the number of methods, constants and structures is huge. Hopefully, Microsoft has released a Roslyn source generator to generate PInvoke for Win32 APIs.
In this post, I'll build a WinForms application that displays the live thumbnail of an existing application. This code uses methods from DwmApi.
First, you need to create a WinForms project:
Shell
dotnet new winforms
Then, you need to reference the NuGet package Microsoft.Windows.CsWin32:
Shell
dotnet add package Microsoft.Windows.CsWin32 --prerelease
The previous command should add the NuGet package to the csproj file:
csproj (MSBuild project file)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.10-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
Then, you need to create a new file named NativeMethods.txt at the root of the project. This file contains the list of methods and constants you want to use. Note that you can use the * symbols to import multiple symbols at once.
NativeMethods.txt
DwmRegisterThumbnail
DwmUpdateThumbnailProperties
DWM_TNP_*
If you compile the application, you should see the generated files in the solution explorer:

You can now use the generated code in your application:
Form1.cs (C#)
public partial class Form1 : Form
{
private nint _thumbnail;
public Form1()
{
InitializeComponent();
Load += Form1_Load;
Resize += Form1_Resize;
}
private void Form1_Resize(object? sender, EventArgs e)
{
UpdateThumbnail();
}
private void Form1_Load(object? sender, EventArgs e)
{
var mainWindowHandle = Process.GetProcessesByName("devenv")[0].MainWindowHandle;
// Register the thumbnail
// Convert IntPtr to HWND using the explicit converter
var hresult = PInvoke.DwmRegisterThumbnail((HWND)this.Handle, (HWND)mainWindowHandle, out var thumbnailId);
// HWND contains properties to check the result (Succeeded, Failed)
if (hresult.Succeeded)
{
_thumbnail = thumbnailId;
UpdateThumbnail();
}
}
private void UpdateThumbnail()
{
// Specify the destination rectangle size
RECT dest = new()
{
left = 0,
top = 0,
bottom = Height,
right = Width,
};
// Set the thumbnail properties for use
DWM_THUMBNAIL_PROPERTIES dskThumbProps = new()
{
dwFlags = PInvoke.DWM_TNP_SOURCECLIENTAREAONLY | PInvoke.DWM_TNP_VISIBLE | PInvoke.DWM_TNP_OPACITY | PInvoke.DWM_TNP_RECTDESTINATION,
fSourceClientAreaOnly = false,
fVisible = true,
opacity = 255,
rcDestination = dest,
};
// Display the thumbnail
var hr = PInvoke.DwmUpdateThumbnailProperties(_thumbnail, in dskThumbProps);
if (hr.Failed)
{
// todo
}
}
}
When you start the application, you can see the live thumbnail of the Visual Studio application

#Additional resources
Do you have a question or a suggestion about this post? Contact me!