空腹おやじのログと備忘録

VBA(主にExcel)でいろいろな実験的な事とか、Linuxのコマンドとか設定とかについて忘れないように、あれこれと・・・

ExcelのVBAで使えるDLLを、C++(Visual Studio 2017)で作る。・・・その1

ExcelVBAでもそこそこの事は出来るけれど、C++で処理したい事があって「DLLにしちゃえ」と思ったはいいが、いろいろと忘れて(いや、覚えてないから忘れられないだろうorz)作るのが大変だったので、まとめてみることにした。

とりあえず、今回は、何もしない、何も返さない関数を含むDLLをビルドして、VBAから呼び出して、エラーが出ない事を確認するまで。

何らかの値を渡して、何らかの処理をした結果を返してもらうのは、次回以降に・・・

  1. 開発環境
  2. DLLの作り方(基礎編)
  3. 使い方(Excelからの呼び出し方)

開発環境

OS:Windows 10 Professional
DLL:Microsoft Visual Studio Community 2017(Microsoft Visual C++ 2017)
VBAMicrosoft Office Personal 2013(Microsoft Excel 2013)

DLLの作り方(基礎編)

プロジェクトの作成

1.Visual Studioを起動
2.「ファイル」-「新規作成」-「プロジェクト」をクリック

f:id:Z1000S:20180415145858j:plain


3.「Visual C++」-「Windows デスクトップ」-「ダイナミック リンク ライブラリ (DLL)」をクリック

f:id:Z1000S:20180415145927j:plain


4.「名前」「場所」「ソリューション名」を指定し、「OK」をクリック
「ソリューションのディレクトリを作成」にチェックが入っていないと、ソリューション名は指定できないので、作成する場合はチェックする。
ソリューションのディレクトリを作成するかどうかは任意。今回は作成した状態で説明しています。作成しない場合は、適宜読み替えて下さい。

サイトに寄っては、Visual Studioを「管理者として実行しないとDLLが作れない」と書かれている所もあるようですが、私の場合は通常の起動でも作成出来ました。

f:id:Z1000S:20180415150056j:plain

モジュール定義ファイル(defファイル)

VBAからアクセスしやすいように、モジュール定義ファイルを追加します。
1.「プロジェクト」-「新しい項目の追加」をクリック。

f:id:Z1000S:20180415150120j:plain


2.「インストール済み」-「Visual C++」-「コード」-「モジュール定義ファイル (.def)」をクリック

f:id:Z1000S:20180415150143j:plain


3.名前をプロジェクト名.def(AccessibleFromVBA.def)として、「追加」ボタンをクリック

f:id:Z1000S:20180415150201j:plain

f:id:Z1000S:20180415150232j:plain

プロジェクトの設定

プロジェクトのプロパティを変更します。

 1.「プロジェクト」-「プロパティ」をクリック

f:id:Z1000S:20180415150254j:plain

2.構成を"アクティブ(Debug)"から"すべての構成"に変更します

 

3.「構成プロパティ」-「全般」-「プロジェクトの既定値」-「文字セット」を"マルチ バイト文字セット" "Unicode 文字セットを使用する"に変更します。(2019/9/25 訂正)

f:id:Z1000S:20190925144310j:plain

4.「構成プロパティ」-「全般」-「全般」-「出力ディレクトリ」を"$(SolutionDir)$(Configuration)\"から"$(ProjectDir)$(Configuration)\"に変更します。
(この操作は必須ではありません。)

f:id:Z1000S:20180415164739j:plain

5.「構成プロパティ」-「リンカー」-「モジュール定義ファイル」の値が、先程作成したモジュール定義ファイル名(AccessibleFromVBA.def)になっているか確認します。

f:id:Z1000S:20180415164817j:plain

6.「適用」ボタンをクリック

 

プリコンパイル済みヘッダーは、使わなくてもいいと思ったが、とりあえずそのまま使用することに。

関数を追加してみる

まずは、何もしない関数を追加してみる。

ヘッダーファイル

デフォルトでヘッダーファイルが無いので、追加する。
1.「プロジェクト」-「新しい項目の追加」をクリック。

f:id:Z1000S:20180415165036j:plain


2.「インストール済み」-「Visual C++」-「コード」-「ヘッダー ファイル (.h)」をクリック


3.名前をプロジェクト名.h(AccessibleFromVBA.h)として、「追加」ボタンをクリック

f:id:Z1000S:20180415165051j:plain

4.まずは、おまじない。

#pragma once

extern "C" {
#define ACCESSIBLEFROMVBA_API __declspec(dllexport)
}

#pragma onceについてはこちらを参照。

extern "C"については、ロベールのC++教室 - 第47章 C±± -あたりを見るか、自分でググって下さい。

"#define ACCESSIBLEFROMVBA_API __declspec(dllexport)"については、以下を参照。

DLL のビルド時には通常、エクスポートする関数のプロトタイプやクラスを含むヘッダー ファイルを作成し、そのヘッダー ファイル内の宣言に __declspec(dllexport) を追加します。 コードを読みやすくするために、次のように __declspec(dllexport) 用のマクロを定義して、そのマクロをエクスポートする各シンボルに使います。
__declspec(dllexport) を使った DLL からのエクスポート

