非托管结构作为托管的返回值

时间:2012-06-01 12:37:23

标签: c visual-c++ struct c++-cli wrapper

我有一个非托管第三方库(C)。我正在尝试用C ++ / CLI编写一个包装器,所以我可以使用C#中的库。我有几个C示例,但我无法弄清楚如何桥接非托管和托管堆。

库中的一个函数返回一个结构,我需要在我的托管包装器中保留它。然后将此结构用作其他函数的参数。

工作C-示例:

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);
}

所以在我的Wrapper中,我尝试重新创建示例所做的事情。我已经发现问题是结构所在的堆,但我还没弄清楚如何解决这个问题。

我的C ++代码,编译为C ++ / CLI .dll,用于我的C#项目。

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()
{
}

这就失败了。如何从非托管代码中获取FOO_BAR结构的实例到托管类中,然后将其用作后续函数中的参数。

编辑:

  

我失败并发出警告LNK4248:未解析的typeref标记(0100000D)
  错误LNK2028:未解析的令牌(0A00000A)“extern”C“struct FOO_BAR_STRUCT
  错误LNK2019:未解析的外部符号“extern”C“struct FOO_BAR_STRUCT

我想问题是我在库附带的任何头文件中都没有FOO_BAR_STRUCT的定义。我开始不喜欢这个图书馆了。

创建一个包含结构引用的非托管类,然后从托管类引用这个非托管类是否合理?

如果我改为“普通”C ++类,我会得到一个不同的编译错误:

FooBarComm.h

#include <library.h>

#pragma unmanaged    
class FooBarComm {
...

我收到编译错误:

  

错误LNK2019:未解析的外部符号_FooBarCreate引用

编辑2:

当然,它是.lib文件的缺失链接。

2 个答案:

答案 0 :(得分:1)

这看起来非常像是由于省略了第三方库的.lib文件而导致的链接器错误。我希望如果你将它添加到链接选项中,那么你的问题就会得到解决。

答案 1 :(得分:0)

考虑使用显式转换为byte []数组。这样就可以明确指定结构的位置。

在C中你有:

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

extern "C" void FooBarUse(FooBar* Data)

在C#中你写道:

    [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;
    }

本机函数由C#直接调用。

StructToByteArray和ByteArrayToStruct函数就在这里:

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);
    }

最后一件事,FooBarManaged(在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;
}

关于这段代码没什么好说的。我使用一个简单的自动生成器进行这种编组,使用本机sqlite3后端作为数据存储。因此需要编组来自C的许多结构。