.NET Exceptions – System.DllNotFoundException

Moving right along through our .NET Exception Handling series, today we’ll take a closer look at the System.DllNotFoundException. As indicated by the name, a System.DllNotFoundException occurs when attempting to import a DLL that doesn’t exist.

In this article we’ll examine the System.DllNotFoundException in more detail, including where it resides in the .NET exception hierarchy, along with a functional C# code example that imports a few Win32 DLLs that allow our application to send keyboard inputs to other applications. Let’s get started!

The Technical Rundown

When Should You Use It?

For many .NET applications there may be little to no reason to import DLLs, since the more modern frameworks like .NET and its respective functionality provides most everything that may be required. Now that .NET Framework has been the standard for well over a decade (and upwards of 15 years for some teams), fewer and fewer new applications are being developed that need to integrate with older Win32-style unmanaged functions. Unfortunately, when your application must perform actions outside its own domain, particularly interacting with other windows and applications, there’s often no other option then to import some DLLs and get into the mess that is unmanaged code and APIs.

That’s why the .NET Framework includes exceptions like the System.DllNotFoundException. We’ll use the rest of this article to explore a relatively simple example of using a Win32 DLL to communicate between our C# application and another application: The one and only notepad.exe.

Below you can find the full working code example. Feel free to glance over it or copy it to your own application if you’re following along:

The purpose of our example application is simple: Open Notepad (if not already open) and send a few keystrokes to the input window, resulting in "hello world" being input.

To get started we’ve created a Screen class that contains most of the window-based logic:

We begin with an instance property called Handle that will store the relevant window handle once we locate it. We also have the VirtualKeyCodes enumeration that contains a few important key codes (the full list can be found here). Following that comes the most critical section of our code: Importing some unmanaged Win32 functions from outside DLLs:

For this example we’re importing five different functions from user32.dll, though we could arguably get away with only two (GetWindow and PostMessage). For those unfamiliar with using DllImport(), the syntax is similar to other attributes in C#, where we indicate the DllImport() settings immediately prior to the function signature we’re importing. A few of the imported functions also include the EntryPoint property declaration for DllImport(), which indicates the explicit name of the function within the DLL library. By specifying an EntryPoint value, this allows us to rename the function that will be available to the rest of our application, typically by making it something more appropriate or easier to use. For example, we’ve changed the base name of FindWindowEx to GetChildWindow.

Next in the Screen class comes the constructor, an explicit destructor, and then a single helper method for sending keystrokes to the window:

As indicated by the comments, the single constructor method allows us to retrieve the handle of a parent window first, and then retrieve (and set) the handle of a child window within that parent. This is important for our purposes, because Notepad itself has a parent window (which contains the title and menus and such), but then the actual window where text is input is a child window with a class name of "Edit".

The ~Screen() destructor allows us to call ReleaseContext(), which aims to release the window context of our retrieved window handle during the finalization process (after we’re all done with the Screen instance). This step isn’t necessary, but illustrates some extra functionality we’re pulling from the Win32 unmanaged code.

Finally, PostKeyDown() accepts a passed Key, then uses the imported PostMessage() function to post a message to the handle of our window. We also need to pass a valid VirtualKeyCode, so here we’re using KeyDown.

That’s most of the setup. Now we can try testing it out in Program.Main():

We start by verifying if the notepad process is already running by checking by name. If the result has no length, that indicates no process was found, so we create a new Process instance with the location of notepad.exe passed in, then Start() it up. Since this code is entirely synchronous, it’s also important that we don’t start sending key post messages to the window before it has fully loaded. Therefore, by calling process.WaitForInputIdle() the application will wait for Notepad to load up and accept user input before continuing.

With Notepad ready to go we create a new Screen instance and pass in the parent class name (“Notepad”), parent window name (“Untitled – Notepad”), and child class name (“Edit”). This sets the screen.Handle property to the child input window of Notepad, which can actually accept our text input.

Finally, we send some inputs using PostKeyDown(). In this case, we’re sending the standard “hello world” string, followed by Return to create a new line.

Sure enough, running this code works as expected; Notepad is opened (if it wasn’t already) and “hello world” is printed to the editing window!

Notepad window showing 'hello world'

Now that we have a working application that is successfully importing outside Win32 DLLs, let’s make a very minor tweak and see what happens. Here we’re modifying one DllImport() method call line with a small typo, changing user32.dll to user23.dll. We’re also adding a try-catch block to our Screen() constructor method so we can catch any exceptions:

Now that we’re attempting to import an invalid DLL, as you might expect, attempting to run our application throws a System.DllNotFoundException at us:

To get the most out of your own applications and to fully manage any and all .NET Exceptions, check out the Airbrake .NET Bug Handler, offering real-time alerts and instantaneous insight into what went wrong with your .NET code, along with built-in support for a variety of popular development integrations including: JIRA, GitHub, Bitbucket, and much more.