2

C++ から C# に構造体の配列を渡す必要があります。次のコードは、最初に一時構造を作成し、次にmemcpyC# 側の構造のアドレスに対して実行します。要素の同じ順序で両側の構造を定義しました。

デバッグすると、一時構造体が正しく埋められていることがわかります。temp_buf (ターゲット変数のアドレス) は、反復ごとに構造体のサイズがインクリメントされ、memcpyエラーは返されません。

ただし、配列の最初の項目のみが C# 側で設定されます。

C# 側の定義は次のとおりです。

[DllImport(@"MyAwesomeDLL.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?GetDevices@@YAHPAXIPAIPAUDEVICE_S@@@Z", CharSet = CharSet.Auto)]
public static extern Int32 GetCoolDevices(out UInt32 p_deviceCount, out Device_s p_devicesBuf);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct Device_s
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
    public String DeviceName;
    public UInt32 DeviceID;
}

C++ 側の定義は次のとおりです。

#pragma pack (1)
typedef struct
{
    TCHAR device_name [51];
    UINT32 device_rid;
} DEVICE_S;


int GetDevices (UINT32 *device_count, DEVICE_S *devices_buf)
{
....

    while(....)
    {
        DEVICE_S tmp_device;
        memset(&tmp_device, 0, sizeof(DEVICE_S));

        tmp_device.device_id = 5;
        sprintf( tmp_device.device_name, "a string");

        DEVICE_S *temp_buf = &(devices_buf[i]);
        size_t mysize =  sizeof(DEVICE_S);
        memcpy(temp_buf, &tmp_device, mysize);

        i++;
        getNextDevice();
    }

.....
}

そして、これが私がそれを呼ぶ方法です:

UInt32 MyDecDeviceCnt = 0;
Device_s[] MyDecDevices = new Device_s[10];
int ret = GetCoolDevices(out MyDecDeviceCnt, out MyDecDevices[0]);

どんな提案でも大歓迎です!

4

1 に答える 1

2

作成した API に問題があります。マネージ側 (10 個の要素) で配列のメモリを割り当てますが、アンマネージ側には配列内の要素数を渡しません。マーシャラーには、10 個の要素をマーシャリングする必要があるかどうかを判断する方法がなく、アンマネージ コードは書き込み先のバッファーのサイズを認識していません。

MarshalAsAttribute.SizeParamIndexを使用して、配列のサイズに関する情報をマーシャラーに提供できます。記事Default Marshaling for Arraysも参照してください。(特に、C スタイルの配列に関する情報。)

API の性質を考慮して、アンマネージ側にメモリが割り当てられるように API を変更できます。マネージ側でメモリを解放できるようにするには、セーフ配列または hglobal を使用する必要があります。

于 2012-04-30T16:48:46.437 に答える