none
Excelがインストールされているか確認する方法 RRS feed

  • 質問

  • エクセルがインストールされていない環境では、エラーメッセージを表示させたいのですが、
    Tryで括ってもFormが表示されようとする段階でエラーになります。
    何か良い方法はありますでしょうか?

      Excel.Application oXL;
     private void Form_Load(object sender, EventArgs e)
     {
      try
      {
       oXL = new Excel.Application();
      }
      catch
      {
       MessageBox.Show("Excelがインストールされていません。");
       Close();
      }
     }

    2011年12月18日 7:57

回答

  • 個人的には今回のケースであれば参照自体持たない方が綺麗かと思います。

        System.Type type = null;
        try
        {
            type = System.Type.GetTypeFromProgID("Excel.Application");
            if (type == null)
            {
                MessageBox.Show("Excelがインストールされていません。", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
        finally
        {
            type = null;
        }

     

    こんな感じで行けた様な・・・


    • 編集済み aviator__ 2011年12月19日 1:32 メッセージ表記を修正
    • 回答としてマーク kgg01234 2011年12月19日 9:12
    2011年12月19日 1:28

すべての返信

  • エラー、正しくは例外だと思いますが、内容を具体的に書きましょう。
    表示されている例外のクラス名、メッセージ、可能であれば呼び出し履歴を添えるとわかりやすいと思います。(ソースコードの場所など、機密性のある情報を含めないように注意してください)

    また、呼び出し履歴があなたのコード特有の場所なのであれば、その場所で何をやっているかも記載してください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年12月18日 8:57
    モデレータ
  • こんにちは。

     

    Loadイベントは、フォームが表示される前の処理ですので、

    この中でCOMExceptionが発生しますので、MessageBoxが表示され、終了となります。

    この動作はこの動作で問題ないと思います。

     

    フォームの表示後にメッセージボックスを表示する場合は、

    別スレッドでメッセージボックスを表示すると良いと思います。

    その理由は、MessageBoxを表示している間はバインドされたスレッドの動作は停止されるためです。

    以下に参考までに、サンプルを書きましたので、参考にしてください。

     

    private void Form1_Load(object sender, EventArgs e)
    {
        Thread oThread = new Thread(new ThreadStart(this.Check));
        oThread.Start();
    }
    
    private void ShowError()
    {
        if (this.InvokeRequired)
        {
            this.Invoke(
                (MethodInvoker)delegate() { ShowError(); });
        }
        else
        {
            MessageBox.Show(this, "Error.");
            this.Close();
        }
    }
    
    private void Check()
    {
        try
        {
            Excel.Application oXL = new Excel.Application();
        }
        catch
        {
            this.ShowError();
        }
    }
    

     

     

    以上です。

    2011年12月18日 13:08
  • > Excel.Application oXL;

    この1行があると、Excel が無い環境ではそのクラス(たぶん、Form1 とかかな?)に変数が確保できないので、読み込めなくなります。

    方法は色々とありますが、おそらく、色々なところから oXL を使用して Excel を操作しようとされているのだと思いますので、Excel を保持する oXL を保持するためのクラスを別につくってしまうのが手軽かな?と思います。

     

    2011年12月19日 0:40
  • 個人的には今回のケースであれば参照自体持たない方が綺麗かと思います。

        System.Type type = null;
        try
        {
            type = System.Type.GetTypeFromProgID("Excel.Application");
            if (type == null)
            {
                MessageBox.Show("Excelがインストールされていません。", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
        finally
        {
            type = null;
        }

     

    こんな感じで行けた様な・・・


    • 編集済み aviator__ 2011年12月19日 1:32 メッセージ表記を修正
    • 回答としてマーク kgg01234 2011年12月19日 9:12
    2011年12月19日 1:28
  • > Excel.Application oXL;

    この1行があると、Excel が無い環境ではそのクラス(たぶん、Form1 とかかな?)に変数が確保できないので、読み込めなくなります。

    この指摘は間違っています。Microsoft.Office.Interop.Excel.Applicationクラスのフィールドが定義されているだけであり、インスタンス作成は行っていません。実際にエラーになるのは new Excel.Application() などを行い、コードを実行した際です。(その際Microsoft.Office.Interop.Excel.dllをロードようとし、Excelが存在しないため読み込みエラーになる。)

    # Win7 SP1 w/o Excel、.NET 3.5.1ターゲットで確認。

    2011年12月19日 4:16
  • > この指摘は間違っています。Microsoft.Office.Interop.Excel.Applicationクラスのフィールドが定義されているだけであり、インスタンス作成は行っていません。

    インスタンス生成に関係なく TypeInitializationException みたいなものが発生すると思ったのですが、Interop クラスなので問題なく生成できるのですね、失礼しました。

     

    2011年12月19日 4:25
  • みなさま、ありがとうございます。

    ボタンが押されたらチェックするようにしてみました。

      private void buttonExcel_Click(object sender, EventArgs e)
      {
       Excel.Application oXL;
       try
       {
        oXL = new Excel.Application();
       }
       catch
       {
        MessageBox.Show("Excelがインストールされていません。");
        return;
       }

       Excel._Workbook oWB = (Excel._Workbook)(oXL.Workbooks.Add(Excel.XlSheetType.xlWorksheet));
       oXL.Visible = true;
       oXL.UserControl = true;
      }

    ボタンを押すと、こちらのエラーが表示されます。

    2011年12月19日 8:25
  • matsukawarさま

    ありがとうございます。

    ご教授いただいたコードを試しましたが、エラーで停止します。


    • 編集済み kgg01234 2011年12月19日 8:46
    2011年12月19日 8:44
  • どこの時点で上記のエラーが出てるのですか?

    2011年12月19日 8:54
  • 「指定されたファイルが見つかりません」とメッセージに出ていますが…

    2011年12月19日 8:56
  • aviator__さま
    ご教授いただいた方法で判断できました。
    ひとつのFormにoXLを入れるとエラーになりますが、分ければ問題なく実行もできました。
    ありがとうございます。

     public partial class Form1 : Form
     {
      public Form1()
      {
       InitializeComponent();
      }

      private void buttonExcel_Click(object sender, EventArgs e)
      {
       if (ExcelCheck())
       {
        Form2 form = new Form2();
        form.ShowDialog();
       }
      }

      private bool ExcelCheck()
      {
       System.Type type = null;
       try
       {
        type = System.Type.GetTypeFromProgID("Excel.Application");
       }
       catch { }

       return (type != null);
      }
     }

     public partial class Form2 : Form
     {
      public Form2()
      {
       InitializeComponent();
      }

      private void Form2_Load(object sender, EventArgs e)
      {
       Excel.Application oXL = new Excel.Application();
       Excel._Workbook oWB = (Excel._Workbook)(oXL.Workbooks.Add(Excel.XlSheetType.xlWorksheet));
       Excel._Worksheet oSheet = (Excel._Worksheet)oWB.ActiveSheet;
       oSheet.Cells[1, 1] = "(^.^)b";
       oXL.Visible = true;
       oXL.UserControl = true;
       Close();
      }
     }

    2011年12月19日 9:11
  • buttonExcel_Click を押すとエラーが表示されました。
    2011年12月19日 9:38
  •     private bool ExcelCheck()
        {
            System.Type type = null;
            try
            {
                type = System.Type.GetTypeFromProgID("Excel.Application");
                return (type != null);
            }
            finally
            {
                type = null;
            }
        }
    
    


    ExcelCheckメソッドですが、エラーがスローされる事は無いと思いますが catch を握りつぶすのはいかがかと・・・

    そうするくらいなら上記の方が綺麗かなぁ。

    2011年12月19日 9:48
  • aviator__さま

    type=nullで初期化されているので、finallyでクリアしなくても大丈夫かなと思いますが、

    catchを握りつぶす?のと何か違いがあるのでしょうか?

    2011年12月19日 11:39
  • 関連する疑問として、Excel2007環境で「プロジェクト→参照の追加」をすると Excel2007のPIAが取り込まれると思いますが、Excel2003環境で上記の方法を実行すると、

    type = System.Type.GetTypeFromProgID("Excel.Application");

    でエラーは発生しませんが、

    Excel.Application oXL = new Excel.Application();

    の行でエラーが発生してしまいます。

    Excel2007がインストールされているかどうかを判断する方法はあるのでしょうか?

     

    下位のExcel2003環境のPIAを利用すれば、Excel2007でも実行できる事は確認しましたが、

    逆は不可なので、何かチェックできる方法があれば便利と思いました。

    2011年12月19日 11:59
  • 使用している.NET Frameworkのバージョンが書かれていませんが、4ならば「方法 : プライマリ相互運用機能アセンブリを利用して Office アプリケーションを使用する」という方法があります。

    先にコメントしたファイルが見つかりません、というものもMicrosoft.Office.Interop.Excel.dllをプログラムと同時に配布しなければならないところを、配布し忘れているために発生している問題です。

    2011年12月19日 12:28
  • > この指摘は間違っています。Microsoft.Office.Interop.Excel.Applicationクラスのフィールドが定義されているだけであり、インスタンス作成は行っていません。

    インスタンス生成に関係なく TypeInitializationException みたいなものが発生すると思ったのですが、Interop クラスなので問題なく生成できるのですね、失礼しました。

    型のメタデータを含むアセンブリが見つからなければ、例外が発生します。それは、Interop であろうが、なかろうが、同じことです。遅延バインディングや PIA の埋め込みなら別ですが。
    今回は Assembly が見つからないことによって起きているので推察通りではありますね。

    ただ、元の投稿では、「インスタンスの生成ができない」と言っているのか、「型のメタデータが見つからないことによる問題」と言っているのかぱっと見てわかりづらかったと思います。慎重に読めば読めますが、読み誤りやすいと思います。(私は読み誤りそう…)

    あと、誤解をただす内容だけでよかったのでは?


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年12月19日 13:48
    モデレータ
  • 先にコメントしたファイルが見つかりません、というものもMicrosoft.Office.Interop.Excel.dllをプログラムと同時に配布しなければならないところを、配布し忘れているために発生している問題です。

    この DLL 単独で配ってよかったんでしたっけ?(PIA を参照していて、PIA を埋め込まない場合)
    再頒布を認められているものであればよいのですが、DLL 単体で再頒布可能というものを見たことがなかったため、ご存じであればお聞きしたいと思い、確認させていただきました。

    再頒布可能パッケージとしてまとまっているものでインストールするのはありなのかなぁ。(裏付けなし)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年12月19日 13:53
    モデレータ
  • 佐祐理さま、ありがとうございます。

    VS2008+.net3.5で作成しております。

    Microsoft.Office.Interop.Excel.dllを探しましたが見つかりませんでした。

    どこに入っているのでしょうか?

    2011年12月19日 14:05
  • 自己レスです。

    参照設定のMicrosoft.Office.Interop.Excelのプロパティにパスがあったので、

    こちらのファイルをコピーしたら、上記のエラーは出なくなりました。

     

    2011年12月19日 14:15
  • "Excel.Application" という PorgID は、なんでもいいから Excel という意味になります。

    特定のバージョンを調べるのであれば、"Excel.Application.11" とかを指定すればよいかと思います。(11 = Excel 2003) ただし、この場合は下位互換性に頼って Excel 2003 以上を確認するのはちょっと面倒だったりしますね。2003/2007/2010 のみとかなら、ProgID が既知なのでよいですが、将来のバージョンにも対応したい場合は、Excel.Application で起動しておいて例外を catch するのがお手軽かと思います。

     

    2011年12月19日 23:58
  • finallyで行っているのは初期化でなくてクリアです。

    catch を握りつぶすと表現したのは、エラーが発生した場合にそれを打ち消すようなコーディングになっているからです。

    まぁCOMオブジェクトを読んでる訳ではないので解放する必要は無いとは思いますが、エラーを無視するコーディングがどうかと思ったので。

    2011年12月20日 0:06
  • 英語力的に自身がないですが、PIA の README によると、Visual Studio の COM 相互運用サポート機能で生成したアセンブリを含めて、頒布用パッケージを使用しない Office の COM Interop は配るな…とお願いされていると思います。(Visual Studio で生成した相互運用アセンブリは頒布が許可されているので)

    > Generally, you must avoid the use of any Office COM interop assembly that is not provided as part of the Office PIAs. You must also avoid the use of any Office COM interop assembly that is generated by Microsoft Visual Studio .NET at design time. Any Office interop assembly that is not included in the Office PIAs download is considered unofficial.
    > To download the official PIAs available to Microsoft Office applications and associated technologies, visit the following Microsoft Download Center Web site:

     


    2011年12月20日 0:08
  • 将来のバージョンにも対応するのであれば、遅延バインディングを用いるべきじゃないですかね?

    ※メソッド名やプロパティ名等が変わったらどうにもなりませんが・・・

     

    System.ActivatorクラスのCreateInstanceとTypeのInvokeMemberなんかをうまく使ってあげれば出来るかと。

    2011年12月20日 0:10
  • OfficeのPIAの再頒布が可能かどうかは私も把握していません。しかし次の点から可能では、と想像しています。

    • OfficeのPIAは再頒布可能パッケージとしてまとまっているため、その条件を満たせばとりあえず再頒布可能。
    • PIA(プライマリ相互運用アセンブリ)の元となる相互運用アセンブリはTlbimp.exeでコンパイラーが自動生成したメタデータに過ぎず、ライセンスに触れるようなコードは含まれていない。
    • プライマリ相互運用機能アセンブリの再配布には「共通言語ランタイムは、グローバル アセンブリとローカル アセンブリの両方に同じ販売元の署名がある場合でも、常にグローバル アセンブリ キャッシュ内のプライマリ相互運用機能アセンブリを使用するようにアプリケーションに指示します。」という記述もあり、ローカル配置も意識されている。(ただし、これはOfficeに関してではなく、PIA全般の話)

    ま、私自身は必要になるまでは詳しく調べないと思います。

    2011年12月20日 0:14
  • 正確にはこの方法では解決になっていません。

    「Excel.Application oXL」と書かれてしまっていて、結局のところMicrosoft.Office.Interop.Excel.dllを参照してしまっています。aviator__さんも後から書かれていますが、全てのExcel操作に対して「遅延バインディングを用い」て記述する必要があります。

    これまた.NET 4の機能になってしまいますが、一通りのコードを書いた後、Microsoft.Office.Interop.Excel.dllに依存する全ての型をdynamicに書き換えてしまえば遅延バインディングできます。

    2011年12月20日 0:59