#define・・・は、externの{}の外の方がいいかもしれないけど、今回は、C++ & VBA Onlyなのでこれでよしとする。


5.extern "C"の後の{}内に関数を宣言する。
今回は、引数を取らず、何も返さない"DoNothing"という関数を追加してみる。

----- 重 要 -----
戻り値の型関数名の間にWINAPIを忘れないように。

#pragma once

extern "C" {
#define ACCESSIBLEFROMVBA_API __declspec(dllexport)
    ACCESSIBLEFROMVBA_API void WINAPI DoNothing();
}

f:id:Z1000S:20180415165729j:plain

赤い波線が出て来るが気にしない。

ソースファイル

関数の宣言が出来たら、次は定義の作成。
1.ソリューションエクスプローラーで、AccessibleFromVBA.cppをクリック(or ダブルクリック)
2.前項で作ったヘッダーファイルのインクルードを確認。無ければ追記。

f:id:Z1000S:20180415170741j:plain


3.宣言に合わせて関数を作成。今回は何もしないので、関数の中身は"return;"のみ

#include "stdafx.h"
#include "AccessibleFromVBA.h"

ACCESSIBLEFROMVBA_API void WINAPI DoNothing()
{
    return;
}

f:id:Z1000S:20180415170804j:plain

モジュール定義ファイル

1.ソリューションエクスプローラーで、AccessibleFromVBA.defをクリック(or ダブルクリック)

f:id:Z1000S:20180415170825j:plain


2."LIBRARY"の後ろに、DLL名を追記。
3."EXPORTS"と追記。
4.エクスポートする関数名を追記。

f:id:Z1000S:20180415170901j:plain


以前は、関数名の後ろに"@"と数字を付けていたけど、特定の場合を除き付けることはお勧めではないようです。

@ordinal を使用して、関数名ではなく番号が DLL のエクスポート テーブルに格納されるように指定できます。 多くの Windows DLL で、レガシ コードをサポートするために序数がエクスポートされます。 DLL のサイズを最小限に抑えるのに役立つため、16 ビットの Windows コードでは序数を使用することが一般的でした。 レガシ サポートのために DLL のクライアントで必要な場合を除き、関数を序数でエクスポートすることはお勧めしません。 .LIB ファイルには序数と関数のマッピングが含まれているため、DLL を使用するプロジェクトでは通常と同様に関数名を使用できます。
EXPORTS
ビルド

1.「ビルド」-「AccessibleFromVba のビルド」をクリック

f:id:Z1000S:20180415171016j:plain


2.出力ウィンドウに、"ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ"と表示されればOK

f:id:Z1000S:20180415171039j:plain


出力先のフォルダに、"AccessibleFromVba.dll"が出来ているはず。

使い方

Excel

Declare ステートメント

1.構文
[ Public|Private ] Declare Sub name Lib "libname" [ Alias "aliasname" ] [ ( [ arglist ] ) ]
[ Public|Private ] Declare Function name Lib "libname" [ Alias "aliasname" ] [ ( [ arglist ] ) ] [ As type ]

指定項目 説明 備考
name 関数名 前述の例では、DoNothing
libname DLL名 必要であればパスも含めて指定
aliasname DLL またはコード リソース内のプロシージャの名前 多分気にしなくてもOK
arglist プロシージャが呼び出されるときにプロシージャに渡される引数を表す変数のリスト オプション
type Function プロシージャによって返される値のデータ型 オプション。VBAの型を指定

詳細はこちらとか こちら

DoNothingの例

標準モジュールに、以下の1行を追記。(パスは自分の環境に合わせて下さい。)

Private Declare Sub DoNothing Lib "C:\Datas\MyDatas\Developer\VisualStudioComunity2017\DllForVBA\AccessibleFromVBA\Debug\AccessibleFromVBA.dll" ()

以下のように呼び出す。

Public Sub DllCallTest()

    'DLLの関数呼び出し
    Call DoNothing

    Debug.Print "Done."

End Sub

f:id:Z1000S:20180415201403j:plain

エラーの発生もなく、無事終了。

次回予告

今回の内容では、投げっぱなしの処理しか出来ないので、次回はデータの受け渡しに関連する事をまとめる予定。

  1. 受け渡しするデータの型について
  2. 値渡しと参照渡しについて
  3. 処理する値を渡せるようにすること
  4. 処理した結果や値を返してもらえるようにすること
  5. データ型による渡し方の注意点

それ以降は、

  • DLLからエクスポートされる関数の確認方法
  • DLLのデバッグ方法

などをまとめる予定。

いつ頃までに、まとめられるかは、不明・・・
気分次第?

 

2018/6/27

やっと、その2を追加しました。
予告までしたのに、遅いし、予告した内容も一部次に先送りしてるし・・・orz

z1000s.hatenablog.com

2019/10/22

その3 文字列の受け渡しを追加しました。

z1000s.hatenablog.com

z1000s.hatenablog.com

z1000s.hatenablog.com

z1000s.hatenablog.com

z1000s.hatenablog.com

