裏技的な非公開ライブラリ「WizHook」はかなりオススメ(ただし自己責任)

平成最後の朝、枕元で聞こえた言葉。

「Wizhookで一ネタ書けば?」

だれが言ったかわかりませんが、GWアドベントカレンダーの4日目(2019/4/30)のネタはコレで行こう、と決めたのですが、当日書き始めたところ、結構深い内容であることに気付き、きちんと時間を取って書き記しておいたほうが自分の備忘録としても有用だと思って、アドベントカレンダーの方には軽く「いい感じで使えるよ」程度で書いたのですが*1、今回きちんとまとめてみます。

Wizhook is なに?

Microsoft Access(2000以降)で使うことができる、非公開のクラスです。非公開なので当然公式サイトでの紹介もされていませんし、リファレンスのドキュメントも公式には存在していません。ただし、「非公式なドキュメント」はユーザー有志によって数多く公開されており*2、このブログで使い方の詳細をまとめるのも気が引けるほど有名な裏技のようです*3

なぜ非公開なのか。Microsoftが情報を公開していないのでわかりませんが、実際に少しだけですが使ってみて、「なんか違和感がある」んです。普段使うクラスメソッドとかと違う、というか。

書き方が。

引数の並びが。

なんかちがう。

社内用とか、ネット情報の受け売りですがAccessのウィザード機能で使っているのではないかとか*4、いろいろと説はありますが、まぁ非公開ですから(ry。

(余談)非公開の項目を見ることができる箇所が1箇所だけある

VBE(VBA Editor)を開いて、オブジェクトブラウザーを開きます*5。オブジェクトブラウザー内の任意の場所で右クリックすると、その中に「非表示のメンバーを表示(H)」という項目があるのでクリック、チェックが入っている状態にします。この状態で左上検索窓に「Wizhook」と入力して検索をすれば、普段見慣れないメソッドが大量に表示されます*6。オブジェクトブラウザーでクラスなどを選択すると、ブラウザーの下に説明が表示されるので、使いにくいですがこれでリファレンスにはなるでしょうね。

なぜWizhookを採用したのか

きっかけ

きっかけはファイルのエクスポートです。Accessなんでよくある要望です。「リストで表示したヤツをExcelにちゃちゃっと出してもらってもいいかなあ?」的な要望です。エクスポート自体はそれほど難しくないコードです。

DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "エクスポート対象テーブル名", "エクスポート先のフルパス(ファイル名も)"
' TransferSpreadsheetの引数の説明は割愛します。

で、エクスポート先のフルパスをハードコーディングすれば(この引数が"FileName"です)「ファイルの名前と出力先は決まってるんで〜」と説明ができるんですが、お客様からの先程の要望に、「もちろんExcelの保存先は選べるようにしておいてね。いろいろあるんだよ*7。」というのがこっそり追加されていたようなので"FileName"も可変になるようにしなくてはいけません。

fileName = ◯◯ ' ここで保存先を選ぶなにかしらのコマンドを入れると、TransferSpreadsheetの呼び出しタイミングでダイアログが表示される
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "エクスポート対象テーブル名", fileName

こんな感じ。

定石:FileDialogを使う

ファイル選択ダイアログといえば、のコレ、です。

docs.microsoft.com

support.office.com

先の例で言えば、

Function fileSelect ()
Dim filename As Office.FileDialog

'fileName = ◯◯ ' ここで保存先を選ぶなにかしらのコマンドを入れると、TransferSpreadsheetの呼び出しタイミングでダイアログが表示される
' Set fileName = Application.FileDialog(msoFileDialogOpen)  '''こっちは現在のAccessでは未対応
Set fileDialogBox = Application.FileDialog(msoFileDialogFilePicker) '''ファイルの「参照」ダイアログが開く。これは現在のAccessでも対応しているので有効。

With fileDialogBox
If .Show Then
For Each itemSelected In .SelectedItems
fileSelect = itemSelected
Next
End If
End With
End Function

というFunctionを書いてあげて、これをボタン押下のマクロコードに仕込む。

' fileName = ◯◯ ' ここで保存先を選ぶなにかしらのコマンドを入れると、TransferSpreadsheetの呼び出しタイミングでダイアログが表示される
fileName = fileSelect() ' ので、さっきのFunctionを呼び出す
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "エクスポート対象テーブル名", fileName
'''このままではコード動きません。というか、エクスポートしたいのにファイルを選択しなくてはならないわけでコードとしてもおかしいです。マネしちゃだめよ。

先の投稿でも書いた通り、このコマンドを使うためにはどうしてもライブラリの「参照設定」が必要になってしまいます。 Office X.X Object Libraryというヤツですが、 f:id:racchie:20190530223203p:plain 通常は、これを使いましょう、ということでMicrosoft公式でも推奨しているやり方ですが、問題点として、

  • 自分が直接メンテナンスを行わない(多重下請け状態の案件などの)場合にエンドユーザーに「参照設定をしてね」と言ってやってくれるとは限らない*8
  • (繰り返しになりますが)msoFileDialogOpen、msoFileDialogSaveAsはサポートされていない。インポートのファイル取り込みであればmsoFileDialogFilePickerが使えるがエクスポートで使う想定のmsoFileDialogSaveAsが使えないのはイタイ。

という問題があります。開発をする立場としては大変困るわけです。そこで代替案としてのWizHookにたどり着いたわけです。

WizHookを使ってみる

使うときのルールとして*9、使う前後に「呼び出し」「解放」を行う必要があります。

’ 利用前
WizHook.Key = 51488399

''’何らかのWizHookのメソッドを使う

' 利用後
WizHook.Key = 0

こんな感じで。

WizHook実践(SaveAs)

WizHookに「getFileName」というメソッドがありますので、これを使います。引数についてはご自分で調べてくださいね。

WizHook.Key = 51488399 ' WizHookを有効にするおまじない
  returnValue = WizHook.getFileName( _
                0, "", strTitle, "", strFilePath, "", strFilter, _
                0, 0, 0, False)
  WizHook.Key = 0 ' WizHookを無効にする
getFileName = strFilePath

メリット/デメリット

参照設定を行わなくていい、というのは(個人的な考えですが)大きなメリットです。何度となく理由を述べているのでそれ以外のメリットやデメリットを探してみましょう。 WizHook(クラスオブジェクト)自体は他にも多数のメソッドを持っているようです。が、実務で使えるかどうか、という話になるとすべてのメソッドが使えるわけではなさそうです。 例えば、KeyboardLangIDというメソッドは、使用しているキーボードの「ロケールID」を取ってくるものですが*10、普段使いでは使わないでしょうし、 半分くらいはデバッグ用途に使うんじゃないのかと思うようなメソッドが多い気もします。 大きなデメリットとしては、このクラスオブジェクトは「非公開」であり、MSとしてのサポートも行われていないことが挙げられます。ぶっちゃけて言うなら、いつ使えなくなるかわからない(使えなくなるというアナウンスすらないだろう)、という状態なので、大変大きな危険をはらんでいる、とも言えます。もともとFileDialogのmsoFileDialogOpen/msoFileDialogSaveAsもサポート外という話でしたが、実際には使えないので、例えば「ファイル名を指定して保存(SaveAs)」によるファイルのエクスポートは他のやり方で実装することになるので、使えるサポート外のメソッドということで仕方なく使う、という状態になってしまいます。使えない、またはDeprecatedなメソッドの代替として利用するということであればそれはメリットにはなりそうですが。

最後に

ちょうどこの記事が公開される頃には、Accessアプリ開発案件が一段落して、全く別の仕事(EC-CUBEとFlutter)をメインでいじっているのだろうな、と推測をしています。Accessは作っていても使っていても面白いですし、いろんな用途があるんですが、ちょっと古臭いなぁ、と、開発をしていて思うようになりました。

Accessは古臭い?

Windows以外のOSも使う、スマホも使う、クラウドサービスだのSaaSだのを利用する頻度も高くなった今の時代、Windowsにしかインストールのできない、マクロ(またはVBA)を自分で組まなければ用を足さない、通常は購入する必要のある、というAccessというアプリ自体が今の時代にマッチしているとはちょっと言えないかな、とは感じてしまいます。ある時期、「Access Web」という、WebアプリのバックエンドDBとして使う&フロントエンドをAccessライクに作る、という機能がありましたが、時代が早すぎたのか(Access2000の時代からあったそうで...)、現在は提供されていません*11。悪く言えば「迷走」なんですが、逆に言えばAccessで実験的に搭載されていた機能が別のところで生きている、ということにもなります。

Accessを使わなくてもよい

Accessの実験的機能が別の箇所で実装されている、という意味では、その「別の箇所」を使うことはアリ、だと思っています。Sharepointとの連携を経て生まれたのだろうと推測できる「Microsoft PowerApps」はその好例です。

powerapps.microsoft.com

ちょいちょいとPowerAppsでテスト開発をしていますが、実際にはAccessの代替品というよりは全く別物と考えていいとは思っているのですが、それにしてもWebアプリをAccessExcelの知識に少し毛が生えた程度の知識レベルで作れるというのは衝撃でしかありませんでした。「Sharepointサーバの導入+公開サーバとしての設置」という結構大きな導入障壁はあるでしょうし、対応できるエンジニアの数も少ないだろうとは思いますが、今までのOffice(ExcelAccess)やMS-SQLServerのデータ資産があって、アプリをもう少し外で使えるようにしたいよね、というのであればわざわざPHPJavaでWebアプリを作らなくてもPowerAppsを使えば便利だし(開発も)安価に済むのではないかな、とは思います。

Excelとの競合

もうひとつ。これは私の持論ですが、Accessでやろうと思っていることは99%Excelで実装できると思っています。開発をする立場として、Accessを使いたいケースは、レコード数が数十万〜数百万に及ぶ場合で、数万件程度の「テーブル」であれば各々シートに展開したほうが良いですし、「ビュー(Accessで言う選択クエリ)」の自動生成/破棄という手法でExcelの肥大化は防げるわけで、昔のように数万件程度のデータをExcelに投入すると遅くなってしまうということはなくなったので*12、多少データが多くなっても軽快に動作します(ただしマシンパワーが非力だとちょっとレスポンスに難ありになってしまいますが)。

もうAccessはいいかな...

結局久しぶりにAccessをいじりながら新しい発見もあったのですが、もうAccessで開発するのは(新規の受託開発受注は)やめようかな、と思っています。Excelより高機能なのがAccess、みたいな感じでご依頼をいただいたり引き合いをいただいたりするのですが、Excelをある程度いじれる人でAccessをあまり知らないような方でこういう「誤解」をされている方が多いようで(だから外注をするのだと思うのですが)、Accessだから自由になんでもできるだろう、仕様の矛盾があってもAccessだからなんとかなるだろう(してくれるだろう)という幻想があるんじゃないのかな、と感じています。誤解を解くための行脚に出るつもりもありませんので、だったら「どうしてもAccessでやりたい」という案件*13については今後お断りする方向で考えてもいいんじゃないのかな、と思い始めています。

*1:と言うかその程度にしておかないと仕事が追いつかなかったです...。

*2:リンクは貼りません。Googleで「Wizhook」と検索すればいくらでも出てきます。

*3:ちなみにワタクシ、2019年4月の初旬くらいに知りました。

*4:個人的にはこの説はないと思っています。もし各種ウィザード機能で使うのだとしたら、なぜAccess2000以降で突然使えるようになるのかの説明ができないです。2000以前のバージョンでもウィザード機能は存在したし、内部PGでしか使わないクラスを2000のタイミングでまとめた、というのはちょっとこじつけにも感じます。

*5:メニューから「表示(V)」→「オブジェクトブラウザー(O)」、またはVBEを開いた状態でF2キーを押すと表示されます。

*6:適当に数えて50〜60くらいのメソッドといくつかの「メンバー」があります。

*7:個人情報を扱うデータだったりすると、ファイルをローカル保存禁止にしているケースもあるのでローカル固定では困る、というか手間がかかるし移動させ忘れるなどのセキュリティリスクになったりはします。

*8:マニュアルなどで対応するにしても、忘れていたりITリテラシーが低かったりすれば当然参照設定の手順は踏まないと私は思います。しかも、忘れていると途中で突然エラーになるわけですから、よくわからないクレームに発展しかねないわけです。「なんかさぁ、マシン変えたら動かなくなったんだけどどゆこと?」みたいな。

*9:ちなみに「なんでこのルールなのか」とか、数字に意味があるのか、とかいうのは私も調べてみたのですがよくわかりません。社内用という仮定で話をするのであれば、部署内等で通用する符牒的な数字であった、という可能性はあるとは思いますが...。

*10:日本語なら1041というコードが帰ってきます。

*11:Access2000の頃は「データアクセスページ」というデータをブラウザで閲覧できるようにできるフォーム/レポート的なもの、その後2010あたりで「Webデータベース」という機能でSharepointサーバのバックエンドDBとしての利用が可能になったのですが、結局次バージョンの2013でSharepoint側に吸収され、2016はもとのスタンドアロン型DBアプリ開発ツールに戻った感じです。

*12:これはOfficeのファイル形式が2007以降変わったことが影響しています。表向きはXMLファイルだと言っていますがWordとExcelのファイルはXMLファイル群を圧縮したZipファイルだったりします。シートの情報はテキスト形式のXMLファイルに格納されるので、最終的にはファイルサイズが圧倒的に小さくなる、という理屈です。

*13:と言うか、私の中で「この手の仕事は危ない」と思ういくつかの条件の1つに当てはまっているんですよね。『開発プラットフォームがあらかじめ指示されている新規開発』。そのうちこのネタでブログ投稿してみましょうかね...。