Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Marshalling C++ struct to C#

Tags:

c++

c#

I have such simple structure in unmanaged C++:

struct Cam
{
    char ip[16];
    char login[16];
    char pass[16];
    char name[16];
};

and C# marshalled structure:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct Cam
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string ip;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string login;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string pass;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string name;
}

When I pass C# struct to C++ lib by function

[DllImport("NVRLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddCameraStruct(Cam cam);
...
Cam cam = new Cam();
cam.ip = "192.168.0.232";
cam.login = "admin";
cam.pass = "admin";
cam.name = "kekekeke";
AddCameraStruct(cam);

and print passed values in C++:

__declspec(dllexport) void AddCameraStruct(Camera cam)
{
    printf(cam.ip);
    printf("\n");
    printf(cam.login);
    printf("\n");
    printf(cam.name);
    printf("\n");
    printf(cam.pass);
    strcpy(camera[CAM_NUM].ip, cam.ip);
    strcpy(camera[CAM_NUM].login, cam.login);
    strcpy(camera[CAM_NUM].pass, cam.pass);
    strcpy(camera[CAM_NUM].name, cam.name);
    CAM_NUM++;
}

it prints:

232
<EMPTY LINE>
192.168.0.232
n

What am I doing wrong?

like image 510
Lukas Avatar asked May 09 '14 10:05

Lukas


2 Answers

After i learned more from your comments,

Change

__declspec(dllexport) void AddCameraStruct(Camera cam)

To

__declspec(dllexport) void AddCameraStruct(Cam cam)
like image 110
Jossef Harush Kadouri Avatar answered Sep 24 '22 04:09

Jossef Harush Kadouri


Update: you wrote:

you were right, I missed that and IDE didn't suggested me nothing because indeed I have also Camera class in c++.. Thank you:)

which I assumed was a copy and paste error. In the example below it was already corrected like that and it works. Not sure why / if you received errors if your code contained Cam, because it is not a recognized structure (in my environment, at least).

Bottom line: the parts of your code that you show, work. You use UnmanagedType.ByValTStr, which depends on the chosen charset, which you define as CharSet = CharSet.Ansi, which in turn maps to char * in your DLL library. This is how it should be marshaled, it is simply correct.

I created a test application PInvokeTestDll as follows:

The DLL part, in a Win32 native DLL project:

#include <atlbase.h>

WIN32_DLL_UNMANGLED_API void AddCameraStruct(Camera cam)
{
    USES_CONVERSION;
    OutputDebugString(A2W(cam.ip));
    OutputDebugString(A2W(cam.login));
    OutputDebugString(A2W(cam.name));
    OutputDebugString(A2W(cam.pass));
}

In the header file:

#define WIN32_DLL_UNMANGLED_API extern "C" _declspec(dllexport)

struct Camera
{
    char ip[16];
    char login[16];
    char pass[16];
    char name[16];
};

WIN32_DLL_UNMANGLED_API void AddCameraStruct(Camera cam);

In the C# project:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct Cam
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string ip;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string login;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string pass;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string name;
}

class Program
{
    [DllImport(@"PInvokeTestDll.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void AddCameraStruct(Cam cam);

    static void Main(string[] args)
    {
        Cam cam = new Cam
        {
            ip = "192.168.0.232", 
            login = "admin", 
            pass = "admin", 
            name = "kekekeke"
        };
        AddCameraStruct(cam);
    }
}

Running this project shows the following in the Output > Debug window in Visual Studio:

192.168.0.232adminkekekekeadmin

As you can see I renamed the Cam struct to the Camera struct, assuming that was a cut and paste error on your side. I also removed the printf statements, because you don't see the output during debugging (and the OutputDebugString requires an LPCWSTR, hence the ATL macro A2W).

My guess is that you are not showing the part that actually causes your structure to fail. I assume it is on the C++ end, because if the structures are what you typed, it "simply works" (this is also "proven" by hovering over the fields of the structure during stepping into the DLL function during debugging). I suggest you isolate the problem by doing what I did: create a test project that only deals with the marshaling and a prototype of the DLL exported function.

like image 38
Abel Avatar answered Sep 23 '22 04:09

Abel