Calling native code from C#

If you are like me, you like optimizing and profiling your code making it as fast as possible. However, at one point you might realize the C# language does not give you the best performance for whatever the purpose of your code is. Since C# is running in the Common Language Runtime (CLR), there is a larger overhead between what the machine is doing and what your code is doing. It is therefore necessary to go a language of lower level. In my case, I went to C++. C++ is compiled into assembly code when compiled, making it a low-level language. You can also go deeper if the C++ code does not give the desired performance. However, if the code you want to have in C++ contains just a few lines of code, it could in fact be a better way to keep it in C#.

This post will create a basic function returning the result of x to the power of n given that n is larger than or equal to 0. A normal C# function would this would be as follows: (I avoid using library functions to make my examples more clear)

 

public static void Main()
{
	Console.WriteLine("5^4 is {0}", Power(5, 4));
	Console.ReadKey();
}

private static int Power(int x, int n) { int value = 1; for (int i = 0; i < n; i++) value = value * x;

return value;

}

 

 

We want to move this code to C++.

First, I add a empty C++ project to my solution file in Visual Studio (DLL Win32 application). Now comes the ugly part. My source file in the C++ project would look like this:

 

#include <windows.h>

extern “C” __declspec(dllexport) int __stdcall Power(const int x, const int n);

BOOL __stdcall DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) { return TRUE; }

extern “C” __declspec(dllexport) int __stdcall Power(const int x, const int n) { int value = 1; for (int i = 0; i < n; i++) value = value * x;

return value;

}

 

The DllMain method just indicates where the main part of the DLL file is. It works a bit like a constructor in classes. All the "extern "C" __declspec(dllexport) int __stdcall" says it is a int function that is used outside the DLL file by other programs. 

In order to call my native C++ function, I would have to add more code to my C# project. 

[DllImport("NativePower.dll")]
private static extern int Power(int x, int n);

public static void Main() { Console.WriteLine(“5^4 is {0}”, Power(5, 4)); Console.ReadKey(); }

It is important to place the NativePower.dll file in the same folder as the debug folder for our C# application unless we want the application to die in a DllNotFoundException. 

This method of calling native code is called P/invoke (Patform invoke). It is often used by the .NET framework itself to call different methods to the Windows kernel or other system DLL files. It can technically be used by any DLL file coded in any programming language as long as the CLR manages to read the DLL file. I miss some debugging functionality in Visual Studio for importing DLL files since this function could be hard to debug. In some situations you might want to pass strings, arrays or pointers as an argument in the DLL file. It is then needed to use marshaling. Marshalling means you take a data type and its values and makes it compatible with another programming language by converting it. 

 

Description

Windows Type

C/C++ Keyword

Managed Type

C# Keyword

8-bit signed integer

CHAR

char

System.SByte

sbyte

8-bit unsigned integer

BYTE

unsigned char

System.Byte

byte

16-bit signed integer

SHORT 

short 

System.Int16

short

16-bit unsigned integer

WORD and USHORT

unsigned short

System.UInt16

ushort

32-bit signed integer

INT, INT32, LONG, and LONG32

int, long

System.Int32

int

32-bit unsigned integer

DWORD, DWORD32, UINT, and UINT32 

unsigned int, unsigned long

System.UInt32

uint

64-bit signed integer

INT64, LONGLONG, and LONG64

__int64, long long

System.Int64

long

64-bit unsigned integer

DWORDLONG, DWORD64, ULONGLONG, and UINT64

unsigned __int64, unsigned long long

System.UInt64

ulong

Floating-point integer

FLOAT

float

System.Double

double

Source: Codeproject

The table describes what the managed data types are in the native world. 

 

It is important to ask yourself if it is really needed to call native DLL files. I was in a situation where I was going to write a faster algorithm for parsing an int from a byte-array in ASCI-encoding. When I stress tested the code in C++ by using P/invoke to call it form my C# project, it turned out the code was 25% slower. This is due to the overhead caused by the marshaling and the P/invoke. Native code may not always be the solution, especially while developing a managed application. However, a larger algorithm (eg. path finding, cryptography, etc.) which demands more CPU-time, could be running more smoothly.