こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

PINVOKEで構造体配列をマーシャルする方法

Cで作ったdll内の関数を.NET(C#)のプログラムからPINVOKEを介して呼び出そうとしているのですが、構造体の配列を引数として渡すにはどうするべきかわかりません。データの流れる向きは、両方向 [In, Out] です。下記の方法を試してうまくいかないのですが、どこが悪いのでしょうか?下記サンプルはうまくいかないのですが、構造体の配列をメンバーに持った構造体を使ってデータを受け渡そうとしています。もしかして、IntPtrを使って自分でマーシャリングしないとできませんか?

助言いただきたく、おねがいいたします。

-- 呼び出し側 --
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace MarshalTest
{
  [StructLayout(LayoutKind.Sequential)]
  class Data
  {
    private const int buffersize = 256;
    [MarshalAs(UnmanagedType.I4)]
    public int count;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = buffersize)]
    public Byte[] data;
    public Data()
    {
      count = 0;
      data = new Byte[buffersize];
    }
  }

  [StructLayout(LayoutKind.Sequential)]
  class DataBundle
  {
    private const int buffersize = 8;
    [MarshalAs(UnmanagedType.I4)]
    public int count;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStruct, SizeConst = buffersize)]
    public Data[] data;
    public DataBundle()
    {
      count = 0;
      data = new Data[buffersize];
      for (int idx = 0; idx < buffersize; idx++)
      {
        data[idx] = new Data();
      }
    }
  }

  class Program
  {
    [DllImport("NativeDll.dll")]
    private static extern void test1([Out] Data data);
    [DllImport("NativeDll.dll")]
    private static extern void test2([Out] DataBundle data);

    static void Main(string[] args)
    {
#if false
      // これはできます。
      Data data = new Data();
      test1(data);
      System.Console.WriteLine(Encoding.ASCII.GetString(data.data, 0, data.count));
#else
      // これができません。
      DataBundle data = new DataBundle();
      test2(data);

      for (int idx = 0; idx < data.count; idx++)
      {
        System.Console.WriteLine(Encoding.ASCII.GetString(data.data[idx].data, 0, data.data[idx].count));
      }
#endif
    }
  }
}

-- 呼ばれ側 --

#include <Windows.h>

extern "C" {

#define MAX_DATA  256
#define MAX_BUNDLE  8

  struct Data
  {
    int count;
    char data[MAX_DATA];
  };

  struct DataBundle
  {
    int count;
    Data data[MAX_BUNDLE];
  };

  __declspec(dllexport) void __stdcall test1(Data *data);
  __declspec(dllexport) void __stdcall test2(DataBundle *data);

};

void __stdcall test1(Data *data)
{
  const char *text = "osite !! goo !!";

  data->count = strlen(text);

  strcpy(data->data, text);
  data->data[strlen(text)] = '\0';
}

void __stdcall test2(DataBundle *data)
{
  const char *text = "osite !! goo !!";

  for (int idx = 0; idx < MAX_BUNDLE; idx++)
  {
    strcpy(data->data[idx].data, text);
    data->data[idx].data[strlen(text)] = '\0';
  }
}

以上です。
 

投稿日時 - 2013-06-24 19:42:27

QNo.8148128

困ってます

質問者が選んだベストアンサー

自分でマーシャリングすればできます。

-- 呼び出し側 --
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace MarshalTest
{
  [StructLayout(LayoutKind.Sequential)]
  class Data
  {
    private const int buffersize = 256;
    [MarshalAs(UnmanagedType.I4)]
    public int count;
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = buffersize)]
    public Byte[] data;
    public Data()
    {
      count = 0;
      data = new Byte[buffersize];
    }
  }

  class Program
  {
    [DllImport("NativeDll.dll")]
    private static extern void test3([In, Out] IntPtr data);

    static void Main(string[] args)
    {
      Data[] data = new Data[4];
      for (int idx = 0; idx < 4; idx++)
      {
        data[idx] = new Data();
      }

      IntPtr result = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Data)) * 4);
      IntPtr pos = new IntPtr(result.ToInt32());

      for (int idx = 0; idx < 4; idx++)
      {
        Marshal.StructureToPtr(data[idx], pos, true);
        pos = IntPtr.Add(pos, Marshal.SizeOf(typeof(Data)));
      }

      test3(result);

      pos = new IntPtr(result.ToInt32());
      for (int idx = 0; idx < 4; idx++)
      {
        data[idx] = (Data)Marshal.PtrToStructure(pos, typeof(Data));
        pos = IntPtr.Add(pos, Marshal.SizeOf(typeof(Data)));
      }

      for (int idx = 0; idx < 4; idx++)
      {
        System.Console.WriteLine(Encoding.ASCII.GetString(data[idx].data, 0, data[idx].count));
      }

      Marshal.FreeHGlobal(result);
    }
  }
}

-- 呼ばれる側 --
#include <Windows.h>

extern "C" {

#define MAX_DATA  256

  struct Data
  {
    int count;
    char data[MAX_DATA];
  };

  __declspec(dllexport) void __stdcall test3(Data *data);

};

void __stdcall test3(Data *data)
{
  const char *text = "osite !! goo !!";

  for (int idx = 0; idx < 4; idx++)
  {
    strcpy(data[idx].data, text);
    data[idx].data[strlen(text)] = '\0';
    data[idx].count = strlen(text);
  }

}

投稿日時 - 2013-06-25 02:52:18

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(1)

あなたにオススメの質問