Office関連

【2019年8月版】Adobe Readerを利用してPDFファイルのページ数を取得するVBAマクロ

7年ほど前に、Acrobatを使わず、Adobe Readerで表示したPDFファイルからページ数を取得するマクロについて記事を書きました。

上記記事でも書いてある通り、バージョン依存で不安定、かつ冗長な処理なので、決してお薦めできるものではないのですが、先日Adobeのフォーラムで、上記コードに関わるスレッドを見つけました。

まさかこのコードを使用している方が居られるとは・・・。
質問としては、Adobe ReaderのバージョンをDCにしたところマクロが動作しなくなったというものなのですが、プロパティダイアログを表示するメニューIDやダイアログの構造などが変更されているので、それはそうだろうと思います。

今回は、実用的ではないことを承知で、Acrobat Reader DCに対応した、PDFファイルのページ数を取得するマクロを書いてみました。

'※64ビット版Officeで実行する場合は要コード変更
'UIAutomationClient(UIAutomationCore.dll)要参照
Option Explicit

Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Const WM_COMMAND As Long = &H111

Public Sub Sample()
  Dim num As Long
  Const PDFPath As String = "C:\Test\T e s t.pdf"
  
  num = GetPDFNumPagesUsingAdobeReader(PDFPath)
  MsgBox "[" & PDFPath & "]ファイルのページ数:" & num, vbInformation + vbSystemModal
End Sub

Private Function GetPDFNumPagesUsingAdobeReader(ByVal PDFPath As String) As Long
'Adobe Readerの[文書のプロパティ]ダイアログからPDFのページ数を取得
  Dim uiAuto As CUIAutomation
  Dim elmApp As IUIAutomationElement
  Dim elmRoot As IUIAutomationElement
  Dim elmDialog As IUIAutomationElement
  Dim elmTabItem As IUIAutomationElement
  Dim elmTextPageNumCaption As IUIAutomationElement
  Dim elmTextPageNum As IUIAutomationElement
  Dim elmButtonCancel As IUIAutomationElement
  Dim elmButtonClose As IUIAutomationElement
  Dim ptnSel As IUIAutomationSelectionItemPattern
  Dim ptnInvoke As IUIAutomationInvokePattern
  Dim hApp As Long
  Dim readerPath As String
  Dim ret As Long
  
  'Adobe Readerで指定したPDFファイルを表示
  readerPath = GetAdobeReaderPath
  If Len(Trim(GetAdobeReaderPath)) < 1 Then GoTo Fin
  Shell """" & readerPath & """" & " " & """" & PDFPath & """", vbNormalFocus
  
  'Adobe Reader取得
  '※PDF表示まで多少時間が掛かる場合有り
  Set uiAuto = New CUIAutomation
  Set elmRoot = uiAuto.GetRootElement
  Do
    Set elmApp = GetElement(uiAuto, _
                            elmRoot, _
                            UIA_ClassNamePropertyId, _
                            "AcrobatSDIWindow", _
                            UIA_WindowControlTypeId)
    Sleep 200
    DoEvents
  Loop While elmApp Is Nothing
  
  '[文書のプロパティ]ダイアログ取得
  hApp = elmApp.GetCurrentPropertyValue(UIA_NativeWindowHandlePropertyId)
  PostMessage hApp, WM_COMMAND, &H1794, 0 '文書のプロパティ表示
  Do
    Set elmDialog = GetElement(uiAuto, _
                               elmApp, _
                               UIA_NamePropertyId, _
                               "文書のプロパティ", _
                               UIA_WindowControlTypeId)
    Sleep 200
    DoEvents
  Loop While elmDialog Is Nothing
  
  '[概要]タブ選択
  Set elmTabItem = GetElement(uiAuto, _
                              elmDialog, _
                              UIA_NamePropertyId, _
                              "概要", _
                              UIA_TabItemControlTypeId)
  If elmTabItem Is Nothing Then GoTo Fin
  Set ptnSel = elmTabItem.GetCurrentPattern(UIA_SelectionItemPatternId)
  ptnSel.Select
  
  '[ページ数]取得
  Set elmTextPageNumCaption = GetElement(uiAuto, _
                                         elmDialog, _
                                         UIA_NamePropertyId, _
                                         "ページ数 :", _
                                         UIA_TextControlTypeId)
  If elmTextPageNumCaption Is Nothing Then GoTo Fin
  Set elmTextPageNum = uiAuto.RawViewWalker.GetNextSiblingElement(elmTextPageNumCaption)
  If elmTextPageNum Is Nothing Then GoTo Fin
  ret = CLng(elmTextPageNum.CurrentName)
  
  'ダイアログを閉じてアプリケーション終了
  Set elmButtonCancel = GetElement(uiAuto, _
                                   elmDialog, _
                                   UIA_NamePropertyId, _
                                   "キャンセル", _
                                   UIA_ButtonControlTypeId)
  If elmButtonCancel Is Nothing Then GoTo Fin
  Set ptnInvoke = elmButtonCancel.GetCurrentPattern(UIA_InvokePatternId)
  ptnInvoke.Invoke
  Set elmButtonClose = GetElement(uiAuto, _
                                  elmApp, _
                                  UIA_NamePropertyId, _
                                  "閉じる", _
                                  UIA_ButtonControlTypeId)
  If elmButtonClose Is Nothing Then GoTo Fin
  Set ptnInvoke = elmButtonClose.GetCurrentPattern(UIA_InvokePatternId)
  ptnInvoke.Invoke
  
