営業「むかーし納品したVB6アプリを、今のWindows10のPCに対応するようにしろ」
俺「VBなんていじったことないですよ」
営業「他に人いないし、同じようなもんでしょ」
俺「(いつも余裕ぶっこいて遊んでいるようにみられているからかなぁ)ぁぃ。。。」
営業「あ、あと追加機能もあるでよ」
当時の担当者は消えており、しかたなく、しぶしぶ対応したときのメモです。
(あまりそっち側に深く入り込む気がなかったので、適当対応です)
1. VB6 -> VB.NET(2017)
今回は追加機能あり、ということなのでコンパイルできないと始まらない
自分の環境だとVB6はインストールしていない
ということで、VS2017でコンパイルできるようにしようと調査したところ、一旦VS2008を間に挟むことによってコンバートできる、とのことでした
VS2008を拾ってきてインストールして、無事VB.NETにコンバート終了
2. MSFlexGrid.ocxのインストール
どれどれ、どんなアプリかな(いい加減な資料しか残ってません><)
と実行してみると、すぐにMSFlexGridがない、と怒られました
Windows10では個別にregsvr32.exe
で登録する必要があるようです
さらに、MSFlexGridは64bitに対応してないのですね><
で、どうにかアプリを起動して使用してみると、別にグリッドをつかわなくてもリストで事足りる内容
多分、以前にそういうアプリを作ったことがあって、その部分だけ使いまわしたんだろうなっていう内容
このあたりで、前任者の先見的配慮の薄さに少し不安を感じました
3. ほとんどの処理をC(C++)で書いたDLLで処理するよ~
アプリの内容はだいたいわかった、とVBのソースコードを眺めてみたところ、、、
嫌な予感的中、流れのわかりにくいコード、場当たり的なコードのオンパレードでした><
こりゃ使いものにならんなぁ、と捨てたり整理していったら、各Formの定義くらいしか残ってませんでした
結局、その他は全部Cで書いたDLLに任せる方針にしました
4. DLL関数引数の定義
言語間の変数型表現の違い、文字列表現の違い、が鬱陶しいです
(1) 引数=変数配列
これは別段わかりにくい点なし
dllヘッダー
int __stdcall GetArrayData(double *d);
VB定義
Public Declare Function GetArrayData Lib "Test.dll" (ByRef d As Double) As Integer
VB実装
Public d(32) As Double
GetArrayData(d);
(2) 引数=文字列
文字列を引数にする場合は、VBではStribgBuilder型を使うのがよいようです
dllヘッダー
int __stdcall SetText(char *t);
VB定義
Public Declare Function SetText Lib "Test.dll" (ByVal t As Text.StringBuilder) As Integer
VB実装
Dim txtbuf As Text.StringBuilder = New Text.StringBuilder(64)
Dim srctxt As String = ""
txtbuf.Append(srctxt)
SetText(txtbuf)
(3) 引数=構造体
よく理解してませんwググって拾ってきたコードをコピペしただけ^^;
文字列を固定長配列にしてしまえば、構造体サイズも固定になって構造体の配列渡しも可能でしょう
この固定長文字列に対して、Cのコードでstrcpy()等をした場合に最後尾に\0が入るのですが、そのままVBのString型に代入するとエラーが起きるみたいです
なので、\0を取り除いた文字列をString型に代入するようにしています
dllヘッダー
typedef struct _S
{
char cstr[64];
}S;
int __stdcall GetStructData(S *s);
VB定義
<StructLayout(LayoutKind.Sequential)> Public Structure S
<VBFixedString(64), MarshalAs(UnmanagedType.ByValArray, SizeConst:=64)>
Public cstr() As Char
End Structure
Public Declare Function GetStructData Lib "Test.dll" (<[In](), Out()> ByVal s() As S) As Integer
VB実装
Public s(256) As S
GetStructData(s)
Private Sub mytrim(ByRef txt As String)
Dim workbuf As String
workbuf = txt
Dim cp As Integer = InStr(workbuf, vbNullChar)
If cp > 0 Then txt = VB.Left(workbuf, cp - 1)
End Sub
Dim c As String = s(0).cstr : mytrim(c)
5. VBのFormにメッセージを飛ばす
dllからVBのFormに通信することを考えると、慣れている分、メッセージを飛ばすのがわかりやすいですよね
VB.NETではお手軽に利用できます。
起動時にFormのHWNDを取得してdllに教え、dllはそのHWNDにPostMessage()するだけ
VB実装(起動時)
Private Sub Test_Form_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load
Dim hWndMain As UInteger = Me.Handle.ToInt32
'起動処理 ...
End Sub
VB実装
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_APP + 1 Then
If m.WParam = 1 Then
'WParam==1のメッセージ処理
End If
End If
MyBase.WndProc(m) ' default message_proc
End Sub