Architect's Log

I'm a Cloud Architect. I'm highly motivated to reduce toils with driving DevOps.

C#でWordファイルをPDFに変換する

検証環境

Visual Studio 2008 SP1、.NET Framework 3.5、Word 2007、Office 2007 SP2

プロジェクトの作成

プロジェクトテンプレートはコンソールアプリケーションを選択します。

参照の追加

[COM] - [Microsoft Word 12.0 Object Library]への参照を追加します。

ソースコード

Program.cs
using System.IO;

namespace WordToPdf {
    class Program {
        static void Main(string[] args) {
            const string Dir = @"D:\Sandbox\Console\WordToPdf";
            WordConverter.SaveAsPdf(Path.Combine(Dir, "hoge.doc"), Path.Combine(Dir, "hoge.pdf"));
            WordConverter.SaveAsPdf(Path.Combine(Dir, "fuga.docx"), Path.Combine(Dir, "fuga.pdf"));
        }
    }
}
WordConverter.cs
using System;
using Microsoft.Office.Interop.Word;

namespace WordToPdf {
    /// <summary>
    /// Wordファイルを変換する機能を提供します。
    /// </summary>
    public static class WordConverter {
        /// <summary>
        /// WordファイルをPDFとして保存します。
        /// </summary>
        /// <param name="wordFilePathName">Wordファイルのパス付きファイル名。</param>
        /// <param name="saveAsPathName">保存するPDFのパス付きファイル名。</param>
        /// <remarks>
        /// <para>
        /// Word 2007がインストールされている必要があります。
        /// Office 2007のSP2、またはPDF保存アドインがインストールされている必要があります。<br/>
        /// </para>
        /// </remarks>
        public static void SaveAsPdf(string wordFilePathName, string saveAsPathName) {
            // Office 2007のSP2、またはPDF保存アドインがインストールされている必要があります。
            // http://support.microsoft.com/kb/953195/ja<br/>
            // http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyId=F1FC413C-6D89-4F15-991B-63B07BA5F2E5&displaylang=ja
            ApplicationClass application = null;
            Documents documents = null;
            DocumentClass document = null;

            // refキーワードと共に渡すので、変数である必要がある。
            object missing = Type.Missing;

            try {
                application = new ApplicationClass();
                /*
                 * application.Documents.Open(...は、Documentsオブジェクトの解放処理ができないので不可。
                 * 必ず変数経由でComRelease.FinalReleaseComObjectsを呼び出すこと。
                 */
                documents = application.Documents;

                object filePathName = wordFilePathName;
                document = (DocumentClass) documents.Open(
                    ref filePathName, ref missing, ref missing, ref missing, ref missing
                    , ref missing, ref missing, ref missing, ref missing, ref missing
                    , ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);

                // http://msdn.microsoft.com/ja-jp/library/microsoft.office.tools.word.document.exportasfixedformat.aspx
                // http://msdn.microsoft.com/en-us/library/bb412305.aspx
                document.ExportAsFixedFormat(
                    saveAsPathName,
                    WdExportFormat.wdExportFormatPDF,
                    false,
                    WdExportOptimizeFor.wdExportOptimizeForPrint,
                    WdExportRange.wdExportAllDocument,
                    0,
                    0,
                    WdExportItem.wdExportDocumentWithMarkup,
                    true,
                    true,
                    WdExportCreateBookmarks.wdExportCreateWordBookmarks,
                    true,
                    true,
                    false,
                    ref missing);
            } finally {
                if(document != null) {
                    try {
                        document.Close(ref missing, ref missing, ref missing);
                    } catch {
                    }
                }

                if(application != null) {
                    try {
                        application.Quit(ref missing, ref missing, ref missing);
                    } catch {
                    }
                }

                ComRelease.FinalReleaseComObjects(document, documents, application);
            }
        }
    }
}
ComRelease.cs
using System;
using System.Runtime.InteropServices;

namespace Com {
    /// <summary>
    /// COMオブジェクトを解放する機能を提供します。
    /// </summary>
    public static class ComRelease {
        /// <summary>
        /// 複数のCOMオブジェクトの参照カウントを0までデクリメントし、解放します。
        /// </summary>
        /// <param name="objects">解放するCOMオブジェクトの配列。</param>
        /// <remarks>解放は配列の要素順に行います。</remarks>
        public static void FinalReleaseComObjects(params object[] objects) {
            foreach (object o in objects) {
                try {
                    if (o == null)
                        continue;
                    if (Marshal.IsComObject(o) == false)
                        continue;
                    Marshal.FinalReleaseComObject(o);
                } catch (Exception) {
                }
            }
        }
    }
}

ご注意

デスクトップアプリケーション以外でのOfiiceオートメーションは推奨されていません。
Office のサーバーサイド オートメーションについて
開発者は、Microsoft Office のオートメーションを使用して、Office 製品に組み込まれている機能を使用したカスタム ソリューションを構築できます。このようなプログラムの開発は、クライアント システム上では比較的簡単ですが、Microsoft Active Server Pages (ASP)、ASP.NET、DCOM、Windows NT サービスなど、サーバーサイドのコードからオートメーションを実行する場合はさまざまな課題が生じる可能性があります。 ...

マイクロソフトは、現在のところ、無人の非対話型クライアント アプリケーションまたはコンポーネント (ASP、ASP.NET、DCOM、および NT サービスを含む) からの Microsoft Office アプリケーションのオートメーションに関して、推奨もサポートも行っていません。それは、このような環境で Office を実行した場合、Office で不安定な動作やデッドロックが発生する可能性があるためです。

関連エントリー

複数のCOMオブジェクトを確実に解放する - プログラマーな日々
.NETでCOMを扱う際に、複数のCOMオブジェクトを確実に解放する方法を紹介します。 ...

C#でExcelファイルをPDFに変換する - プログラマーな日々
C#でExcelファイルをPDFに変換するサンプルです。 ...