Introduction

One of the interesting things that C#9 brought is the introduction of Code Generators. When compiling the code, the C# compiler can generate extra code and add it to your project, thus complementing your code. This has a lot of possible uses:

  • Add an attribute to your code to generate boilerplate code: you can add an attribute to a private member and the code generator will add the property and the INotifyPropertyChanged to your class.
  • Discover the dependencies needed at compile time and wire them in the executable. This can improve execution time, because wiring the dependencies using reflection is very slow. In this case, the dependencies would be already set up.
  • Parse files and generate code for them: you could have a json file with data and the compiler would add the classes without the need of creating them manually. Once the file has changed (and thus the class structure), a new class would be created.

One interesting use of this feature was used by the Windows SDK Team for using the Win32 APIs in our C# code. Instead of using the traditional PInvoke (which you must go to http://pinvoke.net to get the signatures and structures, add them as external methods and call them), you can use C#/Win32, developed to simplify the usage of Win32 APIs.

Using C#/Win32

To use C#/Win32, you must have .NET 5.102 installed in your machine and you must be using Visual Studio 16.8 or newer. With these prerequisites, you can create a new console application in the command line with:

dotnet new console -o UseWin32

This line will create a new console project in the UseWin32  folder. Then you must add the CsWin32  NuGet package with:

dotnet add package Microsoft.Windows.CsWin32 --prerelease

Now you are ready to open the project in Visual Studio or Visual Studio Code and use it. This first project will  enumerate the files in the current directory. Yes, I know that .NET already has methods to do that, but we can also do it using Win32.

Create a new text file and name it NativeMethods.txt. In this file, add the names of the thre API functions that we need:

FindFirstFile
FindNextFile
FindClose

Now, we are ready to use these methods in our program. In Program.cs, erase everything and add this code:

using System;
using System.Linq;
using Microsoft.Windows.Sdk;

var handle = PInvoke.FindFirstFile("*.*", out var findData);
if (handle.IsInvalid)
    return;

We are using another feature of C#9, Top Level Statements. We can see that CsWin32 has generated a new class named PInvoke  and has added the FindFirstFile  method. If you hoveer the mouse over it, you will see something like this:

As you can see, it has added the function and  also added the documentation for it. If you right-click in the function and select Go to Definition, it will open the generated code:

We can then continue the code to enumerate the files:

using System;
using System.Linq;
using Microsoft.Windows.Sdk;

var handle = PInvoke.FindFirstFile("*.*", out var findData);
if (handle.IsInvalid)
    return;
bool result;
do
{
    Console.WriteLine(ConvertFileNameToString(findData.cFileName.AsSpan()));
    result = PInvoke.FindNextFile(handle, out findData);
} while (result);
PInvoke.FindClose(handle);

string ConvertFileNameToString(Span<ushort> span)
{
    return string.Join("", span.ToArray().TakeWhile(i => i != 0).Select(i => (char)i));
}

This code opens the enumeration with FindFirstFile. If the returned handle is invalid, then there are no files in the folder, so the program exits. Then, it will print the file name to the console and continue the enumeration until the last file, when it calls FindClose, to close the handle. The filename is returned as a structure named __ushort_260, that can be converted to a Span<ushort> with the AsSpan method. To convert this to a string, we use the ConvertFileNameToString method, that uses Linq to convert it to a string: it takes all the items until it finds a 0, converts them to an IEnumerable<char> and then uses string.Join to convert this to a string.

If you use this code, you will see that FindClose(handle) has an error. That’s because the FileClose function receives a parameter of type HANDLE, while the handle variable is of the FileCloseSafeHandle type and both are not compatible (FileCloseSafeHandle has a handle field, but it’s protected and cannot be used). The solution, in this case, is to dispose the handle variable, that will call FindClose. This code shows how this is done:

using var handle = PInvoke.FindFirstFile("*.*", out var findData);
if (handle.IsInvalid)
    return;
bool result;
do
{
    Console.WriteLine(ConvertFileNameToString(findData.cFileName.AsSpan()));
    result = PInvoke.FindNextFile(handle, out findData);
} while (result);

We are using here the C#8’s Using Statement, so we don’t need to use a block. When you run this code, you will see the enumeration of the files in the console:

Function callbacks

As you can see, there is no need to dig to use the Win32 APIs, but there are some APIs that are more complex and use a callback function. These can also be used in the same way. to see that, we can enumerate all resources in an executable. To do that, we load the executable with LoadLibraryEx. Once loaded, we use EnumerateResourceTypes to enumerate all resource types and, for every resource type, we enumerate the resources with EnumerateResourceNames.

