0

Using C/C++ within C#, Part 1! – Introduction!

Today we will take a look at using native code (C/C++) within C#. If you really know what you are trying to do and have a good understanding of the theory you can skip most of this post. In this one I will cover most of the theory behind the process, not much code here but it’s a lot of handy Good-to-Knows for programmers anyway.
If you are convinced that you need to do this OR perhaps read this because you simply want to learn, which is awesome, then you can skip the foreword.
For all those with doubts, especially new programmers, please bear with me before we get started, as there are a few things you should know. After this optional chapter we will then have a look at the theory behind cross platform programming and quickly load a C dll into C#.

 

Side Quest: Foreword for the curious and the beginners. (Optional)

Now I don’t claim to be a pro or guru or whatever. Far from it, in fact, but during the last years I had many occasions to rip my hair in frustration, gaining knowledge as I went, as we all do. I met many programmers of the more „elitist“ type that assumed   „C++ and native code is the best“. Now, I myself might or might not have been one of those at some point. I still long for many ‚features‘ (or what some people call design abominations that can be abused) of C++ in other languages. If anyone asked me for my favorite language I would probably still say C++. However, my day job, and that of most other programmers, requires me to use a variety of different languages. It ranges from C#, Python and Java, over  C/C++ all the way to JavaScript, PHP and  more.

Programming is all about finding the right tool for the right job. Before you begin a project, hell, even a feature, you should ask yourself: How do I go about this? Is this the best tool for this? The best language for this?

C++ is hella fast. And you can do amazing things with it. But more often than not you will find that you can achieve the same with other languages and that speed is not that important of a factor for your particular problem. Why waste three days setting up a cross language environment and implementing all the functionality within C++, then marshall it over to another language for execution, if you could implement the very same thing in your project’s main language within one day.

Not to mention… debugging is still a part of a project’s lifecycle, and debugging closed dll files can be a pain, especially if you are not the one who wrote them. Think of your fellow programmers!

But who am I to tell you what to do or think. I am just a black on white font on a third grade programming blog. So let’s dig into the theory!

 

The Theory

This chapter is probaby less interesting for all you CS graduates or long term programmers, as it will contain a simplified explanation of a programs functionality, structure in memory, and why and how this is important for programming with libraries. Most of the code will be found in the next chapter.

One thing you should know is, that every program we write is loaded into your computers memory in it’s entirety. (Unless the program itself handles some additional loading of sub programs or libraries later on).

You probably know that each memory cell has it’s own address. For simplicity we will use decimal instead of hexadecimal addresses to label to those memory cells.
When we start a program the OS loads it into memory for execution. Of course the whole program would not fit into a single memory cell. Usually those are only 8 bits large so our program wouldn’t fit in there. Instead, the OS gives our program a memory range. The address of the first memory cell of our programm is refered to as our programms „base address“.

Let us assume our program has the base address 10000 and ends somewhere at 90000.

The first few cells usually describe the program that follows, they are an identifier which we can use to, duh, identify processes and sub programs. If we load a library or module into our program, this library itself will have an identifier. Looking at programs in a memory editor will thus reveal what kind of libraries where loaded into any given program. A simplified example of how that might look:

Address Value
10000 (-10003) „OurProgram.exe“ Identifier
10004 – 34231 „OurProgram.exe“ Allocated Memory
34232 (-34235) „kernel32.dll“ Identifier
34236 – 89999 „kernel32.dll“ Code
90000 End of Program

The implication of this is, that every line of code we write is somewhere in this range between 10000 and 90000, with the exception of the range that the kernel32 module has taken for itself. If our programm has a base address, everything can have one! Every method and variable we declare and define has it’s own memory address on runtime.

So if we write a program that has a string saved somewhere and a method that prints it, the memory view might be interpreted as follows.

Address Value
10000 (-10003) „OurProgram.exe“ Identifier
10004 – 15335 „OurProgram.exe“ Allocated Memory
15336 – 15339 String
15340 – 15679 Print String() Method
15680 – 34231 „OurProgram.exe“ Allocated Memory
34232 (-34235) „kernel32.dll“ Identifier
34236 – 89999 „kernel32.dll“ Code
90000 End of Program

So, why am I telling you this? Because we can actually use methods from kernel32.dll or any other module we load into our programm by finding the address of said method. It doesn’t really matter if the module was written in a different language.

