近年、VBA(Visual Basic for Applications)はエクセルなどのMicrosoft Office製品で広く使用されていますが、大量のデータや複雑な処理になると、処理が遅くなったり処理落ちすることがあります。このような場合、C++との連携を行うことで処理の高速化が期待できます。簡単に言うと、ボトルネックになる処理をC++側で処理させる事でVBAでは時間のかかる処理を驚くほど高速化できます。この記事では、VBAからC++を呼び出して大きな処理を実行する方法について詳しく解説します。
vbaからc++を使う方法
VBAからC++を呼び出すプロセスは、いくつかのステップで構成されています。以下に、具体的な手順と解説を記述します。
C++ DLLの作成
まず、C++でDLLを作成します。DLLは、VBAから呼び出される関数やプロシージャを提供します。以下は、単純な加算を行うDLLの例です。
// addition.cpp
#include <iostream>
extern "C" __declspec(dllexport) double Add(double a, double b) {
return a + b;
}
このコードをビルドしてDLLを生成します。ビルド方法は開発環境により異なりますが、一般的な手順はコンパイルとリンクです。
g++ -shared -o addition.dll addition.cpp
DLLの配置
生成されたDLLをVBAからアクセスできる場所に配置します。通常は、WindowsのSystem32フォルダやVBAプロジェクトの同じ場所に配置します。
VBAからDLLを呼び出す
VBAからC++ DLLを呼び出すには、Declareステートメントを使用します。
Declare Function Add Lib "path\to\your\dll\addition.dll" (ByVal a As Double, ByVal b As Double) As Double
Sub TestAddition()
Dim result As Double
result = Add(5, 10)
MsgBox "Result: " & result
End Sub
Declare Function
ステートメントでDLL内の関数を宣言し、その後で宣言した関数を呼び出すことができます。
注意事項
- ビット数の一致: VBAとC++ DLLのビット数(32ビットまたは64ビット)は一致している必要があります。
- 関数の型一致: 関数の引数と戻り値の型が一致していることを確認してください。
- エラーハンドリング: DLLの呼び出しでエラーが発生する可能性があるため、エラーハンドリングを適切に行いましょう。
以上が、VBAからC++ DLLを呼び出す基本的な手順です。
実用サンプル① 2次元配列の操作
多次元配列の操作は、VBA の処理の中では時間のかかる処理の一つで、VBA単体での高速化の方法は色々なサイトで紹介されています。
ただ、先述の通り近年のビッグデータを扱おうとすると、VBAでは遅くてまともに処理ができません。
そこでC++でボトルネックになる処理を行うことでExcelでもビッグデータの処理が可能になります。
C++ DLLの作成
まず、C++ DLLを作成します。以下は、2次元配列を受け取り、その要素を2倍にする関数の例です。
// ArrayProcessor.cpp
#include <vector>
extern "C" __declspec(dllexport) void Process2DArray(int rows, int cols, int** array) {
// Processing the 2D array
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
array[i][j] = array[i][j] * 2; // Example: Multiply each element by 2
}
}
}
Process2DArray
という関数を定義しました。この関数は、extern "C"
を使用してC言語の関数としてエクスポートし、__declspec(dllexport)
を使ってDLLから利用可能にします。これにより、この関数をVBAなどから呼び出すことができるようになります。
この関数は3つの引数を受け取ります。
rows
: 2次元配列の行数cols
: 2次元配列の列数array
: 2次元配列へのポインタ
そして、2つのループを使用して2次元配列を処理します。各要素を2倍にする処理が行われていますが、これは単なる例であり、実際の用途によって処理内容を変更して下さい。。
VBAからC++ DLLを呼び出す
次に、VBAからC++ DLLを呼び出します。以下のVBAコードは、C++で作成したDLLを利用して2次元配列を処理し、その結果を取得する例です。
Declare Sub Process2DArray Lib "path\to\ArrayProcessor.dll" (ByVal rows As Long, ByVal cols As Long, ByRef array() As Long)
Sub ExampleUsage()
Dim rows As Long
Dim cols As Long
Dim myArray() As Long
Dim i As Long, j As Long
' Set the dimensions of the 2D array
rows = 3
cols = 4
' Redim the VBA array to hold the values
ReDim myArray(1 To rows, 1 To cols) As Long
' Fill the VBA array with some initial values
For i = 1 To rows
For j = 1 To cols
myArray(i, j) = i * j
Next j
Next i
' Call C++ function to process the array
Call Process2DArray(rows, cols, myArray)
' Display the processed array
Debug.Print "Processed Array:"
For i = 1 To rows
For j = 1 To cols
Debug.Print myArray(i, j);
Next j
Debug.Print ""
Next i
End Sub
VBAの2次元配列がC++で処理された後、処理結果が再びVBAに戻されています。 C++での処理は加算(各要素に2を加える)しているだけです。
効果的な使い方
この方法の効果は、主に以下の状況で発揮されます。
- 大量のデータ処理: 数百行以上のデータがある場合、C++による高速処理が顕著に現れます。
- 複雑なアルゴリズム: 複雑なアルゴリズムや数学的処理が必要な場合、C++の高度な計算機能を利用できます。
ただし、この方法は環境のセットアップが必要であり、誤った操作は予期せぬ問題を引き起こす可能性があるため注意が必要です。適切な利用場面でのみ導入するようにしましょう。
VBAの限界を感じた際にはC++との連携を検討して、処理の高速化を実現してみてください。
実用サンプル② C++ DLLを活用した高速なDB操作
続いてVBAからC++ DLLを呼び出してデータベースのCRUD(Create, Read, Update, Delete)操作を高速化する方法について解説します。DB操作も時間のかかる処理の代表です。
C++ DLLの作成
まず、C++ DLLを作成します。以下は、SQLiteデータベースに対して基本的なCRUD操作を行う例です。SQLiteを使用しますが、他のデータベースも同様の手法で扱えます。
// DatabaseProcessor.cpp
#include <sqlite3.h>
#include <string>
extern "C" __declspec(dllexport) int ExecuteQuery(const char* query, char* errorMessage, const char* dbName) {
sqlite3* db;
char* errMsg = nullptr;
int result = sqlite3_open(dbName, &db);
if (result != SQLITE_OK) {
strcpy(errorMessage, sqlite3_errmsg(db));
sqlite3_close(db);
return result;
}
result = sqlite3_exec(db, query, nullptr, nullptr, &errMsg);
if (result != SQLITE_OK) {
strcpy(errorMessage, errMsg);
}
sqlite3_close(db);
return result;
}
extern "C" __declspec(dllexport) int InsertData(const char* tableName, const char* values, char* errorMessage, const char* dbName) {
std::string query = "INSERT INTO " + std::string(tableName) + " VALUES (" + std::string(values) + ");";
return ExecuteQuery(query.c_str(), errorMessage, dbName);
}
extern "C" __declspec(dllexport) int UpdateData(const char* tableName, const char* setValues, const char* condition, char* errorMessage, const char* dbName) {
std::string query = "UPDATE " + std::string(tableName) + " SET " + std::string(setValues) + " WHERE " + std::string(condition) + ";";
return ExecuteQuery(query.c_str(), errorMessage, dbName);
}
extern "C" __declspec(dllexport) int DeleteData(const char* tableName, const char* condition, char* errorMessage, const char* dbName) {
std::string query = "DELETE FROM " + std::string(tableName) + " WHERE " + std::string(condition) + ";";
return ExecuteQuery(query.c_str(), errorMessage, dbName);
}
extern "C" __declspec(dllexport) int SelectData(const char* tableName, const char* columns, const char* condition, char* result, char* errorMessage, const char* dbName) {
std::string query = "SELECT " + std::string(columns) + " FROM " + std::string(tableName) + " WHERE " + std::string(condition) + ";";
sqlite3* db;
int resultCount = 0;
char* errMsg = nullptr;
int resultDb = sqlite3_open(dbName, &db);
if (resultDb != SQLITE_OK) {
strcpy(errorMessage, sqlite3_errmsg(db));
sqlite3_close(db);
return resultDb;
}
sqlite3_stmt* stmt;
int resultPrepare = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
if (resultPrepare != SQLITE_OK) {
strcpy(errorMessage, sqlite3_errmsg(db));
sqlite3_close(db);
return resultPrepare;
}
int cols = sqlite3_column_count(stmt);
while (sqlite3_step(stmt) == SQLITE_ROW) {
for (int col = 0; col < cols; col++) {
strcat(result, reinterpret_cast<const char*>(sqlite3_column_text(stmt, col)));
strcat(result, ",");
}
strcat(result, "\n");
resultCount++;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return resultCount;
}
このC++コードはSQLite3を使用してデータベースの基本的な操作を行う関数を定義しました。各関数はDLLとしてエクスポートし、VBAなどから呼び出すことができるようにします。以下に各関数の解説をします。
- ExecuteQuery: 与えられたクエリを実行する関数です。データベースファイルのパス(
dbName
)とエラーメッセージを受け取り、実行結果を返します。実行結果やエラーメッセージは引数経由で返されます。 - InsertData: テーブルにデータを挿入する関数です。テーブル名、値(
values
)、データベースファイルのパス(dbName
)を受け取り、ExecuteQuery
関数を用いて実行します。 - UpdateData: テーブル内のデータを更新する関数です。テーブル名、更新する値(
setValues
)、条件(condition
)、データベースファイルのパス(dbName
)を受け取り、ExecuteQuery
関数を用いて実行します。 - DeleteData: テーブルからデータを削除する関数です。テーブル名、条件(
condition
)、データベースファイルのパス(dbName
)を受け取り、ExecuteQuery
関数を用いて実行します。 - SelectData: テーブルからデータを選択する関数です。テーブル名、取得する列(
columns
)、条件(condition
)、データベースファイルのパス(dbName
)を受け取り、指定した条件に一致するデータを取得します。結果は文字列として返され、result
引数を通じて受け取ります。また、結果の行数を整数として返します。
これらの関数は基本的なCRUD操作を定義していて、SQLite3の機能を利用してデータベースの操作を行います。また、エクスポートされた関数は__declspec(dllexport)
によりDLLから直接呼び出すことができるように実装しました。
VBAからC++ DLLを呼び出す
次に、VBAからC++ DLLを呼び出します。以下のVBAコードは、C++で作成したDLLを利用してSQLiteデータベースに対してCRUD操作を行う例です。
Declare Function ExecuteQuery Lib "path\to\DatabaseProcessor.dll" (ByVal query As String, ByVal errorMessage As String, ByVal dbName As String) As Long
Declare Function InsertData Lib "path\to\DatabaseProcessor.dll" (ByVal tableName As String, ByVal values As String, ByVal errorMessage As String, ByVal dbName As String) As Long
Declare Function UpdateData Lib "path\to\DatabaseProcessor.dll" (ByVal tableName As String, ByVal setValues As String, ByVal condition As String, ByVal errorMessage As String, ByVal dbName As String) As Long
Declare Function DeleteData Lib "path\to\DatabaseProcessor.dll" (ByVal tableName As String, ByVal condition As String, ByVal errorMessage As String, ByVal dbName As String) As Long
Declare Function SelectData Lib "path\to\DatabaseProcessor.dll" (ByVal tableName As String, ByVal columns As String, ByVal condition As String, ByVal result As String, ByVal errorMessage As String, ByVal dbName As String) As Long
Sub DatabaseCRUDOperations()
Dim result As Long
Dim errorMessage As String
Dim query As String
Dim values As String
Dim setValues As String
Dim condition As String
Dim tableName As String
Dim columns As String
Dim resultData As String
' SQLiteデータベースファイルのパス
Dim dbName As String
dbName = "C:\path\to\your\database.db"
' CREATE (INSERT)
tableName = "example_table"
values = "'Alice', 25, 'USA'" ' Example values
result = InsertData(tableName, values, errorMessage, dbName)
' READ (SELECT)
tableName = "example_table"
columns = "name, age, country"
condition = "age > 20"
result = SelectData(tableName, columns, condition, resultData, errorMessage, dbName)
Debug.Print "Select Result: " & resultData
' UPDATE
tableName = "example_table"
setValues = "age = 30"
condition = "name = 'Alice'"
result = UpdateData(tableName, setValues, condition, errorMessage, dbName)
' DELETE
tableName = "example_table"
condition = "name = 'Alice'"
result = DeleteData(tableName, condition, errorMessage, dbName)
End Sub
効果的な使い方
この手法は、主に以下の状況で効果を発揮します。
- 大規模なデータベース処理: 複雑な検索やデータベース内の巨大なデータセットに対して、C++の高速性が有利です。
- 高度な計算処理: 特に数学的な処理や統計処理が必要な場合、C++の計算機能が活かされます。
ただし、データベースのセキュリティや整合性には十分な注意が必要です。また、データベースの操作は慎重に行い、エラーハンドリングを適切に行うことが重要です。
この記事を参考にして、VBAでのデータベース操作を効率化し、高速なCRUD処理を実現してみてください。
まとめ
VBAからC++を呼び出すことで、VBAの柔軟性とC++の高性能な処理を組み合わせ、アプリケーション全体のパフォーマンスを向上させることができます。この手法を使用する際には、プロジェクトの要件やセキュリティに十分な注意を払いながら、最適な高速化手段を見つけることが重要です。
今回は簡単な例で示しましたが、C++を利用する事でVBAでは処理に時間がかかり過ぎたり実現が難しかった事柄も簡単に実現可能です。
特に近年、大きなデータ処理が必須になってきています。
PythonとVBAを連携させる記事は私も書いていますが、PythonよりC++の方が圧倒的に高速に処理可能です。
※Pythonは便利なライブラリがあり簡単に処理を実現できるので、使い分けが必要です。
是非、VBA と他のプログラミング言語の連携を覚えてみて下さい。