Msdn フォーラムに「Outlook2010のVBAメニューバー追加処理に関して」という質問がありました。
Outlook 2010でマクロを呼び出すためのメニューをリボンに追加したい、というような質問ですが、OutlookがリボンUIに変更されたことで、コマンドバーを利用したマクロが意図通りに動かなくなってしまったようです。
質問者の方は、ApplicationオブジェクトのItemLoadイベントを利用することで、Outlookアイテムが読み込まれた時点でCommandBarsオブジェクトのAddメソッドを使ってメニューを追加する方法を試されているようでしたが、この方法だとメールをプレビュー表示しただけでマクロが実行されてしまいます。
私の方からはInspectorsオブジェクトのNewInspectorイベントを利用する方法を提案しましたが、マクロ上で何らかのエラーが発生してしまうと、Outlookを再起動しなくてはならないため、こちらもあまり使い勝手がよくありません。
「CommandBars の以前のコードの更新」によると、“CommandBarsを使う代わりにリボンのカスタマイズを使うように!”ということが書かれていますが、アドインを使用してIRibbonExtensibilityインターフェイスを実装する必要があるため、WordやExcel、PowerPointとは違って、Outlookの場合はカスタマイズしたリボンを適用することが容易ではありません。
もちろん、Visual StudioやSharpDevelopを使ってCOMアドインを作ってしまえば良いのですが、VBAだけで処理しなくてはならない場合は、何かしら工夫する必要があります。
そこで考えたのが“officeUIファイルを動的にカスタマイズする方法”です。
officeUIファイルは「Office 2010 でカスタマイズしたリボンとクイック アクセス ツール バーを展開する」にある通り、リボンやクイック アクセス ツール バーのカスタマイズ情報が記録されたファイルです。
通常はアプリケーションのオプション画面にある「リボンのユーザー設定」や「クイック アクセス ツール バー」からカスタマイズを行うわけですが、officeUIファイルの実体はXMLファイルなので、動的に読み込んで内容を書き換えることが可能です。
というわけで、早速コードを考えてみました。
※ 下記コードはOffice 2007では動作しません。
※ 下記コードはOutlook 2010で動作確認を行いました。
'ThisOutlookSession Option Explicit Private Sub Application_Startup() Dim officeUIFolderPath As String Dim officeUIFilePath As String Dim d As Object Dim elmBtn As Object Dim elmScs As Object Dim elmQat As Object Dim elmRbn As Object Dim elmCui As Object Dim atrMacroNs As Object Dim macroNsName As String 'マクロ呼び出し用button要素の属性値 Const button_idQ As String = "btnExecuteProc" Const button_label As String = "マクロ実行" Const button_imageMso As String = "HappyFace" 'アイコン指定 Const button_onAction As String = "Project1.ThisOutlookSession.btnExecuteProc_onAction" '実行するマクロ指定 Const CSIDL_LOCAL_APPDATA = &H1C& '初期化 macroNsName = "" Set d = CreateObject("Msxml2.DOMDocument") d.async = False 'officeUIファイルのパス取得 officeUIFolderPath = AddPathSeparator(CreateObject("Shell.Application") _ .NameSpace(CSIDL_LOCAL_APPDATA).Self.Path) officeUIFolderPath = officeUIFolderPath & "Microsoft\Office\" officeUIFilePath = officeUIFolderPath & "olkmailitem.officeUI" On Error GoTo Err With CreateObject("Scripting.FileSystemObject") 'officeUIファイルがある場合 If .FileExists(officeUIFilePath) = True Then If d.Load(officeUIFilePath) = True Then 'customUI要素にマクロ呼び出し用名前空間が無ければ追加 macroNsName = HasMacroNameSpace(d.FirstChild) If Len(Trim(macroNsName)) < 1 Then macroNsName = "x1" Set atrMacroNs = d.createAttribute("xmlns:" & macroNsName) atrMacroNs.NodeValue = "http://schemas.microsoft.com/office/2009/07/customui/macro" d.FirstChild.Attributes.setNamedItem atrMacroNs End If If d.getElementsByTagName("mso:sharedControls").Length > 0 Then Set elmScs = d.getElementsByTagName("mso:sharedControls")(0) 'sharedControls要素の子要素としてbutton要素が無ければ追加 If elmScs.getElementsByTagName("mso:button").Length < 1 Then Set elmBtn = d.createElement("mso:button") elmBtn.setAttribute "idQ", macroNsName & ":" & button_idQ elmBtn.setAttribute "label", button_label elmBtn.setAttribute "imageMso", button_imageMso elmBtn.setAttribute "onAction", button_onAction elmScs.appendChild elmBtn Else If HasMacroButtonElement(elmScs, button_idQ) = False Then Set elmBtn = d.createElement("mso:button") elmBtn.setAttribute "idQ", macroNsName & ":" & button_idQ elmBtn.setAttribute "label", button_label elmBtn.setAttribute "imageMso", button_imageMso elmBtn.setAttribute "onAction", button_onAction elmScs.appendChild elmBtn End If End If 'Debug.Print d.XML '確認用 d.Save officeUIFilePath 'officeUIファイル(XML)保存 Else: GoTo Err End If Else: GoTo Err End If Else 'officeUIファイルが無い場合 'button要素 Set elmBtn = d.createElement("mso:button") elmBtn.setAttribute "idQ", "x1:" & button_idQ elmBtn.setAttribute "label", button_label elmBtn.setAttribute "imageMso", button_imageMso elmBtn.setAttribute "onAction", button_onAction 'sharedControls要素 Set elmScs = d.createElement("mso:sharedControls") elmScs.appendChild elmBtn 'qat要素 Set elmQat = d.createElement("mso:qat") elmQat.appendChild elmScs 'ribbon要素 Set elmRbn = d.createElement("mso:ribbon") elmRbn.appendChild elmQat 'customUI要素 Set elmCui = d.createElement("mso:customUI") elmCui.setAttribute "xmlns:x1", "http://schemas.microsoft.com/office/2009/07/customui/macro" elmCui.setAttribute "xmlns:mso", "http://schemas.microsoft.com/office/2009/07/customui" elmCui.appendChild elmRbn d.appendChild elmCui d.Save officeUIFilePath 'officeUIファイル(XML)保存 End If End With On Error GoTo 0 Exit Sub Err: MsgBox "処理が失敗しました。", vbExclamation + vbSystemModal End Sub Public Sub btnExecuteProc_onAction() '動的に追加したクイック アクセス ツール バーのボタンから呼び出されるマクロ MsgBox "OK", vbInformation + vbSystemModal End Sub Private Function HasMacroNameSpace(ByVal elmCui As Object) As String 'マクロ呼び出し用名前空間がある場合はprefixを返す Dim ret As String Dim n As Object ret = "" '初期化 For Each n In elmCui.Attributes If n.NodeValue = "http://schemas.microsoft.com/office/2009/07/customui/macro" Then ret = n.nodeName ret = Replace(ret, "xmlns:", "") Exit For End If Next HasMacroNameSpace = ret End Function Private Function HasMacroButtonElement(ByVal elmScs As Object, _ ByVal idQValue As String) As Boolean 'マクロ呼び出し用button要素の有無を判断 Dim ret As Boolean Dim n As Object ret = False '初期化 For Each n In elmScs.getElementsByTagName("mso:button") If InStr(n.Attributes.getNamedItem("idQ").NodeValue, idQValue) Then ret = True Exit For End If Next HasMacroButtonElement = ret End Function Private Function AddPathSeparator(ByVal s As String) As String If Right(s, 1) <> ChrW(&H5C) Then s = s & ChrW(&H5C) AddPathSeparator = s End Function
上記マクロは、ApplicationオブジェクトのStartupイベントを使って、
- olkmailitem.officeUIファイル(「Office 2010 でカスタマイズしたリボンとクイック アクセス ツール バーを展開する」参照)の有無を確認し、ファイルが無ければ動的に作成する。
- olkmailitem.officeUIファイルが存在する場合は、マクロを呼び出すための要素(button)の有無を確認する。
- マクロを呼び出すための要素が存在しない場合には、動的に要素を追加する。
というような処理をOutlook起動時に行います。
従って上記マクロが上手く動作した場合は、メール作成画面のクイック アクセス ツール バーに、マクロを呼び出すためのボタンが自動的に追加されます。
動作確認した限りでは、一応意図通りに動作するようです。
ただ、officeUIファイルの動的な書き換えというのは、上記コードの通り煩雑な処理になりますので、この方法もあまり効率の良い方法とは言えないでしょう。
やはり、Outlookのアドインを作成できる環境があるのであれば、「CommandBars の以前のコードの更新」にある通り、アドインを作成して対応するのが良いだろうと思います。
この記事へのコメントはありません。