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関連

    [リボン・カスタマイズ]カスタムタブを共有する。

    ※ 2015/2/18 コードに一部誤りがあったので修正しました。…

  2. Office関連

    ページ番号を取得するWordマクロ

    Wordマクロで選択位置のページ番号を取得する場合、簡単なのはSele…

  3. Office関連

    Internet Explorer用OneNoteアドオンを利用して指定したWebページをOneNo…

    「Evernote Webクリッパーで指定したWebページをEvern…

  4. Office関連

    ZIP形式で圧縮・解凍を行うVBAマクロ

    この記事のように、処理の中でZIP形式のファイルを扱うことはありました…

  5. Office アドイン

    Office 用アプリの開発資料(日本語)が公開されました。

    Office 用アプリの開発資料(日本語)が公開されました。・…

  6. Office関連

    RESAS-APIをVBAから呼び出す方法

    下記記事の通り「地域経済分析システム RESAS」のAPIが公開された…

コメント

  • コメント (0)

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

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

Time limit is exhausted. Please reload CAPTCHA.

最近の記事

アーカイブ

RapidSSL_SEAL-90x50
PAGE TOP