C#/C++ Interopability
Abstract
A small unity game with C# code that calls code from a C++ dynamic library.
Interopability
C#/C++ interopability can be useful when we want code that has been written and compiled in different programming languages to communicate with each other.
A common example for this is the game engine programming language that differs from the game editor one.
There are 2 ways to communicate with different programming languages; Explicit Platform Invoke also called PInvoke or Implicit Platform Invoke.
In this project we worked with PInvoke, because it is not platform specific, but also does not need an intermediate library.
Not needing an intermediate library means that we can directly call managed functions.
When we start a PInvoke project it always consists out of 3 parts:
- C++ library (dll).
- C wrapper; in this we expose the function from the library.
- .NET library which marshals calls from .NET to the C wrapper and the other way around.
When we want to export the function we can force the compiler to export it as it was a C function, thus preventing C++ mangling.
To avoid using an entry point for our function we can make sure that both of the names of the function match, avoiding an explecit identification.
To be able to transfer the data between the two programming languages we need to transform the memory representation, which is also called marshaling.
For blittable types, e.g. the most data types that have a common representation, you do not need to do anything special.
The project
Approach
When it was hard to discover how I needed to pass pointers and vectors of pointers I made a try out project so that I could debug this faster then in unity itself. This way I was able to work faster and deliver the wanted behaviour faster. To return a vector with pointers in C++ towards C# you need to give the first element and the length. The first element is in this case even a reference of a const pointer to a pointer.
CPPLIBRARY_API void CallGetGameObjectContainer(CppLibrary::GameManager* pGame,
CppLibrary::ObjectContainerType type, CppLibrary::GameObject* const*& pFirstElement,
unsigned long& pLength)
{
if (!pGame)
return;
const std::vector& objects = pGame->GetGameObjectContainer(type);
pLength = static_cast(objects.size());
pFirstElement = objects.data();
}
When you want to loop over these elements in C# code you get:
List objects = new List();
IntPtr firstObject = IntPtr.Zero;
ulong size = 0;
CallGetGameObjectContainer(_nativePtr, type, ref firstObject, ref size);
if (size == 0)
return objects;
GameObject temp = null;
IntPtr pTemp = IntPtr.Zero;
for (ulong i = 0; i < size; i++)
{
unsafe
{
pTemp = ((IntPtr*)firstObject.ToPointer())[i];
temp = (GameObject)Marshal.PtrToStructure(pTemp, typeof(GameObject));
}
objects.Add(temp);
}
return objects;
Links
A lot of information came from the microsoft documentation that can be found by following the website link. The visuals come from a given powerpoint during a class from game tech, this can also be found in the github repository. To see and run the full project, check out my github repository!