Fin:
  GetPDFNumPagesUsingAdobeReader = ret
End Function

Private Function GetAdobeReaderPath()
'Adobe Reader(AcroRd32.exe)のパスを取得
  Dim folderPath As String
  Dim filePath As String
  Dim itm As Object
  Const ExeName As String = "AcroRd32.exe"
   
  '[プログラムと機能]からAdobe Readerのインストール先フォルダを取得
  With CreateObject("Shell.Application").Namespace("shell:::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}")
    For Each itm In .Items
      If InStr(LCase(itm.Name), "adobe") And InStr(LCase(itm.Name), "reader") Then
        '[Extended Asian Language font pack]は除外
        If InStr(LCase(itm.Name), "extended") < 1 Then
          folderPath = .GetDetailsOf(itm, 10)
          Exit For
        End If
      End If
    Next
  End With
  
  'Adobe Readerのパスを取得
  With CreateObject("Scripting.FileSystemObject")
    filePath = .BuildPath(folderPath, "Reader")
    filePath = .BuildPath(filePath, ExeName)
    If .FileExists(filePath) = True Then GetAdobeReaderPath = filePath
  End With
End Function

Private Function GetElement(ByVal uiAuto As CUIAutomation, _
                            ByVal elmParent As IUIAutomationElement, _
                            ByVal propertyId As Long, _
                            ByVal propertyValue As Variant, _
                            Optional ByVal ctrlType As Long = 0) As IUIAutomationElement
  Dim cndFirst As IUIAutomationCondition
  Dim cndSecond As IUIAutomationCondition
      
  Set cndFirst = uiAuto.CreatePropertyCondition( _
                   propertyId, _
                   propertyValue _
                 )
  If ctrlType <> 0 Then
    Set cndSecond = uiAuto.CreatePropertyCondition( _
                      UIA_ControlTypePropertyId, _
                      ctrlType _
                    )
    Set cndFirst = uiAuto.CreateAndCondition( _
                     cndFirst, _
                     cndSecond _
                   )
  End If
  Set GetElement = elmParent.FindFirst(TreeScope_Subtree, cndFirst)
End Function

処理の流れは下記の通りで、UI Automationを使って、アプリケーションやダイアログの操作を行っています。

  1. 「プログラムと機能」からAdobe Readerのパスを取得する。
  2. Adobe ReaderでPDFファイルを開く。
  3. 文書のプロパティダイアログを表示する。
  4. 文書のプロパティダイアログからページ数を取得する。
  5. 文書のプロパティダイアログを閉じる。
  6. Adobe Readerを終了する。

重ねて言いますが、上記コードは、バージョン依存であまり実用的ではないので、本家AcrobatやサードパーティのiTextSharpCPDF(Coherent PDF)といったツールを使える環境であれば、そちらを使うことをお薦めします。

関連記事

関連記事

  1. Office アドイン

    [Office用アプリ]ユーザー設定を保存する。

    ※ この情報はOffice 2013 カスタマー プレビュー版を元にし…

  2. Office関連

    「入門レベルでは決して足りない実務に必須のスキルとは ExcelVBA 実戦のための技術」レビュー

    久しぶりにVBA参考書籍のレビューです。今回は沢内晴彦氏が執筆され…

  3. Office関連

    MemsourceのバイリンガルMXLIFFファイルから情報を抽出するWordマクロ

    近年翻訳業界では「Trados」や「memoQ」といった、“翻訳支援ツ…

  4. Office関連

    「Office 2003 のコマンドに対応する Office 2010 のリファレンス ブック」のダ…

    クリックさんのブログ記事「旧メニュー対応表を使いたい: パソコンのツボ…

  5. Office関連

    [リボン・カスタマイズ]dropDown要素の初期項目を指定する。

    MSDN フォーラムに「リボン:ドロップダウンリストにlabel初期値…

  6. Office関連

    Office 2016関連資料のリンク

    Office 2016関連資料のリンクをメモしておきます。特に「O…

コメント

  • コメント (0)

  • トラックバックは利用できません。

  1. この記事へのコメントはありません。

Time limit is exhausted. Please reload CAPTCHA.

最近の記事

アーカイブ

RapidSSL_SEAL-90x50
PAGE TOP