C++(EXE)からDLLの関数を呼び出す構成についてDLLの言語をVB6から.NET Frameworkへ移行する手順

# 1. 概要

- C++(EXE)からDLLの関数を呼び出す構成について、DLLの言語をVB6からNETFx 4.7.2 (Visual Basic) に移行する際の手順
- 必要なエラー処理等は記載されていないことがあります

# 2. 変更前のプログラム

- 抜粋のみ記載
- 作成したVB6のDLLは`c:\sample\Sample.dll`に配置
- C++はMFCを利用しているが、~~MFCを使わない場合も同様と想定される~~

:::note warn
本例はC++側の実装に問題（VB6側と型の不一致）があり、C++のcheckDataの引数は本来CStringが望ましい。よって、MFCを使わない場合と同様とは言えない。
:::

## 2.1. VB6

- `VB_Exposed = True`はAcitveXコンポーネントとして公開される事を示す値となり、Instancing プロパティに連動して勝手に変更される
    - https://excel-ubara.com/vba_class/VBA_CLASS_11.html

```vb:CHK.cls
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "CHK"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True

Public Function CheckData(ByVal param1 As String) As Boolean
	<処理は省略。param1に応じてTrue、またはFalseを返却する>
End Function

```

## 2.2. レジストリへの登録

- 管理者権限が必要
- 解除は必須ではないが、インタフェース変更時には必要

```bat
; 解除
regsvr32.exe c:\sample\Sample.dll /u

; 登録
regsvr32.exe c:\sample\Sample.dll
```

## 2.3. C++

- importコマンドでビルド時にActiveX DLLからの情報を組み込む

```c++
#import "C:\sample\Sample.dll"
using namespace Sample;

int checkData(int param1)
{
	int iResult = 0;

	//COM初期化
	::CoInitialize(NULL);

	// インターフェースの取得
	_CHKPtr pCheck(__uuidof(CHK));

	// BSTRに変換
	BSTR	bParam1 = param1.AllocSysString();

	// 関数呼び出し
	iResult = pCheck->CheckData(bParam1);

	// 開放
	SysFreeString(bParam1);
	pCheck->Release();

	// COMの終了処理
	::CoUninitialize();

	// 値を返却
	if (iResult == 0 )	{
		//false
		return 0;
	} else {
		//true
		return 1;
	}
}
```

# 3. 変更後のプログラム

- 抜粋のみ記載
- 作成した.NETのDLLは`c:\sample\Sample.dll`に配置
- C++はMFCを利用しているが、~~MFCを使わない場合も同様と想定される~~

:::note warn
本例はC++側の実装に問題（.NET側と型の不一致）があり、C++のcheckDataの引数は本来CStringが望ましい。よって、MFCを使わない場合と同様とは言えない。
:::

## 3.1. .NET (NETFx 4.7.2）

- 検証した.NET のバージョンは、.NET Framework 4.7.2（NETFx 4.7.2）
- 公開メソッドに対するインタフェースを作成し、作成したインタフェースに属性`<ComVisible(True)>`の付加が必要
- 公開メソッドの属性の説明
    - インタフェースの実装となるため、作成したインタフェースを`Implements`で指定する
    - `<ClassInterface(ClassInterfaceType.None)>`を追加
    - `ProgId`（Visual Basic 2008 Express変換で自動付加）はC++側からの呼び出しでは使用しないが、WSHからの呼び出しでは必要
- プロジェクトのプロパティで次を設定
    |項目|設定値|
    |--|--|
    |アセンブリ情報の「アセンブリをCOM参照可」|チェック **ON**|
    |アセンブリ情報の「ビルドタブのCOM相互運用機能」|チェック **OFF**<br>今回の例ではCI（自動ビルド）を行っているためこの設定となる。チェックをONとすると管理者権限が必要となりCIが実施できない。COM相互運用のためのアセンブリ登録（`RegAsm.exe`利用）は、後述の手順で別途実施|

