Excel VBAのコーディング事例をご紹介しています。
事例のテーマを決めて、その機能を実現するためのコーディングを動く形でお示していますが、今回も前回の「Excel VBAでOfficeが使用するTypeLibをレジストリから取得?」に引き続きコーディング事例は含まれてはいるものの、レジストリの中のCLSIDに関する話題が中心になります。
ただし前回考慮した「OfficeのCLSIDのレジストリ登録」につきましては話が長くなるので割愛していますのでお含み置きいただければ幸いです。
コーディング自体は「構造化・標準化は考慮しつつも極力コーディング量は減らし、その上で可読性を維持する」という思いは持ちつつも「テーマに沿って動くことを優先して実装」しています。
32bit版Excel 2016の バージョン2203(ビルド 15028.20160)を使用して検証しています。
COM、COMコンポーネント(オブジェクト)、インターフェースとCOMクラス、クラスID
何だか呪文のような章タイトルではありますが、その個々の内容を順番にご紹介いたします。
COM(Component Object Model)につきましては前回のTypeLibの説明の際にも少し触れていますが、ここで再度ご説明致します。
COMとは
COMとは「実行時に対話する再利用可能なソフトウェア ライブラリを作成するためのバイナリ相互運用性標準」と下記のMicrosoft Docsのページの「COM の技術概要」で説明されています。
ただこれでは何を言っているのか?英語の日本語訳だけに理解し難いのですが、その後に書かれている内容からポイントを簡単にまとめると下記のような文脈になる認識です。
- 「再利用可能なソフトウェア ライブラリ」
- 再利用ができるソフトウェアの集まりで個々のソフトウェアは部品として構成されている。
- 「実行時に対話する」
- ライブラリを呼び出す時に、インターフェースとして定義されているメソッド(メンバー関数)やプロパティ(メンバー変数)を使ってソフトウェアを操作することができる。
- 「バイナリ相互運用性」
- 実装言語に依存せず様々なアプリケーションで使用できるようにするためにコンピュータが直接処理できるバイナリ形式で提供される。ただしOS(オペレーティングシステム)には依存する。
- 「標準」
- インターフェースやリモート処理、セキュリティなどを含めた様々な仕様が定められている。
コンパクトに一つの文章にまとめられると解りづらいのですが、上記のように個々にバラして探って行くと何となく理解できそうな気がしてきます。
COMコンポーネント(オブジェクト)とは
前章のCOMの説明を踏まえた上で、つぎにCOMコンポーネント(オブジェクト)について調べると先ほどの「COMの技術概要」のページに次のような説明がされています。
COMオブジェクトは、インターフェイスを介してその機能を公開します。これは、メンバー関数のコレクションです。
「オブジェクトとインターフェイス」の章の最初に出てきます
再利用可能なインターフェイスの実装には、コンポーネント、コンポーネント オブジェクト、 またはCOMオブジェクトという名前が付けされています。
「COM の概要」の章の最後の方に出て来ます
この説明も微妙な日本語で理解が難しいのですが、言わんとする事は「COMコンポーネント(オブジェクト)とはインターフェースとして定義されているメソッド(メンバー関数)やプロパティ(メンバ―変数)を介して機能が提供されるソフトウェア(部品)である」となる認識です。
インターフェースとCOMクラス
この章タイトルの言葉の説明も「COMの技術概要」のページに掲載されています。
COMインターフェイスのインスタンス自体を作成することはできません。代わりに、インターフェイスを実装するクラスのインスタンスを作成します。
「インターフェイスの実装」の章の最初に出て来ます。
これもまた難解な文章なのですが、この中で使われている「インスタンス」と「クラス」という言葉の意味を明確にしないと理解は難しくなります。
そこで下記のIT用語辞典e-Wordsの「インスタンス」と「クラス」の説明をつぎに引用いたします。
インスタンスとは、(中略)あらかじめ定義されたコンピュータプログラムやデータ構造などを、メインメモリ上に展開して処理・実行できる状態にしたものを指す。
「インスタンス」から
あるオブジェクトがどのようなデータとメソッドから作られるのかを定義した、言わば雛形にあたるものをクラスという。プログラムの実行時にはクラスを元にメモリ空間上に具体的なオブジェクトが生成されるが、この実体化されたオブジェクトのことを「インスタンス」(instance)という。
クラスには、オブジェクト内部で取り扱うデータ(フィールド/メンバ変数)の名称やデータ型・データ構造、アクセス可能な範囲(クラス外から参照・操作可能か否かなど)を宣言し、また、メソッド(メンバ関数)の名称や引数、処理内容の詳細、アクセス範囲を記述する。
「オブジェクト指向プログラミングのクラス」から
以上を踏まえると先ほどの「インターフェイスの実装」の引用文章が意味するところは「COMインターフェースを使用するためにはCOMクラスからインスタンスを生成する必要があり、それによってプロパティ(メンバー変数)やメソッド(メンバー関数)が処理・実行できるようになる」となる認識です。
クラスID(CLSID)とは
これまでのCOMコンポーネント(オブジェクト)の説明をまとめると次のようになります。
COMコンポーネント(オブジェクト)はインターフェースと呼ばれるメソッド(メンバー関数)やプロパティ(メンバー変数)を介して機能を提供する再利用可能なソフトウェアですが、実際にインターフェースを利用できるようにするためにはCOMクラスが必要になります。
COMクラスもソフトウェア(プログラミング言語)ですが「インターフェースのインスタンスを生成する」機能を担っているため、COMコンポーネント(オブジェクト)が機能を提供する上で欠かす事のできない存在です。
そしてCOMクラスは次のような特性があることが「COMの技術概要」のページに掲載されています。
COMクラスは、ファイル システム内の特定の展開にクラスを関連付ける一意の128ビット クラス ID (CLSID) を使用して識別されます。(中略) CLSID はGUIDです。つまり、同じCLSIDを持つクラスは他にはありません。
「インターフェイスの実装」の後半に出て来ます。
CLSIDは「Class Identifier」、GUIDは「Globally Unique Identifier」の省略形になりますが、クラスIDの内容をつぎの章で詳しく調べて行きます。
クラスID(CLSID)のレジストリキー
クラスID(CLSID)につきましてはMicrosoft Docsの下記ページに詳しい説明が載っています。
この中にレジストリキーの場所が記載されています。
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CLSID}
ここで今回のテーマである「TypeLibとクラスIDの対比表を作成する」ためにレジストリーから収集する情報をまとめて引用いたします。
レジストリ キー | 説明 |
---|---|
InprocServer32 | 32 ビットのインプロセス サーバーを登録(以下省略) |
LocalServer32 | 32 ビット ローカル サーバー アプリケーションへの完全パスを指定します。 |
ProgID | ProgID を CLSID に関連付ける。 |
VersionIndependentProgID | ProgID を CLSID に関連付ける。 この値は、オブジェクト アプリケーション の最新バージョンを決定するために使用されます。 |
「インプロセス サーバー」と「ローカル サーバー」については下記のMicrosoft Docsの「In-Process サーバー」のページで説明がされています。
ただ訳されている日本語のままだと解り難いので英語版を和訳して引用いたします。
in-process server(a DLL running in the process space of the container application)
–>インプロセスサーバー(コンテナーアプリケーションのプロセススペースで実行されるDLL)
local server(an EXE running in its own process space)
–>ローカルサーバー(独自のプロセススペースで実行されるEXE)
話が変わりますが上記に掲載されていない属性の中で、レジストリーを見ると登録されているものがあるのですが、その中で今回着目したのは「TypeLib」という属性です。
この属性の登録内容は中かっこで囲われて128 ビットの数値 (16 進数)なのですが、実際にこの数値をレジストリで検索するとTypeLibに登録されているタイプライブラリIDと一致します。
従いまして上記一覧と合わせて「TypeLib」の属性も収集する事にいたします。
CLSIDレジストリデータを取得するコーティング事例
レジストリ データは、WMI(Windows Management Instrumentation)のStdRegProvクラスとそのメソッドを使用して取得する事ができますが、前回のTypeLibの説明の際にも少し触れていますが、WMIが32ビットプログラムであるためにレジストリのWOW6432Nodeの方を参照してしまいます。
「参照してしまいます」とは「HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID」を指定したとしても実際には「…\Classes\WOW6432Node\CLSID」が参照される事を意味しています。
従いまして本コーディング事例ではレジストリー キーは下記を指定いたします。
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WOW6432Node\CLSID
「悪意のあるマクロの検出」の通知について
コーディン事例をご紹介する前に、と言いますか本コーディングを実行される前に事前にお伝えしなければならない事があります。
今回WMI(Windows Management Instrumentation)のStdRegProvクラスとそのメソッドを使用してCLSIDの情報をレジストリから収集するのですが、このようなアクションをマクロで実行するとパソコン上のウイルス スキャンソフトの「マルウェア対策スキャン インターフェイス (AMSI) 機能」によりマクロが「悪意のあるもの」としてExcelに通知するようです。
この機能の詳細は下記Microsoft Officeのページをご参照ください。
それにより、左図のような通知が画面に表示されます。
マクロを実行してからこの画面が表示されるまでしばらく時間(20秒前後)がかかるのですが、この通知が表示されるとその後は何もしなくても自動的にExcelが強制終了されてマクロによる実行結果は元に戻されます。
そのため何も知らずに本VBAを実行すると大変驚かれる事と存じます。
本コーディング事例を実行する場合は、必ず実行する前にコーディングを貼り付けたマクロ有効ブックを保存したフォルダーを「信頼できる場所」に設定する必要がありますのでご注意ください。
なお「信頼できる場所」に設定した後で、登録したフォルダーのマクロ有効ブックを再起動する必要があります。
「信頼できる場所」の設定方法は下記Microsoft Officeのページをご参照ください。
実際のコーディング事例
メインとなるサブルーチン「registryCLSID」と、サブキーを取得する「GetKeys」とサブキーのデータを取得する「GetValue」という2つのファンクションで構成されていますが、「GetKeys」と「GetValue」は前回のTypeLibの説明の際にご紹介しているので、ここでは割愛させていただきます。
Sub registryCLSID() Dim obj_Reg As Object Dim v_key1 As Variant, v_key2 As Variant Dim i As Integer, i1 As Integer, i2 As Integer, iRow As Integer Dim st_wk As String Dim st_clsid As String Dim i_switch As Integer Dim st_subkey(1 To 5) As String Dim SUBKEY(1 To 5) As String Const SHEETNAME As String = "CLSID.1" '追加するシート名をセット '対象キー Const HKEY_LOCAL_MACHINE As Long = &H80000002 Const REG_KEY As String = "SOFTWARE\Classes\WOW6432Node\CLSID" SUBKEY(1) = "PROGID" SUBKEY(2) = "VERSIONINDEPENDENTPROGID" SUBKEY(3) = "TYPELIB" SUBKEY(4) = "INPROCSERVER32" SUBKEY(5) = "LOCALSERVER32" Set obj_Reg = GetObject("winmgmts:\\Localhost\root\default:StdRegProv") st_wk = REG_KEY '戻り値はv_key?(?は整数)に配列で返る Call GetKeys(obj_Reg, HKEY_LOCAL_MACHINE, st_wk, v_key1) iRow = 1 Application.ScreenUpdating = False '画面固定 With addNewSheet(SHEETNAME) '新しいシートを指定の名前で追加し項目タイトルをセット .Cells(iRow, 1).Value = "No" .Cells(iRow, 2).Value = "クラス名" .Cells(iRow, 3).Value = REG_KEY For i = 1 To 5 .Cells(iRow, i + 3).Value = SUBKEY(i) Next i '階層化されたレジストリ情報を取得 For i1 = 0 To UBound(v_key1) st_wk = REG_KEY & Chr(92) & v_key1(i1) st_clsid = GetValue(obj_Reg, HKEY_LOCAL_MACHINE, st_wk, "") If GetKeys(obj_Reg, HKEY_LOCAL_MACHINE, st_wk, v_key2) Then i_switch = 0 For i2 = 0 To UBound(v_key2) st_wk = REG_KEY & "\" & v_key1(i1) & "\" & v_key2(i2) For i = 1 To 5 If UCase(v_key2(i2)) = SUBKEY(i) Then '対象のサブキーが存在するか確認 i_switch = i_switch + 1 st_subkey(i) = GetValue(obj_Reg, HKEY_LOCAL_MACHINE, st_wk, "") Exit For '<<<<<<<< End If Next i Next i2 If i_switch >= 1 Then '対象のサブキーが1つ以上セットされている時書き出す '読み込まれたデータをNEXT行の1~8カラムに書込み iRow = iRow + 1 .Cells(iRow, 1).Value = i1 + 1 .Cells(iRow, 2).Value = st_clsid .Cells(iRow, 3).Value = v_key1(i1) For i = 1 To 5 .Cells(iRow, i + 3).Value = st_subkey(i) st_subkey(i) = "" '消去 Next i End If End If Next i1 Cells.Select '入力範囲を選択 Cells.EntireColumn.AutoFit '列の幅を自動調整 .Cells(1, 1).Select 'A1セルを選択して範囲選択を解消 End With Application.ScreenUpdating = True '画面固定解除 Set obj_Reg = Nothing End Sub
簡単ではありますが下記に仕様をリストアップいたします。なおコーディングはMicorosoft Docsの「レジステリデータの取得」を参考にしています。
- 10行目で追加するシート名を指定しています。
- CLSIDから読み出した結果は追加したシートに書出します。
- 12~13行目で読み出すレジストリ キーを設定しています。
- 14~18行目で属性のサブキー名を配列にセットします。
- ここでセットされたサブキーのいづれかを持つCLSIDを出力します。
- 51~57行目で読み出した結果をシートのセルにセットしますが、出力する項目は下記になります。
- 飛び番
- 読み込んだCLSIDに連番をふっていますが、その中で抽出されたCLSIDにふられた番号になります。
- クラス名
- CLSIDの既定属性に設定されているデータ
- CLSID
- 128 ビットの数値 (16 進数)
- サブキー:PROGID
- サブキー:VERSIONINDEPENDENTPROGID
- サブキー:TYPELIB
- サブキー:INPROCSERVER32
- サブキー:LOCALSERVER32
- 飛び番
出力されたCLSIDの内容を分析する
弊社パソコンで上記マクロを実行した時に出力された行数を下記にまとめています。
なお属性データがダブルクォーテーション(“)でくくられているものは「”」は削除しています。
左表以外で特筆すべき点をまとめますが、あくまでも弊社パソコン環境の場合になりますのでお含み置きください。
- VERSIONINDEPENDENTPROGIDが登録されていてPROGIDに登録が無いものは8個ありました。
- TypeLibに登録がありINPROCSERVER32とLOCALSERVER32が登録が無いものは1個しかありませんでした。
- INPROCSERVER32とLOCALSERVER32ともに登録があるものが24個ありましたが重複削除すると組み合わせとしては8種類になります。
- INPROCSERVER32に登録が無くLOCALSERVER32に登録があるものは207個ありました。
- TypeLibもINPROCSERVER32もLOCALSERVER32も登録が無いものは198個ありました。
以上の分析からTypeLibとCLSIDを紐づけるためには下記のようなステップでデータを突合させるのが良さそうです。
まず「PROGIDとVERSIONINDEPENDENTPROGIDのどちらからデータをとるか」については下記のようにします。
- VERSIONINDEPENDENTPROGIDにデータがある場合はVERSIONINDEPENDENTPROGIDから取得する。
- VERSIONINDEPENDENTPROGIDにデータがない場合はPROGIDからデータを取得する。
つぎにTyoeLibとCLSIDの突合の仕方は下記のようにします。
- まずCLSIDがTypeLib属性を持つ時はTypeLibのLibidで紐づける。
- TypeLib属性を持たない時はINPROCSERVER32のフィルパスとTypeLibに登録されているファイルパスで紐づける。
- 最後にLOCALSERVER32のフィルパスとTypeLibに登録されているファイルパスで紐づける。
前回TypeLibのレジストリーキーには「タイプライブラリのファイルパス」が登録されている事はご紹介いたしましたが、数的にはINPROCSERVER32の中にフィルパスが5,262登録されているので、それと「タイプライブラリのファイルパス」がどれだけ紐づくかがポイントになります。
なお今回実際にレジストリ キーを見ていて気付いた事は、CLSID配下にほとんど属性が登録されていないものやMicorosft Docsの「CLSIDキー」に掲載されている属性以外が登録されているものなどが割と多くあるという事です。
またVBAからCOMクラスを参照する際によく使用されるProgIDが登録されていないものが多く、CLSIDに登録されているものはC#などの言語から呼ばれる方が圧倒的に多い事が分かります。
今回の登録内容を見る限り「COMクラスの作り手によって登録内容にバラつきがある」のが実情のようなので、となるとレジストリをもとに突合でTypeLibとCLSIDを紐づけるやり方には「限界があり、すべてを網羅できていない」と言えそうですが、取敢えず実際に試して見ます。
TypeLibとCLSIDを紐づける
紐づけを実施するためには前回のTypeLibレジストリデータを事前にデータ加工しておく必要があります。
以下加工の内容についてご説明いたします。
①タイプライブラリのファイルパスを加工する
前回のTypeLibレジストリデータを取得する際も触れしたが「タイプライブラリのファイルパス」には最後に「\整数」が付いているものが含まれています。
CLSIDのファイルパスと突合させるためにはこの「\整数」を取り除いておく必要があります。
当然前回ご紹介したコーディング事例を修正して取り除く事はできますが、手元に出力されたExcelシートがあるので今回はワークシート関数を使って取り除いています。
セルF2にセットされている「タイプライブラリのファイルパス」から最後の「\整数」を取り除くための数式は少し複雑ですが次のようになります。なお数式は表外の空いている列に貼り付けます。
=LEFT(F2,IF(ISERROR(FIND("\",F2,LEN(F2)-4)),LEN(F2),FIND("\",F2,LEN(F2)-4)-1))
- LEFT(F2,IF関数で求められる文字数)
- IF(ISERROR(FIND関数の結果),LEN(F2),FIND関数の結果-1)
- FIND(“\”,F2,LEN(F2)-4)
- IF(ISERROR(FIND関数の結果),LEN(F2),FIND関数の結果-1)
- FIND関数でF2にセットされたファイルパスの「後ろから4文字目から最後まで」”\”を探します。
“\”が見つかれば「FIND関数の結果-1」の文字数分をLEFT関数で取り出します。
②TypeLibレジストリデータにのみ存在する拡張子のファイルを取り除く
TypeLibレジストリデータを取得するコーティングを弊社のパソコンで実行して「タイプライブラリのファイルパス」の拡張子を調べると下記のようになっています。(Officeのタイプライブラリは含まれていません)
拡張子 | ax | cpl | dll | EXE | ocx | OLB | tlb |
登録数 | 1 | 2 | 336 | 39 | 9 | 5 | 73 |
この中で、「OLB、tlb」はOLEオブジェクトライブラリとタイプライブラリの拡張子であり、CLSIDのINPROCSERVER32属性の中には登場しないのでこれらの拡張子を持つファイルパスは除外した方が効率的です。
そのためにはファイルパスから拡張子だけを取り出す必要がありますが、この加工もワークシート関数を使って行います。
例えばG2セルに「タイプライブラリのファイルパス」がセットされている場合の数式は次のようになります。なお数式は表外の空いている列に貼り付けます。
=RIGHT(G2,LEN(G2)-FIND(".",G2,LEN(G2)-5))
- FIND関数でG2にセットされたファイルパスの「後ろから5文字目から最後まで」”.”を探します。G2のLEN関数の値から「FIND関数の結果」の文字数分の値を差し引いてRIGHT関数で”.”を除いた拡張子を取り出します。
③加工したファイルパスとタイプライブラリの名称の対比表を作成する
前章までの加工を施したTypeLibレジストリデータを元に、ファイルパスとタイプライブラリの名称との対比表を作成します。
ピボットテーブルを使ったり、並べ替えを使ったりしてファイルパスの昇順でタイプライブラリの名称を並べると、1つのファイルパスに複数のタイプライブラリの名称が紐づいているのが解ります。
弊社のパソコン環境では26ファイルパスがこれに該当していました。
このままで紐づけすると複数のタイプライブラリの名称の内の最初の名称しか対比対象にすることができないのでタイプライブラリの名称を「ひとくくり」に加工する必要があります。
このような場合の処理方法につきましては、別テーマでご説明する事にして今回は手作業で「ひとくくり」にまとめましたのでお含み置きいただければ幸いです。
なお作成した対比表には「PATHLIBNAME」と名前の設定をしてセル範囲の参照が名前でできるようにしておきます。
④タイプライブラリidとタイプライブラリ名称の対比表を作成する
前回のTypeLibレジストリデータの出力結果のワークシートを元に「Libid」128 ビットの数値 (16 進数)とタイプライブラリの名称との対比表を作成します。
ピボットテーブルを使ったり、並べ替えを使ったりして「Libid」の昇順でタイプライブラリの名称を並べると、先ほどと同様に1つの「Libid」に複数のタイプライブラリの名称に紐づいている事が解ります。
弊社のパソコン環境では9つの「Libid」がこれに該当していましたが、こちらも手作業で「ひとくくり」にまとめています。
なおこちらの対比表にも「IDLIBNAME」と名前の設定をしておきます。
⑤紐づけを実施する
以上で紐づけのための準備作業は完了したので、いよいよ本題のTypeLibとCLSIDの紐づけに入ります。
紐づけに使用する属性の候補は3種類あるのですが、実際に紐づけがされて「タイプライブラリの名称」が引けるかどうか?は出たとこ勝負になります。
そのため取敢えず3種類すべての紐づけを実施します。
TypeLib属性
CLSIDの出力結果のワークシートでE2セルにTypeLib属性がセットされている場合の数式は次のようになります。なお数式は表外の空いている列に貼り付けます。
=IF(ISNA(VLOOKUP(E2,IDLIBNAME,2,FALSE)),"",VLOOKUP(E2,IDLIBNAME,2,FALSE))
- IF関数(ISNA関数(VLOOKUP関数の値),””,VLOOKUP関数の値)
→VLOOKUP関数で値が見つからなかった時は#N/Aが返る- VLOOKUP関数で定義された名前「LIBTYPE」のセル範囲を検索し一致した場合はB列の値を返します。
VLOOKUP関数は検索方法をFALSE(完全一致)にしていても、比較対象のなかに大文字・小文字の揺らぎがあったとしても一致するのでこのような場合には便利です。
INPROCSERVER32属性とLOCALSERVER32属性
数式は参照するセルの列以外は同じ形になります。それぞれ表外の空いてる列に数式を貼り付けます。
下記はF2セルを参照する場合です。
=IF(ISNA(VLOOKUP(F2,PATHLIBNAME,2,FALSE)),"",VLOOKUP(F2,PATHLIBNAME,2,FALSE))
3種類の紐づけ結果の考察
弊社パソコン環境では、TypeLib属性とINPROCSERVER32属性で紐づけられたタイプライブラリの名称を比較した結果はつぎのようになりました。
タイプライブラリの名称が等しい | タイプライブラリの名称が異なる |
304 | 52 |
「異なる」ものが存在ことは「由々しき事態」なのですが、重複があるようなので重複削除をすると下記12種類の組み合わせになります。
「ひとくくり」にまとめた事での違いは「致し方ない」として、赤枠でくくった3行が気になるのでそれぞれのファイルパスを調べて見ます。
- Synaptics Device Control Widgets, Version 1.0
- C:\WINDOWS\system32\SynCOM.dll
- SynCom 2.0 Type Library
- C:\WINDOWS\system32\SynCOM.dll
- Microsoft HTML Object Library
- C:\Windows\SysWOW64\mshtml.tlb
- SynTPEnh 1.0 Type Library
- C:\WINDOWS\SysWow64\SynCOM.dll
- OptsHold 1.0 Type Library
- C:\Windows\SysWOW64\mshtmled.dll
上記を見ると違いが「system32」が「SysWow64」だったり、「mshtml.tlb」が「mshtmled.dll」だったりする事が分かり、これらも「致し方ない」と考えます。
なお左側3番目のようにTypeLib属性から拡張子「tlb」のファイルパスを持つタイプライブラリが紐づけられている点には注意が必要です。
つぎに、INPROCSERVER32属性とLOCALSERVER32属性での紐づけを見て行きます。
先の章で「INPROCSERVER32属性とLOCALSERVER32属性で共に登録があるのは24個ありましたが重複削除すると組み合わせとしては8種類」というご説明をいたしましたが、INPROCSERVER32属性とLOCALSERVER32属性の紐づけ結果では共にタイプライブラリの名称がセットされるものはありませんでした。
TypeLibのタイプライブラリの名称とCLSIDのPROGIDの対比表を作成
これまでの結果を踏まえて「TypeLibのタイプライブラリの名称」と「CLSIDのPROGID」との比較表を作成したいと思います。
「TypeLibのタイプライブラリの名称」は前章の考察から次のような優先順位で名称を決定します。
- TypeLib属性に紐づく名称があればそれを採用する。
- つぎにINPROCSERVER32属性に紐づく名称があればそれを採用する。
- 最後にLOCALSERVER32属性に紐づく名称があればそれを採用する。
つぎに「PROGIDとVERSIONINDEPENDENTPROGIDのどちらからデータをとるか」については先の章でもご説明しましたが、つぎのようにします。
- VERSIONINDEPENDENTPROGIDにデータがある場合はそれを採用する。
- VERSIONINDEPENDENTPROGIDにデータがない場合はPROGIDを採用する。
上記の考えて方で対比表を作成した後に「くくった」名称をばらすと、弊社のパソコン環境では202個のタイプライブラリが対比表に出現します。
930行ほどありますが実際作った「対比表」シートを下記にご紹介します。もっとも内容は弊社パソコン環境に基づきますのであらかじめお含み置きください。
なお下記ExcelシートはMicrosoftサポートの「OneDrive から Web ページやブログに Excel ブックを埋め込む」に基づきOneDrive「https://onedrive.live.com」にアクセスして表示しています。※iframeを使用しています。
上記Excelシートの見方を簡単に記載いたします。
①「タイプライブラリの名称」、「PROGID」と「印」になっています。
②最後の「印」の項目には下記の記号がセットされていますが、内容としては「タイプライブラリの名称」に紐づけされた「PROGID」が実際にVBAで呼び出せるか?を確認した結果になります。
なお「印」にセットされている記号は下記4種類になります。
印 | 説明 |
---|---|
× | PROGIDをCreateObject関数で呼び出すとエラーになる。(直接は呼び出せない) |
〇 | GetObject関数で参照できる。 |
■ | 呼び出してしばらく(1~2分ぐらい)待つと下図左のメッセージが表示されるので処理対象外にする。 |
□ | 呼び出すと下図右の「音声認識のセットアップ」が表示されるので処理対象外にする。 |
③薄緑色の背景色を付けたタイプライブラリの名称は割と良く利用されているものになります。
④薄黄色の背景色を付けた「Visual Basic For Applications/Visual Basic runtime objects and procedures」はVBA自身の参照設定になりますが、対応するPROGIDが「(空白)」になっている理由は前回のTypeLibレジストリデータの時にもご説明いたしましたが、「…\Classes\WOW6432Node\TypeLib」配下の「Visual Basic For Applications」は「Visual Basic 6.0ランタイムライブラリ」であって最新のものではないためです。
最後に実際に「対比表」シートB列のPROGIDを読み込んで検証結果をC列に書き込むコーディング事例を下記にご紹介いたします。
Private Sub testProgID() Dim i As Integer, iStart As Integer, iMax As Integer Dim obj_cls As Object On Error Resume Next Application.ScreenUpdating = False '画面固定 With ThisWorkbook.Worksheets("対比表") iStart = 2 'X行目からがデータ行 iMax = .Cells(Rows.Count, 2).End(xlUp).Row '2列目がPROGID。その最後の行 For i = iStart To iMax '2列目が"(空)"、3列目か"■,□"でなければ処理する If .Cells(i, 2) <> "(空白)" And .Cells(i, 3) <> "■" And .Cells(i, 3) <> "□" Then Set obj_cls = GetObject(, .Cells(i, 2).Value) '最初にGetObject関数で呼び出す If Err.Number = 0 Then ThisWorkbook.Worksheets("Sheet7").Cells(i, 3).Value = "〇" Set obj_cls = Nothing Else Err.Clear Set obj_cls = CreateObject(.Cells(i, 2).Value) 'CreateObject関数で呼び出す If Err.Number <> 0 Then ThisWorkbook.Worksheets("対比表").Cells(i, 3).Value = "×" Err.Clear Else ThisWorkbook.Worksheets("対比表").Cells(i, 3).Value = "" Set obj_cls = Nothing End If End If End If Next i End With Application.ScreenUpdating = True '画面固定解除 MsgBox "処理完了!" End Sub
最後に
ご紹介した弊社パソコン環境では202個のタイプライブラリが対比表に出現しました。
一方前回のTypeLibレジストリデータの弊社のパソコン環境での出力結果から拡張子「OLB」と「tlb」を含めたタイプライブラリ名称は346個だったので、今回のやり方では「出現しないタイプライブラリは144個」あることが解ります。
ただしこの内の拡張子が「OLB」と「tlb」である28個については本来Microsoft Docsのドキュメントには存在しないTypeLib属性に依存しているので、一旦取り除くと残り116個になります。
この内win64属性を持つタイプライブラリについてはレジストリデータが取得できないために出現しないと考えられるので、これも一旦取り除くとwin32属性だけを持つもので残るのは30個になります。
その内の1つに「Microsoft TAPI 3.0 Type Library」があるのですが、このタイプライブラリのファイルは「tapi3.dll」になっているので、これをレジストリエディタで検索したところ、どうやら「HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID」には存在するものの「…\Classes\WOW6432Node\CLSID」には存在していないようです。
すべてを確認していなくて恐縮ではありますが、恐らく残りの29個も同様な理由から出現しないのではないか?と思われます。
本来であれば「HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID」を手動でエクスポートとしてデータを加工し突合すべきでありますが、データ量が膨大になるために今回は割愛いたしました。
さらに網羅的にやるのであればOffice配下のTypeLibやCLSIDについても取得する必要がありますが、これらもまた手動でエキスポートする必要があります。
という事で残念ながら網羅性についてはカバーできておりませんのでお含み置きいただければ幸いです。
ただし今回の結果は、現状のレジストリからVBAで取得できる対比表としてはできる限りの内容が盛り込まれているものと推察致します。
以上最後までご一読いただき誠にありがとうございました。