3
votes

I have a unmanaged third party library (C). I'm trying to write a wrapper in C++/CLI, so I can use the library from C#. I have several C examples, but I can't figure out how to bridge the unmanaged and managed heaps.

One of the functions in the library returns a struct, which I need to keep in my managed wrapper. This struct is then used as a parameter for other functions.

Working C-example:

library.h

typedef struct FOO_BAR_STRUCT* FOO_BAR;
DLLEXPORT FOO_BAR FooBarCreate();

Example.c

#include <library.h>

int main(int argc, char* argv[]) {
char* address = "127.0.0.1"; 
FOO_BAR fb = NULL;

fb = FooBarCreate();

FooBarRegister(fb, address);
}

So in my Wrapper, I try to recreate what the example do. I've figured out that the problem is which heap the struct is on, but I haven't been able to figure out how to solve this.

My C++ code, compiled as a C++/CLI .dll for use in my C# project.

FooBarComm.h

#include <library.h>

ref class FooBarComm
{
public:
    FooBarComm(char* address);
    ~FooBarComm();

private:
    FOO_BAR fb;
};

FooBarComm.cpp

#include "FooBarComm.h"

FooBarComm::FooBarComm(char* address)
{
    fb = FooBarCreate();
    FooBarRegister(fb, address);
}

FooBarComm::~FooBarComm()
{
}

And this fails. How can I get the instance of the FOO_BAR struct from the unmanaged code into the managed class, then use it as a argument in later functions.

Edit:

I fails with a warning LNK4248: unresolved typeref token (0100000D)
error LNK2028: unresolved token (0A00000A) "extern "C" struct FOO_BAR_STRUCT
error LNK2019: unresolved external symbol "extern "C" struct FOO_BAR_STRUCT

I guess the problem is that I don't have a definition of the FOO_BAR_STRUCT in any header files that came with the library. I'm starting to dislike this library.

Is it sensible to create a unmanaged class that holds the reference to the struct, and then reference this unmanaged class from a managed class?

If I change to a "normal" C++ class, I get a different compile error:

FooBarComm.h

#include <library.h>

#pragma unmanaged    
class FooBarComm {
...

I get the compile error:

error LNK2019: unresolved external symbol _FooBarCreate referenced

Edit 2:

Ofcourse it was a missing link to the .lib file.

2
Why afapd rather than fb?David Heffernan
Hard to see why the code in the question fails then. And of course you didn't actually state how it fails. You just said "this fails". How can you expect help if you won't supply details. In what way does it fail? Be precise. Supply details.David Heffernan
Looks like I've missed a bit of output from the compiler. Updated the question with more info.Håkon K. Olafsen
typedef struct FOO_BAR_STRUCT* FOO_BAR; could be an incomplete struct. Can't see what that would not work.David Heffernan
I'm not sure why it doesn't compile, when it do in the example I have.Håkon K. Olafsen

2 Answers

1
votes

This looks awfully like a linker error caused by the omission of the .lib file for the 3rd party library. I expect that if you add that to your link options then your problems will be solved.

0
votes

Consider using the explicit conversion to byte[] array. This way you explicitly specify where the struct is.

In C you have:

extern "C" void FooBarCreate(FooBar* Data);

extern "C" void FooBarUse(FooBar* Data)

In C# you write:

    [DllImport("SomeFile.dll")]
    public static extern void FooBarCreate_DLL([In,Out] byte[] BufForFOOBAR);

    [DllImport("SomeFile.dll")]
    public static extern void FooBarUSE_DLL([In,Out] byte[] BufForFOOBAR);


            /// Call DLL with managed struct
    public static Int32 FooBarUse_Managed(ManagedFooBar Value)
    {
        byte[] ValueBuffer = StructureToByteArray(Value);

        return FooBarUse_DLL(ValueBuffer);
    }

            /// Get value from native DLL to managed struct
    public static void FooBarCreate_Managed(ref ManagedFooBar Value)
    {
        byte[] ValueBuffer = new byte[Marshal.SizeOf(Value)];

        FooBarCreate_DLL(ValueBuffer);

        object TmpObj = new ManagedFooBar();

        ByteArrayToStructure(ValueBuffer, ref TmpObj);

        Value = (ManagedFooBar)TmpObj;
    }

The native function is called directly by C#.

The StructToByteArray and ByteArrayToStruct function are right here:

using System.Runtime.InteropServices;
using System.Runtime.Serialization;


    public static byte[] StructureToByteArray(object obj)
    {
        int len = Marshal.SizeOf(obj);

        byte[] arr = new byte[len];

        IntPtr ptr = Marshal.AllocHGlobal(len);

        Marshal.StructureToPtr(obj, ptr, true);

        Marshal.Copy(ptr, arr, 0, len);

        Marshal.FreeHGlobal(ptr);

        return arr;
    }

    public static void ByteArrayToStructure(byte[] bytearray, ref object obj)
    {
        int len = Marshal.SizeOf(obj);

        IntPtr i = Marshal.AllocHGlobal(len);

        Marshal.Copy(bytearray, 0, i, len);

        obj = Marshal.PtrToStructure(i, obj.GetType());

        Marshal.FreeHGlobal(i);
    }

One last thing, the FooBarManaged (in C#):

[Serializable]
    /// Sequential - make sure C does uses the same struct member alignment
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct FooBarManaged
{
    // Ususal POD field
    public Int32 ExpID;

    // Constant-sized string
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)]
    public byte[] FileName;
}

There's not much to tell about this code. I've used a simple autogenerator for this kind of marshalling to use the native sqlite3 backend as a data storage. Thus the need to marshal a lot of structs to and from C.