長年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
|
|