📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

Wiki がページ「C Sharpの基礎 - C++DLL」を「ライブラリの基礎 - C++DLL」に、リダイレクトを残さずに移動しました
 
(同じ利用者による、間の21版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
C# EXEからC++ DLLへ様々なデータ型の変数を渡したいときがある。<br>
C# EXEからC++ DLLへ様々なデータ型の変数を渡したい場合がある。<br>
<br>
しかし、C#の変数とC++の変数はメモリへの配置が基本型以外異なるため、直接、C#からC++に渡すことができない。<br>
<br>
例えば、文字列を扱う場合、C#のstring型とC++のstd::string型は同一ではないため、マーシャリングの処理が必要となる。<br>
マーシャリングとは、異なる2つのシステム間において、データを交換できるようにデータを操作する処理を指す。<br>
<br><br>
<br><br>


== DllImport属性 ==
== DllImport属性 ==
<code>DLLImport</code>属性は、DLLエントリポイントを定義する関数を記述することで、DLLファイルに定義された関数を呼び出すことができる。<br>
<code>DLLImport</code>属性は、DLLエントリポイントを定義する関数を記述することで、DLLファイルに定義された関数を呼び出すことができる。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  [ DllImport( "DLL名" ) ]
  [ DllImport( "DLL名" ) ]
  </source>
  </syntaxhighlight>
<br>
<br>
また、DLLファイルに定義された関数を、関数名または序数で指定するには、<code>DllImport</code>属性の<code>EntryPoint</code>フィールドを使用する。<br>
また、DLLファイルに定義された関数を、関数名または序数で指定するには、<code>DllImport</code>属性の<code>EntryPoint</code>フィールドを使用する。<br>
14行目: 19行目:
<br>
<br>
以下の例では、<code>EntryPoint</code>フィールドを使用して、<code>MessageBoxA</code>をMsgBoxAに置き換える方法を示している。<br>
以下の例では、<code>EntryPoint</code>フィールドを使用して、<code>MessageBoxA</code>をMsgBoxAに置き換える方法を示している。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  using System;
  using System;
  using System.Runtime.InteropServices;
  using System.Runtime.InteropServices;
23行目: 28行目:
     internal static extern int MsgBoxA(IntPtr hWnd, string lpText, string lpCaption, uint uType);
     internal static extern int MsgBoxA(IntPtr hWnd, string lpText, string lpCaption, uint uType);
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
また、以下の例では、DLLファイルに定義された関数を序数で指定している。<br>
また、以下の例では、DLLファイルに定義された関数を序数で指定している。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  [DllImport("DllName", EntryPoint = "Functionname")]
  [DllImport("DllName", EntryPoint = "Functionname")]
  [DllImport("DllName", EntryPoint = "#123")]  // 序数を使用するには、序数値の前にシャープ記号#を付ける必要がある
  [DllImport("DllName", EntryPoint = "#123")]  // 序数を使用するには、序数値の前にシャープ記号#を付ける必要がある
  </source>
  </syntaxhighlight>
<br>
<br>
<code>DllImport</code>属性には、DLLファイル名を指定する以外にも、下表のような引数を与えることができる。<br>
<code>DllImport</code>属性には、DLLファイル名を指定する以外にも、下表のような引数を与えることができる。<br>
<center>
<center>
{| class="wikitable"
{| class="wikitable" style="background-color:#fefefe;"
|-
|-
! 名称 !! 説明
! style="background-color:#66CCFF;" | 名称  
! style="background-color:#66CCFF;" | 説明
! style="background-color:#66CCFF;" | 既定値
|-
|-
| EntryPoint || 呼び出すDLLエントリポイントの名前または序数を指定する。<br>DLLの関数名とC#上で使用する関数名を異なる名前にする時に指定する。
| EntryPoint || 呼び出すDLLエントリポイントの名前または序数を指定する。<br>DLLの関数名とC#上で使用する関数名を異なる名前にする時に指定する。 ||
|-
|-
| CharSet || 文字列パラメータをメソッドにマーシャリングして、名前マングルを制御する方法を指定する。<br>文字コードの相互変換する時に指定する。<br>指定なしの場合は、<code>CharSet.Auto</code>となる。
| CharSet || 文字列パラメータをメソッドにマーシャリングして、名前マングルを制御する方法を指定する。<br>文字コードの相互変換する時に指定する。<br>指定なしの場合は、<code>CharSet.Auto</code>となる。 || CharSet.Auto
|-
|-
| SetLastError || Win32エラー情報を維持するかどうか指定する。<br>指定なしの場合は、<code>false</code>となる。
| SetLastError || Win32エラー情報を維持するかどうか指定する。<br>指定なしの場合は、<code>false</code>となる。 || FALSE
|-
|-
| ExactSpelling || エントリポイントの関数名を厳密に一致させるかどうか指定する。<br>指定なしの場合は、<code>false</code>となる。
| ExactSpelling || エントリポイントの関数名を厳密に一致させるかどうか指定する。<br>指定なしの場合は、<code>false</code>となる。 || FALSE
|-
|-
| PreserveSig || 定義通りのメソッドのシグネチャを維持するかどうか指定する。
| PreserveSig || 定義通りのメソッドのシグネチャを維持するかどうか指定する。
|-
|-
| CallingConvention || アンマネージドコードを呼び出すための<u>エントリポイントの呼び出し規約</u>を、明示的に指定できる。<br>指定なしの場合は、<code>__stdcall</code>(StdCall)となる。<br>詳細は、下表を参照すること。
| CallingConvention || アンマネージドコードを呼び出すための<u>エントリポイントの呼び出し規約</u>を、明示的に指定できる。<br>指定なしの場合は、<code>__stdcall</code>(StdCall)となる。<br>詳細は、下表を参照すること。 || StdCall
|}
|}
</center>
</center>
67行目: 74行目:
<br>
<br>
<center>
<center>
{| class="wikitable"
{| class="wikitable" style="background-color:#fefefe;"
|-
|-
! 列挙子 !! 説明
! style="background-color:#66CCFF;" | 列挙子  
! style="background-color:#66CCFF;" | 説明
|-
|-
| Cdecl || 呼び出し元がスタックを消去する。<br>これを使用すると、<code>varargs</code>で関数を呼び出すことができる。<br>これは、<code>printf</code>関数等の可変長引数のメソッドの呼び出しで使用する。
| Cdecl || 呼び出し元がスタックを消去する。<br>これを使用すると、<code>varargs</code>で関数を呼び出すことができる。<br>これは、<code>printf</code>関数等の可変長引数のメソッドの呼び出しで使用する。
85行目: 93行目:
以下の例では、呼び出し規約を適用する方法をCdeclとしている。<br>
以下の例では、呼び出し規約を適用する方法をCdeclとしている。<br>
これは、呼び出し元がスタックをクリーンアップするために使用する必要がある。<br>
これは、呼び出し元がスタックをクリーンアップするために使用する必要がある。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  using System;
  using System;
  using System.Runtime.InteropServices;
  using System.Runtime.InteropServices;
111行目: 119行目:
     }
     }
  }
  }
  </source>
  </syntaxhighlight>
