The code developed using the .Net framework is called Managed Code. And the code which was NOT developed using .Net (for example, Win32 API or “C/C++” code or COM components, etc.,) is called Unmanaged Code.
When the .Net framework was introduced, it was required to provide a feature in the .Net framework to support Unmanaged code. Because, before introducing .Net; all the code was written in other languages; for example, “C/C++”; and most of the Windows DLLs were developed using Unmanaged code. It is very expensive and time-consuming to re-write the whole code to convert Unmanaged DLLs to Managed DLLs.
To support this; the “.Net Framework” provides an InteropServices namespace that supports the COM interoperability and Platform invoke Services.
In this article, we will look into using Unmanaged Code within the Managed Code by invoking a function from an Unmanaged DLL in the “C#” code.
Lets’ take an example to invoke the Win32 API function, MessageBox
. MessageBox
is the function which displays a message on the window.
Step 1. Lets’ create a simple Windows Forms Application and add a Button to the Form.
Step 2. Also, add a Button handler to the Button we added; where we write the code to display the Win32 API message box.
Step 3. First, we need to declare the MessageBox
Win32 API in our Program. The Syntax of this function looks like below:
int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
Where:
hWnd
is the window handle. This will be the owner window of the Message Box.
lpText
used to display the message in the Message Box.
lpCaption
will display as the title or caption of the Message Box.
uType
decides the type of the message box to be created. For example, the message box with “Yes” and “No” button OR the message box with “OK” and “Cancel” button etc,. The default type is message box with “OK” button.
Lets’ try to use this API function directly into our “C#” code and see what result we get. Below is the statement:
MessageBox(new IntPtr(this.Handle.Int32()), @”Calling MessageBox Win32 API from C#”, @”MessageBox”, 0);
The first argument is the window handle. How do we get the window handle from the Form
? It is simple. Form
‘s attribute Handle
is the window handle. We need to convert it to Int32()
as the first argument is expecting a pointer to an Integer. I will explain to you later in this article, why we used the first argument as IntPtr
. If we pass simply “0”; you will see below Error:
Error 2 Argument 1: cannot convert from ‘int’ to ‘System.IntPtr’
Error 1 The best overloaded method match for ‘DllImportSample.frmDllImportSample.MessageBox(System.IntPtr, string, string, uint)’ has some invalid arguments
Step 4. Build & Run the Project. When I did, I got below Error:
Error 1 'System.Windows.Forms.MessageBox' is a 'type' but is used like a 'variable'
This is because it conflicts with the already existing MessageBox class in “.Net”; which is under System.Windows.Forms namespace.
If there is no conflict with .Net defined types, you may see below Error:
Error 1 The name 'MessageBox' does not exist in the current context
How to resolve this issue then? We must have to instruct the compiler that, we are NOT using the “.Net” version of the MessageBox. To do this; we will declare the function as mentioned above Syntax.
Again there is a problem here. The Syntax was showing Win32 API version, and the data types mentioned in the Syntax were NOT for “.Net Framework”. Then, how do we declare the Win32 API function in “.Net”?
There is a marshaling that takes place when we call Unmanaged Code from Managed Code. To support this, we must declare the relevant data type for Unmanaged Code in “.Net”. Below list shows some of the examples; what data type we need to use in “.Net” for the data types defined in Unmanaged Code.
Unmanaged type | Managed type |
HWND | System.IntPtr |
LPCSTR | System.String |
UINT | uint or System.UInt32 |
int | int or System.Int32 |
VOID | System.Void |
Based on this, we will declare the WIn32 API function in the “.Net” Manage Code. The declaration looks like below:
int MessageBox(IntPtr hWnd, String lpText, String lpCaption, uint uType);
Step 5. Again, Build & Run the Project. Now I found the same Error. After declaring the function prototype also, why we are seeing the same Error? In order to make it work; we must provide the definition of the MessageBox function.
Notice that, this function is Win 32 API function and the definition of the function resides in another library called “user32.dll”. We must instruct to the Compiler that; the definition exists in the “external” library; not within the program; that means this function was implemented somewhere, but not in this Project. So we use the “extern” keyword to instruct this to the Compiler. I want to make this private; so will use private access specifier. Now the declaration looks like below:
private extern int MessageBox(IntPtr hWnd, String lpText, String lpCaption, uint uType);
Step 6. Let’s Build & Run the Program again. I have seen the Warning message; during the compilation. I ignored it and, Ran the Program. This time the Program was crashed and showed the below Error:
System.TypeLoadException was unhandled
Message: Could not load type ‘DllImportSample.frmDllImportSample’ from assembly ‘DllImportSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ because the method ‘MessageBox’ has no implementation (no RVA).
Why TypeLoadException was showing? We have instructed the Compiler that the implementation of the Win32 API function does exist in somewhere. Where? We need to mention where the function does exist; that means, in which Win32 DLL.
As we didn’t include the library where the Win32 API function is implemented; the definition of the function won’t exist, and the Compiler throws the Error.
Add a Reference to the Project
Step 7. We should know in which library the function exists in order to add the library to the Project. In this case, the function is in the”user32.dll
“. Now we will add this library as a Reference to our Project.
Step 7.1. Goto Solution Explorer in the Project. And select References and then right click on it. Visual Studio will display the pop-up menu.
Step 7.2. From the pop-up menu, select “Add Reference…” menu item. Visual Studio will display Reference Manager dialog to allow you to add the Libray to the Project.
Step 7.3. From Reference Manager dialog, click on “Browse…” button and select “user32.dll” to add a Reference to the Project. Once selected, click on OK button. Now it has thrown below Error. Unable to add “user32.dll” as a reference to the Project this way.
—————————
Microsoft Visual Studio
—————————
A reference to ‘C:\Windows\System32\user32.dll’ could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component.
—————————
OK
—————————
What is this error? This means that the library “user32.dll” is not a valid assembly or COM component to add a reference to the Project. How do we use this library then? See below steps.
DllImportAttribute
Step 8. Let’s go back to “Step 6“, where I have ignored the Warning message and Ran the Project. Let’s look at the Warning of what the Compiler was showing. Here it is:
Warning 1 Method, operator, or accessor ‘DllImportSample.frmDllImportSample.MessageBox(System.IntPtr, string, string, uint)’ is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation.
The Warning message clearly suggesting to add an attribute which is DllImport; tells the Compiler that the external implementation of the method. “.Net Framework” provides the DllImport attribute to import the Win32 API function to the Project. Lets’ add the below statement on top of the MessageBox declaration:
[DllImport("user32.dll")]
Step 9. I hope now our Program will work fine. Lets’ try to Build and Run the Program. Now I can see different Errors. Here are those:
Error 2 The type or namespace name 'DllImport' could not be found (are you missing a using directive or an assembly reference?)
Error 1 The type or namespace name 'DllImportAttribute' could not be found (are you missing a using directive or an assembly reference?)
Seems, the Compiler is not able to find the DllImport attribute. As I mentioned at the beginning of the Article; the InteropServices namespace provides features to call Unmanaged Code from the Managed Code. DllImport attribute is defined within this namespace. So, we must include this in our C# code, to avoid the above Errors. Lets’ add it:
using System.Runtime.InteropServices;
Step 10. I believe we have fixed all the issues shown above. Let us try to Rebuild and Run the Program.
It showed up a window with a button on it. When I clicked on the Button the Application shows the Message Box. WoW! I can see the Program is working fine. The screenshot looks like below:
Step 11. Finally, putting it all together, the complete working code is here:
Using Unmanaged Code in Managed Code is a confusing concept. I tried my best to cover as much as I can about this Subject in this Article. And almost tried to explain the issues we see during the calling of Unmanaged Code.
I hope you like this Article. Please don’t forget to share your Comments. We discuss more C# topics through upcoming Articles.
(Raju)
This is the perfect explanation which i was looking for. Great explanation with detailed step by step process. Good for beginners. Thanks