我在Linux上使用Mono / C#并拥有以下C#代码:
[DllImport("libaiousb")]
extern static ResultCode QueryDeviceInfo(uint deviceIndex,
ref uint PID, ref uint nameSize, StringBuilder name,
ref uint DIOBytes, ref uint counters);
我调用Linux共享库调用定义如下:
unsigned long QueryDeviceInfo(
unsigned long DeviceIndex
, unsigned long *pPID
, unsigned long *pNameSize
, char *pName
, unsigned long *pDIOBytes
, unsigned long *pCounters
)
在调用Linux函数之前,我已将参数设置为已知值。我还在Linux函数的开头放了一个printf,所有参数都按预期打印值。所以参数似乎从C#传递到Linux ok。返回值也很好。
但是,所有通过引用传递的其他参数都会返回垃圾。
我修改了Linux函数,因此只需修改值并返回。这是代码:
unsigned long QueryDeviceInfo(
unsigned long DeviceIndex
, unsigned long *pPID
, unsigned long *pNameSize
, char *pName
, unsigned long *pDIOBytes
, unsigned long *pCounters
) {
printf ("PID = %d, DIOBYtes = %d, Counters = %d, Name= %s", *pPID, *pDIOBytes, *pCounters, pName);
*pPID = 9;
*pDIOBytes = 8;
*pCounters = 7;
*pNameSize = 6;
return AIOUSB_SUCCESS;
所有参数参数仍然以垃圾回复。
有什么想法吗?
答案 0 :(得分:2)
<强> libaiousb.c 强>
unsigned long QueryDeviceInfo(
unsigned long deviceIndex
, unsigned long *pPID
, unsigned long *pNameSize
, char *pName
, unsigned long *pDIOBytes
, unsigned long *pCounters
)
{
*pPID = 9;
*pDIOBytes = 8;
*pCounters = 7;
*pNameSize = 6;
return 0;
}
<强> libaiousb.so 强>
gcc -shared -o libaiousb.so libaiousb.c
<强> test.cs中强>
using System;
using System.Runtime.InteropServices;
using System.Text;
class Test
{
[DllImport("libaiousb")]
static extern uint QueryDeviceInfo(uint deviceIndex,
ref uint pid, ref uint nameSize, StringBuilder name,
ref uint dioBytes, ref uint counters);
static void Main()
{
uint deviceIndex = 100;
uint pid = 101;
uint nameSize = 102;
StringBuilder name = new StringBuilder("Hello World");
uint dioBytes = 103;
uint counters = 104;
uint result = QueryDeviceInfo(deviceIndex,
ref pid, ref nameSize, name,
ref dioBytes, ref counters);
Console.WriteLine(deviceIndex);
Console.WriteLine(pid);
Console.WriteLine(nameSize);
Console.WriteLine(dioBytes);
Console.WriteLine(counters);
Console.WriteLine(result);
}
}
<强>将Test.exe 强>
gmcs Test.cs
执行命令
$ mono Test.exe 100 9 6 8 7 0
答案 1 :(得分:1)
有点无关,但需要注意的是C和C ++类型的大小并不是固定不变的。具体来说,sizeof(unsigned long)将在32位平台(ILP32系统)上的32位和64位平台(LP64平台)上的64位之间变化。
然后是Win64,这是一个P64平台,所以sizeof(无符号长)== 4(32位)。
缺点是你的P / Invoke签名:
[DllImport("libaiousb")]
static extern uint QueryDeviceInfo(uint deviceIndex,
ref uint pid, ref uint nameSize, StringBuilder name,
ref uint dioBytes, ref uint counters);
中断 - 它只能在32位平台上正常工作(因为C#uint总是32位,而unsigned long
在LP64平台上将是64位) ,并且将在64位平台上失败(相当可怕)。
有三个修正:
IFF你将永远在Unixy平台上(例如,只有ILP32和LP64平台,不是 P64 Win64),你可以将UIntPtr用于unsigned long
。这将导致它在ILP32平台上为32位,在LP64平台上为64位 - 这是理想的行为。
或者,您可以在C#代码中提供多组P / Invoke签名,并执行运行时检查以确定您正在运行哪个ABI以确定要使用哪组签名。您的运行时检查可以使用IntPtr.Size和Environment.OSVersion.Platform来查看您是在Windows(P64)还是Unix(当IntPtr.Size == 4时为ILP32,当IntPtr.Size == 8时为LP64)。
否则,您需要提供与P / Invoke to的ABI中性C绑定,这将使用例如导出函数。 uint64_t
(C#ulong
)而非导出unsigned long
。这将允许您使用C#中的单个ABI(各处64位),但要求您提供一个包装C库,它位于您的C#代码和您关心的实际C库之间。 Mono.Posix.dll
和MonoPosixHelper
遵循此路由绑定ANSI C和POSIX函数。