詳細は、以下のMSDNのWebサイトを参照すること。<br>
詳細は、以下のMSDNのWebサイトを参照すること。<br>
[https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.callingconvention.aspx CallingConvention 列挙型 (System.Runtime.InteropServices) | MSDN]<br>
[https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.callingconvention.aspx CallingConvention 列挙型 (System.Runtime.InteropServices) | MSDN]<br>
118行目: 126行目:
== StructLayout属性 ==
== StructLayout属性 ==
StructLayout属性とは、クラスまたは構造体のデータメンバを、メモリ内でどのように配置するかを表す。<br>
StructLayout属性とは、クラスまたは構造体のデータメンバを、メモリ内でどのように配置するかを表す。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  [ StructLayout( LayoutKind列挙 ) ]
  [ StructLayout( LayoutKind列挙 ) ]
  </source>
  </syntaxhighlight>
<br>
<br>
下表に、LayoutKind列挙子を示す。<br>
下表に、LayoutKind列挙子を示す。<br>
150行目: 158行目:
</center>
</center>
<br>
<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  [StructLayout( LayoutKind.Sequential )]
  [StructLayout( LayoutKind.Sequential )]
  public struct Position
  public struct Position
158行目: 166行目:
     public double z;
     public double z;
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
詳細は、以下のMSDNのWebサイトを参照すること。<br>
詳細は、以下のMSDNのWebサイトを参照すること。<br>
171行目: 179行目:
<br>
<br>
以下の例では、UInt32型はCLSに準拠しないため、<code>CLSCompliant(false)</code>と指定する必要がある。<br>
以下の例では、UInt32型はCLSに準拠しないため、<code>CLSCompliant(false)</code>と指定する必要がある。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  [CLSCompliant( false )]
  [CLSCompliant( false )]
  public int SetValue( UInt32 value );
  public int SetValue( UInt32 value );
  </source>
  </syntaxhighlight>
<br>
<br>
以下の例では、CLSCompliantAttribute属性をアセンブリ全体に適用する。<br>
以下の例では、CLSCompliantAttribute属性をアセンブリ全体に適用する。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  using System;
  using System;
  [assembly: CLSCompliant(true)]
  [assembly: CLSCompliant(true)]
  </source>
  </syntaxhighlight>
<br>
<br>
詳細は、以下のMSDNのWebサイトを参照すること。<br>
詳細は、以下のMSDNのWebサイトを参照すること。<br>
198行目: 206行目:
詳細は、以下のMSDNのWebサイトを参照すること。<br>
詳細は、以下のMSDNのWebサイトを参照すること。<br>
[https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.marshalasattribute.aspx MarshalAsAttribute クラス (System.Runtime.InteropServices) | MSDN]<br>
[https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.marshalasattribute.aspx MarshalAsAttribute クラス (System.Runtime.InteropServices) | MSDN]<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  // パラメータへの適用
  // パラメータへの適用
  public void M1([MarshalAs(UnmanagedType.LPWStr)]string msg)
  public void M1([MarshalAs(UnmanagedType.LPWStr)]string msg)
218行目: 226行目:
     return "Hello World";
     return "Hello World";
  }
  }
  </source>
  </syntaxhighlight>
<br><br>
<br><br>


234行目: 242行目:
</center>
</center>
<br>
<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  void Method([in] int[] array);
  void Method([in] int[] array);
  </source>
  </syntaxhighlight>
<br><br>
<br><br>


== C#のデータ型とWindows APIのデータ型 ==
== C#のデータ型とC++のデータ型 ==
<center>
<center>
{| class="wikitable"
{| class="wikitable" | style="background-color:#fefefe;"
|-
! style="background-color:#66CCFF;" | Windows APIのデータ型<br>(括弧内は対応するC言語の型)
! style="background-color:#66CCFF;" | 対応するC#のデータ型<br>(括弧内は.NET Frameworkでの型名)
! style="background-color:#66CCFF;" | 備考
|-
| HANDLE (void *)<br>HMODULE<br>HINSTANCE<br>HWND<br>HWM<br>HGDIOBJ<br>HDC || System.IntPtr<br>System.UIntPtr || x86は4バイト<br>x64は8バイト
|-
| char 配列名 [配列サイズ] || MarshalAs(UnmanagedType.ByValTStr, SizeConst = 配列のサイズ)] public string 配列名 ||
|-
| char ** || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder ||
|-
|-
! Windows APIのデータ型<br>(括弧内は対応するC言語の型) !! 対応するC#のデータ型<br>(括弧内は.NET Frameworkでの型名) !! 備考
| BYTE (unsigned char)<br>UCHAR || byte (System.Byte) || UCHAR型は、C#のstring型およびIntPtr型でも可能。
|-
|-
| HANDLE (void *) || System.IntPtr<br>System.UIntPtr || x86は4バイト<br>x64は8バイト
| BYTE[] (unsigned char *)<br>PBYTE (byte *) || System.Byte[]<br>[MarshalAs(UnmanagedType.LPArray)] byte[]<br>[MarshalAs(UnmanagedType.LPArray)] Intptr ||
|-
|-
| BYTE (unsigned char) || byte (System.Byte) ||  
| unsigned char & || ref byte ||  
|-
|-
| SHORT (short) || short (System.Int16) ||  
| SHORT (short) || short (System.Int16) ||  
253行目: 271行目:
| WORD (unsigned short) || ushort (System.UInt16) ||  
| WORD (unsigned short) || ushort (System.UInt16) ||  
|-
|-
| INT (int)<br>LONG (long) || int (System.Int32) ||  
| INT (int)<br>LONG (long)<br>DWORDLONG<br>HPARAM<br>LPARAM<br>WPARAM || int (System.Int32) || System.Int16でも取得できる可能性がある。
|-
| UINT (unsigned int)<br>DWORD (unsigned long)<br>ULONG (unsigned long) || uint (System.UInt32) ||
|-
| DECIMAL || decimal (System.Decimal) ||  
|-
|-
| UINT (unsigned int)<br>DWORD, ULONG (unsigned long) || uint (System.UInt32) ||  
| bool || bool (System.Boolean) ||  
|-
|-
| BOOL (long) || bool (System.Boolean) ||  
| BOOL (long) || bool (System.Boolean) ||  
261行目: 283行目:
| CHAR (char) || 文字を渡すとき<br>char (System.Char)<br>文字を受け取るとき<br>StringBuilder ||  
| CHAR (char) || 文字を渡すとき<br>char (System.Char)<br>文字を受け取るとき<br>StringBuilder ||  
|-
|-
| WCHAR(wchar_t) || 文字を渡すとき<br>char (System.Char)<br>文字を受け取るとき<br>StringBuilder ||  
| WCHAR (wchar_t) || 文字を渡すとき<br>char (System.Char)<br>文字を受け取るとき<br>StringBuilder ||  
|-
|-
| LPSTR (char *, char[])<br>LPWSTR (wchar_t *, wchar_t[]) || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder ||  
| PCAHR (char *)<br>LPSTR (char *, char[])<br>LPWSTR (wchar_t *, wchar_t[]) || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder ||  
|-
|-
| LPCSTR (const char *, const char[])<br>LPCWSTR (const wchar_t *, const wchar_t[]) || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder ||  
| LPCSTR (const char *, const char[])<br>LPCWSTR (const wchar_t *, const wchar_t[]) || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder ||  
|-
| LPTSTR (char * または wchar_t *) || 文字を渡すとき<br>[MarshalAs(UnmanagedType.LPTStr)] string<br>文字を受け取るとき<br>System.Text.StringBuilder ||
|-
| LPCTSTR (const char * または const wchar_t *) || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder ||
|-
| BSTR || 文字を渡すとき<br>string (System.String)<br>文字を受け取るとき<br>System.Text.StringBuilder ||
|-
|-
| FLOAT (float) || float (System.Single) ||  
| FLOAT (float) || float (System.Single) ||  
|-
| float <配列名>[]<br>float <配列名> [配列サイズ] || float[]<br>ref float[] ||
|-
|-
| DOUBLE (double) || double (System.Double) ||  
| DOUBLE (double) || double (System.Double) ||  
|-
| double <配列名>[]<br>double <配列名> [配列サイズ] || double[]<br>ref double[] ||
|-
| VARIANT || System.Object ||
|-
| COLORREF || uint ||
|-
| GUID || Guid ||
|-
| struct <構造体名> <変数名> || ref <構造体名> <変数名> ||
|-
| struct <構造体名> || public struct <構造体名> || <u>out <構造体名></u>であらかじめ構造体をインスタンス化して、変数名を宣言する。
|}
|}
</center>
</center>
<br><br>
<br><br>


