Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Calling All Stations

0.00/5 (No votes)
5 Dec 2014 1  
Interoperability: Calling C++ from C#

Introduction

Interoperability between the different languages in Visual Studio is cool feature, because it enhances posibilities and make code reuse easier. To demonstrate it I wrote some plain vanilla code which works.

Image 1

Background

Some days ago somebody asked "how can I call C++ from C#" and I felt write a short article is good idea to condense my knowledge.

Using the code

The code consists of two parts: the C# code which consumes the C++ code from a dll. The C++ dll is some annoying code like using a C++ class with its function and some standard lib calls.

CPLUSPLUS_API int multiply(int v1, int v2)
{
	//do the heavy lifting with C++
	Worker worker;

	int res = worker.DoMultiply(v1,v2);
	//finally return the result, or error code !!!
	return res;
}

CPLUSPLUS_API int buildText(const char* s1, const char *s2, char* sResult, int len)
{
	int cnt = _snprintf( sResult, len, "%s %s.", s1, s2 );

	int rc  = (len > cnt) ? cnt : -1;

	return rc;
}

Interesting is that Strings and buffers are manipulated, but I stick to the rule that the memory comes from the client and no memory is allocated in the server code to be read in the server code. So the client needs to deliver the bytes, and the server works carefully with it.

CPLUSPLUS_API int buildBuffer(const unsigned char* s1, int l1, unsigned char* sResult, int len)
{
	//some lite error handling
	bool enoughBytes = (len >l1);
	//only copy buffersize
	size_t lenCopy = enoughBytes ? l1 : len;

	memcpy(sResult, s1, lenCopy);

	return enoughBytes ? 0 : -1;
}

A final problem is working with callbacks which are functions and more precisly function pointers. In my sample only a function pointer is set and called. For more complex scenarios, the pointer should be only set and called back later in some threading scenarios.

CPLUSPLUS_API int setCallback( void* callback)
{
	if( callback == NULL ) return -1;

	VOID_CALLBACK_FUNCTION cbFunction = (VOID_CALLBACK_FUNCTION) callback;
	//simple callback
	cbFunction();

	return 0;
}

 

More interesting is the C# side, in which I use the Interop services. In that I declare the the use of the dll function with its interface. If something crashes at most there is the cause. My real trick is to use Ansi characterset and the StringBuilder class which has a char[] operator or use a byte[]. And the callback is type defined in C# as a void delegate and has so the same "footprint" as the pointer is used in the dll.

//for dll imports we need that
using System.Runtime.InteropServices;

// this class manages the calls to the dll
namespace CSharpInterOp
{
    class CDllWrapper
    {
        #region Dll interface
        public CDllWrapper() { }

        [DllImport("CPlusPlus",                //name of the dll
                    EntryPoint = "multiply",   //name of function in dll
                    ExactSpelling = true,
                    CharSet = CharSet.Ansi,
                    CallingConvention = CallingConvention.Cdecl)]
        public static extern unsafe int multiply(int i1, int i2);

        [DllImport("CPlusPlus",               //name of the dll
                    EntryPoint = "buildText", //name of function in dll
                    ExactSpelling = true,
                    CharSet = CharSet.Ansi,
                    CallingConvention = CallingConvention.Cdecl)]
        public static extern unsafe int buildText(StringBuilder s1, StringBuilder s2, StringBuilder sResult, int len);
               
         [DllImport("CPlusPlus",               //name of the dll
                    EntryPoint = "buildBuffer", //name of function in dll
                    ExactSpelling = true,
                    CharSet = CharSet.Ansi,
                    CallingConvention = CallingConvention.Cdecl)]
        public static extern unsafe int buildBuffer(byte[] b1, int l1, byte[] bResult, int len);
        
        [DllImport("CPlusPlus",                     //name of the dll
                    EntryPoint = "setCallback",  //name of function in dll
                    ExactSpelling = true,
                    CharSet = CharSet.Ansi,
                    CallingConvention = CallingConvention.Cdecl)]
         public static extern unsafe int setCallback(Form1.callbackDelegate cb);
        #endregion
    }
}

Points of Interest

In the end looks it all clean and easy, but in this little code are some hard lessons. So I hope many will help it and the question "How to do this" are ending. 

One of the learned lessons, is that "Sh*t happens" and some stable error handling is really need. If the dll is missing or cant be loaded because of other missing dlls you are right in the "DLL Hell". So be warned!!!

Finally I also want to point to my article Full power on the Phone which handles managed C++ with C# on Windows Phone 8, but could be some blueprint for the whole Windows platform.

History

Initial version.

Added simple callback functionality (5 dec 14)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here