A WPF GUI for a C++ DLL: A Complex PInvoke Construct
Suppose you have a C++ DLL that needs to relay some data and possibly some error messages, and you want to present this data in a graphical user interface (GUI) that is compatible with Windows 7 and up?
Then you have quite some options, among which:
– A win32 GUI in C++.
– A GUI in WPF, connected to the DLL by C++ interop, i.e. managed C++.
– A GUI in WPF, connected to the DLL by PInvoke.
– A GUI in Qt or another 3rd party GUI library for C++.
You might want to choose the option that you are familiar with, and that has a future, also. In my case that is a choice for WPF (familiarity) and PInvoke (has a future). That is, I am not familiar with QT or other 3rd party GUI libraries, and a win32 GUI seems too tedious. I also happen to think that managed C++ is, or will be orphaned. Marshalling (which we use for PInvoke) on the other hand, has been enhanced even in .Net 4.5.1, see e.g. the Marshal.SizeOf<T>() method here.
In this blog post we will explore a WPF GUI that sends a pointer-to-callback to a C++ DLL, which uses the callback to send an array of structures (containing strings) back to the GUI, which the GUI presents to the user by means of data binding. The case will be made for a simple weather forecast system that displays some data from (imaginary) distributed measurements collected by the DLL. We will use a separate callback to allow the DLL to log meta level messages.
The reason for writing this article on PInvoke is that in my research for the solution described here, I found many explanations of advanced PInvoke techniques, but few integrated examples, and even less example of connecting PInvoke to data binding.
So, the software presented here combines a number of advanced PInvoke techniques into a coherent, easy-to-use, functional whole, connected to data binding. The complete example code (a vs2013 x64 solution) can be downloaded using the link at the bottom of this article.
I will not assert that this article contains substantial original work. It just integrates insights published (abundantly) on the internet. Places to look for adequate information on PInvoke and Marshalling are:
– The MSDN Library on PInvoke and Marshalling.
– Stack Overflow on PInvoke.
Particularly informative articles (to me) were:
– A support article by David Stucki on wrapping a C++ class for use by managed code.
– An article by JeffB on CodeProject on marshalling a C++ class.
Below we will first explore how to data bind to a string obtained from the DLL, then we will explore the case of an array of structures.
Getting a Log Message String from the DLL by Callback
And data binding it to a TextBlock in the GUI.
In this section we will first review the C++ DLL involved. Then we will discuss the case of obtaining a log message. We will thereby ignore all aspects of the C++ code that have to do with marshalling an array of structures; that will be the subject of the next section.
The Native DLL
The DLL consists of an Interface file of exported symbols, a header file that defines the implementation class, and a cpp file with the implementations.
The DLL interface is defined as follows:
The MeteoInfo structure will be presented in the next section.
We note the logCallback function pointer, the lifetime management functions CreateMeteo and DestroyMeteo, and a Send function that we use to signal the DLL to send data to the WPF GUI using callbacks.
The Header file for the implementation of the interface is as follows.
We see a simple class derived from the Interface. Note the fields, initialized by the constructor, that hold the callback function pointers for local, repeated use.
The cpp implementation file is as follows:
So basically, the Meteo::Send method invokes the log callback (m_lcb) with a wchar_t constant.
Then, what do we need to data bind the result of this call to a TextBlock in the WPF GUI?
WPF GUI: The Logging Text Part
First of all we need to declare that we will use methods from the DLL in the C# code to create a Meteo object that holds a pointer to the log callback, and to signal the DLL to send data to the WPF GUI. In this simple scenario, the DLL responds to invoking the Send method by invoking the callback once, but in a realistic setting the Send method would start a process that repeatedly invokes the callback autonomously – according to its own logic.
The argument for Send is the instance we obtained from CreateMeteo. Obviously (being the argument for CreateMeteo) , we need a delegate called LogDelegate:
Note the CharSet specifier. We need this since the default is ANSI. Next we bind the delegate to an actual method:
Here we copy the native string referred to by the IntPtr pointer to a managed string.
LogString is a property, not a field. We use a property so we can data bind a TextBlock to it. The property is:
The OnPropertyChanged call is part of a standard INotifyPropertyChanged implementation.
The data binding in XAML is just:
And that’s all.
Getting an Array of Structures from the DLL by Callback
The relative simplicity of the above was somewhat surprising to me, since explanations of PInvoking strings, in articles on the internet can be dauntingly complex. The good news is that applying the same pattern to an array of structures, that hold strings as well as other data types, does not lead to much additional complexity.
Recall the C# DLL function declaration that creates a Meteo object:
It also holds a delegate for relaying data that we use to update the GUI. GuiUpdateDelegate is defined as:
The delegate’s parameters are a pointer to a native array, and an int that designates the size of the array.
The array is a simple array of MeteoInfo structures. In order to relay the data we need corresponding structures in C++ and in C# (managed code). The native structure is:
The corresponding managed structure is:
You might expect fields here, instead of properties. We should keep in mind, however, that a property is just some code around a field. If we would define the structure with fields, the size in bytes of an object of the structure would be the same. However, we need (public) properties in order to support data binding.
The delegate specified above is bound to an actual method that produces an array of MMeteoInfo objects:
The Marshal.PtrToStructure method copies the native data into a managed structure.
The InfoList object mentioned in the code above is a ListView defined in XAML
And we’re done. Output from the sample application looks like this: