3.1 Array bounds/配列の範囲
3.2 Default members/初期メンバ
3.3 GoSub, On GoTo, and On GoSub keyword/GoSubとOn GoToとOn GoSub
3.4 Fixed-length strings (FLSs)/固定長文字列
3.5 Type…End Type blocks (UDTs)/Type…End Typeブロック(ユーザー定義型
3.6 Auto-instancing variables/自動インスタンス化変数
3.7 Declare statements/宣言文
3.8 Variant and Control variables/バリアント変数とコントロール変数
3.9 Classes and Interfaces/クラスとインターフェース
3.10 Finalization and disposable classes/終了処理とdisposableクラス
3.11 ActiveX Components/ActiveXコンポーネント
3.12 Persistable classes/持続性クラス
3.13 Resources/リソース
3.14 Minor language differences/小さな言語の相違
3.15 Unsupported features and controls/サポートされない機能とコントロール
3.16 The VB6Config class/VB6Configクラス
3. Converting Language Elements/言語要素の変換
This section illustrates how VB Migration Partner converts VB6 language elements and how you can apply pragmas to generate better VB.NET code.
このセクションではVB Migration PartnerがどのようにVB6言語要素を変換するのか、より良いVB.NETコードを生成するのにどのようにプラグマを適用するのかを説明します。
3.1 Array bounds/配列の範囲
VB Migration Partner can adopt five different strategies when converting arrays with non-zero LBound. Developers can enforce a specific strategy by means of the ArrayBounds pragma, as in:
VB Migration PartnerはノンゼロLBound配列を変換する際に5つの異なった手法を採用することができます。開発者はArrayBoundプラグマを使用し、以下のように特定の手法によって変換することができます。
Please notice that there is a minor but important limitation in how you can apply this pragma to an array variable: if the array isn’t explicitly declared by means of a Dim statement and is only implicitly declared by means of a ReDim statement, pragma at the variable scope are ignored. An example is in order:
配列変数にこのプラグマをどのように適用できるのか小さいですが、重要な制限があることに注意してください。もし配列が明示的にDimステートメントで宣言されていない場合、そして、ただ暗黙的にReDimステートメントで宣言されているのであれば、プラグマは変数のスコープで無視されます。以下に順番に例を示します。
Private Sub Test()
ReDim arr(1 To 10) As String
Redim arr2(1 to 10) As Long
End Sub
Both arrays are implicitly declared by means of a ReDim statement and lack of an explicit Dim keyword. The abovementioned rules states that the second pragma (scoped at the variable level) is ignored, therefore both arrays will be affected by the first pragma and will be forced to have a zero lower index:
両方の配列は暗黙的にReDimステートメントで宣言されています。明示的なDimキーワードが不足しています。上記のルールでは2番目のプラグマ(変数レベルのスコープ)は無視されます。したがって、両方の配列は最初のプラグマによる影響を受け、ゼロインデックスを持つように定義されます。
Private Sub Test()
Dim arr() As String
Dim arr2() As Integer
ReDim arr(10)
ReDim arr2(10)
End Sub
(This limitation is common to all pragmas that apply to array variables, not just the ArrayBounds pragma.)
(この制限はArrayBoundsプラグマだけではなく、配列変数に適用されるすべてのプラグマに共通の制限です。)
Unchanged
The array is emitted as-is, and generates a compilation error in VB.NET if it has a nonzero lower bound. This is the default setting thus you rarely need to use an ArrayBounds pragma to enforce this mode (unless you want to override a pragma with broader scope).
もし配列がそのままの状態で、ノンゼロ範囲であるならば、.NETではコンパイルエラーとなります。これはデフォルトの設定ですので、このモードを実施するためにArrayBoundsプラグマを使用することはほとんどありません。(あえてスコープの範囲を無効にすることをしない場合に限ります)
ForceZero
When this option is selected, the array’s lower bound is changed to zero and the upper bound isn’t modified. This strategy is fine when the VB6 code processes the array using a loop such as this:
このオプションが選択されている場合、配列の最下限値がゼロに変更されます、上限値は変更されません。この方法は、VB6コードが次のようなループを使う配列を処理する場合には特に有効です。
For i = 1 To UBound(arr)
…
Next
Shift
VB Migration Partner decreases (or increases) both the lower and the upper bounds by the same value, in such a way the LBound becomes zero. For example, consider the following VB6 fragment
VB Migration Partnerは同じ値で上限値と下限値を減少させます(または増加させます)。LBoundがゼロになるような手法です。例えば次のようなVB6の断片をご参照下さい。
Dim arr(LoIndex To HiIndex) As String
is translated as follows:
上記のコードは次のように変換されます。
Dim arr(0 To HiIndex - LoIndex) As String
This approach is recommended when it is essential that the number of elements in the array doesn’t change after the migration, and is the right choice when the VB6 code processes the array using a loop such as this:
このアプローチは配列の要素の数が移行後に変化しないことが不可欠であるときに推奨されます。そしてVB6コードが次のようなループを使う配列を処理する場合には特に有効です。
For i = LBound(arr) To UBound(arr)
…
Next
Also, this is often the best strategy for arrays defined inside UDTs, if the UDT is often passed to a Windows API method (in which case it’s essential that their size doesn’t change).
またこれは、ユーザー定義型配列においてもとてもよい手法です。ユーザー定義型配列がWindowsAPIメソッドによく引き渡される場合は。(そのケースにおいてはそれらのサイズが変わらないことは不可欠です)
VB6Array
If the array has a nonzero lower bound, VB Migration Partner replaces the array with an instance of the VB6Array(Of T) generic class. For example, the following VB6 statements:
配列の下限値がゼロでない場合は、VB Migration Partnerは配列を一般的なクラスであるVB6Array(of T)インスタンスに置き換えます。例として次のVB6ステートメントを参照下さい。※(of T)は(of Type型)の意
Dim arr(1 To 10) As String
Dim arr2(1 To 10, -5 To 5) As Integer
Dim arr3(0 To 10) As Long
are translated as follows:
変換すると次のようになります。
Dim arr As New VB6Array(Of String)(1, 10)
Dim arr2 As New VB6Array(Of Short)(1, 10, -5, 5)
Dim arr3(0 To 10) As Integer
Instances of the VB6Array class behave much like regular arrays; they support indexes, assignments between arrays, and For Each loops:
VB6Arrayクラスのインスタンスは標準の配列と同様に動作します。インデックス、配列間の値渡し、For Eachループもサポートします。
arr(1) = "abcde"
For Each v In arr2
sum = sum + v
Next
Interestingly, when traversing a multi-dimensional array in a For Each loop, elements are visited in a column-wise manner (as in VB6), rather than in row-wise manner (as in VB.NET), thus no bugs are introduced if the processing order is significant.
興味深い話ですが、For・・・Eachループ内の多次元配列を横断するとき、要素はVB.NETのルールの行方向に移動するよりも、VB6のルールと同様列方向に移動します。処理順序が明示的であるならば、バグが導入されることはありません。
In order to support VB6Array objects – and for other reasons as well, such support for Variants – VB Migration Partner translates the LBound and UBound methods to the LBound6 and UBound6 methods, respectively. Likewise, the Erase6, Redim6, and RedimPreserve6 methods are used to clear or resize arrays implemented as VB6Array objects. (These methods are defined in the language support library CodeArchitects.VBLibrary.dll.)
VB6Arrayオブジェクトをサポートするために、他の機能と同様に様々なものをサポートします。VBMigrationPartnerはLBound、UBoundメソッドをLBound6、UBound6へ、それぞれ同様に、Erase6、Redim6、RedimPreserve6メソッドへ変換します。そしてそれぞれのメソッドは、VB6Arrayオブジェクトとして実装された配列をクリアまたはリサイズされるように使用されます。(これらのメソッドは言語サポートライブラリのCodeArchitects.VBLibrary.dllに定義されています)
VB Migration Partner fully honors the Option Base directive:
VB Migration PartnerはOption Baseステートメントも完全にサポートします。
Option Base 1
…
Dim arr(10) As String
numEls = UBound(arr)
which is translated as:
上記を変換すると次のようになります。
Dim arr As New VB6Array(Of String)(1, 10)
numEls = UBound6(arr)
Unfortunately, a syntactical limitation of VB.NET prevents from using a VB6Array object to hold an array of UDTs (i.e. Structure blocks). More precisely, if a VB6Array contains structures, you can read a member of a structure stored in the VB6Array but you can’t assign any member. For example, consider the following VB.NET code:
残念ながら、VB.NETの構文の制限は、ユーザー定義型の配列(すなわちStructureブロックのことです)を持つVB6Arrayオブジェクトの使用は制限されます。もっと正確にいえば、VB6Arrayが構造体を含んでいる場合は、VB6Arrayに格納されている構造体のメンバを読み込むことができます。しかし、他のメンバを割り当てることはできません。次のVB.NETコードをご覧下さい。
Structure MyUDT
Public ID As Integer
End Structure
...
Sub Main()
Dim arr As New VB6Array(Of MyUDT)(1, 10)
Dim value As Integer = arr(1).ID
arr(1).ID = value
End Sub
Therefore, in general you should avoid using the VB6Array option to convert an array of structures. However, this is just a rule of thumb and there can be exceptions to it. For example, if your code assigns whole structures to array elements (as opposed to individual structure members) and then reads their individual members, then storing structures in a VB6Array object is fine.
そのため、基本的に構造体の配列を変換する場合、VB6Arrayのオプションを使用することを避けてください。しかしながら、これはただの経験値からです。そして例外もあります。たとえば、プログラムコードが全体の構造体を配列の要素に割り当てているのであれば(個々の構造体と対照的に)、個々のメンバを読み込むことはでき、そしてVB6Arrayオブジェクトに構造体を割り当てることもできます。
ForceVB6Array
This option is similar to the previous one, except it applies to all arrays in the pragma’s scope, regardless of whether the array has a non-zero LBound. This option is useful when the array is declared and created in two different steps – in this case the parser can’t decide which strategy to use by looking at the declaration alone – or when the developer knows that the array is going to be passed to a method that exposes parameters of VB6Array type. For example, consider this VB6 fragment:
このオプションは前のものと同様です。ただし、配列がノンゼロLBoundを持っているかどうかに関わらず、プラグマのスコープの全ての配列に適用されます。このオプションは配列が宣言され、そして2つの異なる手順で作られている場合に便利です。ひとつは、パーサーが単独で宣言しているのを見て、どの方針で使用すればいいか決められない場合。または開発者が配列がVB6Arrayタイプのパラメータを明確にメソッドに渡されるということを知っている場合です。例として次のVB6コードを見てください。
Dim arr() As String
Sub Test()
ReDim arr(1 To 10) As String
End Sub
Remember that the VB6Array strategy applies only to arrays that have a nonzero lower index. However, when VB Migration Partner parses the arr variable it can’t decide whether it has a nonzero lower index, therefore it ignores the pragma and renders the variable as a standard array (thus causing a compilation error). This is the correct way to handle such a case:
VB6Arrayの方針がノンゼロの最下位インデックスを持っている配列だけに適用されるということを覚えておいてください。しかしながら、VB Migration Partnerがarr変数を分析する際に、それがノンゼロ最下位インデックスを持っているかどうか判断できません。そのため、プラグマを無視し、標準の配列として解釈します(そのためコンパイルエラーになります)。以下はそのようなケースを扱う正しい方法です。
Dim arr() As String
Sub Test()
ReDim arr(1 To 10) As String
End Sub
which is rendered as:
これを適用すると、次のようになります。
Private arr As VB6Array(Of String)
Public Sub Test()
Redim6(arr, 1, 10)
End Sub
Unlike other ArrayBounds options, you can apply the ForceVB6Array strategy to methods’ parameters and return values, either with a pragma inside the method with no explicit scope or with a pragma outside the method but that is scoped opportunely:
他のArrayBoundオプションとは異なり、ForceVB6Arrayの方針をメソッドのパラメータと戻り値に適用できます。明確な範囲のないメソッド内にプラグマを、またはメソッドの外側にプラグマを適用できます。しかし、それはスコープされた機会でもあります。
Function GetValues(arr() As String) As Integer()
Dim res() as Integer
…
GetValues = res
End Function
Function InitArray() As Integer()
…
End Function
which is translated as follows:
これを適用すると、次のようになります。
Function GetValues(arr As VB6Array(Of String)) As VB6Array(Of Short)
Dim res As New VB6Array(Of Short)
…
Return res
End Function
Function InitArray() As VB6Array(Of Short)
…
End Function
When dealing with arrays having nonzero lower bound, another pragma can be quite useful. Consider the following VB6 code:
ノンゼロ最下位配列を持っている配列に対処する際には他のプラグマはとても便利です。次のVB6コードをご覧下さい。
Dim primes(1 To 10) As Long
primes(1) = 1: primes(2) = 2: primes(3) = 3: primes(4) = 5: primes(5) = 7
primes(6) = 11: primes(7) = 13: primes(8) = 17: primes(9) = 19: primes(10) = 23
You can use an ArrayBounds pragma to force a zero lower bound or to shift both bounds toward zero, but you need a separate ShiftIndexes pragma to account for the indexes used in the last two lines:
ゼロ下限値に制限させるか、またはゼロに向けて両方の値をシフトするようにArrayBoundプラグマを使用することができます。しかし、最後の2行で使用しているインデックスのために別々のShiftIndexesプラグマを使用する必要があります。
Dim primes(1 To 10) As Long
primes(1) = 1: primes(2) = 2: primes(3) = 3: primes(4) = 5: primes(5) = 7
primes(6) = 11: primes(7) = 13: primes(8) = 17: primes(9) = 19: primes(10) = 23
this is the result of the migration to VB.NET:
以下がVB.NETに移行された結果です。
Dim primes(9) As Integer
primes(0) = 1: primes(1) = 2: primes(2) = 3: primes(3) = 5: primes(4) = 7
primes(5) = 11: primes(6) = 13: primes(7) = 17: primes(8) = 19: primes(9) = 23
The first argument of the ShiftIndexes is False if the delta value specified in the second argument must be applied only to constant indexes, True if the delta value must be applied even when the index is a variable or an expression. Using True or False makes a difference when the array is referenced from inside a loop. Consider this example:
もし2番目の引数で指定されたデルタ値が定数のインデックスだけに適応しなければいけないのであれば、Falseに、インデックスが変数もしくは式である場合に適応しなければいけないのであれば、Trueに、ShiftIndexesの最初の引数を定義します。TrueやFalseを使用することは配列がループ内から参照される際に、効果が得られます。以下に例を示します。
Dim powers(1 To 10) As Double
Dim Fibonacci(1 To 10) As Double
Dim n As Integer
powers(1) = 2
For n = 2 To 10
powers(n) = powers(n - 1) * 2
Next
Fibonacci(1) = 1: Fibonacci(2) = 1
For n = LBounds(Fibonacci) + 2 To Ubound(Fibonacci)
Fibonacci(n) = Fibonacci(n - 2) + Fibonacci(n - 1)
Next
The difference is in how the loop bounds are specified for the two arrays: for the powers array the loop bounds are constant values, therefore it is necessary to compensate in the indexes inside the loop; for the fibonacci array the loop bounds are specified in terms of LBound and UBound functions, therefore the indexes inside the loop should not be altered. This is the resulting VB.NET code:
相違点は2つの配列がどのように指定されるのかです。powers配列のループの範囲が定数の値です、したがってループ内のインデックスで補償することが必要です。フィボナッチ配列のループの範囲はLBoundとUBound関数の範囲で指定されます。したがってループ内のインデックスは修正されるべきではありません。次はVB.NETコードの結果例です。
Dim powers(9) As Double
Dim Fibonacci(9) As Double
Dim n As Short
powers(0) = 2
For n = 2 To 10
powers(n - 1) = powers(n - 1 - 1) * 2
Next
Fibonacci(0) = 1: Fibonacci(1) = 1
For n = LBounds(Fibonacci) + 2 To Ubound(Fibonacci)
Fibonacci(n) = Fibonacci(n - 2) + Fibonacci(n - 1)
Next
Notice that the ShiftIndexes pragma support up to three delta values, thus you can shift indexes also for 2- and 3-dimension arrays, as in this code:
ShiftIndexesプラグマは最大3つのデルタ値をサポートすることに注意してください。従って、2次元、3次元配列にもインデックスをシフトすることができます。次のコードを参照下さい。
Dim mat(1 To 10, -1 To 1) As Double
Delta values can be negative, can be variables and expressions.
デルタ値はネガティブになることもでき、変数や式にもなれます。
The first argument of ShiftIndexes can also be a regular expression that specifies more precisely to which expressions the pragma should be applied. For example, consider the following VB6 code:
ShiftIndexesの最初の引数はプラグマがどの表現に適用させるべきかをより正確に指定するために正規表現になります。例としてVB6のサンプルを次に示します。
Dim arr(1 To 10, 1 To 20) As Integer
Dim k As Integer, row As Integer, col As Integer
arr(1, 1) = 0
For k = 2 To 10
arr(k, 1) = arr(k - 1) + 10
Next
For row = 1 to 10
For col = LBound(arr, 2) + 1 To UBound(arr, 2)
arr(row, 1) = arr(row, 1) + arr(row, col)
Next
Next
In this case you want to apply the index adjustments only when the index expression is “k” or “row”, hence the regular expression used in the ShiftIndexes pragma. Here’s the result after then conversion to VB.NET:
この場合、インデックス表現が「k」か「row」である場合にのみインデックスの調整を適用します。したがって、正規表現のShiftIndexesプラグマで使用されます。
Dim arr(9, 19) As Short
Dim k As Short, row As Short, col As Short
arr(0, 0) = 0
For k = 2 To 10
arr(k - 1, 0) = arr(k - 1 - 1, 0) + 10
Next
For row = 1 To 10
For col = LBound6(arr, 2) + 1 To UBound6(arr, 2)
arr(row - 1, 0) = arr(row - 1, 0) + arr(row - 1, col)
Next
Next
Notice that numeric indexes are always affected by the ShiftIndexes pragma, but symbolic numeric constants are affected only you specify a suitable regular expression (or True) in the first argument.
数値のインデックスはいつもShiftIndexesプラグマによって影響されますが、、シンボリックな数値定数は最初の引数にある適当な正規表現(またはTrue)を指定する場合にのみ影響されるということに注意してください。
3.2 Default members/初期メンバ
The way VB Migration Partner deals with default members depends on how and where the member is defined, and how it is referenced.
VB Migration Partnerがデフォルトメンバに対応する方法は、メンバがどのように、またどこで定義されているか、そしてどのように参照されているかによります。
Default property definitions/デフォルトプロパティ定義
When converting a the definition of a property that is marked as the default member of its class, VB Migration Partner adds the Default keyword if the property has one or more arguments; if the property has no parameters, an upgrade warning is issued, because .NET doesn’t support default properties with zero parameters. For example, if this property is the default member of its class:
クラスのデフォルトメンバとしてマークされたプロパティの定義を変換する際に、もしプロパティがひとつもしくはそれ以上の引数を持っている場合、VB Migration Partnerはデフォルトキーワードを追加します。プロパティがパラメータを持っていない場合は、アップグレード警告が発生します。なぜなら、.NETはゼロパラメータのデフォルトプロパティをサポートしないからです。例として、プロパティがクラスのデフォルトメンバである場合を示します。
Public Property Get Text() As String
Text = "..."
End Property
VB Migration Partner converts it as:
VB Migration Partnerは次のように変換します。
<System.Runtime.InteropServices.DispId(0)> _
Public ReadOnly Property Text() As String
Get
Return "..."
End Get
End Property
Notice that the Property block is tagged with a DispID(0) attribute, so that COM clients see the property as the default member.
プロパティブロックはDispID(0)の属性としてつけられるということに注意してください。よってCOMクライアントがデフォルトメンバとしてプロパティを見ます。
Default method and field definitions/デフォルトメソッドとフィールドの定義
When converting a default method or field’s definition, VB Migration Partner doesn’t modify the definition, except for the addition of the DispID attribute. In this case no Default keyword can be used, because this keyword can be applied only to VB.NET properties.
デフォルトのメソッドやフィールドの定義を変換する際に、VB Migration Partnerは定義を修正しません。DispID属性の追加をのぞきますが。このケースにおいてデフォルトキーワードはまったく使えません。このキーワードはVB.NETプロパティにのみ適用できるからです。
References to default members in early-bound mode/Early Boundモードでのデフォルトメンバへの参照
If the VB6 code references a default property, method, or field through a strong-typed variable, the code generator correctly adds the name of the member. The conversion works correctly for regardless of whether the member belongs to a class defined in the current project, in another project in the solution, or in a type library.
VB6コードがデフォルトプロパティやメソッド、型宣言をされた変数を通したフィールドを参照している場合、コード生成は正確にメンバの名前を追加します。変換は当該プロジェクトにて参照されたクラス、ソリューションのほかのプロジェクト、またはタイプライブラリの中に属したメンバにかかわらず、正確に機能します。
Accessing default members in late-bound mode/Late Boundモードでのデフォルトメンバのアクセス
If the VB6 code references a default property, method, or field through a Variant, Object, or Control variable, by default VB Migration Partner emits a warning. For example, the following VB6 code
VB6コードがデフォルトプロパティ、メソッド、バリアントを通したフィールド、オブジェクト、またはコントロール変数を参照している場合はデフォルトでVB Migration Partnerは警告を放出します。次に示すVB6コードをご覧下さい。
Sub Test(ByVal obj As Object)
MsgBox obj
obj = "new value"
End Sub
is translated as:
変換されると次のようになります。
Sub Test(ByVal obj As Object)
MsgBox6(obj)
obj = "new value"
End Sub
The VB.NET code compiles correctly but delivers bogus results at runtime. You can generate better code by means of the DefaultMemberSupport pragma:
VB.NETは正確にコンパイルします。ですが、ランタイムで正しくない結果を返します。DefaultMemberSupport Pragmaを使用してより良いコードを生成することができます。
Sub Test(ByVal obj As Object)
MsgBox obj
obj = "new value"
End Sub
which delivers this VB.NET code:
結果、このようなVB.NETコードになります。
Sub Test(ByVal obj As Object)
MsgBox6(GetDefaultMember6(obj))
SetDefaultMember6(obj, "new value")
End Sub
The GetDefaultMember6 and SetDefaultMember6 methods are defined in the VBMigrationPartner_Support module. These methods discover and resolve the default member reference at runtime and work correctly also if the default member takes one or more arguments. For example, the following VB6 code:
GetDefaultMember6とSetDefaultMember6メソッドはVBMigrationPartner_Supportモジュールにて定義されています。これらのメソッドはランタイムのデフォルトメンバ参照と正確な動作を見出し、解決します。また、デフォルトメンバはひとつもしくはそれ以上の引数をとります。次のVB6コードサンプルを参照下さい。
Sub Test(ByVal obj As Object)
Dim res As Integer
x = obj(1)
obj(1) = res + 1
End Sub
translates to:
変換すると次のようになります。
Sub Test(ByVal obj As Object)
Dim res As Short
res = GetDefaultMember6(obj, 1)
SetDefaultMember6(obj, 1, res + 1)
End Sub
The discovery process is carried out only the first time the GetDefaultMember6 and SetDefaultMember6 process an object of given type, because the result of the discovery is reused by subsequent calls on variables of the same type. All subsequent references are faster and add no noticeable overhead to the late-bound call.
GetDefaultMember6とSetDefaultMember6が初期のみに特定の型のオブジェクトを処理する際に、発見プロセスが行われます。なぜなら、発見の結果がその後の同じ型の変数呼び出しによって再利用されるからです。全てのその後の参照はより速く、Late Bound呼び出しによる顕著な間接費は発生しません。
3.3 GoSub, On GoTo, and On GoSub keywords/GoSubとOn GoToとOn GoSub
VB.NET doesn’t support GoSub, On Goto, and On Gosub statements. VB Migration Partner, however, is able to correctly convert these VB6 keywords, at the expense of code readability and maintainability. For this reason we strongly recommend that you edit the VB6 application to get rid of all the statements based on these keywords.
VB.NETはGoSub、On GoTo、On GoSubステートメントはサポートしません。しかしVB Migration Partnerではコードの読みやすさとメンテナンス性を犠牲にしますがこれらのVB6キーワードを正確に変換できます。この理由として、これらのキーワードに基づくすべてのステートメントを取り除くためにVB6アプリケーションを編集することを強く推奨しているからです。
Anyway, you can surely take advantage of VB Migration Partner ability to handle these statements during the early stages of the migration process. Let’s start with the following VB6 method:
とにかく、マイグレーション過程の初期段階の間に、これらのステートメントを扱うVB Migration Partnerの能力を活かすことができます。次のVB6メソッドを見てみましょう。
Sub Main()
GoSub First
GoSub Second
Exit Sub
First:
Debug.Print "First"
GoSub Third
Return
Second:
Debug.Print "Second"
Third:
Debug.Print "Third"
Return
End Sub
This is how VB Migration Partner converts the code:
以下はVB Migration Partnerがどのように変換するかを示したものです。
Public Sub Main()
Dim _vb6ReturnStack As New System.Collections.Generic.Stack(Of Integer)
_vb6ReturnStack.Push(1): GoTo First
ReturnLabel_1:
_vb6ReturnStack.Push(2): GoTo Second
ReturnLabel_2:
Exit Sub
First:
Debug.WriteLine("First")
_vb6ReturnStack.Push(3): GoTo Third
ReturnLabel_3:
GoTo _vb6ReturnHandler
Second:
Debug.WriteLine("Second")
Third:
Debug.WriteLine("Third")
GoTo _vb6ReturnHandler
Exit Sub
_vb6ReturnHandler:
Select Case _vb6ReturnStack.Pop()
Case 1: GoTo ReturnLabel_1
Case 2: GoTo ReturnLabel_2
Case 3: GoTo ReturnLabel_3
End Select
End Sub
As you can see, the GoSub keyword is transformed into a GoTo keyword that uses the _vb6ReturnStack variable to “remember” where the Return statement must jump to. The _vb6ReturnStack variable holds a stack that keeps the ID of the return address, a 32-bit integer from 1 to N, where N is the number of GoSub statements in the current method.
お分かりになるように、GoSubキーワードはGoToキーワードに置き換えられます。そのキーワードは、リターンステートメントをどこに移動すればよいかを「覚える」為に、_vb6ReturnStack変数を使います。_vb6ReturnStack変数はリターンアドレスのIDを保持するスタックを持ちます。アドレスは32Bitの整数1からN、Nは現在のメソッドにあるGoSubステートメントの番号の場所です。
The Return keyword is transformed into a GoTo keyword that points to the _vb6ReturnHandler section, where the return address is popped off the stack and used to go back to the statement that immediately follows the GoSub.
ReturnキーワードはGoToキーワードに置き換えられ_vb6ReturnHandlerセクションを指し示します。リターンアドレスはスタックから取り出され、すぐにGoSubに続くステートメントに戻るために使用されます。
Converting a calculated GoSub delivers similar code, except that the GoSub becomes a GoTo pointing to a Select Case block. For example, the following VB6 code:
計算されたGoSubを変換すると、同様なコードを提供します。ただし、GoSubがSelect Caseブロックを示すGoToになることを除きます。次のVB6コードを見てください。
Dim x As Integer
x = 2
On x GoSub First, Second, Third
Exit Sub
is converted as:
変換されると次のようになります。
Dim x As Short = 2
_vb6ReturnStack.Push(4): GoTo OngosubTarget_1
ReturnLabel_4:
OngosubTarget_1:
Select Case x
Case 1: GoTo First
Case 2: GoTo Second
Case 3: GoTo Third
Case Is <= 0, Is <= 255: GoTo ReturnLabel_4
Case Else: Err.Raise(5)
End Select
On…GoTo statements are converted in a similar way.
On・・・GoToステートメントは同様に変換されます。
Important note: We can’t emphasize strongly enough that the code that VB Migration Partner delivers should be never left in a production application, because it is unreadable and hardly maintainable. For this reason, all occurrences of GoSub, On GoTo, and On GoSub keywords cause a warning to be emitted in the generated VB.NET. (This warning has been dropped in examples shown in this section.)
重要事項:VB Migration Partnerが提供するコードが生産適用に決して残されているべきではないと強調することはできません。なぜなら可読性とメンテナンス性に欠けるからです。この理由としては、GoSub、On GoTo、On GoSubキーワードのすべての存在が、生成されたVB.NETで警告を発するからです。(この警告はこのセクションで示したサンプル中で削除されました)
3.4 Fixed-length strings (FLSs)/固定長文字列
A fixed-length strings (FLS) is converted to an instance of the VB6FixedString class. This class exposes a constructor (which takes the string’s length) and the Value property (which takes or returns the string’s value). For example, the following VB6 code:
固定長文字列は、VB6FixedStringクラスのインスタンスに変換されます。このクラスはコンストラクタ(文字列の長さを持つ)とValueプロパティ(文字列の値を取得もしくは戻す)を公開します。次のVB6コードを見てください。
Dim fs As String * STRINGSIZE
fs = "abcde"
is converted as follows:
変換されると次のようになります。
Dim fs As New VB6FixedString(STRINGSIZE)
fs.Value = "abcde"
The Value property returns the actual internal buffer, an important detail which ensures that VB6FixedString instances work well when they are passed to Windows API methods that store a result in a ByVal string argument. Thanks to this approach, calls that pass FLS arguments to Declare methods work correctly after the migration to VB.NET.
Valueプロパティは実際の内部バッファを戻し、重要な詳細情報として、VB6FixedStringのインスタンスがByValストリング引数の結果を保管したWindowsAPIメソッドに渡された際に、うまく機能するということを保証します。この手法のおかげで、Declareメソッドが固定長文字列の引数が渡される呼び出しが、VB.NETに移行された後も正しく動作しています。
Arrays of FLSs require a special treatment and are migrated differently. Consider the following VB6 code:
固定長文字列の配列は特別な処理を要求し異なる移行が行われます。次のVB6コードを見てください。
Dim arr(10) As String * 256
arr(0).Value = "abcde"
becomes:
変換されると次のようになります。
Dim arr() As VB6FixedString_256 = CreateArray6(Of VB6FixedString_256)(0, 10)
arr(0).Value = "abcde"
where VB6FixedString_256 a special class in the VisualBasic6.Support.vb module:
VB6FixedString_256はVisualBasic6.Support.vbモジュールの特別なクラスです。
<StructLayout(LayoutKind.Sequential)> _
Public Class VB6FixedString_256
Private Const SIZE As Integer = 256
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=SIZE)> _
Private Buffer As String = VB6FixedString.GetEmptyBuffer(SIZE)
Public Property Value() As String
Get
Return VB6FixedString.Truncate(Buffer, SIZE, ControlChars.NullChar)
End Get
Set(ByVal value As String)
Buffer = VB6FixedString.Truncate(value, SIZE)
End Set
End Property
End Class
A distinct VB6FixedString_NNN class is generated for each distinct size that appears in FLS declarations inside the current project.
異なったVB6FixedString_NNNのクラスは当該Projectの内部にある固定長文字列宣言に現れるそれぞれの異なったサイズのために生成されます。
As you see above, the FLS array is initialized by means of a call to the CreateArray6 method. This method ensures that all the elements in the array are correctly instantiated, so that no NullReference exception is thrown when accessing any element.
上記のように、固定長文字列の配列はCreateArray6メソッドによって、初期化されます。このメソッドは配列のすべての要素を正確にインスタンス化されます。NullReference例外はどんな要素にアクセスする際にもスローされません。
If the array has a nonzero lower index, you can use the ArrayBounds pragma to maintain full compatibility with VB6:
もし配列がノンゼロ最下位インデックスを持っている場合は、VB6との完全な互換性を維持するのにArrayBoundsプラグマを使用できます。
Dim arr(1 to 10) As String * 256
which is translated to:
変換されると次のようになります。
Dim arr As New VB6ArrayNew(Of VB6FixedString_256)(1, 10)
The VB6ArrayNew(Of T) generic class differs from the VB6Array(Of T) class in that it automatically creates an instance of the T type for each element of the array. Using a plain VB6Array(Of T) type would throw a NullReference exception when accessing any array element.
VB6ArrayNew(Of T)というジェネリッククラスは配列のそれぞれの要素のためにT型のインスタンスを自動的に作成するということがVB6Array(Of T)のクラスと異なっています。単純なVB6Array(Of T)の型を使用するとどんな配列の要素にアクセスする際にもNullReference例外をスローするでしょう。
Finally, notice that you can force a scalar (not array) FLS to be rendered as a VB6FixedString_NNN class by means of a SetStringSize pragma, as in this example:
最後に、SetStringSizeプラグマによるVB6FixedString_NNNクラスとしてスカラー(配列ではない)固定長文字列を強制的にレンダリングされることができるということに注意してください。以下が例です。
Dim s As String * 128
Such a pragma can be useful if you plan to assign a FLS to an array of FLSs. In practice, however, applying this pragma to scalar FLSs is rarely necessary.
そのようなプラグマは固定長文字列を固定長文字列の配列へアサインする予定があるのであれば、便利です。しかしながら実際にはこのプラグマを固定長文字列のスカラーに適用することはあまり必要ではありません。
3.5 Type…End Type blocks (UDTs)/Type・・・End Typeブロック(ユーザー定義型)
The main problem in converting Type…End Type blocks – a.k.a. User-Defined Types or UDT – to VB.NET is that a .NET structure can’t include a default constructor or fields with initializers. This limitation makes it complicated to convert UDTs that include initialized arrays, auto-instancing (As New) object variables, and fixed-length strings, because these elements need to be assigned a value when the UDT is created.
Type・・・End Typeブロックを変換する際の主な問題点は、VB.NETの別名:ユーザー定義型UDTは.NETの構造体が初期化子があるフィールドまたはデフォルトコンストラクタを含めることができないということです。この制限で初期化された配列、自動インスタンス化オブジェクト変数(As New)、固定長文字列を含むユーザー定義型を変換するのは複雑になります。なぜなら、これらの要素はユーザー定義型が作成される際に値を割り当てる必要があるからです。
VB Migration Partner solves this problem by generating a structure with a constructor that takes one dummy parameter and by ensuring that this constructor is used whenever a new instance of the UDT is created. Consider the following UDT:
VB Migration Partnerはひとつのダミーパラメータを持つコンストラクタと共に構造体を生成し、ユーザー定義型の新しいインスタンスが作成される際にはいつもこのコンストラクタが使用されるということを確実にすることによってこの問題を解決します。次のユーザー定義型を参照ください。
Type TestUdt
a As Integer
b As New Widget
c() As Long
d(10) As Double
e(1 To 10) As Currency
f As String * 10
g(10) As String * 10
h(1 To 10) As String * 10
End Type
This is how it is translated to VB.NET:
以下はVB.NETにどのように変換されたかの結果です。
Structure TestUdt
Public a As Short
Public b As Object
Public c() As Integer
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=11)> _
Public d() As Double
Public e As VB6Array(Of Decimal)
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=10)> _
Public f As VB6FixedString
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=11)> _
Public g() As VB6FixedString_10
Public h As VB6ArrayNew(Of VB6FixedString_10)
Public Sub New(ByVal dummyArg As Boolean)
InitializeUDT()
End Sub
Public Sub InitializeUDT()
b = New Object
ReDim d(10)
e = New VB6Array(Of Decimal)(1, 10)
f = New VB6FixedString(10)
g = CreateArray6(Of VB6FixedString_10)(0, 10)
h = New VB6ArrayNew(Of VB6FixedString_10)(1, 10)
End Sub
End Structure
Note: Previous example uses the ArrayBounds VB6Array pragma only to prove that VB6Array objects are initialized correctly; in most cases, the most appropriate setting for this pragma inside UDTs is Shift, because this setting ensures that the size of UDTs doesn’t change during the migration.
注記:先述のサンプルはVB6Arrayオブジェクトが正確に初期化されるということを分かるためにのみArrayBoundsのVB6Arrayプラグマを使用します。ほとんどのケースにおいては、ユーザー定義型の中のこのプラグマのための最も適した設定はShiftです。なぜならこの設定はユーザー定義型のサイズが移行作業の間に変わらないということを保証するためです。
Notice that the constructor takes an argument only because it is illegal to define a parameterless constructor in a Structure, but the argument itself is never used. Such a constructor is generated only if the UDT contains one or more members that require initialization, as in previous listing.
構造体の中でパラメータを必要としないコンストラクタを定義することは認められていないので、コンストラクタは引数をとりますが、引数自体が決して使用されないということに注意してください。前に記述したリストのように、初期化を必要とするひとつもしくはそれ以上のメンバを含むユーザー定義型の場合にのみそのようなコンストラクタは生成されます。
The key advantage of having this additional constructor is that it is possible to declare and initialize a UDT in a single operation. For example, the following VB6 statement:
この追加コンストラクタの主要な利点はひとつの操作でユーザー定義型を宣言し、そして初期化するということが可能であるということです。例として次にあげるVB6ステートメントを参照ください。
Dim udt As TestUdt
is translated to:
変換されると次のようになります。
Dim udt As New TestUdt(True)
VB Migration Partner supports nested UDTs, too. For example, the following VB6 definition:
VB Migration Partnerはネストされたユーザー定義型もサポートします。例として、次のVB6定義を参照ください。
Type TestUdt2
ID As Integer
Data As TestUdt
End Type
is converted to:
変換されると次のようになります。
Friend Structure TestUdt2
Public ID As Short
Public Data As TestUdt
Public Sub New(ByVal dummyArg As Boolean)
InitializeUDT()
End Sub
Public Sub InitializeUDT()
Data = New TestUdt(True)
End Sub
End Structure
A special case occurs when migrating a function or a property that returns a UDT. In this case, the return value is automatically initialized at the top of the code block, as this example demonstrates:
ユーザー定義型を戻すFunctionまたはプロパティを移行する時に特別なケースが生じます。戻り値がコードブロックの上位に自動的に初期化されるケースです。この例を示します。
Function GetUDT() As TestUdt
GetUDT.InitializeUDT()
...
End Function
Arrays of UDTs are migrated correctly, even if the UDT requires initialization. In such cases, in fact, the array is initialized by means of the CreateArray6 method, which ensures that the InitializeUDT method be called for each element in the array:
ユーザー定義型の配列は正確に移行されます。たとえ、ユーザー定義型が初期化を必要とするとしても。そのようなケースにて、実際にはCreateArray6メソッドにて配列は初期化されます。配列のそれぞれの要素にInitializeUDTメソッドが呼ばれるということを保証します。
Dim arr() As TestUdt = CreateArray6(Of TestUdt)(0, 10)
In some cases, a FLS defined inside a UDT must be rendered as a standard string rather than a VB6FixedString object. This replacement is necessary, for example, when the UDT is passed to an external method defined by a Declare statement, because the external method expects a standard string.
いくつかのケースにおいては、ユーザー定義型の内部の固定長文字列はVB6FixedStringオブジェクトよりもむしろより標準的な文字列としてレンダリングされなければなりません。例として、ユーザー定義型がDeclareステートメントにより定義された外部メソッドに渡されるとき、この置き換えは必要です。なぜなら、外部メソッドが標準的な文字列と予想するからです。
You can force VB Migration Partner to migrate a FLS as a standard string by means of the UseSystemString pragma. A FLS affected by this pragma is rendered as a private regular System.String field which is wrapped by a public property which ensures that values being assigned are always correctly truncated or extended. For example, consider the following VB6 code:
UseSystemStringプラグマによって標準的な文字列としての固定長文字列をVB Migration Partnerで強制的に移行することができます。このプラグマによって影響を受けた固定長文字列はプライベートなコードとファンクションによってレンダリングされます。文字列フィールドは共通のプロパティによってラッピングされ、割り当てられている値がいつも正確に省略されたり、拡張されたりすることを保証します。例として次のVB6コードを参照ください。
Public Type CDInfo
Title As String * 30
Artist As String * 30
End Type
Even though the two items are declared in the same way, the UseSystemString pragma changes the way the Title item is rendered:
二つのアイテムは同じ手法で宣言されているにも関わらず、UseSystemStringプラグマはTitleアイテムがレンダリングされるように変更します。
Friend Structure CDInfo
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=30)> _
Private m_Title As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=30)> _
Public Artist As VB6FixedString
Public Sub New(ByVal dummyArg As Boolean)
InitializeUDT()
End Sub
Public Sub InitializeUDT()
m_Title = VB6FixedString.GetEmptyBuffer(30)
Artist = New VB6FixedString(30)
End Sub
Public Property Title() As String
Get
Return VB6FixedString.Truncate(m_Title, 30, ControlChars.NullChar)
End Get
Set(ByVal value As String)
m_Title = VB6FixedString.Truncate(value, 30)
End Set
End Property
End Structure
The UseSystemString pragma can take a boolean value, where True is the default value assumed if you omit the argument. For example, in the following UDT all items are rendered as regular strings except the Year argument:
UseSystemStringプラグマはブール値をとることができ、Trueは引数を省略するのであれば想定されたデフォルト値です。以下のユーザー定義型のすべてのアイテムがYear引数を省略する通常の文字列としてレンダリングされたサンプルをご参照ください。
Public Type MP3Tag
Title As String * 30
Artist As String * 30
Album As String * 30
Year As String * 4
End Type
3.6 Auto-instancing variables/自動インスタンス変数
By default, a declaration of an auto-instancing variable is migrated to VB.NET verbatim. For example, the following statement is translated “as-is”:
デフォルトでは、自動インスタンス変数の宣言は一語一語正確にVB.NETに移行されます。例として次の“as is”変換サンプルステートメントをご覧ください。
Dim obj As New Widget
In most cases, this behavior is correct, even though the VB6 and VB.NET semantics are different. More precisely, a VB6 auto-instancing variable supports lazy instantiation and can’t be tested against the Nothing value, because the very reference to the variable recreates the instance if necessary.
ほとんどのケースにおいて、VB6とVB.NETの動作が異なっているとしても、この変換動作は正確に行われます。もっと正確に言うと、VB6自動インスタンス変数は遅延インスタンス化をサポートし、値がないものに対してテストすることができません。なぜなら、参照する変数は必要に応じてインスタンスを再作成するからです。
VB Migration Partner can generate code that preserves the VB6 semantics, if required. This behavior can be achieved by means of the AutoNew pragma, which can be applied at the project, class, method, and variable level.
VB Migration Partnerは必要であればVB6の動作を保持するコードを生成することができます。この動作はAutoNewプラグマによって成し遂げられます。それはProject、class、メソッド、そして変数レベルに対し適用することができます。
The actual effect of this pragma on local variables is different from the effect on class-level fields:
このプラグマのローカル変数への実際の効果はクラスレベルのフィールドの効果とは異なります。
Function GetValue() As Integer
Dim obj As New Widget
obj.Value = 1234
GetValue = obj.Value
End Function
An auto-instancing local variable that is under the scope of an AutoNew pragma is declared without the “New” keyword; instead, all its occurrences in code are automatically wrapped by the special AutoNew6 method:
AutoNewプラグマのスコープである自動インスタンスローカル変数は「New」キーワードを付けずに定義されます。その代わり、コードのそのすべてのオカレンスは特別なAutoNew6プラグマによって自動的にラッピングされます。
Function GetValue() As Short
Dim obj As Widget
AutoNew6(obj).Value = 1234
GetValue = ByVal6(obj)
End Function
The AutoNew6 method ensures that the variable abides by the “As New” semantics: a new Widget is instantiated (and assigned to the obj variable) when the method is called the first time and it is automatically recreated if the variable is set to Nothing.
AutoNew6メソッドは変数が「As New」動作によって守られることを保証します。メソッドが最初に呼ばれ、そして変数がNothingとセットされるのであれば、それは自動的に再作成されます。その際に、新しいWidgetがインスタンス化されます。(そしてobj変数に割り当てられます)
A class-level field under the scope of an AutoNew pragma is rendered as a property, whose getter block ensures that the lazy instantiation semantics is honored. For example, if obj is a class-level auto-instancing field, VB Migration Partner converts as follows:
AutoNewプラグマのスコープであるクラスレベルのフィールドはプロパティとしてレンダリングされます。そのプロパティのゲッターブロックは遅延インスタンス化動作が称えられるということを保証します。もしobjがクラスレベルの自動インスタンスフィールドである場合、VB Migration Partnerが次のように変換する例をご覧ください。
Public Property obj() As Widget
Get
If obj_InnerField Is Nothing Then obj_InnerField = New Widget ()
Return obj_InnerField
End Get
Set(ByVal value As Widget)
obj_InnerField = value
End Set
End Property
Private obj_InnerField As Widget
VB6 also supports arrays of auto-instancing elements, and VB Migration Partner fully supports them. If either an appropriate ArrayBounds or AutoNew pragma are in effect for such an array, VB Migration Partner renders it as an instance of the VB6ArrayNew(Of T) type. For example, the following VB6 code:
VB6は自動インスタンス化要素の配列もサポートしています。そしてVB Migration Partnerは完全にそれらをサポートします。そのような配列に適切なArrayBoundsまたはAutoNewプラグマによって効果が得られるならば、VB Migration PartnerはVB6ArrayNew(Of T)型のインスタンスとしてそれをレンダリングします。次のVB6コードを見てください。
Dim arr(10) As New TestClass()
is translated as
変換されると次のようになります。
Dim arr() As New VB6ArrayNew(Of TestClass)(0, 10)
The VB6ArrayNew(Of T) generic type behaves exactly as VB6Array(Of T), except the former automatically ensures that all its elements are instantiated before they are accessed.
ジェネリックタイプのVB6ArrayNew(Of T)はVB6Array(Of T)として正確に動作します。前者を除き自動的にそれらがアクセスされる前にそのすべての要素がインスタンスを作成されるということを保証します。
3.7 Declare statements/宣言文
VB Migration Partner is able to automatically solve most of the issues related to converting VB6 Declare statements to VB.NET. More specifically, in addition to data type conversion (e.g. Integer to Short, Long to Integer), the code generator adopts the following techniques:
VB Migration PartnerはVB6宣言ステートメントをVB.NETに変換するのに関係があるほとんどの問題を自動的に解決することができます。より明確にいうと、データ型の変換(Integer→Short、Long→Integerなど)に加えて、コード生成は次のようなテクニックを採用します。
“As Any” parameters/「As Any」パラメータ
If the Declare statement includes an “As Any” parameter, VB Migration Partner takes note of the type of values passed to it and the passing mechanism used (ByRef or ByVal), and then generates one or more overloads for the Declare statement. An example of a Windows API method that requires this treatment is SendMessage, which can take an integer or a string in its last argument:
宣言ステートメントが「As Any」パラメータを含んでいる場合は、VB Migration Partnerはそれに渡された値のタイプと使用される一時的なメカニズム(ByRefまたはByVal)を注意します。そして、宣言ステートメントのためにひとつもしくはそれ以上のオーバーロードを発生させます。この処理を要求するWindowsAPIメソッドの例はSendMessageです。その最後の引数にIntegerかStringをとることができます。
Private Declare Function SendMessage Lib "user32.dll" _
Alias "SendMessageA" (ByVal hWnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, _
lParam As As Any) As Long
Sub SetText()
SendMessage Text1.hWnd, WM_SETTEXT, 0, ByVal "new text"
End Sub
Sub CopyToClipboard()
SendMessage Text1.hWnd, WM_COPY, 0, ByVal 0
End Sub
This is the VB.NET code that VB Migration Partner generates. As you see, the As Any argument is gone and two overloads for the SendMessage method have been created:
これはVB Migration Partnerが生成したVB.NETコードです。見てわかるように、As Any引数はなくなり、SendMessageメソッドのために二つのオーバーロードを作成しています。
Private Declare Function SendMessage Lib "user32.dll" _
Alias "SendMessageA" (ByVal hWnd As Integer, _
ByVal wMsg As Integer, ByVal wParam As Integer, _
ByVal lParam As String) As Integer
Private Declare Function SendMessage Lib "user32.dll" _
Alias "SendMessageA" (ByVal hWnd As Integer, _
ByVal wMsg As Integer, ByVal wParam As Integer, _
ByVal lParam As Integer) As Integer
AddressOf keyword and callback parameters/AddressOfキーワードとcallbackパラメータ
If client code uses the AddressOf keyword when passing a value to a 32-bit parameter, VB Migration Partner assumes that the parameter takes a callback address and overloads the Declare to take a delegate type. For example, consider the following VB6 code inside the ApiMethods BAS module:
32bitのパラメータに値を渡す際にAddressOfキーワードをクライアントコードが使用しているのであれば、VB Migration Partnerはパラメータがcallbackアドレスをとり、デリゲートタイプの宣言をオーバーロードすることを担います。例としてAPIメソッドのBASモジュール内のVB6コードをご覧ください。
Declare Function EnumWindows Lib "user32" _
(ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Sub TestEnumWindows()
EnumWindows AddressOf EnumWindows_CBK, 0
End Sub
Function EnumWindows_CBK(ByVal hWnd As Long, _
ByVal lParam As Long) As Long
EnumWindows_CBK = 1
End Function
This is how VB Migration Partner converts the code to VB.NET:
VB Migration PartnerがどのようにVB.NETコードに変換するかを示します。
Public Delegate Function EnumWindows_CBK(ByVal hWnd As Integer, ByVal lParam As Integer) As Integer
Friend Module Module1
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Integer, _
ByVal lParam As Integer) As Integer
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As EnumWindows_CBK, _
ByVal lParam As Integer) As Integer
Public Sub TestEnumWindows()
EnumWindows(AddressOf EnumWindows_CBK, 0)
End Sub
Function EnumWindows_CBK(ByVal hWnd As Integer, ByVal lParam As Integer) As Integer
Return 1
End Function
End Module
Notice that only the Declare needs to be overloaded: the code that use the Declare doesn’t require any special treatment.
宣言だけがオーバーロードされることが必要であるということを注意してください。特別な処理を必要としない宣言を使うコードです。
Windows API methods that can be replaced by calls to .NET methods/.NETメソッドへの呼び出しによって置き換えられるWindowsAPIメソッド
VB Migration Partner is aware that calls to some specific Windows API methods can be safely replaced by calls to static methods defined in the .NET Framework, as is the case of Beep (which maps to Console.Beep), Sleep (System.Threading.Thread.Sleep), and a few others. When a call to such a Windows API method is found, it is automatically replaced by the corresponding call to the .NET Framework.
VB Migration Partnerはいくつかの特定のWindowsAPIメソッドの呼び出しを.NET Frameworkで定義されたStaticメソッドを呼ぶことによって安全に置き換えられることができるということを知っておいてください。それらのケースとして、Beep(Console.Beepにマップ)、Sleep(System.Threading.Thread.Sleep)、その他にもいくつかあります。そのようなWindowsAPIの呼び出しが見つけられた際には、自動的に.NET Frameworkの呼び出しに対応したものによって置き換えられます。
Windows API methods that have a recommended .NET counterpart/推奨される.NET対応版を持つWindowsAPIメソッド
VB Migration Partner comes with a database of about 300 Windows API methods, where each method is associated with the recommended replacement for .NET. If the parser finds a Declare in this group, a warning is emitted, as in this example:
VB Migration Partnerはおよそ300個のWindowsAPIメソッドのデータベースを搭載しています。そこではそれぞれのメソッドが推奨される.NETの対象APIに関連付けられています。パーサーがこのグループの宣言を見つけた場合は警告を出力します。次の例をご覧ください。
Private Declare Function GetSystemDirectory Lib "kernel32.dll" _
Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, _
ByVal nSize As Integer) As Integer
3.8 Variant and Control variables/バリアントとコントロール変数
By default, Variant variables are converted to Object variables. This default behavior can be changed by means of the ChangeType pragma, which changes the type of all Variant members (within the pragma’s scope) into something else. More specifically, developers can decide that Variant variables are rendered using the special VB6Variant type, as in this code:
デフォルトではバリアント変数はオブジェクト変数に変換されます。このデフォルト機能はChangeTypeプラグマによって変更することができます。それは、すべてのバリアントメンバ(プラグマのスコープ外)の型を他の何かに変更します。もっと正確に言うと、開発者はバリアント変数が特別なVB6Variantタイプを使ってレンダリングされるように決めることができます。次のコードを見てください。
Dim v As Variant
Dim arr() As Variant
which is translated to:
変換されると次のようになります。
Dim v As VB6Variant
Dim arr() As VB6Variant
The VB6Variant type (defined in the language support library) mimics the behavior of the VB6 Variant type as closely as possible, for example by providing support for the special Null and Empty values.
VB6Variantタイプ(Language Support Libraryに定義)は可能な限り厳密にVB6のVariantタイプの機能を踏襲します。たとえば、特別なNULLやEmpty値のサポートを提供していることなどです。
VB6Variant values can be tested by means of the IsEmpty6 and IsNull6 methods, and are recognized by the VarType6 method. Optional parameters of type Variant can be tested with the IsMissing6 function, similarly to what VB6 apps can do.
VB6Variant値はIsEmpty6、IsNull6メソッドによりテストすることができます。それらはVarType6メソッドとして認識されています。タイプVariantのオプションパラメータは同様にVB6のアプリケーションでもできたように、IsMissing6機能でテストすることができます。
The VB6Variant class provides a limited support for null propagation in math and string expressions. This ability is achieved by overloading all math and strings operators. The degree of support offered is enough complete for most common cases, but there might be cases when the result differs from VB6.
VB6Variantのクラスは計算式と文字列式でのNull伝播の限定的なサポートを提供します。この能力はすべての計算と文字列操作をオーバーロードすることによって実現されています。提供されたサポートの度合いは、ほとんどの一般的なケースにおいて十分完全の状態です。しかし、VB6の結果と異なる場合があるかもしれません。
By default VB Migration Partner translates variables and parameters of type Controls to Object variables and parameters. We opted for this approach because the VB6 Control is actually an IDispatch object and inherently requires late binding, as in this example:
デフォルトではVB Migration Partnerは変数とタイプコントロールのパラメータをオブジェクト変数とパラメータに変換します。なぜならVB6コントロールは実際にはIDispatchオブジェクトであり、本質的に遅延バインディングが必要なので、この手法を選択しました。次にサンプルを示します。
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeOf ctrl Is TextBox Then ctrl.Locked = True
Next
If the ctrl variable were rendered as a System.Windows.Forms.Control object, the code wouldn’t compile because the Control class doesn’t expose a Locked property. By contrast, VB Migration Partner renders the variable as an Object variable and produces VB.NET code that compiles and executes correctly:
Ctrl変数がSystem.Windows.Forms.Controlオブジェクトとしてレンダリングされていた場合、コントロールクラスはLockedプロパティがないので、コンパイルできません。それに反して、VB Migration Partnerは変数をオブジェクト変数としてレンダリングし、コンパイルと、正確に実行するVB.NETコードを実現します。
Dim ctrl As Object
For Each ctrl In Me.Controls6
If TypeOf ctrl Is VB6TextBox Then ctrl.Locked = True
Next
In other circumstances, however, changing the default behavior might deliver more efficient code. For example, consider this VB6 code:
その他の事情では、デフォルト動作を変えることで、より効率的なコードを提供することになるかも知れません。次のVB6コードを見てください。
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeOf ctrl Is TextBox Or TypeOf ctrl Is ComboBox Then
ctrl.Text = ""
End If
Next
In this case, you can leverage the fact that the System.Windows.Forms.Control class exposes the Text property, thus you can add a SetType pragma that changes the type for the ctrl variable. This is the resulting VB.NET code:
このケースでは、System.Windows.Forms.ControlクラスがTextプロパティを持つという事実を利用することができます。よって、ctrl変数の型を変更するSetTypeプラグマを追加します。次のVB.NETの結果コードを見てください。
Dim ctrl As Control
For Each ctrl In Me.Controls6
If TypeOf ctrl Is VB6TextBox Or TypeOf ctrl Is VB6ComboBox Then
ctrl.Text = ""
End If
Next
The ctrl variable is now strong-typed and the VB.NET code runs faster.
Ctrl変数は現在強い型付けになり、VB.NETのコードはより早く動作します。
Please notice the difference between the ChangeType pragma (which affects all the variables and parameters of a given type) and the SetType pragma (which affects only a specific variable or parameter).
ChangeTypeプラグマ(すべての変数と与えられた型のパラメータに影響します)とSetTypeプラグマ(特定の変数またはパラメータにのみ影響します)の違いに注意してください。
3.9 Classes and Interfaces/クラスとインターフェース
VB Migration Partner deals with VB6 classes and interfaces in a manner that resembles the way interfaces and coclasses work in COM. More specifically, if a VB6 class named XYZ appears in an Implements statement, anywhere in the current solution, then VB Migration Partner generates an Interface named XYZ and renames the original class as XYZClass. For example, assume that you have the following IPlugIn class:
VB Migration PartnerはCOMで動作するインターフェースとcoclassの手法に類似している作法でVB6クラスとインターフェースは取り扱われます。より明確にいうと、XYZという名前のVB6クラスがImplementsステートメントや現在のSolutionのどこかにあるのであれば、VB Migration PartnerはXYZという名前のインターフェースとXYZClassとしてオリジナルのクラスを改名します。IPlugInクラスの例を参照ください。
Sub Execute()
End Sub
Property Get Name() As String
End Property
Next, assume that the IPlugIn class is referenced by an Implements statement in the SamplePlugIn class, defined elsewhere in the current project or solution:
次に、IPlugInクラスが現在のProjectまたはソリューションのどこかで定義されたSamplePlugInクラスのImplementsステートメントで参照されているとみなしてください。
Implements IPlugIn
Under these assumptions, this is the code that VB Migration Partner generates:
これらの仮定で、VB Migration Partnerは次のようにコードを生成します。
Public Class IPlugInClass
Implements IPlugIn
Sub Execute() Implements IPlugIn.Execute
End Sub
ReadOnly Property Name() As String Implements IPlugIn.Name
Get
End Get
End Property
End Class
Public Interface IPlugIn
Sub Execute()
ReadOnly Property Name() As String
End Interface
This rendering style minimizes the impact on code that references the ISomething class. For example, the following VB6 code:
このレンダリングスタイルはISomethingクラスを参照しているコードの影響を最小限にします。
Sub CreatePlugIn(itf As IPlugIn)
Set itf = New SamplePlugIn
End Sub
is converted to a piece of VB.NET code that is virtually identical, except for the Set keyword being dropped:
次はSetキーワードが削除されていることを除けば、ほとんど同一のVB.NETコードに変換されます。
Sub CreatePlugIn(ByRef itf As IPlugIn)
itf = New SamplePlugIn()
End Sub
References to the IPlugIn type are replaced by references to the IPlugInClass name only when the class name follows the New keyword, as in this VB6 code:
IPlugInタイプの参照はクラス名が新しいキーワードに従うときのみ、IPlugInClassという名前の参照に置き換えられます。次のVB6コードをご覧ください。
Dim itf As New IPlugIn
which translates to
変換されると、
Dim itf As New IPlugInClass
You’ve seen so far that when a VB6 class appears in an Implements statement, by default VB Migration Partner takes a conservative approach and creates a both a VB.NET class and an interface. This approach ensures that the migrated app works correctly in all cases, including when the VB6 class is actually instantiated. In most real cases, however, a type used in an Implements statement never appears as an operand for the New keyword; therefore generating the class is of no practical use. You can tell VB Migration Partner not to generate the class by means of a ClassRenderMode pragma:
今までにVB6クラスがImplementsステートメントにある場合、デフォルトでVB Migration Partnerが保守的な手法をとり、VB.NETのクラスとインターフェースの両方を作ることを見ていただきました。この手法はVB6クラスが実際にインスタンス化されているのを含み、移行されたアプリケーションがすべてのケースにて正確に動作することを保証します。しかしながら、実際のケースではImplementsステートメントで使われる型はNewキーワードとしてのオペランドとして現れることはありません。したがってクラスを生成することは実用性のないものです。ClassRenderModeプラグマを使用してクラスを生成しないようにVB Migration Partnerにて設定することができます。
The ClassRenderMode pragma can’t be applied at the project level and has to be specified for each distinct class.
ClassRenderModeプラグマはプロジェクトレベルでは適用することはできません。それぞれの異なるクラスに対して指定されなければなりません。
3.10 Finalization and disposable classes/終了処理とdisposableクラス
All VB6 and COM objects internally manage a reference counter: this counter is incremented each time a reference to the object is created and is decremented when the reference is set to Nothing. When the counter reaches zero it’s time to fire the Class_Terminate event and destroy the object. This mechanism is known as deterministic finalization, because the instant when the object is destroyed can be precisely determined.
すべてのVB6とCOMオブジェクトは内部的に参照カウンタを管理します。このカウンタはオブジェクトへの参照が作成されるたびに増加し、オブジェクト参照がNothingにセットされるごとに減少します。そのカウンタがゼロに達するときに、Class_Terminateイベントが発動し、オブジェクトを破棄します。このメカニズムは確定終了処理として知られています。なぜならオブジェクトが破棄される瞬間が正確に決定できるからです。
.NET objects don’t manage a reference counter and objects are physically destroyed only some time after all references to them have been set to Nothing, more precisely when a garbage collection is started. One of the biggest challenges in writing a VB6 code converter is the lack of support for deterministic finalization in the .NET Framework.
.NETオブジェクトは参照カウンタを管理せず、オブジェクトはそれらのすべての参照がNothingでセットされた後の少しの時間で物理的に破棄されます。もっと正確にいえば、ガーベージコレクションが開始されたときです。VB6コード変換を書くことの最も大きい挑戦のひとつは、.NET Frameworkの確定終了処理のサポートの不足です。
VB.NET objects that need to execute code when they are destroyed implement the IDisposable interface. Such objects rely on the collaboration from client code, in the sense that the developer who instantiates and uses the object is responsible for disposing of the object – by calling the IDisposable.Dispose method – before setting the object variable to Nothing or letting it go out of scope. In general, any .NET class that defines one or more class-level field of a disposable type should be marked as disposable and implement the IDisposable interface. The code in the Dispose method should orderly dispose of all the objects referenced by the class-level fields.
それらが破棄される際にコードを実行する必要があるVB.NETオブジェクトはIDisposableインターフェースを実装します。そのようなオブジェクトはクライアントコードからのコラボレーションに依存します。開発者はオブジェクト変数をNothingにセットするか、範囲外にする前にIDsiposable.Disposeメソッドを呼びながらオブジェクトの破棄のためにオブジェクトをインスタンス化し、そして使用することに責任があるということに気がつきます。一般にはdisposable型のひとつもしくはそれ以上のクラスレベルフィールドを定義するどんな.NETクラスもdisposableとしてマークされ、IDisposableインターフェースを実装すべきです。Disposeメソッドのコードはクラスレベルフィールで参照される全てのオブジェクトを正しく処理すべきです。
As just noted, the code that instantiates the class is also responsible for calling the Dispose method as soon as the object isn’t necessary any longer, so that referenced disposable objects are disposed as soon as possible. For example, if the class defines and opens one or more database connections (e.g. an SqlConnection object), calling the Dispose method ensures that the connection is closed as quickly as possible. If the call to the Dispose method is omitted, the connection will be closed only later, at the first garbage collection.
注意点としてクラスをインスタンス化するコードはまた、オブジェクトがもう必要でないとすぐにDisposeメソッドを呼ぶ責任があります。よって参照されたdisposableオブジェクトはできるだけ早く処理されます。たとえば、クラスがひとつまたはそれ以上のデータベースコネクション(例SqlConnectionオブジェクト)を定義し、開くなら、Disposeメソッドはコネクションができるだけ素早く閉じられるということ保証します。Disposeメソッドの呼び出しが省略される場合、コネクションは最初のガーベージコレクションで後に閉じられるでしょう。
The .NET Framework also supports finalizable classes. A finalizable class is a class that overrides the Finalize method and defines one or more fields that contain Windows handles or other values related to unmanaged resources. For example, a class that opens a file by means of the CreateFile Windows API method must be implemented as a finalizable class. The method in the Finalize method is guaranteed to run when the object is being removed from memory during a garbage collection. The code in the Finalize method is expected to close all handles and orderly release all unmanaged resources. Failing to do so would create a resource leak.
.NET Frameworkは終了処理のクラスをサポートします。Finalizableクラスは終了処理メソッドを無効にし、非管理リソースに関連する他の値とWindowsハンドルを含む1つ以上のフィールドを定義するクラスです。例として、CreateFile Windows APIメソッドによるファイルを開くクラスは終了処理のクラスとして実装されなければなりません。終了処理メソッドによるメソッドはガーベージコレクションの中のメモリからオブジェクトが削除されているときに、動作することを保証します。終了処理のメソッドのコードは全てのハンドルが閉じられと全ての非管理リソースが順番に解放することを予想します。そうしないとリソース漏れを引き起こすでしょう。
VB Migration Partner supports both disposable and finalizable classes. However, you might need to insert one or more pragmas to help it to generate the same quality code that an experienced .NET developer would write. Let’s start with a VB6 class that handles the Class_Terminate event
VB Migration Partnerは終了処理クラスとdisposableクラスの両方をサポートします。しかし、ひとつもしくはそれ以上のPragmaを経験豊富な.NET開発者が書いたものと同じ品質のコードを生成するために挿入する必要があるかもしれません。ではVB6クラスがClass_Terminateイベントをハンドルするのを見てみましょう。
Private fileHandle As Long
Private Sub Class_Terminate()
CloseHandle fileHandle
End Sub
VB6 classes that include a Class_Terminate are converted to disposable classes that implement the recommended Dispose-Finalize pattern. The generated code ensures that the code inside the original Class_Terminate event runs when either a client invokes the Dispose method or when the garbage collection invokes the Finalize method:
Class_Terminateを含むVB6クラスが推奨されるDispose−Finalizeパターンを実行するdisposableクラスに変換されます。生成されたコードは、クライアントがDisposeメソッドを呼び出すか、またはガーベージコレクションがFinalizeメソッドを呼び出す際に、オリジナルのClass_Terminateイベントにおけるコードが動作するのを保証します。
Public Class Widget
Implements IDisposable
Private fileHandle As Integer
Private Sub Class_Terminate_VB6()
CloseHandle(fileHandle)
End Sub
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
Public Sub Dispose() Implements System.IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
Class_Terminate_VB6()
End Sub
End Class
If the Terminate event is defined inside a Form or a UserControl class, the Dispose method isn’t emitted (because the base class is already disposable); instead, the Form_Terminate or UserControl_Terminate protected method is overridden:
終了処理イベントがFormまたはユーザーコントロールクラス内で定義されている場合は、Disposeメソッドは発令されません。(なぜなら基本クラスはすでに処分されているので)、その代り、Form_TerminateまたはUserControl_Terminateがメソッドが無効されることを保護します。
Protected Overrides Sub Form_Terminate_VB6()
CloseHandle(fileHandle)
End Sub
VB Migration Partner can take additional steps to ensure that, if a class uses one or more disposable objects, such objects are correctly disposed of when an instance of the class goes out of scope. In other words, not only does the code generator mark classes with a finalizer as IDisposable classes (as explained above) but it also marks classes using other disposable objects as IDisposable.
VB Migration Partnerはそれを保証するための追加ステップをとることができます。もしクラスが1つ以上のDisposableオブジェクトを使用しているのであれば、そのようなオブジェクトはクラスのインスタンスが範囲外にするときに、正確に破棄されます。言い換えればコード生成がIDisposableクラス(上記で説明しています)としてのFinalizerをもつクラスをマークするだけではなく、IDisposableとしての他のDisposableオブジェクトを使用しているクラスをもマークします。
To explain how this feature works, a few clarifications are in order. As far VB Migration Partner is concerned, a disposable type is one of the following:
この特徴がどのように機能しているか、順番に明確に説明していきます。VB Migration PartnerとしてはDisposable型としては次に示す通りです。
- a VB6 class that has a Class_Terminate event (as seen above)
VB6クラスはClass_Terminateイベントを持っています。(上記で指摘) - a COM type known to be as disposable (e.g. ADODB.Connection)
COM型はdisposableとして知られています。(例 ADODB.Connection) - a COM type that is explicitly marked as disposable by means of an AddDisposableType pragma, as in this example:
AddDisposableTypeプラグマによるdisposableとして明らかにマークされるCOMタイプ。
例として - a VB6 class that has one or more class-level fields of a disposable type
VB6クラスはdisposable型の1つ以上のクラスレベルフィールドを持っています。
VB Migration Partner applies these definition in a recursive way. For example, assuming that class C1 has a field of type ADODB.Connection, class C2 has a field of type C1, and class C3 has a field of class C2, then all the C1, C2, and C3 classes are all marked as IDisposable.
VB Migration Partnerは再帰的な方法でこれらの定義を適用します。例えば、クラスC1にはタイプADODB.Connectionのフィールドがあって、クラスC2にタイプC1のフィールドがあって、クラスC3にクラスC2のフィールドがあると仮定する場合、すべてのC1、C2、およびC3のクラスは皆、IDisposableとしてマークされます。
If a type is found to be disposable, the exact VB.NET code that VB Migration Partner generates depends on whether it’s under the scope of an AutoDispose pragma. This pragma takes an argument that can have the following values:
型がdisposableであることが分かっているのであれば、VB Migration Partnerが生成する正確なVB.NETコードはAutoDisposeプラグマの範囲内にあるかどうかに依存します。このプラグマは次に示す値をもてる引数をとります。
No
Variables of disposable types aren’t handled in any special way. (This is the default behavior.)
Disposable型の変数はどのような方法でもハンドリングされません。(これはデフォルト機能です)
Yes
If X is a variable of a disposable type, the Set X = Nothing statement is converted as follows:
もしXがDisposable型の変数であれば、Set X=Nothingステートメントは次のように変換されます。
SetNothing6(X)
The SetNothing6 method (defined in CodeArchitects.VBLibrary) ensures that the object is cleaned-up correctly. If the object implements IDisposable then SetNothing6 calls its Dispose method. If the object is a COM object, SetNothing6 ensures that the object’s RCW is correctly released.
SetNothing6メソッド(CodeArchitects.VBLibraryに定義)はオブジェクトが正確に除去されることを保証します。オブジェクトがIDisposableを実装している場合、SetNothing6はそのDisposeメソッドを呼びます。オブジェクトがCOMオブジェクトの場合は、SetNothing6はオブジェクトのRCWは正確にリリースされることを保証します。
Force
In addition to converting explicit Set X = Nothing statements for disposable objects, VB Migration Partner ensures that if a VB6 class uses one or more disposable objects, the corresponding VB.NET class implements the IDisposable interface and all the disposable objects are correctly disposed of in the class’s Dispose method.
DisposableオブジェクトのSet X=Nothingステートメントを明確に変換することに加え、VB Migration PartnerはVB6クラスが1つ以上のDisposableオブジェクトを使用している場合、対応するVB.NETのクラスはIDisposableインタフェースを実装します、そして、すべてのDisposableオブジェクトがクラスのDisposeメソッドで正しく処分されるということを保証します。
Let’s see in practice how to use the AutoDispose pragma, starting with the Yes option:
YesオプションでのAutoDispose Pragmaをどのように使用するか、例をご覧ください。
Sub Test()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Set rs = Nothing
Set cn = Nothing
End Sub
The resulting VB.NET code is identical, except for the SetNothing6 method:
SetNothing6メソッドを除いて、結果としてできたVB.NETコードは同じです。
Sub Test()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
SetNothing6(rs)
SetNothing6(cn)
End Sub
Let’s see now the effects of the Force option, and let’s assume that the following VB6 code is contained in the Widget class:
次にForceオプションの効果を見てみましょう。次のVB6コードがWidgetクラスに含まれていると仮定してご覧ください。
Dim cn As ADODB.Connection
Dim utils As CALib.DBUtils
…
The ADODB.Connection type is known to be disposable, whereas CALib.DBUtils is marked a disposable by the AddDisposableType pragma. (Such a pragma implicitly has a project-level scope.) Because of rule d) above, the Widget class is considered to be disposable, which makes VB Migration Partner generate the following code:
ADODB.Connection型はDisposableになることが知られていますが、CALib.DBUtilsがAddDisposableTypeプラグマによって、Disposableをマークされました。(そのようなプラグマは暗黙的にProjectLevelのスコープを持っています)上記のルールdのために、WidgetクラスはDisposableであると考えられています。それでVB Migration Partnerは次のコードを作ります。
Public Class Widget
Implements System.IDisposable
Dim cn As ADODB.Connection
Dim utils As CALib.DBUtils
…
Public Sub Dispose() Implements System.IDisposable.Dispose
SetNothing6(cn)
SetNothing6(utils)
End Sub
End Class
If the Widget class has a Class_Terminate event handler, the code in the Dispose method is slightly different:
WidgetクラスがClass_Terminateイベントハンドラを持っている場合、Disposeメソッドのコードは多少異なっています。
Public Sub Dispose() Implements System.IDisposable.Dispose
Try
SetNothing6(cn)
SetNothing6(utils)
Finally
Class_Terminate_VB6()
GC.SupporessFinalize(Me)
End Try
End Sub
Notice that a class that uses disposable objects doesn’t necessarily implement the Finalize method, as per .NET guidelines. Only VB6 classes that have a Class_Terminate event are migrated to VB.NET classes with the Finalize method.
Disposableオブジェクトを使うクラスが.NETのガイドラインに従って、必ずFinalizeメソッドを実装する必要がないということに注意してください。Class_Terminateイベントをもつ唯一のVB6クラスは、Finalizeメソッドと共にVB.NETクラスに移行されます。
VB Migration Partner ensures that disposable objects are correctly cleaned-up also when they are assigned to local variables, if a proper AutoDispose pragma is used. For example, consider the following method inside the TestClass class:
適切なAutoDisposeプラグマが使われているのであれば、VB Migration PartnerはDisposableオブジェクトがローカル変数に割り当てられるときに正確に処分されるということを保証します。
Sub Execute()
Dim conn As New ADODB.Connection
If condition Then
Dim wid As Widget
…
End If
End Sub
In such a case VB Migration Partner moves variables declarations to the top of the method, puts the method’s body inside a Try block, and ensures that disposable objects are cleaned-up in the Finally block. Notice that the wid variable is cleaned-up as well, because Widget has found it to be disposable:
このようなケースにおいて、VB Migration Partnerは変数の宣言をメソッドの上部に移動し、Tryブロックをメソッドのボディに配置します。そして最後にDisposableオブジェクトをFinallyブロックにて処分されることを保証します。WidgetがDisposableになると分かったので、wid変数は処分されるということに注意してください。
Sub Execute()
Dim conn As New ADODB.Connection
Dim wid As Widget
Try
If condition Then
…
End If
Finally
SetNothing6(conn)
SetNothing6(wid)
End Try
End Sub
However, if the method contains one or more On Error statements (which can’t coexist with Try blocks) or GoSub statements (which would produce a forbidden GoTo that jumps inside the Try-Catch block), the code generator emits a warning that reminds the developer that a manual fix is needed:
しかし、もしメソッドが1つ以上のOn Errorステートメント(Tryブロックと共存できない)やGoSubステートメント(Try-Catchブロック内に移動する、禁じられているGoToを提供する)を含んでいる場合、コード生成プログラムは手動による修正が必要になるということを開発者にお知らせする警告を出力します。
The approach VB Migration Partner uses to ensure that disposable variables are cleaned-up correctly resolves most of the problems related to undeterministic finalization in .NET. One of the few cases VB Migration Partner can’t handle correctly is when a class field or a local variable points to an object that is referenced by fields in another class, as in this case:
VB Migration Partnerがdisposable変数を正確に処理するのを保証するのに使用するアプローチは、.NETにおけるはっきりしない終了処理に関連したさまざまな問題を解決します。VB Migration Partnerが取り扱うことができないわずかなケースのひとつが、クラスフィールドまたはローカル変数が他のクラスのフィールドによって参照されているオブジェクトを示す場合です。次のようなケースです。
Sub Execute()
Dim conn As New ADODB.Connection
Set GlobalConn = conn
…
End Sub
In this specific case, invoking the Dispose method on the conn variable would close the connection referenced by the GlobalConn variable, which in turn may cause the app to malfunction. Developers can avoid this problem by disabling the AutoDispose feature for a given variable or for all the variables in a method:
この具体的なケースで、conn変数でDisposeメソッドを起動することはGlobalConn変数により参照されているコネクションをクローズします。そうするとアプリケーションを誤動作することになるかもしれません。開発者は与えられた変数や、メソッド内のすべての変数にAutoDispose機能を無効にすることによってこの問題を回避することができます。
Sub Execute()
Dim conn As New ADODB.Connection
…
End Sub
3.11 ActiveX Components/ActiveXコンポーネント
VB Migration Partner supports most of the kinds of COM classes that you can create with VB6. This section explains how you can fine-tune the VB.NET code being generated.
VB Migration PartnerはVB6で作ることができるCOMクラスのほとんどの種類をサポートします。このセクションでは生成されていくVB.NETコードをどのように微調整していくかを説明します。
ActiveX EXE projectsActiveX EXEプロジェクト
ActiveX EXE projects aren’t supported in VB.NET and, by default, VB Migration Partner converts them to standard EXE projects. Developers can change this behavior by means of the ProjectKind pragma:
ActiveX EXEプロジェクトはVB.NETではサポートされません。そしてデフォルトでVB Migration Partnerは標準EXプロジェクトにそれらを変換します。開発者はこの機能をProjectKindプラグマで変更することができます。
MultiUse, SingleUse, and PublicNotCreatable classesMultiUse、SingleUse、およびPublicNotCreatableクラス
MultiUse and SingleUse classes are converted to public VB.NET classes with a public constructor, so that they can be instantiated from a different assembly. PublicNotCreatable classes are converted to public VB.NET classes whose constructor has Friend scope, so that the class can’t be instantiated from outside the current project.
MultiUse、SingleUseクラスは異なったアセンブリから例示できるようにPublicコンストラクタと共に、PublicVB.NETクラスに変換されます。PublicNotCreatableクラスは友好的な範囲をもつPublicのVB.NETクラスに変換されます。よって、クラスは現在のプロジェクトの外側からインスタンス化することはできません。
Notice that the .NET Framework doesn’t support the behavior implied by the SingleUse instancing attribute, therefore SingleUse and MultiUse classes are converted in the same way.
.NET FrameworkはSingleUseのインスタンス化された属性による暗黙的な動作をサポートしません。よって、SingleUseやMultiUseのクラスは同様に変換されるということに注意してください。
In all three cases, the class is marked with a System.Runtime.InteropServices.ProgID attribute, so that it is visible to COM clients. If the VB6 class was associated to a description, it appears as an XML comment at the top of the VB.NET class:
すべての3つのケースにおいて、クラスはCOMクライアントにとって見えるようにSystem.Runtime.InteropServices.ProgID属性にマークされます。もしVB6クラスが説明に関連付けられているのであれば、VB.NETクラスの先頭にXMLコメントとして現れます。
<System.Runtime.InteropServices.ProgID("Project1.Widget")> _
Public Class Widget
Public Sub New()
End Sub
End Class
GlobalMultiUse and GlobalSingleUse classes/GlobalMultiUse と GlobalSingleUse クラス
By default, GlobalMultiUse and GlobalSingleUse classes are translated to standard VB.NET classes. However, when a client accesses a method or property of such classes, VB Migration Partner generates a call to a method of a default instance named ProjectName_ClassName_DefInstance, as in:
デフォルトではGlobalMultiUse と GlobalSingleUse クラスは標準のVB.NETクラスに変換されます。しかし、クライアントがそのようなクラスのメソッドやプロパティにアクセスする場合、VB Migration PartnerはProject名_クラス名_DefInstanceという名前のデフォルトインスタンスのメソッドへの呼び出しを生成します。
res = CALib_Geometry_DefInstance.EvalArea(12, 23)
All the *_DefInstance variables are defined and instantiated in the VisualBasic6.Support.vb module, in the MyProject folder.
全ての*_DefInstance変数はMyProjectフォルダのVisualBasic6.Support.vbモジュールにて定義もしくはインスタンス化されています。
In most cases, a global class is used as a singleton class and is never instantiated explicitly. In other words, a client typically never uses a global class with the New keyword and uses only the one instance that is instantiated implicitly. If you are sure that all clients abide by this constraint, it is safe to translate the class to a VB.NET module instead of a class, which you do by means of the ClassRenderMode pragma:
ほとんどのケースにおいてGlobalクラスはシングルトンクラスとして使用され、決して明確にインスタンス化されません。言い換えれば、クライアントがNewキーワードをもつGlobalクラスを通常使用することはありません。暗黙的にインスタンス化されたたったひとつのインスタンスを使用します。すべてのクライアントがこの制約を守るのを確信しているなら、ClassRenderModeプラグマによってクラスの代わりにVB.NETモジュールにクラスを変換するのは、安全です。
If such a pragma is used, the current class is rendered as a VB.NET Module and no default instance variable is defined in the client project. When a Module is used, methods can be invoked directly, the VB.NET code is more readable, and the method call is slightly faster. Notice that the project name is included in all references, to avoid ambiguities:
そのようなプラグマが使用された場合は、現在のクラスはVB.NETモジュールにレンダリングされ、デフォルトのないインスタンス変数はクライアントのプロジェクトに定義されます。モジュールが使用された際に、メソッドを直接呼び出すことができます。VB.NETコードはより読みやすいコードになり、メソッドの呼び出しも若干早くなります。プロジェクト名があいまいさを避けるためにすべての参照に含まれるということに注意してください。
res = CALib.EvalArea(12, 23)
Notice that you shouldn’t use the ClassRenderMode pragma with global classes that have a Class_Terminate event, because VB Migration Partner automatically renders them as classes that implement the IDisposable interface, and the Implements keyword inside a VB.NET module would cause a compilation error.
Class_Terminateイベントを持っているグローバルなクラスがあるClassRenderModeプラグマを使用するべきでないことに注意してください、VB Migration PartnerがIDisposableインタフェースを実行するクラスとして自動的にそれらをレンダリングし、VB.NETモジュールにおけるImplementsキーワードがコンパイルエラーを引き起こすことになります。
Component initialization/コンポーネントの初期化
If an ActiveX DLL includes a Sub Main method, then the VB6 runtime ensures that this method is invoked before any component in the DLL is instantiated. This mechanism allows VB6 developers to use the Sub Main method to initialize global variables, read configuration files, open database connections, and so forth.
ActiveX DLLがSub Mainメソッドを含んでいる場合は、VB6のランタイムはこのメソッドがDLLのいくつかのコンポーネントがインスタンス化される前に呼び出されるということを保証します。VB6開発者はこのメカニズムでグローバル変数を初期化するためのSub Mainメソッドを使用できます、構成ファイルの読み込み、データベースコネクションのオープンなど。
This mechanism isn’t supported by VB.NET and the .NET Framework in general, therefore VB Migration Partner emits additional code to ensure that the Sub Main is executed exactly once, before any class of the DLL is instantiated.
このメカニズムは一般に.NET FrameworkとVB.NETではサポートされていません。したがって、VB Migration Partnerは、DLLのいくつかのクラスがインスタンス化される前に、Sub Mainが厳密に一度実行されるのを保証するために追加コードを生成します。
Public Class Widget
Shared Sub New()
EnsureVB6LibraryInitialization()
EnsureVB6ComponentInitialization()
End Sub
End Class
The EnsureVB6LibraryInitialization method checks that the language support library is initialized correctly, whereas the EnsureVB6ComponentInitialization method invokes the Sub Main if it hasn’t been already executed.
EnsureVB6LibraryInitializationメソッドは、言語サポートライブラリが正しく初期化されるのをチェックしますが、それが既に実行されていないなら、EnsureVB6ComponentInitializationメソッドはSub Mainを呼び出します。
3.12 Persistable classes/持続性(Persistable)クラス
VB Migration Partner fully supports VB6 persistable classes. To illustrate exactly what happens, assume that you have a VB6 class marked as persistable and that handles the InitProperties, ReadProperties, and WriteProperties to implement persistence:
VB Migration PartnerはVB6の持続性クラスを完全にサポートします。何が起きているかを正確に例証するために、持続性としてマークされたVB6クラスに持続性を実装するためにInitProperties、ReadProperties、WritePropertiesを扱います。
Const ID_DEF As Integer = 0
Const NAME_DEF As String = ""
Public ID As Integer
Public Name As String
Private Sub Class_InitProperties()
ID = 123
Name = "widget name"
End Sub
Private Sub Class_ReadProperties(PropBag As PropertyBag)
ID = PropBag.ReadProperty("ID", ID_DEF)
Name = PropBag.WriteProperty("Name", NAME_DEF)
End Sub
Private Sub Class_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "ID", ID, ID_DEF
PropBag.WriteProperty "Name", Name, NAME_DEF
End Sub
The resulting VB.NET class is marked with the Serializable attribute and implements the System.Runtime.Serialization.ISerializable interface. The class constructor invokes the Class_InitProperty handler:
結果としてできるVB.NETのクラスは、Serializable属性でマークされ、System.Runtime.Serialization.ISerializableインタフェースを実装します。クラスのコンストラクタはClass_InitPropertyハンドラを呼び出します。
Imports System.Runtime.Serialization
<System.Runtime.InteropServices.ProgID("Project1.Widget")> _
<Serializable()> _
Public Class Widget
Implements ISerializable
Public Sub New()
Class_InitProperties()
End Sub
Event handlers are converted as standard private methods:
イベントハンドラはスタンダードプライベートメソッドとして変換されます。
Private Const ID_DEF As Short = 0
Private Const NAME_DEF As String = ""
Public ID As Short
Public Name As String = ""
Private Sub Class_InitProperties()
ID = 123
Name = "widget name"
End Sub
Private Sub Class_ReadProperties(ByRef PropBag As VB6PropertyBag)
ID = PropBag.ReadProperty("ID", ID_DEF)
Name = PropBag.WriteProperty("Name", NAME_DEF)
End Sub
Private Sub Class_WriteProperties(ByRef PropBag As VB6PropertyBag)
PropBag.WriteProperty("ID", ID, ID_DEF)
PropBag.WriteProperty("Name", Name, NAME_DEF)
End Sub
The code in the GetObjectData and the constructor implied by the ISerializable interface invoke the InitProperties, ReadProperties, and WriteProperties handlers:
GetObjectDataのコードとISerializableインタフェースで暗黙的に定義されたコンストラクタはInitProperties、ReadProperties、およびWritePropertiesハンドラを呼び出します。
Private Sub GetObjectData(ByVal info As SerializationInfo, _
ByVal context As StreamingContext) Implements ISerializable.GetObjectData
Dim propBag As New VB6PropertyBag
Class_WriteProperties(propBag)
info.AddValue("Contents", propBag.Contents)
End Sub
Private Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
Dim propBag As New VB6PropertyBag
Class_InitProperties()
propBag.Contents = info.GetValue("Contents", GetType(Object))
Class_ReadProperties(propBag)
End Sub
End Class
All references to the VB6’s PropertyBag object are replaced by references to VB6PropertyBag, a class with similar interface and behavior defined in the language support library. It is important to bear in mind, however, that binary files created by persisting a VB6 object can’t be deserialized into a VB.NET object, and vice versa.
VB6のPropertyBagオブジェクトのすべての参照は同様のインターフェースと、言語サポートライブラリで定義されている動作をもつクラスVB6PropertyBagへの参照に置き換えられます。しかしながら、持続性のVB6オブジェクトによって作成されたバイナリファイルがVB.NETオブジェクトにデシリアライズすることができないということを覚えておくことが重要であり、その逆も同様です。
3.13 Resources/リソース
VB6 resource files are converted to standard .resx files and can be viewed and modified by means of the My Project designer. More precisely, resources are converted to My.Resources.prefixNNN, where prefix is “str” for string resources, “bmp” for bitmaps, “cur” for cursors, and “ico” for icons.
VB6リソースファイルはスタンダード.resxファイルに変換されます。またMy Projectデザイナによって表示させたり、修正することができます。より詳細に説明すると、リソースはMy.Resources.prefixNNNに変換されます。そこでは、接頭語は、ストリングリソースのための「str」と、ビットマップへの「bmp」と、カーソルのための「cur」と、アイコンのための「ico」です。
VB Migration Partner attempts to convert all occurrences of LoadResString, LoadResPicture, and LoadResData methods into references to My.Resource.prefixNNN elements. This is possible, however, only if the arguments passed to these method are constant values or constant expressions, as in the following VB6 example:
VBMPは、LoadResString、LoadResPicture、およびLoadResDataメソッドのすべてのオカレンスをMy.Resource.prefixNNN要素の参照に変換するのを試みます。しかしながら、これらのメソッドに渡された引数が、コンスタント値かコンスタント式である場合にだけ可能です。以下のVB6の例を御参照ください。
Const RESBASE As Integer = 100
Const STRINGRES As Integer = RESBASE + 1
MsgBox LoadResString(STRINGRES)
Image1.Picture = LoadResPicture(RESBASE + 7, vbResBitmap)
which is correctly translated into:
正確に変換されます。
Const RESBASE As Short = 100
Const STRINGRES As Short = RESBASE + 1
MsgBox6(My.Resources.str101)
Image1.Picture = My.Resources.bmp107
If the first or the second argument isn’t a constant, then VB Migration Partner falls back to the LoadResString6, LoadResPicture6, and LoadResData6 support methods. These methods rely on the same ResourceManager instance used by the My.Resources class and therefore return the same resource data. This approach ensures that all .NET localization features can be used on the converted project, including satellite resource-only DLLs.
もし最初か2番目の引数がコンスタントではない場合は、VBMPはメソッドをサポートする、LoadResString6、LoadResPicture6、およびLoadResData6に戻ります。これらのメソッドは、My.Resourcesのクラスによって使用された同じResourceManagerインスタンスに依存します、したがって、同じリソースデータを返します。このアプローチは、サテライトDLL(リソースだけの)を含む変換されたプロジェクトで、すべての.NETローカライズ機能を使用できるのを確実にします。
Interestingly, if an icon resource is being assigned to a VB6 icon property that has been translated to a bitmap property under VB.NET, then VB Migration Partner automatically generates the code that manages the conversion, as in this code:
興味深いことに、アイコンリソースがVB.NET配下でビットマッププロパティに変換されたVB6アイコンプロパティに割り当てられているのであれば、VB Migration Partnerは自動的に変換を管理する次のようなコードを生成します。
Image1.Picture = My.Resources.ico108.ToBitmap()
3.14 Minor language differences/マイナーな言語の違い
VB Migration Partner generates code that accounts also for minor differences between VB6 and VB.NET.
VB Migration PartnerはVB6とVB.NET間でのマイナーな違いを説明するコードを生成します。
Font objects/Fontオブジェクト
VB6’s Font and StdFont objects are converted to .NET Font objects. The main difference between these two objects is that the .NET Font object is immutable. Consider the following VB6 code:
VB6のFontとStdFontオブジェクトは.NETのFontオブジェクトに変換されます。これら二つのオブジェクトの主な違いは、.NET Fontオブジェクトは不変であるということです。次のVB6コードを参照ください。
Dim fnt As StdFont
Set fnt = Text1.Font
fnt.Bold = True
Text2.Font.Name = "Arial"
Assignments to font properties are translated to FontChangeXxxx6 methods in the language support library:
Fontプロパティへの割り当ては言語サポートライブラリのFontChangeXXXX6メソッドに変換されます。
Dim fnt As Font
fnt = Text1.Font
FontChangeBold6(fnt, True)
FontChangeName6(Text2.Font, "Arial")
VB Migration Partner provides support also for the StdFont.Weight property. For example, this VB6 code:
VB Migration PartnerはStdFont.Weightプロパティも同様にサポートを提供します。次のVB6コード例をご覧ください。
Dim x As Integer
x = Text1.Font.Weight
Text2.Font.Weight = x * 2
translates to:
変換されると
Dim x As Integer
x = GetFontWeight6(Text1.Font)
SetFontWeight6(Text2.Font, x * 2)
The GetFontWeight6 and SetFontWeight6 helper functions map the Weight property to the Bold property. They are marked as obsolete, so that the developer can easily spot and get rid of them after the migration has completed.
GetFontWeight6とSetFontWeight6ヘルパー機能はWeightプロパティをBoldプロパティにマップします。それらはサポート外とマークされていますので、開発者は移行が完了した後で、それらを簡単に見つけ取り除くことができます。
VB Migration Partner emits a warning if the original VB6 program handles the FontChanged event exposed by the StdFont object. In this case no automatic workaround exists and code must be fixed manually.
オリジナルのVB6プログラムがStdFontオブジェクトによって露呈されたFontChangedイベントを処理するなら、VB Migration Partnerは警告を出力します。この場合、どのような自動回避策も存在しません、そして、手動でコードを修正しなければなりません。
For Each loop on multi-dimensional arrays/多次元配列におけるFor Eachループ
For Each loops visit multi-dimensional arrays in column-wise order under VB6, and in row-wise order under VB.NET. When such a loop is detected, VB Migration Partner emits the following warning just before the loop:
For EachループはVB6では列方向の順で、.NETでは行方向の順で多次元配列を巡回します。そのようなループが検出されるとき、VBMPはループののすぐ前で以下の警告を出力します。
For Each o As Object In myArr
…
Next
The TransposeArray6 helper function returns an array whose rows and columns are transposed, so that the For Each loop works exactly as in the VB6 program:
TransposeArray6ヘルパー機能は行と列を入れ替えた配列を戻します。よってFor EachループはVB6プログラムと同様に正確に動作します。
For Each o As Object In TransposeArray6(myArr)
…
Next
You can add the call to the TransposeArray6 method at each migration cycle, by means of an ReplaceStatement pragma, while using a DisableMessage pragma to suppress the warning:
それぞれの移行サイクルにて、警告メッセージを抑制するDisableMessageプラグマを使用しながら、ReplaceStatementプラグマでTransposeArray6メソッドの呼び出しを追加することができます。
For Each o As Object In myArr
…
Next
Fields passed to ByRef arguments/ByRef引数に渡されたフィールド
VB6 fields are internally implemented as properties and can’t be modified by a method even if they are passed to a ByRef argument; in the same circumstances, a VB.NET field can be modified. For this reason, converting such calls to VB.NET as-is can introduce subtle and hard-to-find bugs. (being ByRef the default passing mechanism in VB6, such bugs can be rather frequent.)
VB6フィールドは内部でプロパティとして実装されます。それらが、ByRef引数に渡されたとしてもメソッドによって変更されることはありません。同じ事情でVB.NETフィールドは変更されます。この理由としては、そのままでVB.NETへのそのような呼び出しを変換すると捉えにくく、見つけることが難しいバグを導入することになります。(VB6のデフォルト引き渡しメカニズムであるByRefになることは、そのようなバグがかなり頻繁になります。)
VB Migration Partner handles this issue by wrapping the field in a call to the ByVal6 helper method, as in this example:
VB Migration PartnerはByVal6ヘルパーメソッドを呼ぶフィールドをラッピングすることでこの問題を制御することができます。次のサンプルコードをご覧ください。
MethodWithByrefParam( ByVal6(obj.Value) )
ByVal6 is a do-nothing method that simply returns its argument, ensuring that the original VB6 semantics are preserved. The ByVal6 method is marked as obsolete and produces a compilation warning that encourages the developer to double-check the intended behavior and modify either the calling code or the syntax of the called method.
ByVal6はとにかくその引数を戻すという何もしないメソッドです。オリジナルのVB6意味論が失われないようにするのを確実にします。ByBal6メソッドはサポート外とマークされており、コンパイルの警告を発行し、呼ばれたメソッドの構文やコードの呼び出しを修正するように、そして開発者に意図された動作をダブルチェックするように奨励します。
TypeOf keyword/TypeOfキーワード
VB Migration Partner accounts for minor differences in how the TypeOf keyword behaves in VB6 and VB.NET, more precisely:
VB Migration PartnerはTypeOfキーワードがVB6とVB.NETでどのように動作するかという点でのわずかな違いを説明します。より正確には。
- TypeOf can’t by applied to VB.NET Structures.
TypeOfはVB.NET構造体には適用できません。 - using TypeOf to test a Nothing value raises an error in VB6, but not in VB.NET.
Nothing値をテストするのにTypeOfを使用することはVB6ではエラーになりますが、VB.NETではなりません。 - testing against the Object type never fails in VB.NET, except when testing an interface variable.
インターフェース変数をテストする時以外で、Objectタイプに対するテストはVB.NETにおいて決して失敗しません。
Consider the following VB6 code:
次のVB6コードを見てください。
If TypeOf obj Is TestUDT Then
ElseIf TypeOf obj Is Widget Then
ElseIf TypeOf obj Is Object Then
ElseIf TypeOf obj Is ITestInterface Then
End If
To account for these differences, VB Migration Partner performs the test using reflection, except when the second operand is an interface type. This is the converted VB.NET code:
これらの違いを説明するために、VB Migration Partnerは2番目のオペランドがインターフェース型であることを除き、反射を使用したテストを実行します。次は変換されたVB.NETのコードです。
If obj.GetType() Is GetType(TestUDT) Then
ElseIf obj.GetType() Is GetType(Widget) Then
ElseIf (Not TypeOf obj Is String AndAlso Not obj.GetType().IsValueType) Then
ElseIf TypeOf obj Is ITestInterface Then
End If
If the variable being tested is of type VB6Variant, the TypeOf operator is translated as a call to the special IsTypeOf6 method:
テストされている変数がVB6Variantタイプであるならば、TypeOfオペレータは特別なIsTypeOf6メソッドへの呼び出しとして変換されます。
If IsTypeOf6(myVariant, GetType(Widget)) Then
Mod operator/Modオペレータ
The VB6’s Mod operator automatically converts its operands to integer values and returns the remainder of integer division:
VB6のModオペレータは自動的にinteger値へのそのオペランドを変換します、そして整数部の残りの部分を返します
Dim d As Double, i As Integer
d = 1.8: i = 11
Debug.Print i Mod d
VB.NET doesn’t convert to Integer and returns the remainder of floating-point division if any of the two operands is of type Single or Double. VB Migration Partner ensures that the operator behaves exactly as in VB6 by forcing a conversion to Integer where needed:
VB.NETはIntegerに変換しません。2つのオペランドのどちらかがSingleまたはDouble型の場合は、浮動小数点除算の残りを返します。VB Migration Partnerはオペレータが必要であるところでIntegerに変換を強制することによりVB6のように正確に動作することを保証します。
Debug.WriteLine(i Mod CInt(d))
Eqv and Imp operators/EqvとImpオペレータ
Both these operators aren’t supported by VB.NET. VB Migration Partner translates them by generating the bitwise operation that they perform internally. More precisely, the following VB6 code:
これらのオペレータの両方ともVB.NETではサポートされません。VB Migration Partnerはそれらが内部的に実行されるビット単位のオペレーションを生成することによってそれらを変換します。もっと正確に述べると次のようなVB6コードを見てください。
x = y Eqv z
x = y Imp z
translates to
変換すると
x = (Not y And Not z)
x = (Not y Or z)
Strings to Byte array conversions/StringsをByte配列に変換
VB Migration Partner correctly converts assignments between string and Byte arrays. The following VB6 code:
VB Migration PartnerはStringとByte配列の間の割り当てを正しく変換します。次のVB6コードをご覧ください。
Dim s As String, b() As Byte
s = "abcde"
b = s
s = b
translates to:
変換すると
Dim s As String, b() As Byte
s = "abcde"
b = StringToByteArray6(s)
s = ByteArrayToString6(b)
where the StringToByteArray6 and ByteArrayToString6 helper methods perform the actual conversion. If necessary, this transformation is performed also when a string or a Byte array is passed to a method’s argument or returned by a function or property.
ここではStringToByteArray6とByteArrayToString6のヘルパーメソッドが実際の変換を実施します。また、必要であれば、StringかByte配列がメソッドの引数に引き渡されるか、またはFunctionかプロパティによって戻されるとき、この変換は実行されます。
Note: the ByteArrayToString6 method internally uses the UnicodeEncoding.Unicode.GetString method, but adds a dummy null byte if the argument has an odd number of bytes.
注記:ByteArrayToString6メソッドは内部的にUnicodeEncoding.Unicode.GetStringメソッドを使用します。しかし、もし引数が奇数のバイトを持っている場合はダミーのNullバイトを追加します。
Date to Double conversions/Date型をDouble型に変換
VB Migration Partner correctly converts assignments between Date and Double values. The following VB6 code:
VB Migration PartnerはDate値とDouble値の間の割り当てを正しく変換します。次のVB6コードをご覧ください。
Dim d As Date, v As Double
d = #11/1/2006#
v = d
d = v
translates to:
変換すると
Dim d As Date, v As Double
d = #11/1/2006#
v = DateToDouble6(d)
d = DoubleToDate6(v)
where the DateToDouble6 and DoubleToDate6 helper methods internally map to Double.ToOADate and Date.FromOADate methods.
ここではDateToDouble6とDoubleToDate6ヘルパーメソッドは内部的にDouble.ToOADateとDate.FromOADateメソッドにマップします。
For loops with Date variables/Date変数におけるFor Loops
For…Next loops that use a Date control variable are allowed in VB6 but not in VB.NET. For example, consider the following VB6 code:
Dateコントロール変数を使うFor・・・Next loopsはVB6では許可されますが、VB.NETではされません。次のVB6コードを見て下さい。
Dim dt As Date
For dt = startDate To endDate
Debug.Print dt
Next
This is how VB Migration Partner converts the code to VB.NET:
VB Migration PartnerがどのようにVB.NETコードに変換するか、次の結果を見てください。
Dim dt As Date
For dt_Alias As Double = DateToDouble6(startDate) To DateToDouble6(endDate)
dt = DoubleToDate6(dt_Alias)
Debug.WriteLine(dt)
Next
3.15 Unsupported features and controls/サポートされていない機能とコントロール
Here’s a list of VB6 features that VB Migration Partner doesn’t support:
ここにVB Migration PartnerでサポートされないVB6の機能を一覧します。
- ActiveX Documents
ActiveXドキュメント
- DataReport designer
DataReportデザイナー
- OLE and Repeater controls
OLEとRepeaterコントロール
- a few graphic-related properties common to several controls, including DrawMode, ClipControls, Palette, PaletteMode
DrawMode、ClipControls、Palette、PaletteModeを含む数個のコントロールに共通のいくつかのグラフィック関連のプロパティ
- VarPtr, ObjPtr, StrPtr undocumented keywords (the VarPtr keyword is partially supported, though)
VarPtr、ObjPtr、StrPtr言語仕様がないキーワード(VarPtrキーワードは部分的にサポートされます)
- a small number of control features, including:
少数のコントロール機能、以下を含みます
- The ability to customize, save, and restore the appearance of the Toolbar control
Toolbarコントロールの外観をカスタマイズ、保存、復元する機能
- The MultiSelect property of the TabStrip control
TabStripコントロールのMultiSelectプロパティ
- Vertical orientation for the ProgressBar control
ProgressBarコントロールの垂直配向
- The DropHighlight property of TreeView and ListView controls
TreeViewとListViewコントロールのDropHighlightプロパティ
Even if a feature isn’t supported, VB Migration Partner always attempts to emit VB.NET that has no compilation errors. For example, ActiveX Documents and Property Pages are converted into VB.NET user controls. Also, the support library exposes do-nothing properties and methods named after the original VB6 unsupported member.
機能がサポートされない場合、VB Migration Partnerは常にコンパイルエラーのないVB.NETを生成するのを試みます。例えば、ActiveXドキュメントとプロパティページはVB.NETのユーザコントロールに変換されます。またサポートライブラリはオリジナルのVB6でサポートされないメンバにちなんで名づけられた何も動作しないプロパティとメソッドを露呈します。
Unsupported properties and methods/サポート外のプロパティとメソッド
In general, unsupported members in controls are marked as obsolete and return a default reasonable value. For example, the DrawMode property of the Form, PictureBox, UserControl, Line, and Shape classes always return 1-vbBlackness.
一般に、コントロールのサポートされないメンバはサポートされないと位置付けられ、デフォルトの妥当性のある値を戻します。例えば、FormのDrawModeプロパティ、PictureBox、UserControl、Line、Shapeクラスは常にvbBlacknessの1を戻します。
By default, assigning an unsupported property or invoking an unsupported method is silently ignored. If the property or the method affects the control’s appearance you can’t modify such a default behavior.
デフォルトでは、サポートされないプロパティを割り当てるか、サポートされないメソッドを呼び出すことは無視されます。プロパティかメソッドがコントロールの外観に影響するのであれば、そのようなデフォルト動作を修正することはできません。
If the property or the method affects the control’s behavior (as opposed to appearance), however, you can force the control to throw an error when the property is assigned a value other than its default value or when the method is invoked, by setting the VB6Config.ThrowOnUnsupportedMember property to True:
プロパティまたはメソッドがコントロールの動作に影響を与えるのであれば(外観とは対照的に)、デフォルト値以外の値がプロパティに割り当てられるか、またはメソッドが呼び出されるとき、VB6Config.ThrowOnUnsupportedMemberのプロパティをTrueに設定することによって、コントロールにエラーを強制的に投げることができます。
VB6Config.ThrowOnUnsupportedMember = True
This setting is global and affects all the controls and classes in control support library.
この設定はグローバル設定で、コントロールサポートライブラリの全てのコントロールとクラスに影響を与えます。
Unsupported Controls/サポート外のコントロール
Occurrences of unsupported controls – including OLE instances and all controls that aren’t included in the Visual Basic 6 package – are replaced by a VB6Placeholder control, which is nothing but a rectangular empty control with a red background.
サポート外のコントロールのオカレンス(VisualBasic6パッケージにない、全てのコントロールとOLEインスタンスを含む)はただの赤の背景色をもつ長方形の空のコントロールであるVB6Placeholderコントロールによって置き換えられます。
For each unsupported control, VB Migration Partner generates a form-level Object variable named after the original control:
それぞれのサポート外のコントロールのために、VB Migration Patnerはオリジナルコントロールにちなんだ名前のFormレベルオブジェクト変数を生成します。
Friend OLE1 As Object
The variable is never assigned an object reference, therefore any attempt to use it causes a NullReferenceException to be thrown at runtime. This variable has the only purpose of avoiding one compilation error for each statement that references the control.
変数はオブジェクト参照を割り当てることはありません。そのため、それを使用するどんな試みもNullReferenceExceptionをランタイムに投げさせます。この変数には、コントロールを参照する各ステートメントのコンパイルエラーを回避する唯一の効果があります。
3.16 The VB6Config class/VB6コンフィグクラス
Some facets of the runtime behavior of VB.NET applications converted with VB Migration Partner can be modified by means of the VB6Config class. This class exposes only static members, therefore you never need to instantiate an object of the class. Here is the list of exposed members:
VB6ConfigクラスによってVB Migration Partnerと共に変換されたVB.NETアプリケーションのランタイム動作のいくつかの一面を変更できます。このクラスはStaticなメンバのみを出力します。よってクラスのオブジェクトをインスタンス化する必要はありません。以下は出力されたメンバのリストです。
VB6Config.ThrowOnUnsupportedMembers
This property specify whether unsupported properties of controls throw an exception when they are assigned an invalid value. For example, converted VB.NET forms and PictureBox controls don’t support the DrawMode and ClipControls properties, even though they expose two properties named DrawMode and ClipControls. By default ThrowOnUnsupportedMembers is False, therefore assigning a value other than 1-Blackness to the DrawMode property or the True value to the ClipControls property doesn’t raise an error and the assignment is just ignored. However, if you set the ThrowOnUnsupportedMembers property to True, the same action raises a runtime error whose code is 999 and whose message is “Unsupported member”:
このプロパティは、無効の値がそれらに割り当てられるとき、コントロールのサポートされないプロパティが例外を投げるかどうかを指示します。例えば、変換されたVB.NETのFormとPictureBoxコントロールはDrawModeとClipControlsプロパティをサポートしておりませんが、DrawModeとClipControlsという名前の2つのプロパティを出力します。デフォルトではThrowOnUnsupportedMembersはFalseです。したがって、DrawModeプロパティに1-Blackness以外の値を割り当てる、またはClipControlsプロパティにTrue値を割り当てることはエラーを起こしません。割り当てはただ無視されます。しかしながら、ThrowOnUnsupportedMembersをTrueに設定する場合、同じ動作をするとRunTimeErrorを引き起こします。その時のエラーコードは“999”でメッセージは“Unsupported member”です。
VB6Config.ThrowOnUnsupportedMembers = True
The ThrowOnUnsupportedMembers also affects the behavior of unsupported methods, such as RichTextBox.SelPrint or SSTab.ShowFocusRect. Usually, calls to these unsupported methods are simply ignored. However, if ThrowOnUnsupportedMembers is True than the method throws an exception.
ThrowOnUnsupportedMembersはまたRichTextBox.SelPrintやSSTab.ShowFocusRectといったサポートされないメソッドの動作にも影響します。通常では、これらのサポートされないメソッドを呼ぶこと単純に無視されます。しかしThrowOnUnsupportedMembersをTrueに設定するとそのメソッドは例外をスローします。
Please notice that there are a few unsupported language keywords – namely VarPtr, StrPtr, and ObjPtr – that always raise an error, regardless of the value of ThrowOnUnsupportedMembers. We implemented this behavior because typically the value of these functions is passed to a Windows API, in which case passing an invalid value might compromise the entire system.
サポートされない言語キーワードがいくつかあることを覚えておいてください。すなわち、VarPtr、StrPtr、ObjPtrです。それらは、ThrowOnUnsupportedMembersの設定値に関係なく常にエラーを引き起こします。この動作を実装しましたが、これらのファンクションの値は一般的に、WindowsAPIに渡されます。その場合、システム全体の動作に影響をきたすことになるかもしれません。
VB6Config.LogFatalErrors
All .NET applications immediately stop when an unhandled error is raised (unless they run inside a debugger). The VB.NET applications converted by VB Migration Partner are no exception, however they have the added ability to display a message box containing the complete stack dump of where the error occurred. This information can be very important when debugging an application that runs fine on the development machine and fails elsewhere. You enable this feature by setting the LogFatalErrors to True:
全ての.NETアプリケーションはハンドルされないエラーが引き起こされた場合、直ちにストップします。(デバッガで起動している場合を除く)VB Migration Partnerによって変換されたVB.NETアプリケーションは例外エラーはありません。しかし、どこでエラーが発生したか完全なスタックダンプを含んでいるメッセージボックスを表示する追加された機能を持っています。問題なく開発環境マシンで動いて、ほかの場所で失敗するアプリケーションをデバッグする際に、この情報は非常に重要である場合があります。LogFatalErrorsをTrueに設定することによってこの機能を有効にできます。
VB6Config.LogFatalErrors = True
VB6Config.FocusEventSupport
VB6 and VB.NET controls slightly differ in the exact sequence of events that fire when a control receives or loses the input focus. For example, when you click on a VB6 control, the control raises a MouseDown event followed by a GotFocus event, whereas a VB.NET control fires the GotFocus event first, followed by the MouseDown event.
VB6とVB.NETではコントロールがフォーカスの入力を受け取ったり、放したりする際の正確なイベントの順番がわずかに異なります。例えば、VB6コントロールをクリックすると、コントロールはMouseDownイベントを先に、続いてGotFocusイベントに移ります。VB.NETコントロールではGotFocusイベントを先に、続いてMouseDownイベントに移ります。
Another difference is the event sequence that is fired when the focus is moved away from the control. VB6 controls fire the Validate event first, then fire the LostFocus event only if validation succeeded. (If validation failed, the LostFocus event is never raised). Conversely, the behavior of standard VB.NET depends on whether the focus is moved away by means of the keyboard or the mouse. If the keyboard is used, a Validate event is raised, followed by a LostFocus event. If the mouse, the event order is just the opposite. Worse, a GotFocus event is fired if validation fails.
他の異なる点はフォーカスがコントロールから外部に移動された際に、起こるイベントの順番です。VB6コントロールは最初にValidateイベントを発生させ、Validationが成功した場合にのみLostFocusイベントを発生させます(もしValidationが失敗した場合は、LostFocusイベントは発生しません)。逆に、スタンダードのVB.NETの動作はキーボードかマウスによるフォーカスの移動によります。キーボードを使用した場合は、Validateイベントが起動され、次にLostFocusイベントに移ります。マウスの場合は、イベント順は反対です。より悪いことに、Validationが失敗しても、GotFocusイベントが発生します。
In most cases, these differences are negligible and don’t affect the application’s overall behavior. For this reason, therefore, by default converted applications follow the .NET standard behavior. If you experience a problem related to these events, however, you can have VB.NET controls behave exactly like the original VB6 controls, simply by setting the FocusEventSupport to True:
ほとんどのケースにおいて、これらの違いはとるに足らないことで、アプリケーションの全体の動作に影響はありません。その理由としては、デフォルトで変換されたアプリケーションは.NETスタンダード動作に従います。これらのイベントに関連する問題を経験するなら、オリジナルのVB6コントロールのような正確な動作をVB.NETコントロールに持たせることができます。それは簡単にFocusEventSupportをTrueに設定するだけです。
VB6Config.FocusEventSupport = True
VB6Config.ReturnedNullValue
Some VB6 functions return a Null value when the caller passes Null to one of their parameters. This behavior is duplicated under converted VB.NET applications. By default, the .NET equivalent for the Null value is the DBNull.Value, a detail that might cause a problem when the value is passed to a string or used in an expression. For example, consider this VB6 code:
いくつかのVB6の機能は呼び出し側がパーサに引数のうちのひとつにNullを渡す場合、Null値が戻されます。この動作は変換されたVB.NETのアプリケーションでも再現されます。デフォルトではNull値に対する.NET相当物はDBNull.Valueです。詳細に、値がStringに渡されるか式に使われる場合に問題が起こるかもしれません。例として、次のVB6コードをご覧ください。
txtValue.Text = Hex(rs("value").Value)
This code fails under VB.NET if the field “value” of the recordset contains Null, because the Hex function would return DBNull.Value and the assignment to the Text property would raise an error.
Recordsetのフィールド“値”にNullが含まれているのであれば、VB.NETではこのコードは失敗します。なぜなら、Hex関数がDBNull.Valueを戻し、Textプロパティへの割り当てがエラーを引き起こすからです。
To work around this issue, you can assign the ReturnNullValue property a value other than DBNull.Value. In most cases, using an empty string solves the problem:
この問題を回避するために、DBNull.Value以外の値をReturnNullValueのプロパティに割り当てることができます。ほとんどのケースにおいて、空文字列を使用することで問題を解決できます。
VB6Config.ReturnedNullValue = True
VB6Config.DragIcon
When a “classic” drag-and-drop operation begins, by default VB6 shows the outline of the control that acts as the source of the drag-and-drop. This behavior isn’t replicated in converted VB.NET apps, which instead display a default image. This default image is the one returned by the VB6Config.DragIcon property, but you can assign this property to change the default image:
クラシックなdrag-and-drop操作を行うとき、デフォルトで、VB6はdrag-and-dropの起点として機能するコントロールのアウトラインを示します。この動作は変換されたVB.NETアプリケーションでは模写されておりません。代わりにデフォルトイメージを表示します。このデフォルトイメージはVB6Config.DragIconプロパティによって戻されるイメージです。デフォルトイメージを変更するためにこのプロパティを割り当てることができます。
VB6Config.DragIcon = LoadPicture6("c:\dragicon.bmp")
As in VB6, the default image is used only for controls whose DragIcon is nothing.
VB6では、デフォルトイメージはDragIconがないコントロールにおいてのみ使用されます。
VB6Config.DDEAppTitle
By default, a VB6 app that works as DDE server reacts to requests from client controls whose LinkTopic property is formatted as follows:
デフォルトでは、DDEサーバがLinkTopicのプロパティが以下の通りフォーマットされるクライアントコントロールからの要求に反応するとき、VB6アプリケーションは動作します。
appname|formlinkopic
where appname is the application title (same as the App.Title property in VB6) and formlinktopic is the value of the LinkTopic property of a form whose LinkMode property has been set to 1-vbLinkSource).
appnameはアプリケーションのタイトル(VB6のApp.Titleと同じ)です。そしてformlinkopicはLinkModeプロパティが1-vbLinkSource でセットされているformのLinkTopicプロパティの値です。
Converted VB.NET forms work much like in the same way, except that the appname value is checked against the VB6Config.DDEAppTitle property. The initial value of this property is the same it was in the VB6 project, therefore it most cases the application should work as it used to do under VB6. However, the ability to change the appname your DDE server app responds to adds a degree of flexibility to your migrated projects.
変換されたVB.NETのformは同じ方法で同じように動作します。appnameの値がVB6Config.DDEAppTitleのプロパティに対してチェックされるのを除きます。このプロパティの初期値はVB6プロジェクトであった時と同様です。よって、ほとんどのケースにおけるアプリケーションがVB6で動作していたのであれば、機能します。しかし、DDEサーバーアプリケーションのappnameを変える能力は移行されたプロジェクトのフレキシビリティの度合いにより追加するために応答します。