Architect's Log

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

Windows7のOfficeサーバーサイドオートメーションで例外がスローされる

Windows7のOfficeサーバーサイドオートメーションで例外がスローされるという現象が発生しました。
具体的には、WindowsサービスでのExcelファイルのオープンで例外が発生していました。

エラーメッセージ


System.Runtime.InteropServices.COMException (0x800A03EC): ファイル '[ファイル名]' にアクセスできません。次のいずれかの理由が考えられます。• ファイル名またはパスが存在しません。• ファイルが他のプログラムによって使用されています。• 保存しようとしているブックと同じ名前のブックが現在開かれています。

解決方法

以下のフォルダを作成します。

x86
C:\Windows\System32\config\systemprofile\Desktop
x64
C:\Windows\SysWOW64\config\systemprofile\Desktop

原因

どうやら、2003R2サーバーまでは存在していた「SystemProfile が実行する仮想デスクトップフォルダが存在しない」ため実行ユーザーがログオンしていない状態で実行(タスク実行とか)すると、デスクトップがないのでEXCEL(つかオフィス?)が実行できないらしい。

「0x800A03EC」が出た…だと…?: へっぽこプログラマーのチラ裏

ちなみに

マイクロソフトが推奨しない実装であることは承知しています。

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

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

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

C#でExcelファイルをPDFに変換するサンプルです。

検証環境

Visual Studio 2010 SP1、.NET Framework 4、Excel 2007、Office 2007 SP2

プロジェクトの作成

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

参照の追加

以下のスクリーンショットは「Productivity Power Tools」の「Searchable Add Reference Dialog」機能を使用したものになっています。
Productivity Power Tools 拡張機能
不正使用をマイクロソフトに報告する その他の Microsoft - Visual Studio Platform Team Microsoft Visual Studio フィードバック ツール (21)   Microsoft Visual Studio 2013 フィードバック ツール (2) Productivity Power Tools 無料 A set of extensions to Visual Studio Professional (and above) which improves developer productivity.

右クリックでコンテキストメニューを表示します。

[COM] - [Microsoft Excel 12.0 Object Library]を選択し、[Add]をクリックします。

チェックマークの表示を確認したら[Close]をクリックします。

参照が追加されたことを確認します。

ソースコード

Program.cs
using System.IO;

namespace ExcelToPdf {
    class Program {
        static void Main(string[] args) {
            const string Dir = @"D:\Sandbox\Console\ExcelToPdf";
            ExcelSave.SaveAsPdf(Path.Combine(Dir, "TS0100738771.xlsx"), Path.Combine(Dir, "TS0100738771.pdf"));
        }
    }
}
ExcelSave.cs
using System;
using Microsoft.Office.Interop.Excel;

namespace ExcelToPdf {
    /// <summary>
    /// Excelファイルを保存する機能を提供します。
    /// </summary>
    public static class ExcelSave {
        /// <summary>
        /// ExcelファイルをPDFとして保存します。
        /// </summary>
        /// <param name="excelFilePathName">Excelファイルのパス付きファイル名。</param>
        /// <param name="saveAsPathName">保存するPDFのパス付きファイル名。</param>
        /// <remarks>
        /// <para>
        /// Excel 2007がインストールされている必要があります。
        /// </para>
        /// <para>
        /// Office 2007のSP2、またはPDF保存アドインがインストールされている必要があります。</ br>
        /// 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
        /// </para>
        /// </remarks>
        public static void SaveAsPdf(string excelFilePathName, string saveAsPathName) {
            Application application = null;
            Workbooks workbooks = null;
            Workbook workbook = null;

            try {
                application = new Application();

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

                workbook = workbooks.Open(
                    excelFilePathName, Type.Missing, Type.Missing, Type.Missing, Type.Missing
                    , Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing
                    , Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

                // http://msdn.microsoft.com/ja-jp/library/microsoft.office.tools.excel.workbook.exportasfixedformat(v=vs.90).aspx
                workbook.ExportAsFixedFormat(
                    XlFixedFormatType.xlTypePDF,
                    saveAsPathName,
                    XlFixedFormatQuality.xlQualityStandard,
                    true,
                    true,
                    Type.Missing,
                    Type.Missing,
                    false,
                    Type.Missing);
            } finally {
                if (workbook != null) {
                    try {
                        workbook.Close(true, Type.Missing, Type.Missing);
                    } catch {
                    }
                }

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

                Com.ComRelease.FinalReleaseComObjects(workbook, workbooks, 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) {
                }
            }
        }
    }
}

変換前Excelファイル

変換後PDFファイル


ご注意

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

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

相互運用型 'Microsoft.Office.Interop.Excel.ApplicationClass' を埋め込むことができません。代わりに適用可能なインターフェイスを使用してください。

どういうこと?

.NETからExcelを扱うときに、以下のビルドエラーが発生しました。

相互運用型 'Microsoft.Office.Interop.Excel.ApplicationClass' を埋め込むことができません。代わりに適用可能なインターフェイスを使用してください。
環境
  • Visual Studio 2010 SP1
  • .NET Framework 4
  • Excel 2007
ビルドエラー発生箇所
// using Microsoft.Office.Interop.Excel;
Application application = new ApplicationClass();

どうすれば?

以下のように修正したら、ビルドエラーが解消されました。

// using Microsoft.Office.Interop.Excel;
Application application = new Application();

参考

.NET Framework 4.0でIMultiLanguage2の作成に失敗する
文字コードを判別するプログラムを、mlang.dllに搭載されているIMultiLanguage2インターフェイスより利用したいと考えています。しかしながら、下記の場所でコンパイルエラーが発生します。 ...

皆で1つの Excel を編集する便利な方法

皆で1つの Excel を編集する便利な方法/SharePoint で共有してみよう(1/4):企業のIT・経営・ビジネスをつなぐ情報サイト EnterpriseZine (EZ)
社外秘などの機密情報の場合や、複数の人が頻繁に更新してバージョン管理が気になるファイルの場合は、もっと厳密な管理が必要になります。そんな時に使えるのが、Microsoft SharePoint Server 2010 と SharePoint Online です。 ...

SharePointの導入も方法の一つですが、手軽に導入するなら「ブックの共有」が便利です。ただし、共有しているときはマクロなど一部の内容が編集不可になります。
(以下はExcel2007のスクリーンショットですが2003にも同じ機能があります)


SQL Server のリンク サーバーおよび分散クエリで Excel を使用する方法

役に立ちそうなのでメモ。

SQL Server のリンク サーバーおよび分散クエリで Excel を使用する方法
Microsoft SQL Server では、他の OLE DB データ ソースへの永続的またはアドホックな接続をサポートしています。永続的な接続はリンク サーバーと呼ばれ、単独のクエリのために作成されるアドホックな接続は分散クエリと呼ばれます。 ...

SQL Server Management Studio または Enterprise Manager、システム ストアド プロシージャ、SQL-DMO (分散管理オブジェクト) または SMO (SQL Server 管理オブジェクト) を使用して、SQL Server のリンク サーバーとして Excel データ ソースを構成できます (SMO は Microsoft SQL Server 2005 の場合のみ使用できます)。