ExcelのVBAで使えるDLLを、C++(Visual Studio 2017)で作る。・・・その2
次回予告までしておきながら、他にVBA関係の記事を書いていたこともあり、既に2ケ月以上が過ぎ、
「いつ頃までに、まとめられるかは、不明・・・」の記載通りになってしまった。
前回も書きましたけど、64bit版のVBAではどうなるかわかりませんので!!!
今回の内容は次の通り。
受け渡しするデータの型について
とりあえず必要なのは、VBAとC++のデータ型の対応。VBA独自の型は、C++に渡せないし、逆にC++にしかない型は、VBAが受け取れない。
(条件付きであれば、例外もあるけど・・・)
VBAの主な型を中心に対応を見てみると次のような感じ。
VBAの型 | C++の型 | 備考 |
---|---|---|
Byte | unsigned char BYTE |
|
Integer | short SHORT |
|
Long | int long INT LONG |
|
Single | float | |
Double | double | |
Boolean | BOOL |
C++の型 | VBAの型 | 備考 |
---|---|---|
HANDLE | Long | |
char CHAR |
Byte | 最上位ビットがOFFならばそのまま使用可 |
unsigned short WORD |
Integer | 最上位ビットがOFFならばそのまま使用可 |
unsigned int unsigned long UINT ULONG DWORD |
Long | 最上位ビットがOFFならばそのまま使用可 |
VBAにあってC++にない型(抜粋) |
---|
Currency |
Date |
Decimal |
String |
Object |
Variant |
C++にあってVBAにない型(抜粋) |
---|
WCHAR |
LONGLONG |
ULONGLONG |
VBAのString型に対応するC++の型はありません。
ちょっと特殊です。データの受け渡しにはchar型のポインタまたは、BSTR型を使用します。
これについては、次回(?)にでも・・・
処理する値を渡せるようにすること
DLLに何らかのデータ処理をしてもらうためには、必要なデータを渡してあげないと出来ません。データはVBAの関数と同様にパラメータに引数を渡します。
ただ、C++とVBAでは、構文が違うので予め覚えておく必要があります。
値渡しと参照渡しについて
- 値渡し
- 引数のアドレスをプロシージャに渡すのではなく引数の値を渡す方法。
VBAでは、ByValを指定する事により値渡しとなる。 - 参照渡し
- 引数の値をプロシージャに渡すのではなく引数のアドレスを渡す方法。
VBAでは、デフォルトで参照渡しである。明示的に指定する場合には、ByRefを指定する。
C++の場合
- 値渡し
- データ型の後ろに変数を指定する。
void doAnything(int hoge); intがデータ型で、hogeが変数 - 値渡し
- データ型の後ろに"*"を付けその後ろに変数を指定する。(ポインタですな。)
void doSomething(long* fuga); longがデータ型で、fugaが変数
処理した結果や値を返してもらえるようにすること
値を返してもらう方法は、大きく分けて2つ。一つは、関数の復帰値による方法。
これは、VBAでいえば、ファンクションプロシージャによる復帰値で結果を得る方法と同じ。
C++の場合には、関数名の前に復帰値のデータ型を指定する。
復帰値の型 関数名(パラメータリスト)
もう一つは、渡したパラメータにデータを入れてもらい、返してもらう方法。
こちらは、VBAでいえば、パラメータにByRefを指定し、返してもらう方法と同じ。
どちらを使うかは、状況に応じて使い分ければよろしいかと。
場合によっては、両方というのもありです。
数値型の場合の受け渡しの例
VBAからLongの値を渡して、2倍した結果を返してもらう「GetNumberI」という関数と、300倍した値を返してもらう「GetNumberI2」を作ってみます。
GetNumberIは、パラメータを値渡しして、復帰値で結果をもらいます。
GetNumberI2は、パラメータを参照渡しし、そのパラメータ値を変更して返してもらいます。
前回のファイルに、書き加えていきます。
まず、ヘッダファイル(AccessibleFromVBA.h)
#pragma once extern "C" { #define ACCESSIBLEFROMVBA_API __declspec(dllexport) ACCESSIBLEFROMVBA_API void WINAPI DoNothing(); ACCESSIBLEFROMVBA_API int WINAPI GetNumberI(int i); ACCESSIBLEFROMVBA_API void WINAPI GetNumberI2(int* pi); }
次に、ソースファイル(AccessibleFromVBA.cpp)
#include "stdafx.h" #include "AccessibleFromVBA.h" ACCESSIBLEFROMVBA_API void WINAPI DoNothing() { return; } //int型の値を受け取って、int型の復帰値を返す(VBAのファンクションプロシージャ相当) ACCESSIBLEFROMVBA_API int WINAPI GetNumberI(int i) { return i * 2; } //int型の値を受け取って、内部で値を変更して返す(VBAでパラメータをByRef で受け渡しするイメージ) ACCESSIBLEFROMVBA_API void WINAPI GetNumberI2(int* pi) { *pi *= 300; return; }
最後に、モジュール定義ファイル(AccessibleFromVBA.def)
LIBRARY AccessibleFromVba EXPORTS DoNothing GetNumberI GetNumberI2
全て追加したら、プロジェクトをビルドします。
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========
と表示されればOK。
呼び出すExcel側は、
Private Declare Function GetNumberI Lib "C:\Datas\MyDatas\Developer\VisualStudioComunity2017\DllForVBA\ForTest\AccessibleFromVBA.dll" (ByVal l As Long) As Long Private Declare Sub GetNumberI2 Lib "C:\Datas\MyDatas\Developer\VisualStudioComunity2017\DllForVBA\ForTest\AccessibleFromVBA.dll" (ByRef l As Long)
呼び出してみる
Public Sub DllCallTest2() Dim lValue As Long Dim lResult As Long lValue = 1000 lResult = GetNumberI(lValue) Debug.Print "GetNumberI:", lValue, lResult Debug.Print "" Debug.Print "GetNumberI2(Before):", lValue Call GetNumberI2(lValue) Debug.Print "GetNumberI2(After ):", lValue End Sub
実行結果は、
call DllCallTest2
GetNumberI: 1000 2000
GetNumberI2(Before): 1000
GetNumberI2(After ): 300000
当てにならない次回予告
とりあえず、数値の受け渡しは出来たので、次は、- 文字列
- 配列
- 構造体
の受け渡しあたりをまとめられたらいいなぁ~
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com