C# 中的 typedef 等效于使用 C++ DLL
typedef equivalent in C# to use C++ DLL
我正在尝试从 C# 代码调用导出到 DLL 的 C++ 结构
这是我要调用的方法的 C++ 接口:
typedef void *Handle;
typedef void (*Callback)(Info *info);
typedef void (*Timeout)(Info *info);
typedef struct {
WORD port;
WORD flag;
char name[16];
} Info;
__declspec(dllexport) Handle open(Info *info, Callback c,
Timeout timeout);
This article 教我如何在 C# 中声明信息结构:
[StructLayout(LayoutKind.Explicit,
Pack=1,Size=36)]
public struct Info
{
[FieldOffset(0)]
public ushort port;
[FieldOffset(2)]
public ushort flag;
[FieldOffset(4)]
public char name;
}
然后我会在 C# 中导入方法:
[DllImport ("MyDLL")] private static extern void Handle open(Info
*info, Callback c, Timeout timeout);
然后我就卡住了,因为我不知道如何将Handle、Callback 和Timeout 的typedef 转移到C#。有什么建议吗?
这些 typedef 用于将从您正在调用的 open(..) 方法调用的回调函数。这些回调函数中的每一个都将采用 *Info 参数。有关如何声明这些函数的一些示例,请参阅 answers for calling C++ functions containing callbacks in C#。当然,您需要做的不仅仅是声明它们;您还必须编写代码来完成这些功能中的工作。
它非常复杂...试试这个...它可能会爆炸,但您可以告诉我它是如何爆炸的,我可以帮助您:
public class MyDllhelper
{
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Info
{
public ushort port;
public ushort flag;
public fixed byte name[16];
public unsafe string Name
{
get
{
fixed (byte* ptr = name)
{
IntPtr ptr2 = (IntPtr)ptr;
return Marshal.PtrToStringAnsi(ptr2, 16).TrimEnd('[=10=]');
}
}
set
{
fixed (byte* ptr = name)
{
IntPtr ptr2 = (IntPtr)ptr;
byte[] bytes = Encoding.Default.GetBytes(value);
int length = Math.Min(15, bytes.Length);
Marshal.Copy(bytes, 0, ptr2, length);
ptr[length] = 0;
}
}
}
}
public VoidRefInfoDelegate C { get; set; }
public VoidRefInfoDelegate Timeout { get; set; }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VoidRefInfoDelegate(ref Info info);
[DllImport("MyDLL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr open(ref Info info, VoidRefInfoDelegate c, VoidRefInfoDelegate timeout);
public IntPtr Open(ref Info info, VoidRefInfoDelegate c, VoidRefInfoDelegate timeout)
{
C = c;
Timeout = timeout;
return open(ref info, C, Timeout);
}
}
请注意,您必须执行以下操作:
public void CMethod(ref MyDllhelper.Info info)
{
Console.WriteLine("C method called");
}
public void TimeoutMethod(ref MyDllhelper.Info info)
{
Console.WriteLine("Timeout method called");
}
var info = new MyDllhelper.Info();
info.Name = "012345678901234567890"; // Use Name, not name!
info.flag = 1;
info.port = 2;
var helper = new MyDllhelper();
IntPtr handle = helper.Open(ref info, CMethod, TimeoutMethod); // Use Open, not open!
您需要使用属性->构建->允许不安全代码来编译代码。
代码中有两三个有趣的地方:C 数组已转换为 fixed
字节数组。我添加了一个 getter/setter Name
来处理从 Ansi/ASCII 到 Unicode 的转换。
C 函数有两个回调方法(c
和timeout
)。要使用它们,您需要 "save" 某处 C#-side 您将使用的委托,否则垃圾收集器将释放委托,并且您将收到一个异常(参见示例 ) . C
和 Timeout
属性用于此目的。
我正在尝试从 C# 代码调用导出到 DLL 的 C++ 结构
这是我要调用的方法的 C++ 接口:
typedef void *Handle;
typedef void (*Callback)(Info *info);
typedef void (*Timeout)(Info *info);
typedef struct {
WORD port;
WORD flag;
char name[16];
} Info;
__declspec(dllexport) Handle open(Info *info, Callback c,
Timeout timeout);
This article 教我如何在 C# 中声明信息结构:
[StructLayout(LayoutKind.Explicit,
Pack=1,Size=36)]
public struct Info
{
[FieldOffset(0)]
public ushort port;
[FieldOffset(2)]
public ushort flag;
[FieldOffset(4)]
public char name;
}
然后我会在 C# 中导入方法:
[DllImport ("MyDLL")] private static extern void Handle open(Info
*info, Callback c, Timeout timeout);
然后我就卡住了,因为我不知道如何将Handle、Callback 和Timeout 的typedef 转移到C#。有什么建议吗?
这些 typedef 用于将从您正在调用的 open(..) 方法调用的回调函数。这些回调函数中的每一个都将采用 *Info 参数。有关如何声明这些函数的一些示例,请参阅 answers for calling C++ functions containing callbacks in C#。当然,您需要做的不仅仅是声明它们;您还必须编写代码来完成这些功能中的工作。
它非常复杂...试试这个...它可能会爆炸,但您可以告诉我它是如何爆炸的,我可以帮助您:
public class MyDllhelper
{
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Info
{
public ushort port;
public ushort flag;
public fixed byte name[16];
public unsafe string Name
{
get
{
fixed (byte* ptr = name)
{
IntPtr ptr2 = (IntPtr)ptr;
return Marshal.PtrToStringAnsi(ptr2, 16).TrimEnd('[=10=]');
}
}
set
{
fixed (byte* ptr = name)
{
IntPtr ptr2 = (IntPtr)ptr;
byte[] bytes = Encoding.Default.GetBytes(value);
int length = Math.Min(15, bytes.Length);
Marshal.Copy(bytes, 0, ptr2, length);
ptr[length] = 0;
}
}
}
}
public VoidRefInfoDelegate C { get; set; }
public VoidRefInfoDelegate Timeout { get; set; }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VoidRefInfoDelegate(ref Info info);
[DllImport("MyDLL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr open(ref Info info, VoidRefInfoDelegate c, VoidRefInfoDelegate timeout);
public IntPtr Open(ref Info info, VoidRefInfoDelegate c, VoidRefInfoDelegate timeout)
{
C = c;
Timeout = timeout;
return open(ref info, C, Timeout);
}
}
请注意,您必须执行以下操作:
public void CMethod(ref MyDllhelper.Info info)
{
Console.WriteLine("C method called");
}
public void TimeoutMethod(ref MyDllhelper.Info info)
{
Console.WriteLine("Timeout method called");
}
var info = new MyDllhelper.Info();
info.Name = "012345678901234567890"; // Use Name, not name!
info.flag = 1;
info.port = 2;
var helper = new MyDllhelper();
IntPtr handle = helper.Open(ref info, CMethod, TimeoutMethod); // Use Open, not open!
您需要使用属性->构建->允许不安全代码来编译代码。
代码中有两三个有趣的地方:C 数组已转换为 fixed
字节数组。我添加了一个 getter/setter Name
来处理从 Ansi/ASCII 到 Unicode 的转换。
C 函数有两个回调方法(c
和timeout
)。要使用它们,您需要 "save" 某处 C#-side 您将使用的委托,否则垃圾收集器将释放委托,并且您将收到一个异常(参见示例 ) . C
和 Timeout
属性用于此目的。