3

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

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

工作 C 示例:

图书馆.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”结构 FOO_BAR_STRUCT
错误 LNK2019:未解析的外部符号“extern “C”结构 FOO_BAR_STRUCT

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

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

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

FooBarComm.h

#include <library.h>

#pragma unmanaged    
class FooBarComm {
...

我得到编译错误:

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

编辑2:

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

4

2 回答 2

1

这看起来非常像由于 3rd 方库的 .lib 文件遗漏而导致的链接器错误。我希望如果您将其添加到链接选项中,那么您的问题将得到解决。

于 2012-06-01T14:55:00.160 回答
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 之间编组大量结构。

于 2012-06-01T13:04:09.737 回答