一時、エクセルのアドイン作成に猛烈に凝っていた時期がある。
エクセルのアドインは種類が多くて、初めての人にはなかなか理解しづらいと思う。
ワテもそれらの違いや、具体的な開発手法をマスター出来るまで数か月掛かった記憶がある。
一応、今では、どんな手法でも開発する事が出来る。頼まれれば作っても良いが、今のところそういう依頼は無い。
この記事では、ワテがエクセルアドイン開発で苦労した点など、過去の記憶を頼りに皆さんの為に解説したい。
この記事によって、EXCELアドイン作成に挑戦しようという人の一助になれば幸いである。
Visual StudioのC#でVSTOアドイン(=COMアドイン)を作成する手順の解説記事だ。
Visual StudioのC or C++でXLLアドインを作成する手順の解説記事だ。
では、本題に入ろう。
EXCELアドインとは何か
エクセルアドインを自作しようと思っている人であればアドインの意味は良くご存じだと思う。
英語で書くと
Add-in(アドイン)
何らかのソフト(今はEXCEL)に追加して、機能を強化するプログラムだ。
そういう目的のプログラムは、
Add-on(アドオン)
と呼ばれる場合もあるが(例えば Add-ons for Firefox – Mozilla)、エクセルの場合は昔からアドインと呼ばれている。
エクセルの場合には、このアドインの種類が多く、それに応じてその開発手法も異なるので、その辺りがEXCELアドインと一口で言ってもなかなか理解しづらいのだ。
どの手法を使っても、同じようなものが出来るんなら一つだけ覚えれば良いのでは?と言う人がいるかもしれない。
単にエクセルの機能強化と言う点では同じ物が出来るが、アドインの種類に応じてその特徴、長所短所、開発手法の難易度など、いろいろな違いがあるのでまずはエクセルアドインの全体像を掴んでおくと良いだろう。
EXCELアドインの種類
アドインの 種類 |
開発 言語 |
開発の 難易度 |
処理速度 |
アドインの インストール方法 |
特徴 |
|
1 |
xlamアドイン 拡張子xlam |
VBA | 簡単 | 遅い | 手動(手作業でファイルのコピペ)か、何らかのインストーラーを自作する | VBAプログラミングの経験者であれば、自作のVBAプログラムを少し手直しすればxlamアドイン化する事も簡単に出来る。 |
2 |
COMアドイン 拡張子dll |
C# VB NET |
簡単 ただしVisual Studioを使う必要があり |
普通 | Visual StudioでCOMアドインを作成するとインストーラーも作成出来る |
無料版Visual Studio 2015 CommunityにはCOMアドインテンプレートが無いが、そこにOffice Developer Tools(無料)をインストールするとEXCELやWordなどのCOMアドイン開発が可能となる。 テンプレートに従って作成すれば比較的簡単にCOMアドインを作成出来る。C#, VB.NET, Dot Net Frameworkの知識がある程度必要。 |
3 |
XLLアドイン 拡張子xll |
C++ |
難しい Visual Studioを使う |
高速 | 手動(手作業でファイルのコピペ)か、何らかのインストーラーを自作する |
無料版Visual Studio 2015 Community でも作成可能。 C++言語を使って作成する。 特徴としては、C++で作成するので処理速度が速い。マルチスレッド版の組み込み関数を作成するなども可能。 ただし、ネットを検索してもXLL開発に関する情報は皆無なので、自力で開発するのは非常に難しい。 |
表 1 三種類のEXCELアドイン
以下では、それぞれのアドインに関して、もう少し詳しく説明したい。
xlamアドイン
エクセルVBAでプログラムを作ると、そのEXCELファイルを保存する場合に幾つかのファイルタイプを選択出来る。
通常はxlsm(Excel マクロ有効ブック)を使う。EXCEL2003まではxlsと言う形式だったが、EXCEL2007以降はこのxlsm形式に変わった。
例えばごく普通のxlsmファイルをxlamファイル形式で保存してみる。
xlsmファイルはこれを使った。
【入門編】 初心者向け EXCEL VBA (4/10) – EXCEL VBAのFor Nextループで文字列を置換する
このxlsmファイルを開いて、xlamで保存する。
そうすると、保存先のパスが自動的に指定されて、デフォルトでは以下のフォルダに保存するようになっている。
C:\Users\wate\AppData\Roaming\Microsoft\AddIns
まあ、そこに入れても良いが実験なのでとりあえず保存先をデスクトップに変更してxlamファイルに保存した。
そのxlamファイルをダブルクリックで開くと以下の確認画面が出る(EXCEL2013)。
マクロを有効にするをクリックすると、下図のように空のエクセルが開く。
開発タブのVBAエディタを開くと下図の通り。
ここでオレンジ色の枠で囲んだ部分にSheet1やThisWorkbookがあるので混乱するかもしれないが、ここに出ているシートやワークブックは今読み込んでいるxlamファイルに付属しているものである。それらは非表示の状態だ。
この空のエクセルに新規ブックを追加したのが下図だ。
緑色の枠で示しているのが今追加したBook1であり、そこにもSheet1やThisWorkbookがあるが、それは今表示されている普通のシートやワークブックである。
オレンジ色で囲んだ非表示のシートやワークブックは、ThisWorkbookのIsAddinプロパティを現状のTrueからFalseに変更すると表示状態にする事も出来る。
そうすると、元のxlsmファイルに入っていたおかきの会社の住所が表示されるだろう。
この非表示シートやワークブックは通常は非表示にしておいて、必要に応じてそのシートを各種の処理に利用すれば良い。
例えば、
- 事前に何らかの初期データなどを保持しておくためのシート
- 途中の処理結果を一時保管するためのシート
などかな。
参考(Excelの主なファイル形式)
形式 | 拡張子 | 説明 |
Excel ブック | .xlsx | Excel 2007 ~ 2013 の既定の XML ベースのファイル形式。Microsoft Visual Basic for Applications (VBA) マクロ コードや Microsoft Office Excel 4.0 マクロ シート (.xlm) は保存できません。 |
Excel マクロ有効ブック | .xlsm | Excel 2007 ~ 2013 向けの XML ベースのマクロ有効ファイル形式。VBA マクロ コードや Excel 4.0 マクロ シート (.xlm) を保存します。 |
Excel バイナリ ブック | .xlsb | Excel 2007 ~ 2013 向けのバイナリ ファイル形式 (BIFF12)。 |
Excel アドイン | .xlam | Excel 2007-2013 向けの XML ベースのマクロ有効アドイン形式。アドインは、追加のコードを実行するための補助プログラムです。VBA プロジェクトや Excel 4.0 マクロ シート (.xlm) の使用をサポートします。 |
Excel 97 – 2003 アドイン | .xla | Excel 97 ~ 2003 のアドイン。アドインは、追加のコードを実行するための補助プログラムです。VBA プロジェクトの使用をサポートします。 |
Excel 4.0 ブック | .xlw | ワークシート、グラフ シート、マクロ シートだけを保存する Excel 4.0 ファイル形式。Excel 2013 ではこのファイル形式のブックを開くことができますが、この形式で Excel ファイルを保存することはできません。 |
引用元 他のファイル形式でブックを保存する – Excel – Office Support
COMアドイン
コムアドインと言う。
Windowsの世界でCOMというのを理解するのはかなり難しい。実はワテ自身、COM技術のほんの数パーセントくらいしか理解出来ていない。
COM (Component Object Model) とは、Windowsのプログラム間での通信手段の規格だ。
複数のプログラム間で相互に通信しながら目的とする処理を行う手法にはいくつもの手法がある。
UNIX, Linuxの世界ではプロセス間通信(IPC, Inter-Process Communication)として良く使われる技術に、IPCソケット通信がある。
あるいはネットワーク経由で他のコンピュータ上のプロセスと通信を行うTCPソケットの手法もある。
これらの技術はWindowsでも実装されているが、Windowsにはこれらの一般的な手法とは別にWindows固有のプロセス間通信の手法も採用されている。
DDE(Dynamic Data Exchange、DDE、動的データ交換)
DDEが発表されたのは1987年のWindows 2.0リリース時なのでかなり古い技術だ。現在のWindowsでもサポートされており、EXCELでも利用できる。
このDDE機能を使うと、別のプログラムから実行中のEXCELに対して何らかのデータを送信してシートに書き込んだり、あるいは逆にEXCELからデータを読み取るなどの操作が可能だ。また、EXCELを起動したり終了したりする事も可能だ。
現在ではこのDDEの手法でEXCELを操作する事は殆どない(あるいは全くない)ので、不要ならEXCELのオプション設定画面で、
Dynamic Data Exchange(DDE)を使用する他のアプリケーションを無視する
に☑を入れても良いだろう。そうするとDDE通信機能は無効化出来る。でもまあ無効化しても有効化しても普通にEXCEL作業をする上では何も変わらないと思うが。
冒頭でも述べたようにCOMアドインというのはWindowsの世界で用いられている通信手法の一つだ。
ところがWindowsにはCOM以外にも
- OLE (Object Linking and Embedding、オブジェクトのリンクと埋め込み)
- ActiveX (アクティブエックス)
などの技術があり、二つのアプリケーション間で相互にデータ(オブジェクトと言うべきかな)を交換する事が出来る。
例えばEXCELの表をWORDに埋め込んで、WORDの中でそのEXCELの表を編集出来るなどの技術だ。
しかしながら、このCOM, OLE, ActiveXなどの技術は調べれば調べるほど難解な用語が登場して、ワテの場合はあまり理解できていない。
兎に角難しい分野だ。
さて、EXCELでCOMアドインを作る場合には、COMの仕様を完全に理解しておくなどの必要性は全く無くて、C#あるいはVB.NETで作成して出来あがる拡張子DLLのCOMアドインファイルが、EXCEL.EXEを相手にCOMという手法で通信しながら上手い具合に連携し、目的とする処理をしてくれるという程度の理解でも良いだろう(ワテの意見)。
XLLアドイン
XLLアドインはVisual StudioのC++言語を使って開発する。
無料版Visual Studio 2015 Community でもC++の開発環境は入っているのでXLLアドインは作成可能だ。
XLLアドインの特徴としては、C++で作成するので処理速度が速い。
XLLと言う拡張子が付いているが、その実体はDLLである。
ただしEXCEL.EXEから呼び出して互いに連携しながら動作する特殊なDLLなのでXLLという名前になっている(と思う)。
XLLアドインで作成した自作の関数は、EXCELの組み込み関数として登録出来るので、関数の挿入ダイアログを開くとその中に自作関数を出す事も出来る。
上図には自作関数は出ていないが、もしXLLアドインを読み込んで自作の関数が追加されるとこのメニューの中に出て来る。
関数の分類の部分には、自分で好きな名前を付けられるので「自作関数群」とでも名付けておいて、それを選択すると自作関数の関数名が、
MyFunc1
MyFunc2
のようにリストに出て来る。
それを選択すると普通にセル関数として
= MyFunc1(E2:F6)
のように利用出来る。
これらの関数は、マルチスレッド版の関数として作成するなども可能なので、一つのシートの多数のセルにMyFunc1()入れて計算する場合には並列計算が出来る。
XLLアドインの開発に関しては、ネットを検索してもXLL開発に関する情報は皆無なので、自力で開発するのは非常に難しい。
手順としてはEXCEL XLL SDKというのがマイクロソフトのサイトにあるので、それをダウンロードするとXLLアドインのC++サンプルプログラムが入っている。
Excel 2013 XLL SDK をダウンロードする(マイクロソフトのサイト)
それを応用すれば原理的にはXLLアドインの作成は出来るのだが、そのサンプルはあくまで最低限の簡単な幾つかのサンプルしか入っていないので、それだけでは高機能なXLLアドインを作成する事は困難だろう。
XLLアドインに関する情報源としては、同じくマイクロソフトのサイト
Excel XLL SDK – MSDN – Microsoft
にXLLアドインに関する詳細な仕様が公開されているのでそれをよく読めば必要な情報は得られる。
サンプルコードも多いので大変参考になるサイトではあるのだが、いきなりC++のプログラムが出て来るのと、その中で使われているXLL固有の関数が難解でなかなかすんなりとは理解するのは難しい。
例えば、こんな感じ。
short WINAPI xlSheetIdExample(void) { XLOPER12 xSheetName, xRes; xSheetName.xltype = xltypeStr; xSheetName.val.str = L"\022[BOOK1.XLSX]Sheet1"; Excel12(xlSheetId, &xRes, 1, (LPXLOPER12)&xSheetName); Excel12f(xlcAlert, 0, 1, TempNum12(xRes.val.mref.idSheet)); Excel12(xlFree, 0, 1, (LPXLOPER12)&xRes); return 1; }
この関数では、BOOK1.XLSXのSheet1に関連付けられているSheetIDという整数値を取得して表示する例だが、なんか良く分からないExcel12()という関数があるし、さらにfが付いたExcel12f()なんて言うのもあるし、ワテも最初はもう訳分からないと言う感じだった。
その上、LPXLOPER12と言う型にキャスト(LPXLOPER12)している部分もあるが、XLLアドインではこのような普段見慣れぬ変数型が沢山登場する。
なので、このあたりのサンプルを必死で解読すればXLLを作れるようになる。
OFFICEアドインとの関連
OFFICEアドインと言う場合には、マイクロソフトオフィスに含まれる以下の製品
- Excel
- Word
- PowerPoint
- Access
- InfoPath
- Outlook
- Publisher
- Visio
の各製品に対して、VBAやCOMアドインで作ったものを指す。
EXCELに限っては、VBA、COMアドインに加えてXLLアドインがあるのだ。
ワテの場合、Word, PowerPointに関してもCOMアドインを作った経験があるのだが、それ以外のMS製品に関してはあまり詳しくない。
またVBAはEXCELとACCESSでしか使った事が無い。なので、それ以外のMS製品に関してのVBAやCOMアドインに関してはこれ以上の詳しい説明は出来ない。
でも、まあ、対象となる製品が違っても、COMアドインを開発するのはVisual Studioなので、基本的な部分は同じだ。
以下では、各種のアドイン開発手法を解説したい。
EXCELアドインを作る(VBAでxlsmマクロ有効ブック)
本記事に興味を持って訪問して頂いた皆さんであれば、当然VBAでの開発経験はお持ちだと思うので、VBAに関してはあまり詳しく説明する必要は無いと思う。なので簡単に説明すると、EXCELのVBAでユーザーフォームUserFormや標準モジュールModuleなどを組み合わせて作り、拡張子xlsmで保存したものがxlsmマクロ有効ブックだ。xlsmはアドインとは呼ばないのかな?あまり聞いた事が無い。マクロ有効ブックがxlsmの正式名称だ。
EXCELのVBAプログラミングと言うと、通常はこのxlsmマクロ有効ブックの開発を指す場合が多い。
EXCELアドインを作る(VBAでxlamアドイン)
xlsmマクロ有効ブックを拡張子xlamでファイル保存するとxlamアドインに化ける。
なので、xlamアドインを作るのはとても簡単である。xlsmを作るのと同じ感覚で作ればよい。
両者の違いは、上の章でも述べたように、
xlsm Workbook, Sheetが表示
xlam Workbook, Sheetが非表示
の違いだ(マイクロソフトの厳密な定義ではなくてあくまでワテの理解)。
xlamアドインを作成して、
C:\Users\wate\AppData\Roaming\Microsoft\AddIns
フォルダに保管しておくと、EXCELを起動する度にそのxlamアドインがEXCELに読み込まれる。
xlamの実体はVBAプログラムなので、C#, C++で作成するCOMやXLLアドインと比較して速度的には遅い。
EXCELアドインを作る(C#でCOMアドイン)
EXCELの機能を強化する目的や、独自のユーザーインターフェースを追加する目的では、VBAでも殆ど全ての事が可能だと思う。
実際、今の世の中でもVBAでプログラミングをしている人は多い。
VBAは古くからある技術なので広く使われている技術ではあるが、COMアドインなどと比較するとやはり古臭い側面が目立つ。
具体的には、
- 開発環境のVBAエディタがもう何年(何十年?)も変化していないので高性能なVisual Studioの開発環境と比較すると貧弱。
- 例えばユーザーフォームを作る場合にはVBAで作るよりもC#でCOMアドインを作るほうが沢山の部品が利用できる。
- その名の通りBasic言語がベースであり、利用者の多いC, C++, C#などのC言語的文法に慣れた人には馴染みにくい面もある。
などか。
COMアドインを作るために必要な物は以下の通り。
注意 追記(2017/4/1)
最新のVisual Studio 2017 Community の場合には、インストール時に全部の機能を選択するとOffice COMアドイン(新規プロジェクト作成のメニューではVSTOアドインと表示されるがどちらも同じ物)を作成する為に必要なツールも入る。
もしHDD容量が少ない場合には、インストールする機能を選択出来るので、その中からVSTOアドイン作成機能を選択すれば良いと思う。
なので、↓で説明しているOffice Developer ToolsのインストールはVS2017では不要である。
無料のVisual Studio 2015 Community(Office COMアドイン開発機能は入っていない)
この二つだ。
上記のリンク先から、
OfficeToolsForVS2015.exe
をダウンロードしてVisual StudioにインストールすればOffice COMアドインの開発環境が整う。これも無料だ。
以下のその手順。
特に難しい事はなくてインストーラを実行するだけで良い。
無事にインストールが成功すると下図が出る。
インストールが完了すると下図のようにVisual Studio 2015 Community の中に Office/SharePointというメニューが追加されて、その中にVSTOアドインと言うカテゴリーなどが追加される。
この中のEXCELアドインのテンプレートを使うと簡単にEXCEL COMアドインが作成出来る。
- EXCELに自作のリボンを追加する
- 独自のユーザーインターフェースを追加する
- ユーザー定義関数を作成する
などを非常に簡単に行う事が出来る。
使用する言語はC#またはVB.NETであるので、VBAをやっている人であれば少し勉強してVB.NETを覚えれば比較的スムーズにVB.NETを使ったCOMアドイン開発をマスターする事が出来るだろう。
COMアドイン開発の詳細な説明は、別の記事で紹介したい。
ちなみに、VSTOはVisual Studio Tools for Officeの略で、Visual StudioでOfficeアドインを作成するための追加機能だ。
確かVisual Studio 2003の頃には既にVSTOは有ったと思うが、有料だったと思う。でもそれを買わなくても確かVisual Studio 2003 Professionalの機能だけでCOMアドインを作成する事も出来た。なので、有料版のVSTOがどういう追加機能を持っていたのかは定かではない。またワテの記憶が曖昧な部分もあるので間違っているかもしれない。
いずれにせよ本日(2016年7月8日)の時点では、無料のVisual Studio 2015 Communityに無料のOffice Developer Toolsを組み合わせれば無料でCOMアドイン開発が出来ると言う事だけを覚えておけば良いだろう。
余談
ちなみに、アドインエクスプレスなんて言う会社もある。
エクセルやオフィス関連のアドイン開発を効率よく行う事が出来るツールを開発・販売している会社だ。
Visual Studio にこの会社の製品をインストールすると、より効率よくアドイン開発が出来るらしい。
あくまでワテの理解では、この製品で作れるのはCOMアドインだ。
ユーザーインターフェース作成に利用できる部品の種類が増えるなどの特徴があるようだ。
また、リボンのデザインや開発もより簡単に出来るらしい。
なお、ワテ自身この製品を使っていないのでこの理解は間違っているかもしれない。
ManagedとUnmanaged
さて、C#、VB.NETあるいは.NET Framework関連でネット検索していると、
- Managed マネージド
- Unmanaged アンマネージド
と言う用語が良く登場する。
当初は、ワテはなんのこっちゃサッパリ分からなかった。
でも簡単だ。
Managed(マネージド)とは、ドットネットフレームワークを使って開発したプログラムを指す。つまり、C#, VB.NET言語を使って開発したEXEやDLLなどを指す。
なぜそれをマネージドと言うのかに関しては、DotNET Frameworkという機能によってアプリケーションの実行やメモリなどが管理(マネージメント)されていると言う理解で良いだろう(ワテの理解)。
一方、C++などの従来からある言語では、DotNET Frameworkの手法は使っていないのでUnmanaged(アンマネージド)と呼ばれる。
あるいはNative(ネイティブ)などと呼ばれる場合もある。
ちなみにVisual Studioにはマネージドもアンマネージドもミックスして開発出来る
C++/CLI
という驚くべき開発環境がある(Visual Studio Community には無いようだ。有料版にはある)。
このC++/CLIのテンプレートを使ってプロジェクトを開発すると、例えばユーザーインターフェースはC#のWinFormを使って作成し、C++で作成した関数を実行するなどという或る意味超変則的な開発が可能だ。
トラッキングハンドルとか言う山型の記号^が登場するなど、とってもややこしい言語だ。
EXCELアドインを作る(C#でEXCEL-DNA)
エクセルのアドインで検索していると、EXCEL DNAなどというヘンテコな名前を見た事もある人もいるかも知れない。
DNA 遺伝子か?
何のこっちゃ分からん。ワテの第一印象だ。
ネットを検索してもEXCEL DNAに関する情報は少ない。
公式サイトはここだ。
オープンソースのプロジェクトで、govert と言う人が一人で作っているようだ。
日本語で解説しているサイトも僅かながらあるので、それらを参考にワテもEXCEL DNAを試した経験がある。
EXCEL DNAとは何か?
以下、あくまでワテの理解なので間違っているかもしれない。興味ある人はご自身でも確認して下さい。
What and why? – An introduction to .NET and Excel-DNA
Excel-DNA のDNAの意味は上の英文サイトにも書かれているが、DotNet for Applicationsの略だ。
それは、Visual Basic for ApplicationsをVBAと略すのに相当する。
EXCEL DNAの目的
EXCELのアドイン開発ではXLLアドインが速度的にも最も良いのだが、XLLアドイン開発はC/C++言語を使い、かつ、XLLアドイン開発向けに公開されている各種の関数群(API)を組み合わせて作る。しかし、それは非常にハードルが高いのだ。その一因に、XLLアドイン開発に関する書籍が少ない事が挙げられる。
なので、XLLアドインを簡単に開発できるようにするツールや手法を独自に開発している人やグループは幾つかある。
その一つがEXCEL DNAだ。
EXCEL-DNAの場合には、開発はVisual StudioのC#あるいはVB.NETを使ってCOMアドインを作成する感じでXLL開発を行う事が可能だ。基本的な処理は全てC#を使って記述し、EXCELと連携する部分(つまりXLLとして機能させるための部分)はEXCEL-DNAが上手い具合にやってくれて、拡張子XLLのファイルが生成出来る。あるいはDLLのままEXCELから実行する手法もある。
なので、発想としてはそれほど目新しくは無いのだが、それを実際に実装してしまう所が凄いと思う。
何故なら、C#で作ったDLLをXLLアドインとして動かす訳だから、結局XLLの仕組みを完璧に理解していないとそんなものは作れないのだ。
特にEXCEL.EXEがXLLアドインを読み込んで実行したり、逆に解放する辺りの処理は、ワテもEXCEL SDK付属のサンプルプログラムで勉強したが、とてもややこしい。ワテの場合はサンプルコードをそのまま流用して使っていたのだが、どうやればC#で作成したDLLをXLL化してEXCEL.EXEで利用できるように出来るんだろう。良くそんなややこしいプログラムを書けるもんだなあと感心する。
なので作者の人は、XLLの達人と言っても良いだろう。
主な無料のXLL開発ツール
さて、EXCEL-DNA以外には、ワテが知っているXLL開発ツールは以下の通り。
- XLW – A Wrapper for the Excel API
- PyXLL: Excel functions written in Python
- xll add-ins
- XLL Plus – Planatech (有料製品)
pythonでXLLを書くためのwrapperまである。世の中、いろんな事を考える人がいるようだ。
ちなみに、ワテはEXCEL-DNA以外にもXLW、xll add-insも試したことがある。
あくまでワテの感想であるが、ワテが試した
- EXCEL-DNA
- XLW
- xll add-ins
はいろいろ勉強にはなったが、どれも何か不満が残る感じ。
結局、XLLのラッパーなので、そういうのを介してXLLを実行するわけだから、ネイティブのXLLに比較すると動きが遅いのだ。まあ、遅いと言ってもVBAよりはかなり速いが。例えば、セルのデータを大量に読み取って処理してセルに書き出すなどの重い処理をするとやはりXLLが最速だった。
あるいは、これらのラッパーは利用者も少なくネットでも参考となる情報が少ないとか、あるいは完成度が高くないものもあり原因不明のエラーに悩まされても解決方法が分からないなど、何かと問題が多かったように思う。
と言う事で、ワテの場合には、どうせ情報が少ないんならマイクロソフト純正のXLLをゼロから開発するのが最も確実だろうと思って、XLL開発の手法を勉強する事にした。
EXCELアドインを作る(C++でXLLアドイン)
EXCEL SDKをダウンロードするとその中にXLLのサンプルがあるので、まずはそれをVisual Studioに読み込んで、必要なライブラリやヘッダーファイルなどのパスを通して、ビルドしてエラー無くコンパイルする作業から始めた。
EXCEL2013用のサンプルCプログラムとはいえ、実際はEXCECL2007 SDKでも全く同じサンプルが入っていたと思うので、サンプル自体が古いのだ。
なので最新版のVisual Studio2013や2015でビルドしようとしても、すんなりとは行かなかった記憶がある。
この辺りから、すでにXLL開発はハードルが高いという印象を受ける人も多いと思う。
無事にビルドに成功して生成されたXLLをEXCELがうまく読み込んでくれて、サンプルのプログラムを動かすだけでも一苦労した。
でも一旦コツを掴むと、あとはそのサンプルをいろいろと書き換えて実験をしてXLLがどんな風に動作するのか試しながら学習していった。
ただしXLLに関する情報はネットを検索しても非常に少ない。
日本語に関するサイトでは、確か幾つか見つかると思うが、そのどれもがEXCEL SDKのサンプルを実行したと言う程度だったと思う。
本格的なXLL開発について解説してあるようなサイトは日本語はおろか英語のサイトでも見た記憶が無い。
一方、XLLに関する書籍も少ない。VBAの教科書なら腐るほど出ていると言うのに。
ワテが知る限り、XLLアドイン開発に関する唯一の書籍はこれだ。
検索すると同じタイトルで表紙の色違いがヒットすると思うが、それは1st Editionと2nd Editionの違いだ。なので上記の2nd Edition版がお勧めだ。
この本には大変お世話になった。
マイクロソフトの上記のサイトに書いてある各種のサンプルコードの意味は当初はチンプンカンプンだったのだが、この本に書いてある内容を理解すると、徐々にマイクロソフトのサイトの説明も理解出来るようになった。
この本は、XLLに関する事は全て網羅していると言っても良いくらい、包括的かつ実践的な非常に良い本だと思う。
付属のCD-ROMに各種の実用的なサンプルが入っているのでとても役立つ。
XLLで何が出来るのか?
XLLを使うと、EXCELがやっていることは全て出来るというのが正しいと思う。
つまり、
- 組込み関数も作れる
- 対話型のダイアログ(ユーザーインターフェース)も作れる
- リボンの上に各種アイコンを配置してユーザーインターフェースも作れる(確か作れたと思うが、要確認)
などなど
なので、あくまでワテの推測だが、マイクロソフトでEXCELを開発している人たちはこのXLLを使ってEXCELの新機能を作り込んでいるのではないかと思う。
だから、一般の人もXLLを使えばマイクロソフトの人がやっているのと同じかそれ以上の凄い機能をEXCELに組み込む事が出来る。
XLLは速度が速い
速度的にもVBAやCOMアドインに比べて、はるかに速い。
COMアドインとXLLアドインで処理速度を比較した場合には、いろんな状況や条件が考えられるので一概には断定出来ないが、ワテが少し試した限りでは、
例えば大量のセルを選択して(例えば数万行x数百列)、その中の全データを二次元配列に読み取るなどの場合には、やはりXLLアドインのほうが速かった。
その理由は、COMアドインの場合にはCOMサーバーであるEXCEL.EXEとCOMクライアントである自作のDLLがCOMの仕様に従って通信するのだが、このCOMの手法は速度的には遅いようだ。ただしあくまで、ワテの実験では。
一方、XLLの実体はC++でビルドしたネイティブなDLL(.NET FrameworkのDLLではないという意味でネイティブ)なので、それは普通にEXEとDLLの関係となるので、この両者が連携して動く場合には速度的には速い事は容易に想像できる。
そういう点で、やはりEXCELのアドインを開発するならXLLアドインがお勧めだ。
XLLアドインの中でCOMも使える
なお、C/C++で作成するXLLアドインの中でCOMの手法もミックスさせる事が可能だ。
つまりEXCELの各種の操作をXLL SDKの関数で行っても良いし、EXCELをCOMサーバー、アドインをCOMクライアントとして、OLEの手法でEXCELを操作しても良い。
例えばどう言う時にそんな事をする必要があるかと言うと、XLL SDKのAPIでは低レベルなコマンド群が用意されている。具体的には、セルの値を読み取るとか書き込むとか。その他色々あるが、比較的低レベルなコマンド群が多い。
でも例えば、ブックを上書き保存するとか、ブックを別名で保存するなどの上位レベルの処理は、COMの手法でやるほうが手っ取り早い。XLL SDKでブックの上書き保存が出来るのかどうかは、今すぐには思い出せない。ちょっと忘れた。
逆に、セルに値を代入したり読み出したりする操作もCOMでやっても良い。
その場合には、速度的にはXLL SDK APIよりも若干遅いので大量のセルを操作する場合にはXLL SDK APIが向いている。
そう言う感じで、速度優先処理ならXLL SDKのAPIで行う。多少遅くても良いので手っ取り早く便利な関数を使いたいならCOMでやると言う開発スタイルが現実的だと思う。
XLLでCOMを使うやり方
このマイクロソフトのサイトに以下の技術文書がある(現在はリンク切れ)。
[HOWTO] Visual C++ を使用してオートメーションで文書プロパティにアクセスする方法
この資料では、Microsoft Visual C++ で Microsoft Word のオートメーションを使用して、文書プロパティの取得と操作を行う方法について説明します。この資料のサンプルは、特に Word のオートメーションを行うように記述されていますが、同じ考え方を Microsoft Excel や Microsoft PowerPoint にも同様に適用できます。
引用元 https://support.microsoft.com/ja-jp/help/238393/how-to-use-visual-c-to-access-documentproperties-with-automation
ここに書いてあるように、この手法はEXCELにも利用出来るので、下に引用する AutoWrap() と言う関数を自分のXLLのCソースコードの中に組み込めば良い。
#include "stdafx.h" #include <ole2.h> // // AutoWrap() - Automation helper function... // HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...) { // Begin variable-argument list... va_list marker; va_start(marker, cArgs); if(!pDisp) { MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error", 0x10010); _exit(0); } // Variables used... DISPPARAMS dp = { NULL, NULL, 0, 0 }; DISPID dispidNamed = DISPID_PROPERTYPUT; DISPID dispID; HRESULT hr; char buf[200]; char szName[200]; // Convert down to ANSI WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL); // Get DISPID for name passed... hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID); if(FAILED(hr)) { sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err0x%08lx", szName, hr); MessageBox(NULL, buf, "AutoWrap()", 0x10010); _exit(0); return hr; } // Allocate memory for arguments... VARIANT *pArgs = new VARIANT[cArgs+1]; // Extract arguments... for(int i=0; i<cArgs; i++) { pArgs[i] = va_arg(marker, VARIANT); } // Build DISPPARAMS dp.cArgs = cArgs; dp.rgvarg = pArgs; // Handle special-case for property-puts! if(autoType & DISPATCH_PROPERTYPUT) { dp.cNamedArgs = 1; dp.rgdispidNamedArgs = &dispidNamed; } // Make the call! hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL); if(FAILED(hr)) { sprintf(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx", szName, dispID, hr); MessageBox(NULL, buf, "AutoWrap()", 0x10010); _exit(0); return hr; } // End variable-argument section... va_end(marker); delete [] pArgs; return hr; }
引用元 https://support.microsoft.com/ja-jp/help/238393/how-to-use-visual-c-to-access-documentproperties-with-automation(リンク切れ)
現在はリンク切れだが、関数名 AutoWrap() などで検索すれば関連情報は沢山あるので、そう言うのを地道に調べればこの辺りの知識は独学でもマスター出来る。実際、ワテはそれをやった。
なお、この関数を使う場合には、同じく上のサンプルコードの続きの部分を以下に引用するが、
int main(int argc, char* argv[]) { // Initialize COM for this thread... CoInitialize(NULL); // Get CLSID for Word.Application... CLSID clsid; HRESULT hr = CLSIDFromProgID(L"Word.Application", &clsid); if(FAILED(hr)) { ::MessageBox(NULL, "CLSIDFromProgID() failed", "Error", 0x10010); return -1; }
この例ではmain関数の中でやっている CoInitialize(NULL); などのCOM初期化処理をXLLの中で行う事になる。
具体的にどんな感じでやれば良いのかは、過去に作成したXLLプロジェクトを見れば分るのだが、今は忘れた。
もし興味ある人はコメント欄から問い合わせて頂ければ、昔のコードを見てみて、その辺りの手法を解説しても良いですが(ただし、気分が乗れば)。
ちなみに、このAutoWrap() 関数は有名な関数のようで、C/C++でCOMを使ってOLEの手法でEXCELやWORDなどを操作する場合には、ネット上のサンプルなどでも良く見かける。
なお、ワテの場合、AutoWrap() 関数の中身の細かい意味は理解出来ていない。分ってるのはAutoWrap() をコールすればXLLの中からCOM方式でEXCELに対して各種のコマンドを実行出来ると言う点だけ。まあ、それでも十分かな。XLLを作る場合には。
ただし、AutoWrap() 関数を使うには、関数に渡す引数の意味を理解しないといけない。それはかなり苦労したが、分かってしまうと簡単だ。
XLLの古臭い側面
なお、XLLアドインの手法は古くからあるので、その古さを引きずっているマイナスの側面もあるように思う。
ダイアログの作成が非常に煩わしい
例えば対話型のユーザーインターフェースを作成する事は可能なのだが、VBAやC#で作る場合なら、ユーザーフォームを一個作成して、その上に自分の好きな部品(ボタン、テキストボックス、その他)を自由に配置して大きさや位置もマウス操作で自由に決められる。
各部品に対するコールバック関数も、部品をダブルクリックするとエディタが自動的にコールバック関数のテンプレートを追加してくれる機能が普通に使えるが、C++の開発環境に於いて、XLLでダイアログを作る場合にはそうは行かない。
ダイアログのデザインは非常に特殊な手法が必要で、ダイアログの部品に関する情報を配列データに入れておいて、ダイアログを登録する関数を実行する必要がある。
EXCEL SDKの中に入っているダイアログ定義の配列の例を以下に引用してみた。
#define g_rgDialogRows 16 #define g_rgDialogCols 7 static LPWSTR g_rgDialog[g_rgDialogRows][g_rgDialogCols] = { {L"\000", L"\000", L"\000", L"\003494", L"\003210", L"\025Generic Sample Dialog", L"\000"}, {L"\0011", L"\003330", L"\003174", L"\00288", L"\000", L"\002OK", L"\000"}, {L"\0012", L"\003225", L"\003174", L"\00288", L"\000", L"\006Cancel", L"\000"}, {L"\0015", L"\00219", L"\00211", L"\000", L"\000", L"\006&Name:", L"\000"}, {L"\0016", L"\00219", L"\00229", L"\003251", L"\000", L"\000", L"\000"}, {L"\00214", L"\003305", L"\00215", L"\003154", L"\00273", L"\010&College", L"\000"}, {L"\00211", L"\000", L"\000", L"\000", L"\000", L"\000", L"\0011"}, {L"\00212", L"\000", L"\000", L"\000", L"\000", L"\010&Harvard", L"\0011"}, {L"\00212", L"\000", L"\000", L"\000", L"\000", L"\006&Other", L"\000"}, {L"\0015", L"\00219", L"\00250", L"\000", L"\000", L"\013&Reference:", L"\000"}, {L"\00210", L"\00219", L"\00267", L"\003253", L"\000", L"\000", L"\000"}, {L"\00214", L"\003209", L"\00293", L"\003250", L"\00263", L"\017&Qualifications", L"\000"}, {L"\00213", L"\000", L"\000", L"\000", L"\000", L"\010&BA / BS", L"\0011"}, {L"\00213", L"\000", L"\000", L"\000", L"\000", L"\010&MA / MS", L"\0011"}, {L"\00213", L"\000", L"\000", L"\000", L"\000", L"\021&PhD / Other Grad", L"\0010"}, {L"\00215", L"\00219", L"\00299", L"\003160", L"\00296", L"\015GENERIC_List1", L"\0011"}, };
う~ん、初めて見る人にはさっぱり分からないと思う。
このデータをもとに作成されるダイアログは以下の通り。
たかがこの程度のダイアログを作るのにこんなにややこしいデータを準備しなくてはならないのかよ!という人も多いだろう。
それよりも、上記の多数の数値のどこにラジオボタンやチェックボックスの定義が入っているんだよ?と言う疑問も湧くに違いない。それが入っているんです。
あるいは、自作の組み込み関数を作る例として、同じくEXCEL SDKの中のサンプル関数のソースコード例を引用してみた。
///*************************************************************************** // FuncSum() // // Purpose: // // This is a typical user-defined function provided by an XLL. This // function takes 1-29 arguments and computes their sum. Each argument // can be a single number, a range, or an array. // // Parameters: // // LPXLOPER12 ... 1 to 29 arguments // (can be references or values) // // Returns: // // LPXLOPER12 The sum of the arguments // or #VALUE! if there are // non-numerics in the supplied // argument list or in an cell in a // range or element in an array // // Comments: // // History: Date Author Reason ///*************************************************************************** __declspec(dllexport) LPXLOPER12 WINAPI FuncSum( LPXLOPER12 px1, LPXLOPER12 px2, LPXLOPER12 px3, LPXLOPER12 px4, LPXLOPER12 px5, LPXLOPER12 px6, LPXLOPER12 px7, LPXLOPER12 px8, LPXLOPER12 px9, LPXLOPER12 px10, LPXLOPER12 px11, LPXLOPER12 px12, LPXLOPER12 px13, LPXLOPER12 px14, LPXLOPER12 px15, LPXLOPER12 px16, LPXLOPER12 px17, LPXLOPER12 px18, LPXLOPER12 px19, LPXLOPER12 px20, LPXLOPER12 px21, LPXLOPER12 px22, LPXLOPER12 px23, LPXLOPER12 px24, LPXLOPER12 px25, LPXLOPER12 px26, LPXLOPER12 px27, LPXLOPER12 px28, LPXLOPER12 px29) { static XLOPER12 xResult;// Return value double d = 0; // Accumulate result int iArg; // The argument being processed LPXLOPER12 FAR *ppxArg; // Pointer to the argument being processed XLOPER12 xMulti; // Argument coerced to xltypeMulti __int64 i; // Row and column counters for arrays LPXLOPER12 px; // Pointer into array int error = -1; // -1 if no error; error code otherwise // // This block accumulates the arguments passed in. Because FuncSum is a // Pascal function, the arguments will be evaluated right to left. For // each argument, this code checks the type of the argument and then does // things necessary to accumulate that argument type. Unless the caller // actually specified all 29 arguments, there will be some missing // arguments. The first case handles this. The second case handles // arguments that are numbers. This case just adds the number to the // accumulator. The third case handles references or arrays. It coerces // references to an array. It then loops through the array, switching on // XLOPER12 types and adding each number to the accumulator. The fourth // case handles being passed an error. If this happens, the error is // stored in error. The fifth and default case handles being passed // something odd, in which case an error is set. Finally, a check is made // to see if error was ever set. If so, an error of the same type is // returned; if not, the accumulator value is returned. // for (iArg = 0; iArg < 29; iArg++) { ppxArg = &px1 + iArg; switch ((*ppxArg)->xltype) { case xltypeMissing: break; case xltypeNum: d += (*ppxArg)->val.num; break; case xltypeRef: case xltypeSRef: case xltypeMulti: if (xlretUncalced == Excel12f(xlCoerce, &xMulti, 2, (LPXLOPER12)*ppxArg, TempInt12(xltypeMulti))) { // // That coerce might have failed due to an // uncalced cell, in which case, we need to // return immediately. Microsoft Excel will // call us again in a moment after that cell // has been calced. // return 0; } for (i = 0; i < (xMulti.val.array.rows * xMulti.val.array.columns); i++) { // obtain a pointer to the current item // px = xMulti.val.array.lparray + i; // switch on XLOPER12 type // switch (px->xltype) { // if a num accumulate it // case xltypeNum: d += px->val.num; break; // if an error store in error // case xltypeErr: error = px->val.err; break; // if missing do nothing // case xltypeNil: break; // if anything else set error // default: error = xlerrValue; break; } } // free the returned array // Excel12f(xlFree, 0, 1, (LPXLOPER12)&xMulti); break; case xltypeErr: error = (*ppxArg)->val.err; break; default: error = xlerrValue; break; } } if (error != -1) { xResult.xltype = xltypeErr; xResult.val.err = error; } else { xResult.xltype = xltypeNum; xResult.val.num = d; } //Word of caution - returning static XLOPERs/XLOPER12s is not thread safe //for UDFs declared as thread safe, use alternate memory allocation mechanisms return(LPXLOPER12)&xResult; }
この関数はXLLとしてEXCELに読み込まれると、組込み関数としてリストに出てきて普通のEXCEL関数のように利用出来る。
A1セルに
=FuncSum()を入れて、
範囲を指定すると、その範囲の数字を合計してA1セルに代入する関数だ。
まあ、かなり特殊な感じの記述なので慣れるまでは苦労するが、慣れてしまえば簡単だ。
処理内容はいつもVBAでやっているのと同じで、EXCELのセルのデータを足したり引いたりという感じなので、それをC++を使ってXLLの各種API関数を使って書けば良いだけだ。
是非、皆さんも挑戦して頂きたい。
まとめ
EXCELアドインの各種の開発手法を説明した。
- VBAによるxlamアドイン
- C#, VB.NETによるCOMアドイン
- Visual Studio C++によるXLLアドイン
などの手法がある。
VBAを使いこなせる人は、VB.NETによるCOMアドインを作ってみると良いだろう。
Visual Studioの操作方法は確かに難しい。
とは言っても、それは高性能ゆえにメニューやツールが豊富で、どれを使ったら何が出来るのか良く分からないというだけであり、単なる知識の問題だ。
操作方法さえ覚えてしまえば、あとは、プログラミングに専念すれば良い。VBAが出来る人ならVB.NETは比較的スムーズに習得できるだろう。
その時には、Visual Studioの豊富なメニューやツール群は逆に心強い開発手段となる。もうVBAの貧相な環境には戻りたくないと感じる人もいるかも知れない。
XLL開発の本を読む
VBAの本は沢山ある。
XLLアドイン開発に挑戦したい人はこの本がお勧めというか、この本しか無い。
安くは無いが、この本を一冊持っておけばXLL開発は完璧だろう。Financial Applicationsというタイトルが付いているので、何か金融関係の難しい理論とかややこしい計算式など出て来るのかなと思ったが、内容的にはXLLの基本的な解説や作成方法など一般向けの内容だ。VBAやCOMアドインの説明もあり、それらの違いなども詳しく説明されている。
要するに金融分野ではEXCELでいろんな計算をするので、XLLを使ってそれらの計算式を組み込んだ独自のユーザー定義関数を作るなどが一般的に行われているようである。証券取引ではミリ秒のオーダーで売り買いがされているから、高速な計算をXLLでやる必要があるのではないかとワテは思う(ワテの推測)。著者はそういう金融分野の経験が長く、そういう背景でFinancial Applicationsと名前が付いているようだ。あくまでワテの理解である。
分厚い本であり、XLLの作成に関する詳細な説明が各種のサンプルコードを交えて詳細に解説されている。ワテの場合、この本が無ければXLL学習を挫折していたと思う。
付属のCD-ROMのサンプルプログラムがとても役に立つ。
EXCEL関連書籍の売れ筋ランキング
を見る。
エクセルXLLアドイン開発の解説記事 – 具体的手順
エクセルのアドインを作るなら高速動作するXLLがお勧めだ。
ネット検索しても、ここまで詳しく解説した記事は、なかなか見付からないと思う。
コメント
ワテさん。
visualstudioでxllを作ろうとしても、ビルドでプロジェクト・プロパティで出力の種類に「クラスライブラリ」を指定してもdllは作ることができますがxllを作ることができません。
あちこち彷徨ってみたのですが、記事にもある通りどうやって作るのか???は「どこにも、何も書いていない」ような感じです。
道しるべすら無いみたいです???
ということで、visualstudioからxllをビルドするための道筋を示しているようなページをご存知でしたら教えていただけますか?
おいおいさん
コメントありがとうございました。
Visual StudioでXLLを作る手順は以下の記事に書いています。
【ワレコVisual Studio講座】Excel XLLアドイン開発 SDKをビルド【1/10】
https://www.wareko.jp/blog/post-25030
全10回シリーズの予定ですが、今のところ第一回のみです。
その第一回目ではEXCEL SDKの中にあるサンプルXLLをVisual Studio 2017でビルドする手順を紹介しています。
参考にして下さい。