Create a new project with

dotnet new console -o EnumerateResources

Then add the CsWin32 NuGet package with

dotnet add package Microsoft.Windows.CsWin32 --prerelease

Then, open the project in Visual Studio and add a new text file and name it NativeMethods.txt. Add these functions in the file:

LoadLibraryEx
FreeLibrary
EnumResourceTypes
EnumResourceNames

In Program.cs, erase all text and add this code:

using System;
using Microsoft.Windows.Sdk;

var hInst = PInvoke.LoadLibraryEx(@"C:\Windows\Notepad.exe",
    null, LoadLibraryEx_dwFlags.LOAD_LIBRARY_AS_DATAFILE);
try
{

}
finally
{
    PInvoke.FreeLibrary(hInst);
}

This code calls LoadLibraryEx to load Notepad. The LOAD_LIBRARY_AS_DATAFILE constant was changed to an enum. This function returns the Instance handle, that will be used to enumerate the resources. At the end, we free the instance using FreeLibrary.

Now, we’ll start to enumerate the resources with:

var hInst = PInvoke.LoadLibraryEx(@"C:\Windows\Notepad.exe",
    null, LoadLibraryEx_dwFlags.LOAD_LIBRARY_AS_DATAFILE);
try
{
    PInvoke.EnumResourceTypes(hInst, EnumerateTypes, 0);
}
finally
{
    PInvoke.FreeLibrary(hInst);
}

You can see that the second parameter in EnumerateResourceTypes is a callback function (which I don’t even know the signature :-)). Let’s see if Visual Studio will help us with this. We type Ctrl-. and it will propose us to create the method. We select it and it is created:

BOOL EnumerateTypes(nint hModule, PWSTR lpType, nint lParam)
{
    throw new NotImplementedException();
}

We can add our code to enumerate the types:

BOOL EnumerateTypes(nint hModule, PWSTR lpType, nint lParam)
{
    Console.WriteLine(PwStrToString(lpType));
    PInvoke.EnumResourceNames(hModule, lpType, EnumNames, 0);
    return true;
}

This code will write the resource type to the console and enumerate the resources for that type. We are using a function, PwStrToString to convert the resource name (a PWSTR) to a string:

string PwStrToString(PWSTR str)
{
    unsafe
    {
        return ((ulong)str.Value & 0xFFFF0000) == 0 ?
            ((ulong)str.Value).ToString() :
            str.AsSpan().ToString();
    }
}

This struct has a Value property, that can be an integer or a string. To know which of them we must use, we test against the high order byte and see if it’s empty. If it is, then the value is an integer and we convert it to a string. If not, we convert it to a Span and get the string from it. All this code must be marked as unsafe, as we are working with the pointers.

EnumerateResourceNames has a callback, which we get the signature in the same way we did before, by using Visual Studio refactoring:

BOOL EnumNames(nint hModule, PCWSTR lpType, PWSTR lpName, nint lParam)
{
    Console.WriteLine("  " + PwStrToString(lpName));
    return true;
}

Now the program is complete and we can run it to list all notepad’s resources:

As you can see, working with the Win32 API is much easier now, we don’t have to use custom P/Invokes, everything is at one place and all we have to do is to add the functions we want to the NativeMethods file. This is really a great and welcome improvement.

Ah, and if you want to know what those resource type numbers are, you can find them here.

  • 4 – Menu
  • 5 – Dialog
  • 6 – String
  • 9 – Accelerator
  • 16 – Version
  • 24 – Manifest

All the source code for this article is at https://github.com/bsonnino/UseWin32

 

2 Thoughts on “Using the Win32 Api in a C# program with C#/Win32

  1. Miral Shah on September 1, 2021 at 7:27 am said:

    Great Post !!
    I very new to win32 API and want to use it for faster search operation to find files and folder and simultaneously add to database. Project is document management with help of database sql server. This would be useful resource for the same.

  2. Do you know how we get COM interfaces to come out of this? According to Microsoft the metadata is there to generate strongly typed signatures for DirectX and DXGI COM interfaces but thus far I’ve only been able to generate some function signatures for free C-style functions like CreateDXGIFactory2 and such … it defines a signature with a System.object for the IDXGIFactory pointer. I’m missing something here …

Leave a Reply

Your email address will not be published. Required fields are marked *

Post Navigation