== サンプルコード ==
== 整数型および浮動小数点型のマーシャリング ==
==== ポインタのマーシャリング ====
==== Linux ====
下記に、Linux向けC++ライブラリを記述する。<br>
<syntaxhighlight lang="c++">
// SampleLib.cpp
#include <cstdio>
#include <cstring>
extern "C" int __attribute__((visibility("default"))) SampleFunc01(int a)
{
    printf(--<SampleDll:SampleFunc01>--\r\n");
    printf("a = %d\r\n", a);
    printf("---------------------------\r\n");
    return 10;
}
extern "C" double __attribute__((visibility("default"))) SampleFunc02(double a)
{
    printf("--<SampleDll:SampleFunc02>--\r\n");
    printf("a = %f\r\n", a);
    printf("---------------------------\r\n");
    return 3.14;
}
</syntaxhighlight>
<br>
==== Windows ====
Windows向けC++ DLLの作成方法は[[ライブラリの基礎 - DLLの作成(C/C++/MFC)|ライブラリの基礎 - DLLの作成(C/C++/MFC)]]を参照する。<br>
<br>
下記に、C++ DLLを記述する。<br>
<syntaxhighlight lang="c++">
// SampleDLL.h
int __stdcall SampleFunc01(int a);
double  __stdcall SampleFunc02(double a);
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// SampleDll.cpp
#include <stdio.h>
#include <string.h>
#include "SampleDll.h"
int __stdcall SampleFunc01(int a)
{
    printf(--<SampleDll:SampleFunc01>--\r\n");
    printf("a = %d\r\n", a);
    printf("---------------------------\r\n");
    return 10;
}
double __stdcall SampleFunc02(double a)
{
    printf("--<SampleDll:SampleFunc02>--\r\n");
    printf("a = %f\r\n", a);
    printf("---------------------------\r\n");
    return 3.14;
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// SampleDll.def  // モジュール定義ファイル
LIBRARY SampleDll
EXPORTS
          ; 公開する関数名をリストアップ
          SampleFunc01  @1
          SampleFunc02  @2
</syntaxhighlight>
<br>
==== Linux / Windows ====
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
<br>
<syntaxhighlight lang="c#">
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace SampleEXE
{
    class Program
    {
      [DllImport("SampleDLL.dll")]  // Windows
      [DllImport("SampleLib.so")]  // Linux
      private static extern int SampleFunc01(int a);
      [DllImport("SampleDLL.dll")]  // Windows
      [DllImport("SampleLib.so")]  // Linux
      private static extern double SampleFunc02(double a);
      static void Main(string[] args)
      {
          var iRet= SampleFunc01(1);
          Console.WriteLine(iRet);
          Console.WriteLine();
          var dRet= SampleFunc01(0.01);
          Console.WriteLine(dRet);
          Console.WriteLine();
          Console.ReadKey();
      }
    }
}
</syntaxhighlight>
<br><br>
 
== ポインタのマーシャリング ==
例えば、C++ DLLから次のような関数がエクスポートされているとする。<br>
例えば、C++ DLLから次のような関数がエクスポートされているとする。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  void WINAPI ConvertToShort(char *pstr, short *pret);
  void WINAPI ConvertToString(char *pstrRet);
  </source>
void WINAPI ConvertToShort(short *psRet);
void WINAPI ConvertToDouble(double *pdRet);
  </syntaxhighlight>
<br>
<br>
上記の関数において、C# EXEから使用する時は、第1引数の<code>char*</code>型は文字列なので、<code>string</code>型または<code>StringBuilder</code>型を渡す。<br>
上記の関数において、C# EXEから使用する時、ConvertToString関数の引数の<code>char*</code>型は文字列なので、<code>string</code>型または<code>StringBuilder</code>型を渡す。<br>
第2引数の<code>short*</code>型は、<code>IntPtr</code>型を渡す。(<code>IntPtr</code>型は汎用ポインタを表す型であり、<code>void*</code>型とほぼ同義)<br>
ConvertToShort関数の引数の<code>short*</code>型は、<code>IntPtr</code>型を渡す。<br>
(<code>IntPtr</code>型は汎用ポインタを表す型であり、<code>void*</code>型とほぼ同義)<br>
ConvertToDouble関数の引数の<code>double*</code>型は、<code>IntPtr</code>型を渡す。<br>
<br>
<br>
ただし、C#は厳しい型付け言語なので、曖昧さを解決するために変換メソッドを経由する必要がある。<br>
ただし、C#は厳しい型付け言語のため、曖昧さを解決するために、変換メソッドを経由する必要がある。<br>
<br>
<br>
具体的には、<code>IntPtr</code>型の変数に<code>Marshal.AllocHGlobal</code>関数で必要なサイズのメモリを確保して、それをC++ DLLに渡した後、<br>
具体的には、<code>IntPtr</code>型の変数に<code>Marshal.AllocHGlobal</code>メソッドで必要なサイズのメモリを確保して、それをC++ DLLの関数へ受け渡しを行う。<br>
<code>Marshal.ReadInt16</code>関数(型によって異なる)等で変換した後、確保したメモリを<code>Marshal.FreeHGlobal</code>関数で解放するというプロセスを経る必要がある。<br>
<code>Marshal.ReadInt16</code>メソッド(型により異なる)等で変換した後、確保したメモリを<code>Marshal.FreeHGlobal</code>関数で解放するというプロセスを経る必要がある。<br>
<br>
<br>
以下の例では、C++ DLLを呼ぶC# EXEのソースコードを記述している。<br>
以下の例では、C++ DLLを呼ぶC# EXEのソースコードを記述している。<br>
  <source lang="c#">
  <syntaxhighlight lang="c#">
// DllImportを使用するために必要
  using System.Runtime.InteropServices; // DllImportを使用するために必要
  using System.Runtime.InteropServices;
   
   
  // 呼び出し元の関数名を変更する
  // 呼び出し元の関数名を変更する
[DllImport("DrsUtil.dll", EntryPoint = "ConvertToShort")]
extern static void _ConvertToShort(string pstr, IntPtr pret);
   
   
  public static short ConvertToShort(string str)
// C++ DLL側の文字コードがUnicodeの場合は"CharSet = CharSet.Unicode"と明示的に指定する
[DllImport("Sample.dll", EntryPoint = "ConvertToString", CharSet = CharSet.Unicode)]
extern static void _ConvertToString(StringBuilder pstrRet);
[DllImport("Sample.dll", EntryPoint = "ConvertToShort")]
extern static void _ConvertToShort(IntPtr psRet);
[DllImport("Sample.dll", EntryPoint = "ConvertToDouble")]
extern static void _ConvertToDouble(IntPtr pdRet);
public static string ConvertToString()
{
    // 値を受け渡しを行う場合、StringBuilderクラスを使用する
    var strRet = = new System.Text.StringBuilder(256);
    // 値を渡すだけの場合、string型を使用する
    //string strRet = "";
 
    // C++ DLLの関数を呼ぶ
    _ConvertToString(strRet);
    return strRet;
}
 
  public static short ConvertToShort()
  {
  {
     // 2バイトのメモリ確保
     // 2バイトのメモリ確保
305行目: 484行目:
   
   
     // C++ DLLの関数を呼ぶ
     // C++ DLLの関数を呼ぶ
     _ConvertToShort(str, buffer);
     _ConvertToShort(buffer);
   
   
     // 2バイトのメモリをshort型に変換
     // 2バイトのメモリをshort型に変換
     short sval = Marshal.ReadInt16(buffer);
     short sRet = Marshal.ReadInt16(buffer);
   
   
     // メモリの開放
     // メモリの開放
     Marshal.FreeHGlobal(buffer);
     Marshal.FreeHGlobal(buffer);
   
   
     return sval;
     return sRet;
}
public static double ConvertToDouble()
{
    // 8バイトのメモリ確保
    IntPtr buffer = new IntPtr();
    buffer = Marshal.AllocHGlobal(8);
    // C++ DLLの関数を呼ぶ
    _ConvertToDouble(buffer);
    // IntPtr型からdouble型の数値を取得する
    // Int64型へ変換後、BitConverter.Int64BitsToDoubleメソッドでdouble型に変換する
    var i64Ret = Marshal.ReadInt64(buffer);
    var dRet  = BitConverter.Int64BitsToDouble(i64Ret);
    // 必ずメモリを解放する
    Marshal.FreeHGlobal(buffer);
    return dRet;
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
<code>IntPtr</code>型の変数は様々なものが入る。<br>
<code>IntPtr</code>型の変数は様々なものが入る。<br>
ただし、構造体を取得することもできるが、C# EXEで構造体を定義しなければならない。<br>
構造体を取得することもできるが、C# EXEで構造体を定義しなければならない。<br>
ネイティブコードと.NET Frameworkでは型の管理方法が違うため、実際には型の相互変換(マーシャリング)が行われる。<br>
ネイティブコードと.NET Frameworkでは型の管理方法が違うため、実際には型の相互変換(マーシャリング)が行われる。<br>
<br>
<br>
なお、Windows APIではBOOL型の実体はLONG型なので、.NET Frameworkではboolの代わりにintを指定することも可能である。<br>
なお、Windows APIではBOOL型の実体はLONG型なので、.NET Frameworkではboolの代わりにintを指定することも可能である。<br>
<br>
<br><br>
==== 配列のマーシャリング ====
 
== 配列のマーシャリング ==
このセクションでは、C# EXEからC++ DLLへ配列を渡す方法を記載する。<br>
このセクションでは、C# EXEからC++ DLLへ配列を渡す方法を記載する。<br>
<br>
<br>
まず、以下にC++ DLLを記述する。<br>
まず、以下にC++ DLLを記述する。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // SampleDLL.h
  // SampleDLL.h
   
   
  #pragma once
  #pragma once


  int __stdcall SampleArray(int array[], int length);
  void __stdcall SampleArray01(int array[], int length);
  </source>
int* __stdcall SampleArray02(int length);
void __stdcall SampleArray03(int array[], int length);
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // SampleDll.cpp
  // SampleDll.cpp
   
   
342行目: 544行目:
  #include "SampleDll.h"
  #include "SampleDll.h"
   
   
  void __stdcall SampleArray(int array[], int length)
  void __stdcall SampleArray01(int array[], int length)
  {
  {
     for (int i = 0; i < length; i++)
     for (int i = 0; i < length; i++)
349行目: 551行目:
     }
     }
  }
  }
  </source>
int* __stdcall SampleArray02(int length)
{
    int* iary = new int[length];
    for (int i = 0; i < length; i++)
    {
        iary[i] = i;
    }
    return iary;
}
void __stdcall SampleArray03(int array[], int length)
{
    for (int i = 0; i < length; i++)
    {
      arr[i] = i;
    }
}
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // モジュール定義ファイル
  // モジュール定義ファイル
  // SampleDll.def
  // SampleDll.def
359行目: 581行目:
  EXPORTS
  EXPORTS
           ; 公開する関数名をリストアップ
           ; 公開する関数名をリストアップ
           SampleArray   @1
           SampleArray01   @1
  </source>
          SampleArray02  @2
          SampleArray03  @3
  </syntaxhighlight>
<br>
<br>
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
371行目: 595行目:
# 使用したアンマネージド配列のメモリを解放する。
# 使用したアンマネージド配列のメモリを解放する。
<br>
<br>
  <source lang="c#">
  <syntaxhighlight lang="c#">
  namespace SampleEXE
  namespace SampleEXE
  {
  {
     [DllImport("SampleDll.dll", CallingConvention = CallingConvention.Cdecl)]
     [DllImport("SampleDll.dll", EntryPoint = "SampleArray01", CallingConvention = CallingConvention.Cdecl)]
     static extern void SampleArray(IntPtr array, int length);
     static extern void _SampleArray01(IntPtr array, int length);
    [DllImport("SampleDll.dll", EntryPoint = "SampleArray02", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr _SampleArray02(int length);
    [DllImport("SampleDll.dll", EntryPoint = "SampleArray03", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr _SampleArray03(IntPtr array, int length);
   
   
     static void Main()
     static void Main()
    {
      SampleArray01();
      SampleArray02();
      SampleArray03();
    }
    // SampleArray01
    // 配列を渡す場合
    private static void SampleArray01()
     {
     {
       // 配列の定義
       // 配列の定義
       var array = new int[Length] { 0, 1, 2, 3, 4 };
       var array = new int[] { 0, 1, 2, 3, 4 };
   
   
       // アンマネージド配列のメモリの確保
       // アンマネージド配列のメモリの確保
389行目: 628行目:
   
   
       // C++ DLLに配列を渡す(ポインタを渡す)
       // C++ DLLに配列を渡す(ポインタを渡す)
       SampleArray(ptrRet, array.Length);
       _SampleArray01(ptrRet, array.Length);
   
   
       // アンマネージドメモリの解放
       // アンマネージドメモリの解放
      Marshal.FreeCoTaskMem(ptrRet); 
    }
    // SampleArray02
    // 配列を受ける場合 (戻り値がint型ポインタ)
    private static void SampleArray02()
    {
      // 空の配列の定義
      int[] array = new int[5];
      // 戻り値のポインタをIntPtrで受け取る
      IntPtr ptrRet = _SampleArray02(array.Length);
      // アンマネージド配列からマネージド配列へコピー
      Marshal.Copy(ptrRet, array, 0, array.Length);
      foreach(int n in array)
      {
          Console.WriteLine(n + " ");
      }
    }
    // SampleArray03
    // 配列を受ける場合 (引数がint型の配列)
    private static void SampleArray03()
    {
      // 空の配列の定義
      int[] array = new int[5];
      // アンマネージド配列のメモリを確保
      IntPtr ptrRet = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * array.Length);
      // 引数でポインタを渡す
      _SampleArray03(ptrRet, array.Length);
      // アンマネージド配列からマネージド配列へコピー
      Marshal.Copy(ptrRet, array, 0, array.Length);
      // アンマネージド配列のメモリを解放
       Marshal.FreeCoTaskMem(ptrRet);
       Marshal.FreeCoTaskMem(ptrRet);
      foreach(int n in array)
      {
          Console.WriteLine(n + " ");
      }
     }
     }
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br><br>
==== 構造体のマーシャリング ====
 
== 文字列のマーシャリング (1) ==
C++ DLLの作成方法は[[ライブラリの基礎 - DLLの作成(C/C++/MFC)|ライブラリの基礎 - DLLの作成(C/C++/MFC)]]を参照する。<br>
C++ DLLの作成方法は[[ライブラリの基礎 - DLLの作成(C/C++/MFC)|ライブラリの基礎 - DLLの作成(C/C++/MFC)]]を参照する。<br>
<br>
<br>
下記にもC++ DLLを記述する。<br>
下記にもC++ DLLを記述する。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  SampleDLL.h
  // SampleDLL.h
   
   
  double __stdcall SampleFunc01(int a);
  int            __stdcall Mul(int x, int y)
  void   __stdcall SampleFunc02(int a, char *pstr)
  void           __stdcall MulUsePointer(int *x, int *y, int *result)
  void   __stdcall SampleFunc03(int a, char *pstr)
  void           __stdcall SetNameStr(const char *t)
  void  __stdcall SampleFunc04(SampleStruct st)
  const char*    __stdcall GetNameStr()
  void   __stdcall SampleFunc05(SampleStruct *pStructure)
  void           __stdcall SetNameWStr(const wchar_t *t)
  </source>
const wchar_t* __stdcall GetNameWStr()
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  SampleDll.cpp
  // SampleDll.cpp
   
   
  #include <stdio.h>
  #include <stdio.h>
418行目: 703行目:
  #include "SampleDll.h"
  #include "SampleDll.h"
   
   
  typedef struct tagSampleStruct
  static std::string _str  = "";
static std::wstring _wstr = L"";
int __stdcall Mul(int x, int y)
{
    return x * y;
}
void __stdcall MulUsePointer(int *x, int *y, int *result)
{
    *result = (*x) * (*y);
}
void __stdcall SetNameStr(const char *t)
{
    _str = std::string(t);
}
const char* __stdcall GetNameStr()
{
    return _str.c_str();
}
void __stdcall SetNameWStr(const wchar_t *t)
{
    _wstr = std::wstring(t);
}
const wchar_t * __stdcall GetNameWStr()
{
    return _wstr.c_str();
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// SampleDll.def  // モジュール定義ファイル
LIBRARY SampleDll
EXPORTS
          ; 公開する関数名をリストアップ
          Mul            @1
          MulUsePointer  @2
          SetNameStr      @3
          GetNameStr      @4
          SetNameWStr    @5
          GetNameWStr    @6
</syntaxhighlight>
<br>
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
<br>
DllImpoort文は、DLLファイル名を指定する。<br>
この時、C#の実行ファイルと同一階層にDLLファイルがある場合、DLLファイル名のみを指定するだけでよい。<br>
異なる階層にDLLファイルがある場合、フルパスで指定する必要がある。<br>
<br>
C++ DLLの関数を実行する時、<u>"エントリーポイントが見つかりません。"</u>とエラーが出力される場合、DllImport文に誤りがある可能性が高い。<br>
<br>
<syntaxhighlight lang="c#">
using System;
using System.Runtime.InteropServices;
namespace SampleCSharp
{
    class Program
    {
      [DllImport("SampleDLL", EntryPoint = "Mul")]
      static extern int _Mul(int x, int y);
      [DllImport("SampleDLL", EntryPoint = "MulUsePointer")]
      static extern void _MulUsePointer(ref int x, ref int y, out int result);
      [DllImport("SampleDLL", EntryPoint = "SetNameStr", CharSet = CharSet.Ansi)]
      static extern void _SetNameStr(string t);
      [DllImport("SampleDLL", EntryPoint = "GetNameStr", CharSet = CharSet.Ansi)]
      static extern IntPtr _GetNameStr();
      [DllImport("SampleDLL", EntryPoint = "SetNameWStr", CharSet = CharSet.Unicode)]
      static extern void _SetNameWStr(string t);
      [DllImport("SampleDLL", EntryPoint = "GetNameWStr", CharSet = CharSet.Unicode)]
      static extern IntPtr _GetNameWStr();
      static void Main(string[] args)
      {
          int x = 10;
          int y = 20;
          int resultMul = _Mul(x, y);
          Console.WriteLine("Mul = " + resultMul);
          _MulUsePointer(ref x, ref y, out int resultMulUsePointer);
          Console.WriteLine("MulUsePointer = " + resultMulUsePointer);
          string testString = "東京都新宿 1-1";
          _SetNameStr(testString);
          Console.WriteLine("GetNameStr = " + Marshal.PtrToStringAnsi(_GetNameStr()));
          _SetNameWStr(testString);
          Console.WriteLine("GetNameWStr = " + Marshal.PtrToStringUni(_GetNameWStr()));
      }
    }
}
</syntaxhighlight>
<br><br>
 
== 文字列のマーシャリング (2) ==
==== Linux ====
Linux向けC++ライブラリを記述する。<br>
<syntaxhighlight lang="c++">
// SampleLib.cpp
#include <cstdio>
extern "C" double __attribute__((visibility("default"))) SampleFunc01(int a)
{
    printf("--<SampleDll:SampleFunc01>--\r\n");
    printf("a = %d\r\n", a);
    printf("---------------------------\r\n");
    return 3.14;
}
extern "C" void __attribute__((visibility("default"))) SampleFunc02(int a, char *pstr)
  {
  {
     int index;
     printf("--<SampleDll:SampleFunc02>--\r\n");
     char name[128];
     printf("[%d] %s\r\n", a, pstr);
     int data[50];
     printf("-----------------------------\r\n");
  } SampleStruct, *pSampleStruct;
  }
   
   
  typedef struct tagSampleStruct2
  extern "C" void __attribute__((visibility("default"))) SampleFunc03(int a, char *pstr)
  {
  {
     int length;
     printf("--<SampleDll:SampleFunc03>--\r\n");
     double *data;
    printf("[%d] %s\r\n", a, pstr);
  } SampleStruct2, *pSampleStruct2;
 
    sprintf(pstr, u8"C++ DLL側から文字列を返す場合は、StringBuilderクラスを使用する");
     printf("------------------------\r\n");
}
</syntaxhighlight>
<br>
==== Windows ====
Windows向けC++ DLLの作成方法は[[ライブラリの基礎 - DLLの作成(C/C++/MFC)|ライブラリの基礎 - DLLの作成(C/C++/MFC)]]を参照する。<br>
<br>
下記に、C++ DLLを記述する。<br>
<syntaxhighlight lang="c++">
// SampleDLL.h
double __stdcall SampleFunc01(int a);
void  __stdcall SampleFunc02(int a, char *pstr)
  void  __stdcall SampleFunc03(int a, char *pstr)
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// SampleDll.cpp
#include <stdio.h>
#include <string.h>
#include "SampleDll.h"
   
   
  double __stdcall SampleFunc01(int a)
  double __stdcall SampleFunc01(int a)
454行目: 885行目:
     printf("------------------------\r\n");
     printf("------------------------\r\n");
  }
  }
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// SampleDll.def  // モジュール定義ファイル
LIBRARY SampleDll
EXPORTS
          ; 公開する関数名をリストアップ
          SampleFunc01  @1
          SampleFunc02  @2
          SampleFunc03  @3
</syntaxhighlight>
<br>
==== Linux / Windows ====
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
<br>
文字列をC++ DLL側に渡す場合は、<code>string</code>型を使用する。<br>
文字列をC++ DLL側から返す場合は、<code>StringBuilder</code>クラスを使用する。<br>
<u><code>StringBuilder</code>クラスは受け渡しの両方が可能なので、文字列は全て<code>StringBuilder</code>クラスを使用すべきである。</u><br>
<br>
<syntaxhighlight lang="c#">
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace SampleEXE
{
    class Program
    {
      /// <summary>
      /// 最も基本的な関数のインポート例
      /// </summary>
      /// <param name="a">4 バイト符号付き整数を指定します。</param>
      /// <returns>倍精度浮動小数を返します。</returns>
      [DllImport("SampleDLL.dll")]  // Windows
      [DllImport("SampleLib.so")]  // Linux
      private static extern double SampleFunc01(int a);
      [DllImport("SampleDLL.dll", CharSet = CharSet.Unicode)]  // Windows
      [DllImport("SampleLib.so", CharSet = CharSet.Auto)]      // Linux
      // C++ DLL側の文字コードがUnicodeの場合は"CharSet = CharSet.Unicode"と明示的に指定する
      private static extern void SampleFunc02(int a, string str);
      [DllImport("SampleDLL.dll", CharSet = CharSet.Unicode)]  // Windows
      [DllImport("SampleLib.so", CharSet = CharSet.Auto)]      // Linux
      // C++ DLL側の文字コードがUnicodeの場合は"CharSet = CharSet.Unicode"と明示的に指定する
      private static extern void SampleFunc03(int a, StringBuilder str);
      static void Main(string[] args)
      {
          var dRet= SampleFunc01(1);
          Console.WriteLine(dRet);
          Console.WriteLine();
          var str = "string型で文字列を渡すことができます。";
          SampleFunc02(2, str);
          var strb = new System.Text.StringBuilder(256);
          strb.Append("文字列のバッファを渡す場合は StringBuilder クラスで受け渡します。");
          SampleFunc03(3, strb);
          Console.WriteLine(strb);
          Console.ReadKey();
      }
    }
}
</syntaxhighlight>
<br><br>
== 構造体のマーシャリング ==
C++ DLLの作成方法は[[ライブラリの基礎 - DLLの作成(C/C++/MFC)|ライブラリの基礎 - DLLの作成(C/C++/MFC)]]を参照する。<br>
<br>
下記にもC++ DLLを記述する。<br>
<syntaxhighlight lang="c++">
// SampleDLL.h
void  __stdcall DisplayFunc(SampleStruct1 Structure)
void  __stdcall GetFunc(SampleStruct1 Structure)
void  __stdcall SetFunc(SampleStruct2 *pStructure)
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// SampleDll.cpp
#include <stdio.h>
#include <string.h>
#include "SampleDll.h"
typedef struct tagSampleStruct
{
    int  index;
    char name[128];
    int  data[50];
} SampleStruct1, *pSampleStruct;
   
   
  void __stdcall SampleFunc04(SampleStruct st)
  typedef struct tagSampleStruct2
  {
  {
     printf("--<SampleDll:Sample04>--\r\n");
    int    length;
    double *data;
} SampleStruct2, *pSampleStruct2;
void __stdcall DisplayFunc(SampleStruct1 st)
{
     printf("--<SampleDll:DisplayFunc>--\r\n");
     printf("index = %d\r\n", st.index);
     printf("index = %d\r\n", st.index);
     printf("name = %s\r\n", st.name);
     printf("name = %s\r\n", st.name);
464行目: 996行目:
  }
  }
   
   
  void __stdcall SampleFunc05(SampleStruct2 *pStructure)
  void __stdcall GetFunc(SampleStruct1 *pst)
  {
  {
     dData[256] = {0};
     printf("--<SampleDll:GetFunc>--\r\n");
     printf("--<SampleDll:Sample05>--\r\n");
    pst->index = 10;
    memcpy_s(pst->name, sizeof(pst->name), "Tarou Yamada", sizeof("Tarou Yamada"));
    memset(pst->data, 20, sizeof(pst->data));
    printf("------------------------\r\n");
}
void __stdcall SetFunc(SampleStruct2 *pStructure)
{
     printf("--<SampleDll:SetFunc>--\r\n");
     memset(pStructure, 0, sizeof(SampleStruct2));
     memset(pStructure, 0, sizeof(SampleStruct2));
     pStructure->length = 10;
     pStructure->data = dData;
    // double dAryData[256] = {0.0f};
     pStructure->length                 = 10;
    double dAryData[pStructure->length] = {0.0f};
     pStructure->data                   = dAryData;
     for(int i = 0; i < pStructure->length; i++)
     for(int i = 0; i < pStructure->length; i++)
     {
     {
       dData[i] = (i + 1) / 10.0;
       dAryData[i] = (i + 1) / 10.0f;
     }
     }
     printf("------------------------\r\n");
     printf("------------------------\r\n");
  }
  }
  </source>
  </syntaxhighlight>
  <source lang="c++">
<br>
  SampleDll.def  // モジュール定義ファイル
  <syntaxhighlight lang="c++">
  // SampleDll.def  // モジュール定義ファイル
   
   
  LIBRARY SampleDll
  LIBRARY SampleDll
485行目: 1,032行目:
  EXPORTS
  EXPORTS
           ; 公開する関数名をリストアップ
           ; 公開する関数名をリストアップ
           SampleFunc01   @1
           DisplayFunc   @1
           SampleFunc02  @2
           GetFunc      @2
           SampleFunc03  @3
           SetFunc      @3
          SampleFunc04  @4
  </syntaxhighlight>
          SampleFunc05  @5
  </source>
<br>
<br>
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
文字列をC++ DLL側に渡す場合は、string型を使用する。<br>
文字列をC++ DLL側から返す場合は、string型ではなくStringBuilderクラスを使用する必要がある。<br>
StringBuilderクラスは受け渡しの両方が可能なので、文字列はStringBuilderクラスを使用すべきである。<br>
<br>
<br>
C++では構造体のサイズはコンパイル時に決定されるが、C#では実行時に決定される。<br>
C++では構造体のサイズはコンパイル時に決定されるが、C#では実行時に決定される。<br>
したがって、C#側で構造体のサイズを予め指定する必要がある。<br>
したがって、C#側で構造体のサイズを予め指定する必要がある。<br>
この場合、構造体は固定長サイズとなるため、配列などを定義する場合は異なるサイズの配列を後からインスタンス化することができなくなる。<br>
この時、構造体は固定長サイズとなるため、配列等を定義する場合は異なるサイズの配列を後からインスタンス化することができなくなる。<br>
<br>
<br>
構造体をC++ DLL側から返す場合、IntPtr型からdouble型の配列を取得するときは、一度Int64型に変換し、これをBitConverter.Int64BitsToDouble()メソッドでdouble型に変換する。<br>
構造体をC++ DLL側から返す場合、<code>IntPtr</code>型から<code>double</code>型の配列を取得する時は、<code>Int64</code>型へ変換した後、<code>BitConverter.Int64BitsToDouble</code>メソッドで<code>double</code>型に変換する。<br>
  <source lang="c#">
  <syntaxhighlight lang="c#">
  using System;
  using System;
  using System.Text;
  using System.Text;
512行目: 1,054行目:
     {
     {
       /// <summary>
       /// <summary>
       /// 最も基本的な関数のインポート例
       /// 構造体を引数に持つ関数のインポート例
       /// </summary>
       /// </summary>
       /// <param name="a">4 バイト符号付き整数を指定します。</param>
       /// <param name="st">DLL 側に渡す構造体を指定します</param>
      /// <returns>倍精度浮動小数を返します。</returns>
       [DllImport("SampleDLL.dll", EntryPoint = "DisplayFunc", CallingConvention = CallingConvention.Cdecl)]
      [DllImport("SampleDLL.dll")]
       private static extern void _DisplayFunc(SampleStruct1 st);
      private static extern double SampleFunc01(int a);
       [DllImport("SampleDLL.dll", CharSet = CharSet.Unicode)]
      // C++ DLL側の文字コードがUnicodeの場合は"CharSet = CharSet.Unicode"と明示的に指定する
      private static extern void SampleFunc02(int a, string str);
      [DllImport("SampleDLL.dll", CharSet = CharSet.Unicode)]
      // C++ DLL側の文字コードがUnicodeの場合は"CharSet = CharSet.Unicode"と明示的に指定する
       private static extern void SampleFunc03(int a, StringBuilder str);
   
   
       /// <summary>
       /// <summary>
       /// 構造体を引数に持つ関数のインポート例
       /// DLL側からメンバにポインタを含む構造体を受け取る関数のインポート例
       /// </summary>
       /// </summary>
       /// <param name="st">DLL 側に渡す構造体を指定します</param>
       /// <param name="pst">受け渡す構造体の先頭アドレスを示すポインタを指定する</param>
       [DllImport("SampleDLL.dll")]
       [DllImport("SampleDLL.dll", EntryPoint = "GetFunc", CallingConvention = CallingConvention.Cdecl)]
       private static extern void SampleFunc04(SampleStruct st);
       private static extern void _GetFunc(IntPtr pst);
   
   
       /// <summary>
       /// <summary>
538行目: 1,071行目:
       /// </summary>
       /// </summary>
       /// <param name="pst">受け渡す構造体の先頭アドレスを示すポインタを指定する</param>
       /// <param name="pst">受け渡す構造体の先頭アドレスを示すポインタを指定する</param>
       [DllImport("SampleDLL.dll")]
       [DllImport("SampleDLL.dll", EntryPoint = "SetFunc", CallingConvention = CallingConvention.Cdecl)]
       private static extern void SampleFunc05(IntPtr pst);
       private static extern void _SetFunc(IntPtr pst);
   
   
       /// <summary>
       /// <summary>
545行目: 1,078行目:
       /// LayoutKind.Sequentialを指定することで、C/C++同様、変数の宣言順通りにメモリに配置されるようになる
       /// LayoutKind.Sequentialを指定することで、C/C++同様、変数の宣言順通りにメモリに配置されるようになる
       /// </summary>
       /// </summary>
       [StructLayout(LayoutKind.Sequential)]
       [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
       private struct SampleStruct
       private struct SampleStruct1
       {
       {
           /// <summary>
           /// <summary>
580行目: 1,113行目:
       static void Main(string[] args)
       static void Main(string[] args)
       {
       {
           var dRet= SampleFunc01(1);
           SampleFunc01();
           Console.WriteLine(dRet);
           SampleFunc02();
           Console.WriteLine();
           SampleFunc03();
   
   
           var str = "string型で文字列を渡すことができます。";
           Console.ReadKey();
          SampleFunc02(2, str);
      }
   
   
          var strb = new System.Text.StringBuilder(256);
      private static void SampleFunc01()
          strb.Append("文字列のバッファを渡す場合は StringBuilder クラスで受け渡します。");
      {
          SampleFunc03(3, strb);
           var structure = new SampleStruct1()
          Console.WriteLine(strb);
           var structHoge = new SampleStruct()
           {
           {
             index = 4,
             index = 4,
599行目: 1,129行目:
           };
           };
   
   
           structHoge.data[0] = 11;
           structure.data[0] = 11;
           structHoge.data[1] = 22;
           structure.data[1] = 22;
           structHoge.data[2] = 33;
           structure.data[2] = 33;
           SampleFunc04(structHoge);
          _SampleFunc01(structure);
      }
      private static void SampleFunc02()
      {
          var structure = new SampleStruct1()
          {
            index = 0,
            name = "",
            data = new int[50],
          };
           // COMタスクメモリアロケータから、C#の構造体のサイズ分のメモリブロックを割り当てる
          IntPtr structurePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(structure));
   
   
          _GetFunc(structurePtr);
          // Marshal.PtrToStructureメソッドを使用して、IntPtr変数が示すメモリに格納された情報をC#の構造体にコピーする
          structure = (SampleStruct1)Marshal.PtrToStructure(structurePtr, structure.GetType());
          Marshal.FreeCoTaskMem(structurePtr);
          Console.WriteLine($"index : {structure.index}");
          Console.WriteLine($"name : {structure.name}");
          Console.WriteLine($"data : {structure.data}");
      }
      private static void SampleFunc03()
      {
           // SampleStruct2構造体のサイズを取得する
           // SampleStruct2構造体のサイズを取得する
           // 指定サイズ分だけメモリ領域を確保して、その先頭アドレスをstructPiyoに格納する
           // 指定サイズ分だけメモリ領域を確保して、その先頭アドレスをstructure1に格納する
           var structPiyo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SampleStruct2)));
           var structure1 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SampleStruct2)));
           try
           try
           {
           {
             SampleFunc05(structPiyo);
             // C++ DLLのSampleFunc02関数の実行
             // 受け取ったstructPiyoからSampleStruct2構造体の情報に構築し直す
            SetFunc(structure1);
             var structFuga = (SampleStruct2)Marshal.PtrToStructure(structPiyo, typeof(SampleStruct2));
             for (int i = 0; i < structFuga.length; i++)
             // 受け取ったstructure1からSampleStruct2構造体の情報に構築し直す
             var structure2 = (SampleStruct2)Marshal.PtrToStructure(structure1, typeof(SampleStruct2));
             for (int i = 0; i < structure2.length; i++)
             {
             {
                 // IntPtr型からdouble型の数値を取得するときは、一度Int64型に変換して、これをBitConverter.Int64BitsToDoubleメソッドでdouble型に変換する
                 // IntPtr型からdouble型の数値を取得するときは、一度Int64型に変換して、これをBitConverter.Int64BitsToDoubleメソッドでdouble型に変換する
                 var v = Marshal.ReadInt64(structFuga.data, i * sizeof(double));
                 var v = Marshal.ReadInt64(structure2.data, i * sizeof(double));
                 Console.WriteLine("data[{0}] = {1}", i, BitConverter.Int64BitsToDouble(v));
                 Console.WriteLine("data[{0}] = {1}", i, BitConverter.Int64BitsToDouble(v));
             }
             }
626行目: 1,186行目:
           {
           {
             // 必ずメモリを解放する
             // 必ずメモリを解放する
             Marshal.FreeHGlobal(sample06_a);
             Marshal.FreeHGlobal(structure1);
           }
           }
          Console.ReadKey();
       }
       }
     }
     }
  }
  }
  </source>
  </syntaxhighlight>
<br><br>
 
== 関数ポインタのマーシャリング ==
C# EXEからC++ DLLへコールバック関数を渡す。<br>
<br>
<br>
==== 関数ポインタのマーシャリング ====
ただし、<code>Func</code>型、<code>Action</code>型、構造体内にある<code>char</code>型の配列のようなNon-Bittable型(非Blittable型)が含まれている場合、以下の例外が発生する。<br>
このセクションでは、C# EXEからC++ DLLへコールバック関数を渡す方法を記載する。<br>
このような型の場合は、属性を用いたマーシャルの変換は不可能である。<br>
Non-blittable generic types cannot be marshaled.
<br>
<br>
まず、以下にC++ DLLを記述する。<br>
==== Linux ====
  <source lang="c++">
Linux向けのC++ライブラリを記述する。<br>
<syntaxhighlight lang="c++">
// SampleLib.cpp
#include <cstdio>
#include <cstring>
using UnManagedCallback = bool (*)(int);
int __stdcall SampleCallback(UnManagedCallback CallbackFunc, int lPalam)
{
    int iRet = 0;
 
    bool bRet = CallbackFunc(lPalam);
    if(bRet)
    {
      iRet = 1;
    }
    else
    {
      iRet = -1;
    }
    return iRet;
}
</syntaxhighlight>
<br>
==== Windows ====
Windows向けのC++ DLLを記述する。<br>
  <syntaxhighlight lang="c++">
  // SampleDLL.h
  // SampleDLL.h
   
   
645行目: 1,237行目:


  int __stdcall SampleCallback(int handle, int lPalam);
  int __stdcall SampleCallback(int handle, int lPalam);
  </source>
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // SampleDll.cpp
  // SampleDll.cpp
   
   
672行目: 1,264行目:
     return iRet;
     return iRet;
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // モジュール定義ファイル
  // モジュール定義ファイル
  // SampleDll.def
  // SampleDll.def
683行目: 1,275行目:
           ; 公開する関数名をリストアップ
           ; 公開する関数名をリストアップ
           SampleCallback  @1
           SampleCallback  @1
  </source>
  </syntaxhighlight>
<br>
<br>
==== Linux / Windows ====
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
次に、C# EXEからC++ DLLを呼び出す方法を記述する。<br>
<br>
<br>
690行目: 1,283行目:
以下の例では、ManagedCallBackというデリゲートを宣言している。<br>
以下の例では、ManagedCallBackというデリゲートを宣言している。<br>
C++ DLLのSampleCallBack関数にデリゲートを引数として渡すことで、既知のコールバック形式に自動的に変換される。<br>
C++ DLLのSampleCallBack関数にデリゲートを引数として渡すことで、既知のコールバック形式に自動的に変換される。<br>
  <source lang="c#">
  <syntaxhighlight lang="c#">
  using System;
  using System;
  using System.Runtime.InteropServices;
  using System.Runtime.InteropServices;
700行目: 1,293行目:
     class Program
     class Program
     {
     {
         [DllImport("SampleDLL.dll")]
         [DllImport("SampleDLL.dll")] // Windows
        [DllImport("SampleLib.so")]  // Linux
         private static extern int SampleCallback(ManagedCallBack CallBack, int lPalam);
         private static extern int SampleCallback(ManagedCallBack CallBack, int lPalam);


721行目: 1,315行目:
     }
     }
  }
  }
  </source>
  </syntaxhighlight>
<br><br>


== トラブル対処 ==
== トラブル対処 ==