How to Share an Enum Declaration Between C# and Unmanaged C++

Is it possible to share an enum declaration between C# and unmanaged C++?

You can use a single .cs file and share it between both projects. #include in C++ on a .cs file should be no problem.

This would be an example .cs file:

#if !__LINE__    
namespace MyNamespace
{
public
#endif

// shared enum for both C, C++ and C#
enum MyEnum { myVal1, myVal2 };

#if !__LINE__
}
#endif

If you want multiple enums in one file, you can do this (although you have to temporarily define public to be nothing for C / C++):

#if __LINE__
#define public
#else
namespace MyNamespace
{
#endif

public enum MyEnum { MyEnumValue1, MyEnumValue2 };
public enum MyEnum2 { MyEnum2Value1, MyEnum2Value2 };
public enum MyEnum3 { MyEnum3Value1, MyEnum3Value2 };

#if __LINE__
#undef public
#else
}
#endif

Is it possible to share an enum class between C++ and C#?

C# preprocessor is way more limited compared to the C++ preprocessor. You can leave C# syntax in the source file, and use C++ preprocessor trickery to make it valid C++ code. Like this:

// enum.cs
public enum MyEnum : int { ONE, TWO, THREE };

And to consume in C++:

// enum-cpp.h
#define public
#define enum enum struct

#include "enum.cs"

#undef enum
#undef public

Sharing an enum from C#, C++/CLI, and C++

Just put your #include "Enum.cs" directive inside an outer namespace to resolve the naming collision.

EDIT: A variation suggested by Brent is to use #define to substitute one of the namespaces (or even the enum name itself) declared in the .cs file. This also avoids the naming collision, without making the namespace hierarchy deeper.

How to expose constants from unmanaged C++ to C#

Use the public enum class keywords to declare a managed enumeration type. And yes, this is ugly since you cannot export the native C++ enumeration. Repeating yourself is unfortunately required.

C++11 adopted the enum class keyword as well but it is still distinct from the managed version. This caused a syntax ambiguity in C++/CLI since both language flavors now use the same keywords. The compiler can see the distinction from the accessibility keyword (use public or private), it is not valid for native C++.

Techniques to keep corresponding native and managed enumerations in sync

Place them into a separate C# file, #include from C++ side. #define away all the C# fluff (like the initial public). Like this:

#define public
#include "enums.cs"
#undef public

Marshal unmanaged structure with enum member to c#

The enum looks like this:

public enum MMTPCnxNckRsn {
MMTPCnxNckRsn_NoAnswer = -2,
MMTPCnxNckRsn_SendError = -1,
MMTPCnxNckRsn_Ok = 0,
MMTPCnxNckRsn_InvalidMember,
MMTPCnxNckRsn_HubNotReady,
MMTPCnxNckRsn_UnknownMember,
MMTPCnxNckRsn_LastCnxTooRecent,
MMTPCnxNckRsn_InvalidVersion,
MMTPCnxNckRsn_InvalidOptions,
MMTPCnxNckRsn_TooManyCnx
}

The containing struct is:

public struct MMTPConxNack {
public MMTPCnxNckRsn Reason;
}

The union is:

[StructLayout(LayoutKind.Explicit)]
public struct MMTPMsgDataUnion
{
[FieldOffset(0)]
public MMTPConxReq ConxReq;
[FieldOffset(0)]
public MMTPConxAck ConxAck;
[FieldOffset(0)]
public MMTPConxNack ConxNack;
[FieldOffset(0)]
public MMTPErrInd ErrInd;
}

This is the tricky part. You use LayoutKind.Explicit and FieldOffset to specify that all the members of the C++ union is overlayed on each other. Obviously you need to have definitions for the other 3 types contained in this union, definitions that we cannot see in the C++ code in the question. I presume you already know how to define those.

Once you have the union declared, the final structure is simple:

public struct MMTPMMsg
{
public int Length;
public short Type;
public MMTPMsgDataUnion Data;
}

c# P/Invoke : Pass Enum value with IntPtr to Function; AccessViolationException

Normally, C++ enumerators are defined as follows:

enum class ENUM_FIELD
{
VAR1,
VAR2,
VAR3,
VAR4
};

By default, enum class types are int sized, which means that you can translate them to C# as follows:

public enum EnumField
{
VAR1,
VAR2,
VAR3,
VAR4
}

In such case, both enums will be mapped without problem within the interop environment. But if the C++ enum is defined in a different way, or if the unmanaged method requires a different type, you may need to change the underlying type in your C# declaration in order to map the values correctly. For example:

public enum EnumField : short
{
VAR1,
VAR2,
VAR3,
VAR4
}

Anyway, I think the problem is not caused by the enum, but rather by what you pass as the const void* parameter. Casting an enum value to IntPtr doesn't look right to me:

UInt32 field = (UInt32)EnumField.VAR1;
GCHandle handle = GCHandle.Alloc(field, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();

// pass the ptr variable to the unmanaged function
// and don't forget to handle.Free() when you are done

Marshalling C# array of enums to be modified in c++ Code

I managed to get it working. Some of these steps may be unnecessary, but they don't seem to hurt:

I added a SecurityPermission attribute to the top of the function:

[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]

and wrapped the calling code itself with a fixed block:

fixed (WrapperEnum* ptr_to_enum = &outArray[0]) { DoAThing(ref outArray, length); }

I also, wrapped the enum array in a GCHandle using GCHandle.Alloc and free'd it after the call to ensure it gets cleaned up properly.



Related Topics



Leave a reply



Submit