VB6からVB.NETへの変換について

# 目的

Windows 10においてもランタイムがサポートされている、十分な改修費用が出せない、特殊なOCXで当時は.NETでは代替できなかったなど、諸事情により残っているVB6の案件はあると思う。

ここでは、そのような案件を.NETに変換する際の注意点、最低限やっておいたほうが良いことについて記載する。

**簡単にいうと、そのままコンバートしただけでは、VB6のままでも変わらないのでは、と思うからだ**

ここでは、細かいロジックの良否については記載しない。

そもそも、言語により保守しやすいプログラムの成否が決まるのではなく、構成の考慮、リファクタリングを考えているか、が問題と考えるからである。

もちろん、VB6の特性（簡単さとあいまいさ、クラス仕様の機能不足（Implimentsはあるが。Inheritsはないことによる）、テストプロジェクトが公式では存在しない）が、雑然となりがちなことを招いていると思うが。


# 1. 変換の基本的な流れ

先に、VB6を.NETに変換する際の基本的な流れについて記載する

1. VB6がインストールされた環境に、Visual Basic 2008 Expressをインストールする
2. Visual Basic 2008 Expressで対象のVB6プロジェクトを開き、コンバートする
3. 最新のVisual Studioでコンバートしたプロジェクトを開く
4. コンバーターが自動で付加した、エラー、警告、情報のコメントをみて、対処を行う
5. コンパイルエラー、警告を確認しながら、エラー対策を行う

次の記事も手順がわかりやすく参考になりました。

https://qiita.com/onetechnologycompany/items/413715d75ddfca8e88c1

# 2. 変換時に最低限行うべきこと

コンバート、という案件であれば、そもそも顧客が画面・機能が変わること自体を望んでいない、という契約になっていると思われる。

しかし、その場合でも単純なコンバートに加え、最低限やったほうが良いことを記載する。

## 2.1. COM、OCXを使用しない（ADODB、DAOも含む）

### 理由

* マネージドなライブラリを使用することで得られる、メモリ上の処理やパフォーマンスの恩恵を受けることができない。
* 思わぬところで不可解な不具合（仕様）に遭遇し、解決できない、ということがあり得る。

### 対策

