このところプログラミング関連の記事を書いていなかった。
2018年のゴールデンウイークは、今のところどこにも遠出する予定が無いので前から書きたいと思っていたEXCEL XLL関連の記事を書く事にした。
以下に示すXLL講座の第一回目の記事では、マイクロソフト社のEXCEL SDKの中にあるサンプルプログラムであるExampleプロジェクトをビルドする手順を紹介した。
その結果、Example.xll は生成出来たのだが、EXCELに読み込もうとするとこんなエラーが出る。
当記事では、このエラーの原因と対策を解説したい。
無事に問題を解決出来るとExample.xllはEXCELに正常に読み込まれる(ロードされる)。
このXLLの中には幾つかのユーザー定義関数が定義されているので、関数の挿入メニューを開くとそれらのユーザー定義関数が選択出来る。
まあ、ワテの場合、この辺りのやり方は数年前に独学でマスターしたのだが、このサンプルExample.xllやもう一つのサンプルGeneric.xllをエクセルに読み込めるようにするだけでも数カ月くらい悪戦苦闘した記憶がある。
なお、当記事はあくまでワテが独学で習得した内容なので、ワテの勘違い、間違いなども入っている可能性も大いにある。
なので、皆さん自身でも十分確認の上、記事を参考にして下さい。
では、本題に入ろう。
EXCELに読み込めるXLLを作成する手順
ワテの開発環境は以下の通り。
- 自作パソコン(Core i7-4770K, 32GBメモリ, SSD750GB, HDD3TB,5TB)
- Windows 10 Pro 64
- Visual Studio 2017 Community(全パッケージインストール)
- Office 2013 Professional(Excel, Word, PowerPoint, Access, Publisher, Outlook, OneNote)
さて、皆さんはこの時点で、第一回目記事の手順に従ってExcel 2013 SDKの中にある二つのサンプルXLLプロジェクト、
- Example
- Generic
のビルドに成功しているとする。
その結果、例えばDebugビルドを行うと、
- Debug/Example.dll
- Debug/Generic.dll
が生成される。
拡張子を .xll に変更する
まあ、.dllを手作業で.xllに変更しても良いのだが、毎回手作業でやるのは面倒なのでプロジェクトの設定で行う事にする。
その為には、プロジェクトのプロパティウインドウ(下図)を表示して、
[構成プロパティ]
[全般]
で[ターゲットの拡張子]を .xll にする。
これでビルド後に生成されるファイルは Example.xll となる。
モジュール定義ファイルを指定する
次に行うのがモジュール定義ファイルの指定だ。
[構成プロパティ]
[リンカー]
[入力]
を開いて、
[モジュール定義ファイル]に EXAMPLE.DEF を指定する。
[モジュール定義ファイル] EXAMPLE.DEF とは何か?
モジュール定義ファイルはCやC++プログラムを書く場合に使うのだが、詳しい説明はマイクロソフト社の技術文書など参考にして頂きたい。
例えばこの辺り。
DEF ファイルを使った DLL からのエクスポート
Exporting from a DLL Using DEF FilesLearn more about: Exporting from a DLL Using DEF Filesモジュール定義 (.def) ファイル
Module-Definition (.Def) FilesLearn more about: Module-Definition (.Def) Files
まあ要するに、モジュール定義ファイルとは、DLL(今の場合はXLL)の中にある関数のうち、どれをエクスポートするかなどを記述しておくのだ。
エクスポート属性を持つ関数は、他のプログラムからも見えるので呼び出す事が出来る。今の場合はEXAMPLE.XLLの中の関数のうち、EXCEL.EXEが必要とするものをエクスポートしておく訳だ。
なお、ワテの場合、この辺りの知識はあまり詳しくないので、これ以上詳しい説明は出来ない。
兎に角、XLLを作成する場合には、このモジュール定義ファイルに以下のように記述しておく。
[モジュール定義ファイル] EXAMPLE.DEF の解説
モジュール定義ファイル(EXAMPLE.DEF)の先頭には、以下の記述をしておく。
LIBRARY EXAMPLE.XLL
大文字・小文字の区別は無い。
次に、EXPORTS の部分であるが、その後にズラズラと並んでいるのがエクスポートしたい関数名だ。
EXPORTS xlAutoOpen xlAutoClose xlAutoRegister12 xlAutoAdd xlAutoRemove xlAddInManagerInfo12 xlAutoFree12 CallerExample debugPrintfExample EvaluateExample Excel12fExample Excel12Example InitFrameworkExample TempActiveCellExample ・・・ ・・・
ここで重要なのは、xlAuto***** で始まる幾つかの関数群だ。
xlAuto*****関数群はEXCEL.EXEがXLLを読み込むのに必要
以下の7個のxlAuto*****関数群がある。
xlAutoOpen xlAutoClose xlAutoRegister12 xlAutoAdd xlAutoRemove xlAddInManagerInfo12 xlAutoFree12
詳しい説明は次回にするとして、EXCEL.EXEがXLLファイルを読み込んでアドインとして利用する為には、これらの関数が必要になる。
全部必要かどうかは未確認だが、まあ、サンプルに従ってこれらの関数を記述しておくのが良い。
注意事項としては、関数名末尾に12が付いているものと付いていないものがある。
第一回講座で説明したように、EXCEL XLLアドインの開発においては、データ型や関数名の末尾に12とか4が付くものがある。何も付かない場合は4と同じだと思う(ワテの理解)。
LPXLOPER12 LPXLOPER XLOPER12 XLOPER ・・・
同じく、Frameworkプロジェクトの中では、
Excel12f Excel4f LPXLOPER TempNum(double d) LPXLOPER12 TempNum12(double d) ・・・
など。
この4や12の意味であるが、EXCEL2007以降のEXCELで動くXLLを作成する場合には12版の関数やデータ型を使う必要がある。
一方、EXCEL2007より以前のEXCEL向けにXLLを開発するなら4版を使う必要がある。
なので、もし全部のEXCELで動くXLLを作りたい場合には、作成するXLLの中でエクセルのバージョンを調べてそのバージョンに応じて4版関数を使うか、12版関数を使うかを自分で切り替える必要がある(と思う)。
ワテの場合は、面倒なので4版は使わずに12版のみを使うようにしている。
その場合は、当然ながらEXCEL2007以降でしか動かないXLLが生成される。
さて、ここで気になる事がある。
それは、冒頭で示した以下の7個のxlAuto*****関数群を再び掲載するが、
xlAutoOpen xlAutoClose xlAutoRegister12 xlAutoAdd xlAutoRemove xlAddInManagerInfo12 xlAutoFree12
12が付かないものが混じっている。
この辺りはワテも良く分かっていない。
確か、
xlAutoOpen12
などに名前を変えても正式なXLLとしてエクセルにロードされて動いたかな?
その辺りは、必死で試行錯誤していろいろ試したのだが、すっかり忘れた。
でもまあ、XLL SDKの中にあるExampleプロジェクトやGenericプロジェクトが上のように7個のxlAuto*****関数群を定義しているので、自作XLLを作成する場合もそれと全く同じネーミングにしておけば間違いない。
まあ、興味ある人はその辺りは各自いろいろ実験すると良いだろう。
ちなみに、これらのxlAuto*****関数群の中で、4版関数を使うと仮にビルドには成功してXLLが生成出来たとしても、そのXLLをEXCEL.EXEにロードするとエラーで読み込めないなどの症状が出る場合がある(必ずエラーするかな)。
つまり、冒頭で示した以下のエラーダイアログが表示されるのだ。
なので、もしこのダイアログが表示された場合には、まずは xlAuto*****関数群の中身をチェックすると良いだろう。
XLLをEXCEL.EXEに読み込む
ここまで出来ればあとはそのXLLをEXCEL.EXEに読み込めば良い。
やり方は幾つかある。
最も簡単なのは、ビルドで生成された Example.xll をダブルクリックで開けば良い。
そうすると、EXCELが起動して、そのExample.xllをロードする。
もしロード出来なかった場合には、上記のエラーメッセージウインドウが出るはずだ。
デバッガーの環境でXLLをロードする方法
Visual Studioのデバッガーを利用してXLLをデバッグ実行する事も可能だ。
その為には以下のようにプロジェクトの設定を行う。
[構成プロパティ]
[デバッグ]
の画面を開いて、以下のように記述する。
コマンド:C:\Program Files\Microsoft Office 15\root\office15\EXCEL.EXE
この場合、EXCEL 2013のパスを指定している。皆さんは各自自分のEXCELのバージョンに合わせてパスを指定すると良い。
コマンド引数でXLLを指定する。
コマンド引数:"D:\2013 Office System Developer Resources\Excel2013XLLSDK\SAMPLES\EXAMPLE\Debug\EXAMPLE.xll"
絶対パスで指定するのが簡単なのでワテはそうしている。
相対パスでも指定出来るとは思うが、調べていない。
ちなみに、この例では一個のXLLを引数で指定しているが、複数のXLLを指定する事も可能だ。
その為にはコマンド引数に読み込みたいXLLを並べれば良い。
コマンド引数:"D:\・・・\EXAMPLE.xll" "D:\・・・\二番目.xll"
あるいは、XLLとXLAMを読み込みたい場合もあるだろう。
その場合もコマンド引数に並べれば良い。
ここまでの設定が完了すれば、あとはVisual Studioのデバッグ実行(F5)をすれば良い。
デバッグ実行(F5)を行う
デバッガーを使うので、適当なところにブレークポイントを設定してみる。
下図のようにxlAuto関数群やDllMain関数にブレークポイントを設定しておく。
F5でデバッグ実行を開始すると下図が出る。
[このアドインをこのセッションに限り有効にする]を選択する。
DllMainにはいろんなタイミングで飛んで来るので、毎回停止すると煩わしいから、ここでブレークは解除しておくと良いだろう。
次に飛んでくるのがxlAutoOpenだ。
その名前の通り、 XLLがロードされたら実行される関数だ。
xlAutoOpen関数のコメントを引用すると以下の通り。
/*
** xlAutoOpen
**
** xlAutoOpen is how Microsoft Excel loads XLL files.
** When you open an XLL, Microsoft Excel calls the xlAutoOpen
** function, and nothing more.
**
** More specifically, xlAutoOpen is called by Microsoft Excel:
**
** – when you open this XLL file from the File menu,
** – when this XLL is in the XLSTART directory, and is
** automatically opened when Microsoft Excel starts,
** – when Microsoft Excel opens this XLL for any other reason, or
** – when a macro calls REGISTER(), with only one argument, which is the
** name of this XLL.
**
** xlAutoOpen is also called by the Add-in Manager when you add this XLL
** as an add-in. The Add-in Manager first calls xlAutoAdd, then calls
** REGISTER(“EXAMPLE.XLL”), which in turn calls xlAutoOpen.
**
** xlAutoOpen should:
**
** – register all the functions you want to make available while this
** XLL is open,
**
** – add any menus or menu items that this XLL supports,
**
** – perform any other initialization you need, and
**
** – return 1 if successful, or return 0 if your XLL cannot be opened.
*/
ユーザー定義関数を使ってみる
Example.xllの中には十数個のユーザー定義関数が記述されている。
それを呼び出してみよう。
[数式]
[関数の挿入]
を開いて、適当なセルを選択する。
例えばA1.
そこで適当に関数を選ぶ。
例えば CalcCircum を選ぶ。
下図のようにA1にCalcCircumユーザー定義関数を入れて、引数にはB1を与える。
そのB1に数字の2を書き込む。
その結果、CalcCircum は 12.56637062 と表示する。
その理由は、CalcCircum関数の定義は以下の通り。
__declspec(dllexport) double WINAPI CalcCircum(double pdRadius) { return pdRadius * 6.283185308; }
これは何の計算?
地球の半径と関係しているのかな?良く分からん。
まあ兎に角、XLLで定義しているユーザー定義関数がEXCELから実行出来た。
他のユーザー定義関数も試してみると良いだろう。
でもExample.xllに入っているやつはどれも今一つ動きが分かりにくいと思う。
一方、Generic.xllのほうが面白いサンプルが多いと思う。
Generic.xllも動かしてみる
もう一方のサンプルXLLプロジェクトのGenericも、上で示した手順で正式なXLLとしてビルドする。
そしてデバッガーの環境でEXCEL.EXEに無事に読み込めたとしよう。
エクセルの[アドイン]タブをクリックして開く。
リボンの中に[Generic]が表示されているとGeneric.xllの読み込みに成功している。
ここで例えば[Dialog]を実行する。
そうすると、下図のようにダイアログが表示される。
上図で Reference: をクリックすると自分の好きな範囲を選択出来る(点線枠)。
こんな感じでXLLのダイアログを表示して、セル範囲を取得するなどが可能になる。
このサンプルを応用すれば、ユーザーインターフェースを作成する事出来るだろう。
同じく[Dance]と言う項目を選択すると、数個のセルが目まぐるしくアクティブになり数字が移動するだろう。
停止するには[ESC]キーの押下だ。
まあ、ヘンテコなサンプルではあるが、エクセル操作の基本処理が沢山入っている。
- セルの選択
- セルをアクティブにする
- セルに値をセットする
- セルの値を読み取る
などなど。
従って、このDance関数のソースコードを解読すると色々と勉強になる。
ワテの場合も、その辺りからXLLの勉強を開始した記憶がある。
まとめ
当記事では、EXCEL 2013 SDKの中に入っているExample、Genericと言う二つのサンプルXLLプロジェクトをビルドして正式なXLLとしてエクセルにロードする手順を解説した。
この手順はワテが数年前に独学で色々と試行錯誤しながら到達した手法なので、まあ、一応問題無く動くが、あくまでワテ流なのでマイクロソフト社純正の手法かどうかは分からない。
ビルドしたXLLを正しいXLLとしてエクセルにロードする為には、xlAuto*****と言う名前の幾つかの関数が必要になる。
またそれらの関数においては、EXCEL2007以降で動くXLLかXECEL2003以前で動くXLLかに応じてExcel4fとかExcel12fと言った二種類の関数、データ型を使い分ける必要がある。
さらに、モジュール定義ファイルにおいて、これらの関数群をエクスポートしておく必要がある。
これらの手続きを一つでも忘れると、XLLはビルド出来たのだがエクセルにロードするとエラーが発生する場合がある。
逆に言えば、ExampleやGenericのサンプルを流用して自作のXLLを作れば、まあ間違い無く上手く行くと思う。
と言う事で、ワテの場合は、独学でXLLの学習を始めてからこれらのサンプルを無事にエクセルにロードするだけでも数カ月掛ったと思う。
それ以前に、正常にビルドするだけでも一ケ月くらい掛かった。
世の中にはVBAやVSTOアドインの解説書や解説サイトは多いのだが、何故か知らないのだが、XLLに関する情報源は殆ど無い。
不思議だ。
そんなに人気が無いのかなあ~。
つづく。
XLLの本を読む
ワテの知る限り、XLLの解説書はこれしかない。
英文で500ページくらいある分厚い本だ。
付録のCD-ROMにあるサンプルプログラムが役立つ。
XLLをゼロから作成すると低レベルなコマンド群(=関数群)の組み合わせになる。例えばセルに入っているデータを文字列として取得するだけでも非常に煩わしいのだ。
その辺りは次回以降に解説する予定だが、この本の作者が作成したクラスを使うとXLLプログラミングが非常にやり易いので、そう言う点でもこの本はお勧めしたい。
ただしこの本を買ったとしても、その内容を理解して使いこなすまでには可成りの忍耐・やる気が必要だろう。
VBAプログラミングの本なら買ったその日からサンプルを実行して試すなどが可能だが、XLLの場合には正しく作らないとEXCEL.EXEにロードする事すら出来ない。
無事にロード出来たとしても、セルにデータを書き込んだり読み取ったりするだけでもとってもややこしいのだ。
その辺りの解説は次回。
コメント