This method is called pinvoking and has nothing to do with pins, but rather with invoking pointers. A reference to an address in memory is called a pointer. We can ‚pointer invoke‘ a method that we have the base pointer of. However, a language like C# does not know what on earth to do with the bits and bytes behind that functions base pointer. We will have to „Marshall“ our methods as something that the language we use understands.

Marshalling allows us to define a way for our language to handle everything that is behind that pointer. It is a way to „assimilate“ the bits and bytes into it’s own runtime using an understandable label.

We tell our programm: „Alrighty, here is the pointer to a method. Please treat this as if it was a method that returns a string and takes two integers as parameters!“ (And pray your memory doesn’t corrupt)

Managed and Unmanaged Code

So C# supports the interpretation, or the pinvoking of native C and C++ code. You can also execute Java code, but that will require a weird way to wrap Java classes into C#/C code that executes the jvm internally or some other vodoo, but that is a topic for a different post! It is also possible to host different interpreters for scripting within C#, so e.g. execute python code within your C# application. That might be a bit redundant since C# is not compiled the slow tedious way C++ is, which often makes scripting interfaces with LUA or Python necessary to ensure a fast and easy workflow, but it’s definetly nice to have.

Now, all code that lies within the .NET runtime is Managed Code. Most CLI (Common Language Infrastructure) languages, that compile into the CIL (Common Intermediate Language) which is then interpreted by the CLR (Common Language Runtime) or the DLR (Dynamic Language Runtime) are considered „Managed Code“. Phew. Microsoft sure had it’s way with their conventions.

Now feel free to research those at your leisure, the important part for now is, that languages like C#, VB and ‚C++/CLI (CLI variant of C++)‘ can produce managed code. Native C and C++ do not.

So when we import a native C dll into C#, we effectively cross the boundaries of managed code and wander into the realm of unmanaged madness. Which is not as bad as it sounds. Not at all. Not really. Most times.Sometimes.

Loading native code into C#

In order to access the vast unmanaged space of native code, we will have to add a using to our file, so let’s start by adding the following line to our file.

using System.Runtime.InteropServices;

Now that we’ve done that, we have access to C#’s DllImport functionality. DllImport is an attribute that allows us to load a library into our program and access it’s methods. Sort of…

Let’s take a look at a DllImport in C#. The shell32.dll implements a method called „PathYetAnotherMakeUniqueName“. Yes, this method actually exists within shell32.dll and I think it is a wonderful example of developer frustration. But anecdotes aside:

If we want to use this method within C#, we would write the following code:

[DllImport("shell32.dll", EntryPoint = "PathYetAnotherMakeUniqueName", CharSet = CharSet.Unicode)]
internal static extern bool PathYetAnotherMakeUniqueName(
        System.Text.StringBuilder pszUniqueName,
        string pszPath,
        string pszShort,
        string pszFileSpec);

This declares a static external method that returns a bool and takes a stringbuilder object and three strings as a parameter. The funny thing is, that the StringBuilder object is simply C#’s way to deal with the fact that the actual method takes a string pointer (or something else that can be marshalled as a string builder). With this declaration we effectively translate the C declaration to a C# declaration, providing datatypes that are compatible.

The actual DllImport attribute tells our program, that the implementation of our declared method is somewhere within „shell32.dll“. The EntryPoint of this method or function (the method/function name) is called „PathYetAnotherMakeUniqueName“ and forces the used CharSet to unicode. Never. Underestimate. CharSet conversion problems.

If you are not sure about how to import a method that you know exists in a windows library, the website pinvoke.net has an amazing database of such DllImport templates for you to copy and paste!

Let me mention some drawbacks of this method. The DllImport Attribute will load the specified library into your program’s runtime and provide you with a static way to use one given method. This is a very rigid implementation and gives you absolutely nothing in way of dynamically managing your loaded libraries and native functions. It clutters your runtime and you can’t really get rid of the loaded methods once they are in your program, which can effectively make runtime debugging and dll reloads a pain in the bottocks.

Fun trivia: Kernel32.dll is a windows library that defines a series of functions and methods that are necessary for your OS‘ runtime. This library exists since Windows 95 and contains functions that help you manage your systems or programs memory. (Think, loading and unloading of libraries, screening of processes and threads etc.)

I guess it’s obvious now where we will be going with this.

And because nobody likes book-length posts we will stop here.

Thanks for reading and see you soon in part 2, where we will start using kernel32 to dynamically load and unload methods from libraries and write our own C++ library to use within C#.

Alexander

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.