```vb:CHK.vb
<ComVisible(True)>
Public Interface ICHK
	Function CheckData(ByVal param1 As String) As Boolean
End Interface

<ClassInterface(ClassInterfaceType.None)>
<ProgId("CHK_NET.CHK")>
Public Class CHK
	Implements ICHK

    Public Function CheckData(ByVal param1 As String) As Boolean Implements ICHK.CheckData
        <処理は省略。param1に応じてTrue、またはFalseを返却する>
    End Function
End Class
```

## 3.2. レジストリへの登録

- 管理者権限が必要
- 解除は必須ではないが、インタフェース変更時には必要
- `/codebase`グローバル アセンブリ キャッシュにインストールしない場合に利用する
- `/tlb`によりタイプ ライブラリ（アセンブリ内で定義されたアクセス可能な型の定義）が生成される。このファイルをC++で`import`する
- 登録でアセンブリのレジストリ登録が行われ`Sample.tlb`が生成される

```bat
SET BIN=C:\Windows\Microsoft.NET\Framework\v4.0.30319

; 解除
%BIN%\RegAsm.exe /tlb c:\sample\Sample.dll /unregister

; 登録
%BIN%\RegAsm.exe c:\sample\Sample.dll /codebase /tlb
```


## 実施結果

```bat
C:\WINDOWS\system32>C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe C:\sample\sample.dll /unregister
Microsoft .NET Framework Assembly Registration Utility 4.8.4084.0
for Microsoft .NET Framework Version 4.8.4084.0
Copyright (C) Microsoft Corporation.  All rights reserved.

型は正常に登録が解除されました。

C:\WINDOWS\system32>C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe c:\sample\sample.dll /codebase
Microsoft .NET Framework Assembly Registration Utility 4.8.4084.0
for Microsoft .NET Framework Version 4.8.4084.0
Copyright (C) Microsoft Corporation.  All rights reserved.

RegAsm : warning RA0000 : 署名されていないアセンブリを /codebase を使用して登録すると、同じコンピューターにインストール されるその他のアプリケーションとの競合が生じる可能性があります。/codebase スイッチは署名されたアセンブリのみに使用できます。アセンブリに厳密な名前を付けて、再登録してください。
型は正常に登録されました。
```

## 3.3. C++

- importコマンドでビルド時にタイプライブラリからの情報を組み込む
    - import対象がタイプライブラリ（`.tlb`）に変更
- インターフェースの取得時のインタフェース名がVB6 ActiveX DLLの場合から変更となる

```c++
#import "C:\sample\Sample.tlb"
using namespace Sample;

int checkData(int param1)
{
	int iResult = 0;

	//COM初期化
	::CoInitialize(NULL);

	// インターフェースの取得
	ICHKPtr pCheck(__uuidof(CHK));

	// BSTRに変換
	BSTR	bParam1 = param1.AllocSysString();

	// 関数呼び出し
	iResult = pCheck->CheckData(bParam1);

	// 開放
	SysFreeString(bParam1);
	pCheck->Release();

	// COMの終了処理
	::CoUninitialize();

	// 値を返却
	if (iResult == 0 )	{
		//false
		return 0;
	} else {
		//true
		return 1;
	}
}
```


# 参考情報

https://docs.microsoft.com/ja-jp/cpp/preprocessor/hash-import-directive-cpp?view=msvc-170

https://docs.microsoft.com/ja-jp/dotnet/framework/tools/regasm-exe-assembly-registration-tool

https://kagasu.hatenablog.com/entry/2017/12/31/220239

http://k3tec.net/neko_wiki/index.php?COM%B9%D6%BA%C2%C2%E82%B2%F3

https://qiita.com/tera1707/items/7e22812f27884d0e3c49

http://supermab.com/wp/excel-%E3%81%8B%E3%82%89%E4%BD%BF%E3%81%86%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8dll-%E3%82%92%E4%BD%9C%E3%82%8B%E3%80%82/