何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 761
  • 記事 - 18
  • コメント - 37042
  • トラックバック - 222
ニュース
  • IE7以前では、表示がおかしい。div の解釈に問題があるようだ。
    IE8の場合は、「互換」表示を OFF にしてください。
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2011
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

背景

Word 2003 から、文書を XML 化して保存することが出来るようになりました。これがどういうことかというと、文書をデータベース(のようなもの)として扱える、ということです。XML は、プレーンなテキスト文書です。したがって、どんなアプリケーションでも読み込むことが可能です。また、スタイルシートを作成すれば、簡単に見栄えを変えることが出来ます(ただし、スタイルシートを作成することは、そんなに簡単ではありません)。RTF もプレーンなテキストファイルですが、データとして応用が出来るというところで、WordML の方が、色々と都合がいいです。

ところが不幸なことに、.NET Framework の標準のコントロールには、WordML を扱うコントロールがありません。そこで、書式付き文字列を作成できる RichTextBox が出力する RTF File を、WordML 化することにします。これについて、WordML を直接出力できるコンポーネントや、RTF から WordML に変換するコンポーネントを探しましたが、Word なしにこのことを行ってくれるコンポーネントはありませんでした(2006/01/30 現在 「convert rtf wordml OR wordprocessingml」をキーに Google で検索)。Word がインストールされていれば変換してくれるコンポーネントはあったのですが、有料でした。Word をインストールしてオートメーションで使用するならそんなにたいした作業ではないので、自分で作ることにします。

なお、そのコンポーネントは、Word がインストールされていなくても、HTML や doc ファイルを読み込んで変換することが可能です。RTF と WordML を扱うためには、Word が必要です。

前提条件

  • Word 2003 がインストールされていること
  • 実行するアプリケーションに、File IO の完全な権限があること

コード

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Word;
namespace RtfToWordML {
    /// 
    /// オートメーションを使用して、Word を操作するクラス
    /// 
    class WordContainer : IDisposable {
        #region 非公開のフィールド
        static private object TRUE = true;
        static private object FALSE = false;
        static private object MISSING = Type.Missing;
        private ApplicationClass wordApplication = null;
        private bool disposed;
        #endregion
        #region コンストラクタとデストラクタ
        /// 
        /// 新規にインスタンスを生成します。
        /// 
        public WordContainer() {
            wordApplication = new Microsoft.Office.Interop.Word.ApplicationClass();
            wordApplication.Visible = false;
            disposed = false;
        }
        ~WordContainer() {
            this.Dispose();
        }
        #endregion
        /// 
        /// RTF ファイルを読み込んで、WordML ファイルに出力します。
        /// 出力されるファイルは、RTF ファイルと同じディレクトリに、
        /// 拡張子を xml に変更して保存されます。
        /// 
        /// 
        /// 対象の RTF ファイルのファイル名
        /// 
        public void ConvertRtfToWordML(string fileName) {
            if (disposed == true) {
                throw new ObjectDisposedException("WordContainer");
            }
            Documents wordDocuments = null;
            Document wordDocument = null;
            object format = WdOpenFormat.wdOpenFormatRTF;
            object saveFormat = WdSaveFormat.wdFormatXML;
            object fname = fileName;
            object saveName = SaveFileName(fileName);
            try {
                wordDocuments = wordApplication.Documents;
                wordDocument = wordDocuments.Open(ref fname, ref FALSE
                    , ref TRUE, ref FALSE, ref MISSING
                    , ref MISSING, ref FALSE, ref MISSING
                    , ref MISSING, ref format, ref MISSING
                    , ref TRUE, ref TRUE, ref MISSING, ref TRUE, ref MISSING);
                wordDocument.SaveAs(ref saveName, ref saveFormat, ref MISSING
                    , ref MISSING, ref FALSE, ref MISSING, ref FALSE
                    , ref FALSE, ref MISSING, ref FALSE, ref MISSING
                    , ref MISSING, ref MISSING, ref MISSING
                    , ref MISSING, ref MISSING);
                wordDocument.Close(ref FALSE, ref MISSING, ref MISSING);
            } finally {
                if (wordDocument != null) {
                    Marshal.ReleaseComObject(wordDocument);
                }
                if (wordDocuments != null) {
                    Marshal.ReleaseComObject(wordDocuments);
                }
            }
        }
        private static string SaveFileName(string fileName) {
            return System.IO.Path.Combine(
                System.IO.Path.GetDirectoryName(fileName)
                , System.IO.Path.GetFileNameWithoutExtension(fileName))
                + ".xml";
        }
        #region IDisposable メンバ
        public void Dispose() {
            if (wordApplication != null) {
                wordApplication.Quit(ref FALSE, ref MISSING, ref MISSING);
                Marshal.ReleaseComObject(wordApplication);
                wordApplication = null;
            }
            disposed = true;
        }
        #endregion
    }
}
 

説明

もっともシンプルな、なんのチェックもしていないコードです。実際に動作させるには、セキュリティ権限のチェック、Word 2003 がインストールされていることのチェック、ファイルの存在チェックなどが必要です。VS2005 の権限チェックでは、フル トラストでなければ動作しないと診断されました。ClickOnce などで配布する際には、注意が必要です。

また、WordContainer クラスは IDisposable インターフェイスを実装しています。クラス インスタンスの使用後、速やかに Dispose メソッドを呼び出す必要があります。

動作はとても簡単です。オートメーションで Word を起動し、引数で指定したファイルを読み込みます。そうしたら拡張子を XML に変更したファイルに、WordML 形式で保存します。連続実行させることを考え、Word のインスタンスをクラス変数にしました。

WordDocuments.Open メソッドの、最後の引数について、ドキュメントに記載がありませんでした。何か情報をお持ちでしたら、コメントをお願いします。

COM オブジェクトのリリースについては、注意が必要です。"Documents" と、"Document" の変数を用意していることに注意してください。ここを、[ wordApplication.Documents.Open(...) ]とすると、Documents オブジェクトの参照を解放することが出来なくなります。

応用

実際使おうとすると、いったんファイルを経由するのはとても使い勝手が悪いことがわかると思います。ClickOnce などのアプリケーションに組み込もうとすると、そのことがひとつの障害となります。この辺は、応用事項とします。

参考

修正履歴

2006/02/02
  • SaveFileName メソッドで、GetFileNameWithoutExtension メソッドはファイル名しか返さないため、フルパスを作成するように修正
  • デストラクタを追加

迷惑コメントが多いため、再投稿。

投稿日時 : 2007年5月5日 11:26
コメント
タイトル
名前
Url
コメント