Tech Notes

C#でのマーシャリングにおけるサイズ制限(回避可能)

どつぼに嵌ったのでめも。

C++によるdllとC#によるGUIでとあるアプリを構成していたのだが、そこで問題が発生した。

情報の一括した受け渡しの方法として、巨大な構造体に全てを詰めていたのだが、その構造体のサイズが65520バイトを超えると、C#のコードからC++の関数を呼んだあと処理が戻ってこないのだ。

[StructLayout(LayoutKind.Sequential)]
struct Data {
  // ...
}

class Form1 {
  Data dat;

  [DllImport("xxx.dll")]
  static extern void GetData(int handle, out Data dat, int ex);

  public Form1() {
    // ...
    GetData(handle, out dat, ex);
    // ...
  }
}
DLLEXPORT void GetData(HANDLE handle, Data* dat, void* ex)
{
  // ...
}

このData構造体のサイズが65520バイトを超えると当該の現象が再現する。handleやexなどといった他の引数が影響するかどうかは面倒なので調べていないのだが、再現性のため一応コードに含めた。

このようなコードを書くと、GetDataを呼んだあと処理がC#側に戻ってこないのである。

なぜ65520バイトなのか?一体どういう原理で起きているのか?といったことについての調査はものぐさなので諦めてしまったが、ひとまず解決には至った。

①Dataにstructではなくclassを使用する。

②Dataをclassにしたので適当なところでnew

③C#側におけるGetDataの宣言について、out datではなく[Out] datとする。

自分のものとちょっと違うマーシャリングのコードを手当たり次第にネットから取ってきて試したところ、このような方法で謎のサイズ制限は回避できた。

C#というか.NETの実装と言うものをよく理解してないので、僕はこれに関するまともな説明をすることができない。classにしたとたんoutではなくOutAtrributeにしなければいけない理由もよく分からなかった。

構造体サイズが一定以下ならOKというのもまるでよく分からない。完全に理解し次第追記するかもしれない。