none
VB6とVS2015上でのByValとByRefについて RRS feed

  • 質問

  • VB6で開発したプログラムのソースをVisual Stuidio 2015にマイグレーションしようとしています。

    VB6のソース上の関数の定義でByValまたはByRefの定義を省略していた場合、Visual Stuidio の機能で自動的に置換されたソースでは、一律でByRefに変換されるようです。

    例:Func(aaa As String)→自動変換→Func(ByRef aaa As String)

    この変換による影響として以下の①の内容が例としてありますが、手作業で修正するボリュームを簡素化または修正を自動化し、作業量を最小限にとどめたいというのが最大の目的です。

    ①VB6とVS2015での動作の差異について

    以下のように、ラベルの値を引数として参照渡しをしてProcAを実行した際、ProcA内で受け取った変数に代入している場合、ProcA実行後、VB6ではラベルの値は変わりませんが、VS2015では、ラベルの値がProcA内で代入した値に置き換わってしまいます。

    なぜこのような差異が発生するのでしょうか。ご存知の方がいらっしゃいましたらご教授いただきたいです。

    <VB6のソース例>

    Call ProcA(Label1.Text)

    --------

    Private Sub ProcA(ByRef sDate As String)
     ・
     ・
     ・
     sDate = Mid(sDate, 4, 2) & Mid(sDate, 7, 2)
     ・
     ・
    End Sub

    --------

    ②効率化について

    冒頭でも目的を記載しましたが、①のような現象が起きる場合、現時点では、ソース内でByRefを使用している関数をひとつひとつ確認し、ByRefである必要のない場合は、ByValで定義しなおすといった作業が必要となってしまいます。

    関数の量は膨大のため、機械的に修正する方法(一括置換、修正のためのツール作成など)、または手作業だとしても、頭を使って判断せずとも機械的に修正できる方法などがあれば、工数削減に非常に有効だと思っております。

    何かアイデアがありましたらご教授いただけますでしょうか。


    • 編集済み maclourin 2017年9月4日 23:41
    2017年9月4日 10:16

すべての返信

  • VB6でByval,Byrefを指定しておかないとByValとして実行されるのに、変換するとByRefにされてしまうということでしょうか。

    VB6のソースコードの*.frmや*.basなどをテキストファイルとして読んで、正規表現でByVal,ByRefの無い定義部分の引数を機械的に抽出してやればよいのでは。
    変換前なら見つけた引数にByValを追記して、変換してしまった後であればファイル名と関数名で突き合わせて探してByValに書き換えてやればそれなりに見つけられると思います。
    #VB6では同名の識別子はプロパティしかありえないので、プロパティだけGet/Setの判定も必要ですが。

    Module Module1
        Sub Main()
            Dim reg As System.Text.RegularExpressions.Regex = New System.Text.RegularExpressions.Regex("^\s*(Public|Private)\s+(Sub|Function)\s+(?<NAME>[^\(]*)\((?<ARGS>[^\)]+)\)", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
    
            Using sr As New System.IO.StreamReader("test.frm")
                Do While (Not sr.EndOfStream)
                    Dim line As String = sr.ReadLine()
                    Dim match = reg.Match(line)
                    If (match.Success) Then
                        Console.WriteLine(match.Groups("NAME").Value & vbTab & match.Groups("ARGS").Value)
                    End If
                Loop
            End Using
        End Sub
    End Module

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年9月4日 11:13
  • VB6では省略時はByRefではないですか。

    VB.NetがByRefにPropertyを渡せる、(関数内部でいじると、Property Putが呼ばれる)という仕様とバッティングしてるのかな、って感じですね。


    jzkey

    2017年9月4日 12:50
  • ①の仕様については、「プロパティの参照渡し」などを読んでみてください。

    ByRef な引数にプロパティを渡した場合、VB.NET では
     temp = Label1.Text
     Call ProcA( temp )
     Label1.Text = temp
    に相当する処理にコンパイルされます。VB6 の場合は、最後の書き戻し作業は行われません。

    VB6 と同様に、メソッド内で変更されないように呼び出したいのであれば
     Call ProcA( (Label1.Text) )
    のように、実引数に括弧をつけて呼び出すことで、Property Get のみの動作となります。

    ちなみに VB2 の頃は ByRef というキーワードすら存在せず、「値渡しをしたい場合にのみ ByVal を付与する」という言語仕様でした。(VB2 の頃は、参照渡しの仮引数に別のインスタンスを Set できない仕様だったので、またちょっと事情が異なるのですが)

    ②の置換効率化についてですが、VB6 側の実装の意図次第によって文脈依存となるので、悩ましいところですね。VB6 では ProcA(Label1.Text) は書き換わらず、ProcA(Moduel1.gstrFileName) だと書き換えるという動作になりますから、それが意図通りなのだとしたら、仮引数の宣言を直すのではなく、実引数側の受け渡し部分を修正せざるを得なくなります。

    gekka さんの正規表現案などのようにして、省略されているものを機械的に列挙するのは一つの手ですが、その後の置換作業については、上記の理由により個別判断が必要でしょうね。

    また、*.bas 以外のファイルの場合には、「ByVal 指定のないイベント引数」の扱いにも注意が必要かもしれません。たとえば QueryUnload / Unload の Cancel 引数に ByVal 引数を付与してしまうと(本来は ByRef)、VB6 側ではコンパイルエラー「プロシージャの宣言が、イベントまたはプロシージャの定義と一致していません。」になります。もっとも、イベント引数の定義は、VB6 と .NET とで別物なので、そのことが直接問題になることは無いかもしれませんが。


    2017年9月4日 15:01
  • maclourin さん、こんにちは
    フォーラム オペレーターの立花楓です。
     
    本件についてその後いかがでしょうか。
    gekka さん、jzkey さん、魔界の仮面弁士 さんから情報をお寄せいただいておりますので、ご確認いただき、進展等をご返信をいただけましたら幸いです。
     
    また、参考になる情報がありましたら、投稿者からの [回答としてマーク] をお願いします。
     
    宜しくお願い致します。

    MSDN/TechNet Community Support 立花楓

    2017年9月12日 0:39
    モデレータ