1

構造体の配列を渡すことはできませんが、1 つを渡すことはできます。

このサイトで、正しいと示されているいくつかの回答を見てきました。しかし、それらのどれも私のために働きません。答えが PInvoke 署名の「out IntPtr someName」として IntPtr を持っているすべての場合で、私が常に取得する値はゼロです。IntPtr が戻り値の場合、構造体ポインターの配列として解決できません。

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 構造体のレイアウトが正しいことはわかっています。これは、DeviceEndPoint 構造体へのポインターを 1 つだけ渡すように C++ コードを変更すると、その単一の構造体がコレクションに正しく読み込まれるためです。

もう 1 つの試みは、値を 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、out の代わりに 'ref' を試しましたが、それらはすべて同じ方法で失敗します。問題は C# の実装にあります

int result = 0;
IntPtr arrayValue = IntPTr.Zero;
CallStartDiscovery(_pZbTransportObject, out arrayValue, ref length, ref result);

arrayValue の値は常に null (0) です。したがって、この時点以降に行うことは関係ありません。少なくとも戻り値を使用すると、null 以外の値が得られます。

私が言うことができる限り、私は他のすべての人がしていることをやっています. 私は自分が間違っていることを見ることができません。それ以外の場合、これはこのフォーラムで何度も「回答」された質問の繰り返しです。

4

2 に答える 2

2

David は正しい結果を出し、私のアプローチがうまくいかなかった理由を説明しています。私は同じ結論に達しましたが、問題を別の方法で解決しました。他の誰かが使用する可能性がある場合に備えて、読み取り可能なコード (コメントはコードの場所ではありません) を提供すると思いました。私は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)。

于 2013-06-07T00:55:41.580 に答える
1

間接的なレベルがありません。ネイティブ コードは double ポインターを返します。ただし、マネージ コードは単一のポインター逆参照のみを実行します。間接的な 2 番目のレイヤーを追加すれば、問題ありません。

それ以外の

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 を使用します。

于 2013-06-06T22:15:30.620 に答える