Someone had a native C++ DLL, that exported a C-interface function. This exported function expected a const pointer to a custom structure, defined like this:
// Structure expected by the C++ native DLL
struct DllData
{
GUID Id;
int Value;
const wchar_t* Name;
};
The declaration of the function exported from the C++ DLL looks like this:
extern "C" HRESULT
__stdcall MyCppDll_ProcessData(const DllData* pData);
The request was to create a custom structure in C# corresponding to the DLL structure shown above, and pass an instance of that struct to the C-interface function exported by the C++ DLL.
A Few Options to Connect the C++ Native World with the C# Managed World
In general, to pass data between managed C# code and native C++ code, there are several options available. For example:
- Create a native C++ DLL that exports C-interface functions, and call them from C# via P/Invoke (Platform Invoke).
- Create a (thin) C++/CLI bridging layer to connect the native C++ code with the managed C# code.
- Wrap the native C++ code using COM, via COM objects and COM interfaces, and let C# interact with those COM wrappers.
The COM option is the most complex one, but probably also the most versatile. It would also allow reusing the wrapped C++ components from other programming languages that know how to talk with COM.
C++/CLI is an interesting option, easier than COM. However, like COM, it’s a Windows-only option. For example, considering this GitHub issue on .NET Core: C++/CLI migration to .Net core on Linux, it seems that C++/CLI is not supported on other platforms like Linux.
On the other hand, the P/Invoke option is available cross-platform on both Windows and Linux.
In the remaining part of this article, I’ll focus on the P/Invoke option to solve the problem at hand.
Using P/Invoke to Pass the Custom Structure from C# to the C++ DLL
To be able to pass the custom structure from C# to the C++ DLL exported-function, we need two steps:
- Define the C++ struct in C# terms; in other words, we need to map the existing C++ struct definition into some corresponding C# structure definition.
- Use DllImport to write a C# function declaration corresponding to the original native C-interface exported function, that C# will be able to understand.
Let’s start with the step #1. This is the C++ structure:
// Structure expected by the C++ native DLL
struct DllData
{
GUID Id;
int Value;
const wchar_t* Name;
};
It contains three fields, of types: GUID, int, and const wchar_t*. We can map those in C# using the managed types Guid, Int32, and String. So, the corresponding C# structure definition looks like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct DllData
{
public Guid Id;
public Int32 Value;
public String Name;
}
Note that the CharSet field is set to CharSet.Unicode, to specify that the String fields should be copied from their managed C# format (which is Unicode UTF-16) to the native Unicode format (again, UTF-16 const wchar_t* in the C++ structure definition).
Now let’s focus on the step #2, which is the use of the DllImport attribute to import in C# the C-interface function exported by the native DLL. The native C-interface function has the following declaration:
extern "C" HRESULT
__stdcall MyCppDll_ProcessData(const DllData* pData);
I crafted the following P/Invoke C# declaration for it:
[DllImport("MyCppDll.dll",
EntryPoint = "MyCppDll_ProcessData",
CallingConvention = CallingConvention.StdCall,
ExactSpelling = true,
PreserveSig = false)]
static extern void ProcessData([In] ref DllData data);
The first parameter is the name of the native DLL: MyCppDll.dll in our case.
Then, I used the EntryPoint field to specify the name of the C-interface function exported from the DLL.
Next, I used the CallingConvention field to specify the StdCall calling convention, which corresponds to the C/C++ __stdcall.
With ExactSpelling=true we tell P/Invoke to search only for the function having the exact name we specified (MyCppDll_ProcessData in this case). Platform Invoke will fail if it cannot locate the function with that exact spelling.
Moreover, with the PreserveSig field set to false, we tell P/Invoke that the native function returns an HRESULT, and in case of error return codes, these will be automatically converted to exceptions in C#.
Finally, since the DllData structure is passed by pointer, I used a ref parameter in the C# P/Invoke declaration. In addition, since the pointer is marked const in C/C++, to explicitly convey the input-only nature of the parameter, I used the [In] attribute in the C# P/Invoke code.
Note that to use the above P/Invoke services, you need the System and System.Runtime.InteropServices namespaces.

Once you have set the above P/Invoke infrastructure, you can simply pass instances of the C# structure to the native C-interface function exported by the native C++ DLL, like this:
// Create an instance of the custom struct in C#
DllData data = new DllData
{
Id = Guid.NewGuid(),
Value = 10,
Name = "Connie"
};
// Pass it to the C++ DLL
ProcessData(ref data);
Piece of cake 😉
P.S. I uploaded some related compilable demo code here on GitHub.