* 参照からCOM、OCXの参照をすべて削除する。
* データベースであれば、Entity Frameworkが理想だが、最低限、生のADO.NETに移行する。
* [Dapperの導入](https://qiita.com/sin_cos_tan/items/c681da327ec64847cbe7)も検討する。

## 2.2. On Error文を使用しない（集約例外ハンドラの導入）

### 理由

- [.NETの集約例外ハンドラの恩恵](https://docs.microsoft.com/ja-jp/archive/blogs/nakama/net-part-1)を受けることができない。
- 仕様のあいまいさにより、VB6で作成者により一番記述、構成に差が出てくるため、構造化された例外を使用することで整理したい。

### 対策

* [集約例外ハンドラを導入](https://docs.microsoft.com/ja-jp/archive/blogs/nakama/net-part-1)し、`On Error ～`は削除する（そもそも、業務例外とする場合を除き、基本的にはTry Catchは書いてはならない）。
* `On Error Resume Next`でエラー番号で判断している場合は、Try Catchで代替する。その場合も限定的な例外をキャッチすること（`Catch ex As Exception`は基本は推奨されない）。
* `On Error Resume Next`で例外を無視しているのであれば、削除し、例外が発生しない対策をとる。

## 2.3. Option Stirct Onとする

### 理由

* `Option Strict Off`のあいまいさによる潜在的な不具合が、コンパイル時にわからない。

### 対策

- プロジェクト全体でOption Strint Onとし、各モジュール先頭の`Option Strict Off`は削除する。
- 文字列を結合している個所などは、`$"{～}"`構文を使用することで、より明確、かつ簡潔に記載することができる。

## 2.4. 警告を無視しない

### 理由

- 警告を無視する文化となる恐れがある。

    コンパイル時の警告については、必要だから警告となっている。例えば変数が初期化されていないのに使用されている、という警告であれば、意図的なのかどうかを示している。
    動いているから、と警告を無視するようであれば、言語変換した意味がない。

### 対策

- 警告について正しく理解し、適切な対策をとる。チーム開発であれば、どのような対策をとったのかを共有する。

## 2.5. コンバーターが付加したコメントに対処する

### 理由

- 問題に気付くのが遅くなる

    コンバーターが自動で付加したコメント（`'UPGRADE_ISSUE:`、`'UPGRADE_WARNING:`、`'UPGRADE_NOTE:`）、特に`UPGRADE_ISSUE`と`UPGRADE_WARNING`は対処しないと、問題が起こる可能性がある個所に付加されている。

    内容を理解して対策することで、ビルド前の不具合対応、例外的な動作に対する対応を確実に行うことができる。

### 対策

- タスク一覧に、`UPGRADE_ISSUE`、`UPGRADE_WARNING`、`UPGRADE_NOTE`を追加する

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204150/4eace233-16ee-e5e3-d69b-3a4612327a8e.png)

- コメント内のURLをVisual Studio 2008のヘルプで開き、内容に応じた対策を実施する。チーム開発であれば、どのような対策をとったのかを共有する。

- 対処が終わったら、正規表現での置換なども使いながら、コメントを確実に削除する。不要な情報はノイズとなる。

## 2.6. 構成管理ソフトの変更

現在でもVB6を使用しているならば、いまだにVSSを使用している開発環境もあるかもしれない。

しかし先進的な構成管理ソフトを使用することで、単に構成管理の利便性向上にとどまらず、情報共有の方法まで改善していくことでができる（Wiki、コードレビューなど）。

構成管理ソフト措定は、個人的には**Azure DevOps Server**をお勧めする。

構成管理方式はTFVC or GITが選択でき、分岐を利用した開発、コードレビューやプルリクエストも行うことができる。

もししばらくVB6のままだとしても、VB6でAzure DevOps Serverを使うことも可能なので、先に慣れておくのもよいかもしれない。

- [VB6でAzure DevOps Serverを使用する](https://qiita.com/tfukumori/items/37f035694751dc098fcd)
- [Azure DevOps ServerにVB6のプロジェクトを追加する](https://qiita.com/tfukumori/items/68aca8cdc04fe76d6eee)

# 3. 変換時、または少しずつでも可能な限り行うべきこと

:::note
この項目は、パッケージであるシステムで機能変更がこれからも多く見込まれて、将来的な変更コストを下げたい場合にのみ実施する意味がある。またはごく部分的に導入など。
例外としては、「3.3. 継続的ビルド（CI）とリリース管理」は、Azure DevOps Serverを導入済みの場合はそれほどコストのかかる話ではないので、自動ビルドだけでも導入を検討しても良い。
:::


## 3.1. Document-View、MVVMの導入

将来的にテスト駆動開発を導入するためにも、[Document-View アーキテクチャ](https://docs.microsoft.com/ja-jp/cpp/mfc/document-view-architecture?view=vs-2017)、[MVVM](http://yohshiy.blog.fc2.com/blog-entry-215.html)の考え方を導入することが有効となる。

WinFormsであれば、できれば IDataErrorInfo ベースの双方向データバインドを使うことが望ましい。

もしそこまで行うのが困難な場合でも、とりあえずデータ取得部分を別プロジェクトに分け、画面ごとに別クラスとすることで画面処理とデータ/ロジック処理を分離したいところ。さらに、直接DBに接続する方法から、Webサービス等を導入する、とまでしておけば、パフォーマンス向上にもつながる。

将来的なテスト駆動開発につながることももちろんだが、画面処理とデータ/ロジック処理が結合していて、それを当然だと思っているような開発者を意識改善することにもつながる

### 参考：入力チェックも含めたバインド方法の改善

・[エラーチェックの体系的な分類方法](https://blogs.msdn.microsoft.com/nakama/2009/09/28/293/)
・[単体入力エラーチェックの実装パターン](https://blogs.msdn.microsoft.com/nakama/2009/09/28/283/)
> Silverlight, WPF, Windows フォームそれぞれで、双方向データバインドの実装方法は少しずつ異なりますが、根本にある基本的な考え方は、「UI コントロールの表示と、データソースオブジェクト間の値を、双方向にリアルタイムに同期させる」というものです。このため、双方向データバインドを利用すると、UI コントロールからのデータ取り出し作業（例：string customerName = tbxCustomerName.Text; などといった取り出し作業や、decimal price = decimal.Parse(tbxPrice.Text); といったパース処理）が不要となり、バインドされているオブジェクトを、UI から入力されたデータであるとみなしてそのまま使うことができます。これが、双方向データバインドを用いたデータ入力制御の根底にある、基本的な考え方です。

・[スマートクライアントにおける単体入力データ検証](https://blogs.msdn.microsoft.com/nakama/2009/02/26/part-2-2/)

### 参考：依存性の注入

・[VB.NET + Windows Forms + Unity.Container で Dependency Injection（依存性注入）](https://rikupapa-shima.hatenablog.com/entry/vb_winforms_unity_dependency_injection)

## 3.2. テスト駆動開発の導入

おそらく、一気にしていくことは難しいが、複雑で頻繁な変更が見込まれるところにだけでも導入するべきである。

複雑で頻繁な個所に導入するのは難しいのでは、と思うかもしれないが、だからこそ先に苦労しておく意味が生まれ、顧客サービス向上と時短開発につながる。

## 3.3. 継続的ビルド（CI）とリリース管理

VSSを使用していた場合も、リリース管理はソース管理（VSS）で行っている（ビルドしたバイナリを格納している）運用がほとんどとは思う。

しかし、Azure DevOps Server(TFS)を使用した場合、分岐、継続的ビルド、そしてリリース管理まで行うことが最大限の利点となり、工数削減、ひいては空いた時間での顧客への良い提案につながるので、ぜひ導入すべきである。

継続的ビルドを使用し、ビルドサーバー（PCも可）で定期的、または手動でビルドを行うことで、以下の利点がある。

- ビルドにどの変更点が含まれているか、ということがわかりやすくなる
- ビルド結果をダウンロードできるため、いつ時点のビルドでテストをしたのか、ということも分かりやすい
- どの時点でビルド、テストに失敗したかということを早めに見つけることができる

Azure DevOpsのオンプレミス版であるAzure DevOps Serverを使用すれば、仮に社外に出すことが許されない、という場合でも、Azure DevOpsと同様に継続的ビルドを実現できる。

参考：
・[Azure DevOps Server 継続的インテグレーション（CI）簡易手順](https://qiita.com/tfukumori/items/11d24a5eee8d77a17a80)

# 4. おまけ

## 4.1. Visual Studioの機能で、コードレビューを行い、TFSのWikiで対策を共有する

VS 2015から導入されたコードレビュー、TFS 2018から導入されたWikiは、チーム内の意識、業務特有の言葉の共有として有効な対策となる

Azure DevOps（Serverも）であれば、Git Hubの機能である[プルリクエストも使用することも可能](https://news.mynavi.jp/article/zeroazure-27/)だ。

## 4.2. 作業項目を利用した開発

開発の要件を決め、各項目に対するタスクを決め、タスクに対する作業（コードの変更）を関連づけて作業を行っていくことで、作業内容とソースを関連付けた開発を行っていくことができる。（Redmineのチケット駆動開発と同様の考え方となる。）

・[Azure と Azure DevOps を使った開発の流れをふんわりと](https://qiita.com/mstakaha1113/items/1fa52135a7a61dfc30e4)
> "Boards(ボード)"を使い
> 
> ・どんなものが欲しいのか
> ・なぜ欲しいのか
> ・そのためにどんな機能が必要なのか
> ・どう作るのか
> ・どうなっていたら欲しいモノが手に入ったとするのか
> ・具体的にいつまでに何をするのか
> ・何を今やらないのか
> などなど、決めていきます。
> 決まったら開発を実施します。開発環境はどのようなものでもかまいません。
> "MicrosoftのサービスだからVisual Studioしか使えないでしょ？"という誤解を抱いている方に時々お会いしますが、そんなことはありません。

参考：
・[スクラム開発を始めよう！TFS を使った日常コミュケーションとチームワーク ](https://www.slideshare.net/shinichikoga355/102-ver4)


# 5. どこまで行うか

変換をどこまで行うかは、プロジェクトごとに判断は異なるところかと思う。

例えば、プロジェクトの規模によっては、`Microsoft.Visualbasic.Compatibility`の中のメソッド(VB6.Format)、コントロール配列を使用する、という判断を行うところもあるかもしれない。なぜなら、「保守性」という観点に立てば、ドメインオブジェクト（データとロジック）とビューの切り分けを行うことの方が、重要であるからだ。

もちろん、`Microsoft.Visualbasic.Compatibility`を使うことでx86となること、廃止予定である（.NET Coreにはない）ということは勘案する必要があるが。

----

# 変換の詳細な手順

変換の詳細な手順として、@hotaten さんから提供いただいた情報を記載しました。
情報提供ありがとうございます。

> 印刷、描画
> 　問題点：Printerオブジェクトの処理からPrintDocumentのイベント駆動に変わる
> 　解決案：互換クラス/処理の作成
> Twips単位の処理
> 　問題点：VB6では画面や印刷でTwipsが多く利用されていた
> 　解決案：変換処理の作成
> コントロール配列
> 　問題点：コントロール配列がVB.Netには存在しない
> 　解決案：互換クラスの作成
> 文字列操作関連関数
> 　問題点：MS932ベースのバイト単位処理が存在していた
> 　解決案：互換処理の作成(ただしMS932に依存してしまう点に注意)
> 構造体の扱い
> 　問題点：VB6では変数への代入を行うと、値全てがコピーされていた　VB.Netでは参照型のメンバはポインタが渡る
> 　解決案：DeepCopyを作成する
> 下記のガイドも参考になります。
> https://www.microsoft.com/ja-jp/download/details.aspx?id=10849

----

# VB6を利用し続けた場合のリスク

@onetechnologycompany 様に情報提供いただきましたが、VB6を利用し続けた場合のリスクとして以下があげられます

- エンジニアの減少
- セキュリティリスクの増大
  - ただし標準ライブラリであればWindows 10では[深刻な不具合や重大なセキュリティの問題を回避](https://docs.microsoft.com/ja-jp/lifecycle/faq/developer-tools#smallwhat-lifecycle-policy-does-visual-basic-60-followsmall)すると明記されている
- アプリケーションの互換性

詳細は次の資料をご参照ください

https://onetech.jp/blog/why-need-to-migrate-from-vb6-to-vb-net-8665

----

# 履歴


## 追記（2019年2月25日）　

* 自動ビルドとリリース管理について追記

## 追記（2019年5月17日）

* 変換の詳細について追記

## 追記（2019年12月11日）　

* 継続的ビルドに関する記述、作業項目を利用した開発などについて追記

## 追記（2020年8月4日）

* 構成管理ソフトの変更について追記

## 追記（2021年6月10日）

* VB6を利用し続けた場合のリスクについて追記