NonSoft

VB6とC#.NETのAPI宣言(Declare)の違いを
比較したサンプル


 サンプル(ソース/コード)
長年VB6のプログラマをやっていた技術者がC#.NETでプログラムを作る時によく悩むのが
APIの宣言方法と引数の渡し方だと思います。特にVB6でAny、String、構造体を使っていた時に
C#.NETではどうするのか、、、長時間、検索エンジンに頼る事になると思います。
(C#.NETでAPIなんか使うな!! とか言わないの・・・)

VB6とC#.NETのDeclare(宣言)方法の違いのポイントは以下のような感じです。
(1)VB6のDeclareはC#.NETの[DllImport("mydll.dll")]に対応します。
(2)VB6のLongは4バイトなのでVB.NETのIntegerは4バイト(Longは8バイト)に対応します。
(3)VB6の"String * 10"のような変数の宣言はC#.NETの"new String((char)0, 10)"に対応します。
  しかしC#.NETではString型に文字列を受け取れないようなのでnew StringBuilder(10)を使用します。
  ただし、構造体のメンバ変数の場合は(3)参照。
(4)構造体のメンバ変数が配列の場合の対応は以下のようになります。(サンプル有り)
  ・文字列の場合
     (VB6   の場合)s2 As String * 7
      (C#.NETの場合)[MarshalAs(UnmanagedType.ByValTStr, SizeConst=7)] String s2;
  ・配列の場合
     (VB6   の場合)s4(1) As Long
      (C#.NETの場合)[MarshalAs(UnmanagedType.ByValArray, SizeConst=2)] int[] s4;
(5)C#.NETにはAnyのような曖昧な宣言は出来ないので、最適な型に置き換えます。
(6)VB6のVariantはC#.NETのobjectに対応します。

サンプルソースを見た方が速いと思いますので、
VB6でのDeclare(宣言)方法とC#.NETでのDeclare(宣言)方法を比較したサンプルソースを以下に載せます。

このサンプルはVB.NETで作ったものをC#.NETに移植したものです。
オリジナルはVB6とVB.NETのAPI宣言(Declare)の違いを比較したサンプルです。
VB6とVB.NETとC#.NETのAPI宣言の比較が出来ます。

ちなみに、今回使用したAPIはVC++で作ったサンプルで、第一引数を第二引数にコピーする仕様です。
戻り値は第一引数のサイズを返します。VC++でDLLを作成するサンプルはこちらです。
VCからもVBからも呼び出せるDLLのサンプル(VC6)


<比較結果詳細> 1.VC++で作ったAPIのサンプルソース typedef struct { char s1; char s2[7]; int s3; int s4[2]; } MYSTRUCT; __declspec(dllexport) int __stdcall MyCopyChar(char v1, char *v2){ *v2 = v1; return sizeof(v1); } __declspec(dllexport) int __stdcall MyCopyString(char *v1, char *v2){ strcpy(v2, v1); return strlen(v1); } __declspec(dllexport) int __stdcall MyCopyInteger(int v1, char *v2){ *v2 = v1; return sizeof(v1); } __declspec(dllexport) int __stdcall MyCopyStruct(MYSTRUCT *v1, MYSTRUCT *v2){ v2->s1 = v1->s1; memcpy(v2->s2, v1->s2, sizeof(v1->s2)); v2->s3 = v1->s3; memcpy(v2->s4, v1->s4, sizeof(v1->s4)); return sizeof(*v1); }
2.VB6からVC++のAPIを呼ぶサンプルソース Private Type MYSTRUCT s1 As Byte s2 As String * 7 s3 As Long s4(1) As Long End Type Private Declare Function MyCopyChar Lib "mydll.dll" _ (ByVal v1 As Byte, ByRef v2 As Byte) As Long Private Declare Function MyCopyString Lib "mydll.dll" _ (ByVal v1 As String, ByVal v2 As String) As Long Private Declare Function MyCopyInteger Lib "mydll.dll" _ (ByVal v1 As Long, ByRef v2 As Long) As Long Private Declare Function MyCopyStruct Lib "mydll.dll" _ (ByRef v1 As MYSTRUCT, ByRef v2 As MYSTRUCT) As Long Private Sub Command1_Click() Dim rtn As Long ' APIの引数としてByteの値とByteの参照(アドレス)を渡す例 Dim v1_b As Byte Dim v2_b As Byte v1_b = 65 rtn = MyCopyChar(v1_b, v2_b) Debug.Print ("------MyCopyChar------") Debug.Print "v1_b=" & v1_b & ":" & "v2_b=" & v2_b Debug.Print "rtn=" & rtn ' APIの引数としてStringの値とStringの参照(アドレス)を渡す例 Dim v1_s As String Dim v2_s As String * 10 v1_s = "abcdefg" rtn = MyCopyString(v1_s, v2_s) Debug.Print ("------MyCopyString------") Debug.Print "v1_s=" & v1_s & ":" & "v2_s=" & v2_s Debug.Print "rtn=" & rtn ' APIの引数としてLongの値とLongの参照(アドレス)を渡す例 Dim v1_l As Long Dim v2_l As Long v1_l = 65 rtn = MyCopyInteger(v1_l, v2_l) Debug.Print ("------MyCopyInteger------") Debug.Print "v1_l=" & v1_l & ":" & "v2_l=" & v2_l Debug.Print "rtn=" & rtn ' APIの引数として構造体(配列メンバ有り)の参照(アドレス)を渡す例 Dim v1_t As MYSTRUCT Dim v2_t As MYSTRUCT v1_t.s1 = 65 v1_t.s2 = "abcdefg123" v1_t.s3 = 66 v1_t.s4(0) = 67 v1_t.s4(1) = 68 rtn = MyCopyStruct(v1_t, v2_t) Debug.Print ("------MyCopyStruct------") Debug.Print "v1_t.s1=" & v1_t.s1 & ":" & "v2_t.s1=" & v2_t.s1 Debug.Print "v1_t.s2=" & v1_t.s2 & ":" & "v2_t.s2=" & v2_t.s2 Debug.Print "v1_t.s3=" & v1_t.s3 & ":" & "v2_t.s3=" & v2_t.s3 Debug.Print "v1_t.s4(0)=" & v1_t.s4(0) & ":" & "v2_t.s4(0)=" & v2_t.s4(0) Debug.Print "v1_t.s4(1)=" & v1_t.s4(1) & ":" & "v2_t.s4(1)=" & v2_t.s4(1) Debug.Print "rtn=" & rtn End Sub ===実行結果======================================== ------MyCopyChar------ v1_b=65:v2_b=65 rtn=1 ------MyCopyString------ v1_s=abcdefg:v2_s=abcdefg rtn=7 ------MyCopyInteger------ v1_l=65:v2_l=65 rtn=4 ------MyCopyStruct------ v1_t.s1=65:v2_t.s1=65 v1_t.s2=abcdefg:v2_t.s2=abcdefg v1_t.s3=66:v2_t.s3=66 v1_t.s4(0)=67:v2_t.s4(0)=67 v1_t.s4(1)=68:v2_t.s4(1)=68 rtn=20
3.C#.NET(2005)からVC++のAPIを呼ぶサンプルソース using System.Diagnostics; using System.Runtime.InteropServices; public partial class Form1 : Form { private struct MYSTRUCT1{ public Byte s1; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=7)] public String s2; public int s3; [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)] public int[] s4; } private struct MYSTRUCT2{ public Byte s1; [MarshalAs(UnmanagedType.ByValArray, SizeConst=7)] public Byte[] s2; public int s3; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public int[] s4; } private struct MYSTRUCT3{ public Byte s1; public Byte[] s2; public int s3; public int[] s4; public MYSTRUCT3(int dummy) { s1 = 0; s2 = new Byte[7]; s3 = 0; s4 = new int[2]; } } [DllImport("mydll.dll")] extern static int MyCopyChar(Byte v1, out Byte v2); [DllImport("mydll.dll")] extern static int MyCopyString(String v1, StringBuilder v2); [DllImport("mydll.dll")] extern static int MyCopyString(String v1, Byte[] v2); [DllImport("mydll.dll")] extern static int MyCopyInteger(int v1, out int v2); [DllImport("mydll.dll")] extern static int MyCopyStruct(ref MYSTRUCT1 v1, out MYSTRUCT1 v2); [DllImport("mydll.dll")] extern static int MyCopyStruct(ref MYSTRUCT2 v1, out MYSTRUCT2 v2); [DllImport("mydll.dll")] extern static int MyCopyStruct(ref MYSTRUCT3 v1, out MYSTRUCT3 v2); public Form1() { InitializeComponent(); } private void Button1_Click(object sender, EventArgs e) { int rtn; // APIの引数としてByteの値とByteの参照(アドレス)を渡す例 Byte v1_b; Byte v2_b; v1_b = 65; rtn = MyCopyChar(v1_b, out v2_b); Debug.WriteLine("------MyCopyChar------"); Debug.WriteLine("v1_b=" + v1_b + ":" + "v2_b=" + v2_b); Debug.WriteLine("rtn=" + rtn); // APIの引数としてStringの値とStringの参照(アドレス)を渡す例1 String v1_s1; StringBuilder v2_s1 = new StringBuilder(10); v1_s1 = "abcdefg"; rtn = MyCopyString(v1_s1, v2_s1); String v2_s1_o = v2_s1.ToString(); Debug.WriteLine("------MyCopyString1------"); Debug.WriteLine("v1_s1=" + v1_s1 + ":" + "v2_s1=" + v2_s1_o); Debug.WriteLine("rtn=" + rtn); // APIの引数としてStringの値とStringの参照(アドレス)を渡す例2 String v1_s2; Byte[] v2_s2 = new Byte[10]; v1_s2 = "abcdefg"; rtn = MyCopyString(v1_s2, v2_s2); String v2_s2_o = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(v2_s2); int l2= v2_s2_o.IndexOf((char)0); if (l2 >= 0) v2_s2_o = v2_s1_o.Substring(0, l2); Debug.WriteLine("------MyCopyString2------"); Debug.WriteLine("v1_s2=" + v1_s1 + ":" + "v2_s2=" + v2_s2_o); Debug.WriteLine("rtn=" + rtn); // APIの引数としてintの値とintの参照(アドレス)を渡す例 int v1_l; int v2_l; v1_l = 65; rtn = MyCopyInteger(v1_l, out v2_l); Debug.WriteLine("------MyCopyInteger------"); Debug.WriteLine("v1_l=" + v1_l + ":" + "v2_l=" + v2_l); Debug.WriteLine("rtn=" + rtn); // APIの引数として構造体(配列メンバ有り)の参照(アドレス)を渡す例1 MYSTRUCT1 v1_t1 = new MYSTRUCT1(); MYSTRUCT1 v2_t1 = new MYSTRUCT1(); v1_t1.s1 = 65; v1_t1.s2 = "abcdefg123"; v1_t1.s3 = 66; v1_t1.s4 = new int[2] {67, 68}; rtn = MyCopyStruct(ref v1_t1, out v2_t1); Debug.WriteLine("------MyCopyStruct1------"); Debug.WriteLine("v1_t1.s1=" + v1_t1.s1 + ":" + "v2_t1.s1=" + v2_t1.s1); Debug.WriteLine("v1_t1.s2=" + v1_t1.s2 + ":" + "v2_t1.s2=" + v2_t1.s2); Debug.WriteLine("v1_t1.s3=" + v1_t1.s3 + ":" + "v2_t1.s3=" + v2_t1.s3); Debug.WriteLine("v1_t1.s4[0]=" + v1_t1.s4[0] + ":" + "v2_t1.s4[0]=" + v2_t1.s4[0]); Debug.WriteLine("v1_t1.s4[1]=" + v1_t1.s4[1] + ":" + "v2_t1.s4[1]=" + v2_t1.s4[1]); Debug.WriteLine("rtn=" + rtn); // APIの引数として構造体(配列メンバ有り)の参照(アドレス)を渡す例2 MYSTRUCT2 v1_t2 = new MYSTRUCT2(); MYSTRUCT2 v2_t2 = new MYSTRUCT2(); v1_t2.s1 = 65; v1_t2.s2 = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetBytes("abcdefg123"); v1_t2.s3 = 66; v1_t2.s4 = new int[2] {67, 68}; rtn = MyCopyStruct(ref v1_t2, out v2_t2); Debug.WriteLine("------MyCopyStruct2------"); Debug.WriteLine("v1_t2.s1=" + v1_t2.s1 + ":" + "v2_t2.s1=" + v2_t2.s1); Debug.WriteLine("v1_t2.s2=" + System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(v1_t2.s2) + ":" + "v2_t.s2=" + System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(v2_t2.s2)); Debug.WriteLine("v1_t2.s3=" + v1_t2.s3 + ":" + "v2_t2.s3=" + v2_t2.s3); Debug.WriteLine("v1_t2.s4[0]=" + v1_t2.s4[0] + ":" + "v2_t.s4[0]=" + v2_t2.s4[0]); Debug.WriteLine("v1_t2.s4[1]=" + v1_t2.s4[1] + ":" + "v2_t.s4[1]=" + v2_t2.s4[1]); Debug.WriteLine("rtn=" + rtn); // APIの引数として構造体(配列メンバ有り)の参照(アドレス)を渡す例3 MYSTRUCT3 v1_t3 = new MYSTRUCT3(0); MYSTRUCT3 v2_t3 = new MYSTRUCT3(0); v1_t3.s1 = 65; v1_t3.s2[0] = (Byte)'a'; v1_t3.s2[1] = (Byte)'b'; v1_t3.s2[2] = (Byte)'c'; v1_t3.s2[3] = (Byte)'d'; v1_t3.s2[4] = (Byte)'e'; v1_t3.s2[5] = (Byte)'f'; v1_t3.s2[6] = (Byte)'g'; v1_t3.s3 = 66; v1_t3.s4[0] = 67; v1_t3.s4[1] = 68; rtn = MyCopyStruct(ref v1_t3, out v2_t3); Debug.WriteLine("------MyCopyStruct3------"); Debug.WriteLine("v1_t3.s1=" + v1_t3.s1 + ":" + "v2_t3.s1=" + v2_t3.s1); Debug.WriteLine("v1_t3.s2=" + System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(v1_t3.s2) + ":" + "v2_t3.s2=" + System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(v2_t3.s2)); Debug.WriteLine("v1_t3.s3=" + v1_t3.s3 + ":" + "v2_t3.s3=" + v2_t3.s3); Debug.WriteLine("v1_t3.s4[0]=" + v1_t3.s4[0] + ":" + "v2_t3.s4[0]=" + v2_t3.s4[0]); Debug.WriteLine("v1_t3.s4[1]=" + v1_t3.s4[1] + ":" + "v2_t3.s4[1]=" + v2_t3.s4[1]); Debug.WriteLine("rtn=" + rtn); } } ===実行結果======================================== ------MyCopyChar------ v1_b=65:v2_b=65 rtn=1 ------MyCopyString1------ v1_s1=abcdefg:v2_s1=abcdefg rtn=7 ------MyCopyString2------ v1_s2=abcdefg:v2_s2=abcdefg rtn=7 ------MyCopyInteger------ v1_l=65:v2_l=65 rtn=4 ------MyCopyStruct1------ v1_t1.s1=65:v2_t1.s1=65 v1_t1.s2=abcdef:v2_t1.s2=abcdef v1_t1.s3=66:v2_t1.s3=66 v1_t1.s4[0]=67:v2_t1.s4[0]=67 v1_t1.s4[1]=68:v2_t1.s4[1]=68 rtn=20 ------MyCopyStruct2------ v1_t2.s1=65:v2_t2.s1=65 v1_t2.s2=abcdefg:v2_t.s2=abcdefg v1_t2.s3=66:v2_t2.s3=66 v1_t2.s4[0]=67:v2_t.s4[0]=67 v1_t2.s4[1]=68:v2_t.s4[1]=68 rtn=20 ------MyCopyStruct3------ v1_t3.s1=65:v2_t3.s1=65 v1_t3.s2=abcdefg:v2_t3.s2=abcdefg v1_t3.s3=66:v2_t3.s3=66 v1_t3.s4[0]=67:v2_t3.s4[0]=67 v1_t3.s4[1]=68:v2_t3.s4[1]=68 rtn=20