我无法传递一系列结构,但我可以放弃一个。
我在本网站上看了几个答案,这些答案都是正确的。但它们都不适合我。在所有情况下,答案都将IntPtr作为PInvoke签名中的'out IntPtr someName',我总得到的值为零。如果IntPtr是返回值,我无法将其解析为struct指针数组。
这是我尝试使用IntPtr作为返回码: Unamanged C ++
extern "C" EXPORTDLL_API sDeviceEndPoint **CallStartDiscovery(ZBTransport *zbTransport, int *length, int *result)
{
if(zbTransport != NULL)
{
std::list<sDeviceEndPoint *> deviceEndPoints;
sDeviceEndPoint **devEndPoints;
zbTransport->StartDiscovery(&deviceEndPoints, result);
*length = deviceEndPoints.size();
if(*length > 0)
{
devEndPoints = zbTransport->getDiscoveredEndPointPtrs();
devEndPoints[*length] = devEndPoints[0]; // Test duplicate
*length = *length + 1;
return &devEndPoints[0];
}
}
return NULL;
}
C#PInvoke签名:
[DllImport("PInvokeBridge.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr CallStartDiscovery(IntPtr pZbTransportObject, ref int length, ref int result);
C#实施
public Collection<DeviceEndPoint> DiscoverHCSensors()
{
Collection<DeviceEndPoint> discoveredZigBeeDevices = new Collection<DeviceEndPoint>();
int length = 0;
int result = 0;
IntPtr arrayValue = CallStartDiscovery(_pZbTransportObject, ref length, ref result);
if (ErrorCodes.ConvertResultCode(result) == ResultCode.rc_SUCCESS)
{
var deviceEndPointSize = Marshal.SizeOf(typeof(DeviceEndPoint));
for (var i = 0; i < length; i++)
{
discoveredZigBeeDevices.Add((DeviceEndPoint)Marshal.PtrToStructure(arrayValue, typeof(DeviceEndPoint)));
arrayValue = new IntPtr(arrayValue.ToInt32() + deviceEndPointSize);
}
return discoveredZigBeeDevices;
}
else
{
String error = "Error"; // Put something helpful in here
TransportException transEx = new TransportException(error);
transEx.Method = "DiscoverHCSensors";
transEx.ErrorCode = result;
transEx.TransportType = _transportString;
throw transEx;
}
}
我知道在C#中定义的DeviceEndPoint结构的布局是正确的,因为如果我更改C ++代码只传递一个指向DeviceEndPoint结构的指针,我就会在Collection中正确加载单个结构。
另一种尝试是将值作为out参数传递,因此在C ++代码中我有一个参数sDeviceEndPoint ** devs。
我的C#签名是
[DllImport("PInvokeBridge.dll", CharSet = CharSet.Unicode)]
public static extern void CallStartDiscovery(IntPtr pZbTransportObject, out IntPtr devs, ref int length, ref int result);
我也尝试过IntPtr [] devs,'ref'而不是out,它们都以同样的方式失败了。问题出在C#实现
中int result = 0;
IntPtr arrayValue = IntPTr.Zero;
CallStartDiscovery(_pZbTransportObject, out arrayValue, ref length, ref result);
arrayValue的值始终为null(0)。所以我在这一点之后所做的事情并不重要。至少使用返回值我得到一个非null值。
据我所知,我正在做其他人正在做的事情。我看不出我做错了什么。否则,这是在本论坛中多次“回答”的问题的重复。
答案 0 :(得分:2)
大卫有正确的结果,他解释了我的方法不起作用的原因。我得出了相同的结论,但不同地解决了问题。我以为我会提供可读代码(注释不适用于代码)以防其他人使用它。我没有测试David的代码,但我使用了以下代码并且它有效:
IntPtr arrayValue = CallStartDiscovery(_pZbTransportObject, ref length, ref result);
if (result == 0) // If success
{
if (length > 0 && arrayValue != IntPtr.Zero)
{
IntPtr[] devPtrs = new IntPtr[length];
Marshal.Copy(arrayValue, devPtrs, 0, length); // De-reference once to get array of pointers
for (var i = 0; i < length; i++)
{
// Now marshal the structures
discoveredZigBeeDevices.Add((DeviceEndPoint)Marshal.PtrToStructure(devPtrs[i], typeof(DeviceEndPoint)));
}
}
return discoveredZigBeeDevices;
}
我使用了返回IntPtr的方法,而不是参数(原帖中的案例1)。
答案 1 :(得分:1)
您缺少一个间接级别。您的本机代码返回一个双指针。但托管代码仅执行单个指针取消引用。添加第二层间接,你会没事的。
而不是
Marshal.PtrToStructure(arrayValue, typeof(DeviceEndPoint)
你需要
Marshal.PtrToStructure(Marshal.ReadIntPtr(arrayValue), typeof(DeviceEndPoint)
而不是
arrayValue = new IntPtr(arrayValue.ToInt32() + deviceEndPointSize);
你需要
arrayValue = new IntPtr(arrayValue.ToInt64() + Marshal.SizeOf(IntPtr));
说实话,这是一个相当复杂的界面。如果我面临创建互操作层,我会使用C ++ / CLI。