我有一个非托管第三方库(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文件的缺失链接。
答案 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的许多结构。