.NET製Winフォーム上にPDFを表示する

最近の仕事で、「.NET製アプリのフォームにPDFを表示させる」というのがあった。
とりあえず調べたことをまとめておく。

環境

どうやって表示するか

ざくっと調べたところ、3つの候補が出てきた

  1. WebBrowserコントロールを利用
  2. Adobe PDF Readerを利用
  3. Adobe Acrobatx.x type Libraryを利用
WebBrowserコントロールを利用する

特筆すべきことはなにもない。
フォームデザイナ上でツールボックスから「WebBrowser」を選択して配置するのみ。
Navigateメソッドを使用してPDFファイルの表示を行う。
通常Webブラウザ上でPDFを表示する環境と全く同じことになる。
つまり、PDF表示プラグインがインストールされていなければならない。
Adobe ReaderやFoxit Readerをインストールしておく。

プロセスが終了しないことがあるので、必要であれば以前のメモを参照。
.NETでPDFを開くときの注意 - やりたくないことはやらずに済む努力をすべき

Adobe PDF Readerを利用する

これも簡単。
まず、VisualStudioのデザイナ画面でツールボックスにAdobeReaderのコントロールを追加する。
右クリック→アイテムの選択→COMコンポーネントタブ→「Adobe PDF Reader」にチェック。
(実体はC:\Program Files\Common Files\Adobe\Acrobat\ActiveX\AcroPDF.dll)
フォームに貼りつけて準備完了。
LoadFileメソッドでPDFファイルの表示を行う。

Adobe Acrobatx.x type Libraryを利用する

この方法はAdobe Acrobatのインストールが必要。
まず、参照設定でCOMコンポーネントの中からAdobe Acrobatx.x type Libraryを探して参照を追加する。


表示にはAVDocオブジェクトを使用する。こんな感じでオブジェクトを生成する。

AVDoc = CType(CreateObject("AcroExch.AVDoc"), Acrobat.CAcroAVDoc)


次に、OpenInWindowExメソッドを利用して表示する。

' ここではDocumentPanelというコントロールを配置して、その上にPDFを表示
AVDoc.OpenInWindowEx(DocPathForView, _
                     Me.DocumentPanel.Handle.ToInt32, _
                     AV_EXTERNAL_VIEW, 1, 0, PDUseNone, _
                     AVZoomFitWidth, 0, 0, 0)

ちなみに、AV_EXTERNAL_VIEW = 1, PDUseNone = 1, AVZoomFitWidth = 2。
ここらへんは、AdobeのページからSDKサンプルをダウンロードすると、VBC#のサンプルが入手できるのでそこから朴る。



Acrobatを利用する際のポイントをいくつか羅列しておく

  • COMで定義されているクラスは、まともなインターフェイスをもたないので遅延バインディング必須。
  • Acrobat関連のオブジェクトを生成すると、必ずAcroAppオブジェクトが生成される。
  • AcroAppオブジェクトは、複数存在しない。AcroApp = CType(CreateObject("AcroExch.App"), Acrobat.CAcroApp)が意味するのは、既存のオブジェクトへの参照の取得。*1
  • Win32APIを使っているためか、リサイズ時は自分でリサイズしてやる必要がある。以下のようにする。Win32APIのラッパをつくらなければならないが、PInvokeを使えばOK。
''' <summary>PDF表示領域のリサイズ</summary>
''' <remarks>自動でリサイズしないので、ここでWIN32APIを利用して大きさを調整</remarks>
Private Sub DocPanel_Resize(ByVal sender As Object, ByVal e As EventArgs) Handles DocumentPanel.Resize
    If (Me.Disposing) Then
        Throw New ObjectDisposedException(MyClass.GetType.Name, "DocPanel_Resize")
    End If
    Dim parent As Control = CType(sender, Control)                                  ' AVDocが貼り付いている親ウィンドウ
    Dim pWndChild As Int32 = Win32API.GetWindow(parent.Handle.ToInt32, GW.CHILD)    ' 親ウィンドウのハンドルからAVDocのウィンドウハンドルを取得
    If Not (pWndChild.Equals(0)) Then
        Dim r As Win32API.RECT
        Win32API.GetClientRect(parent.Handle.ToInt32, r)
        Win32API.SetWindowPos(pWndChild, HWND.TOP, 0, 0, _
                              r.Right - r.Left, _
                              r.Bottom - r.Top, _
                              SWP.NOZORDER Or SWP.NOMOVE)
    End If
End Sub

*1:もちろん無ければ生成されるが