z1000s.hatenablog.com

Excelの列のアルファベット-数値変換

ThisWorkbook.Worksheets(1).Columns("AA").Column
===>27

ThisWorkbook.Worksheets(1).Columns(28).Address(ColumnAbsolute:=False)
===>AB:AB
ThisWorkbook.Worksheets(1).Cells(1, 28).Address(RowAbsolute:=False, ColumnAbsolute:=False)
===>AB1

 

数値→アルファベットへの変換の場合、余計な部分を削除する必要がある。

ManjaroLinux 筑波ミラーが復活したようです

2/11のブログpacmanのミラーのうち、筑波がエラーが発生すると書きましたが、今日pacman-mirrorsが更新された後、レポジトリとの同期をすると筑波のミラーが復活していました。

f:id:Z1000S:20180217202750p:plain

筑波のミラーは応答は早いのですが、時々不安定な感じがするのは私だけでしょうか?
安定性では理研の方が上のような気がします。
でも面倒なので、そのまま筑波を使ってますが・・・

Manjaro Linux で pacmanが接続拒否エラーを返す

2、3日前位からでしょうか、pacman -Syuを実行すると、エラーが発生します。筑波に繋がらないようです。

エラー: ファイル 'core.db' を ftp.tsukuba.wide.ad.jp から取得するのに失敗しました : Failed to connect to ftp.tsukuba.wide.ad.jp port 80: 接続を拒否されました

 

とりあえずミラーを更新してみると、やはり筑波にエラーが発生しましたが、理研に切り替わって、結果的には大丈夫そうです。

f:id:Z1000S:20180211202734p:plain

scpコマンドで秘密鍵を使ってファイルをコピーする

普段使用しているPCから、他のPCにファイルをコピーしようとする場合、scpコマンドを使用しますが、コピー先のPCと使用中のPCのユーザー名が異なる場合、明示的に接続先のユーザー名の指定が必要となる場合の指定方法です。

scp -i 秘密鍵ファイル コピーするファイル名 コピー先のユーザー名@コピー先のアドレス:コピー先のファイルを保存するディレクトリ

暗くなると点灯するLED回路を作ってみた Part2

暗くなると点灯するLED回路の、抵抗値を見直しました。

点灯、消灯するタイミングおよび、点灯時の明るさを調整した結果、最終的な回路は、以下のようになりました。

f:id:Z1000S:20180204104610p:plain

 フル充電したエネループ(単4×4本)で、この回路で1ヶ月くらいは使えるようでした。

暗くなると点灯するLED回路を作ってみた

昨年12月の事、妻が外に置いていたプランターを、寒さ対策のために家の中(廊下の片隅)に持ち込んだ。
日頃から廊下の電灯を点けずに(暗い中を)歩く私にとって、本来何もないはずの所に物があると、それを蹴飛ばしてしまう可能性は非常に高い。
電灯を点けて歩けば何も問題がないのだが、昔からの習慣でなかなかそうできずにいた。
そこで、プランターが見えるように(というか、そこにプランターがあることを意識させるため)手持ちのLEDを点灯させようという結論に。
手持ちのエネループ4本と抵抗、LED、スイッチを繋げて完成。しばらくは、それで過ごしていたが、朝と夜にスイッチの操作が面倒くさくなってきた。

なんとかこの手間を省きたい。
で、「明るさに応じて、勝手に点灯、消灯するようにしてしまえ」ということになり、調べてみると、明るさはCDSで検知して、それを元にトランジスタでLEDに流す電流をオン、オフすればいいらしい。

 

回路は、こんな感じ。

f:id:Z1000S:20180101213828p:plain

 

抵抗は、手持ちの物を使ったので、ひとつでいいところが並列になっていたりする。

 

LEDと直列に接続している抵抗は、電源のエネループ4本がフル充電時5.8Vだったので、そこにLEDの定格20mAを流すとすると

5.8V / 20mA = 290Ω

手持ちの抵抗に近いのがないので、460Ωを並列にして230Ωで妥協。

5.8V / 230Ω = 25mA

許容範囲内でしょう。

(ベース側の抵抗が、いい加減で大きめにしてあるので、実際にはそんなに流れていないのは、点灯したLEDの明るさからみて明らか)

 

電池とベース間の抵抗(100kΩ)や、CDSと並列に接続した抵抗(100kΩ、46kΩ)は、結構いい加減ベースで。最終的には、実際にLEDを点灯させてみて、点灯消灯する明るさを見ながら調整して決定。時間があったらもう少し抵抗値を下げる方向で調整してみよう。

 

春になって暖かくなれば使わなくなるので、基盤に半田付けなどせず、ブレッドボードに部品を差し込んでおしまい。

 

電池は最終仕様前の抵抗値で使って、10日くらいで暗くなって来たので、1週間くらいは使えそうかな?

 

完成した全体は、こんな感じ

f:id:Z1000S:20180101214327j:plain

消灯状態

f:id:Z1000S:20180101214351j:plain

点灯状態

f:id:Z1000S:20180101214426j:plain

配線状態

 

f:id:Z1000S:20180101214449j:plain