2012-03-12 17 views
7

私はいくつかのMicrosoft Accessファイルを開き、それぞれの中から関数を実行するC#プログラムを持っています。デバッグエラーがVBAコード内で発生した場合にC#コードからMicrosoft Access VBAのデバッグエラーをキャプチャするにはどうすればよいですか?

Microsoft.Office.Interop.Access.Application app = 
    new Microsoft.Office.Interop.Access.Application(); 

app.Visible = true; 
app.OpenCurrentDatabase(accessFileFullPath, false, ""); 

//Call the function 
app.Eval(function); 

しかし、私は私のC#プログラムでトラップそれをしたいと思います:

基本的に、コードは次のようになります。

Please 答え: "あなたのVBAプログラムのエラーをトラップする"。私が入り込まない理由で、これは不可能です。

私が過去に使用したメソッドは、Visual Basic Debugウィンドウ(FindWindowEx Win32関数はゼロ以外の値を返します)へのハンドルを断続的に監視することです。私はこの方法が気に入らず、使い続けることを望んでいません。

Microsoft Excelに適用されるthis threadが見つかりました。本質的には、Microsoft.VisualBasic.CallByName()関数を使用しています。ユーザーの介入なしにtry/catchブロックに閉じ込めることができます。しかし、私はこれをMicrosoft Accessで動作させることができませんでした。主に、このコマンドを使用して関数/サブを呼び出す方法を理解できないためです。

誠にありがとうございます。

編集:私は以下の回答の1で述べたように、私はtry/catchブロックで​​をラップ試してみましたが、私のC#のプログラムは、ユーザーが上で「終了」ボタンをヒットするまで、それを無視しているようです"Microsoft Visual Basic"エラーダイアログ。私はユーザーとのやり取りは望んでおらず、むしろC#プログラムでの処理のためにVBAエラーをトラップしたいと思っています。

答えて

4

更新:何らかの理由でAccessファイル形式は2000年だったとき、私が投稿していた以前のコードは、私は、この新しいコードでもアクセス2002年と2010年のファイルで動作することが確認されている働いていました。

コード:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 
using VBA = Microsoft.Vbe.Interop; 

namespace CaptureVBAErrorsTest 
{ 
    class CaptureVBAErrors 
    { 
     public void runApp(string databaseName, string function) 
     { 
      VBA.VBComponent f = null; 
      VBA.VBComponent f2 = null; 
      Microsoft.Office.Interop.Access.Application app = null; 
      object Missing = System.Reflection.Missing.Value; 
      Object tempObject = null; 

      try 
      { 
       app = new Microsoft.Office.Interop.Access.Application(); 
       app.Visible = true; 
       app.OpenCurrentDatabase(databaseName, false, ""); 

       //Step 1: Programatically create a new temporary class module in the target Access file, with which to call the target function in the Access database 

       //Create a Guid to append to the object name, so that in case the temporary class and module somehow get "stuck", 
       //the temp objects won't interfere with other objects each other (if there are multiples). 
       string tempGuid = Guid.NewGuid().ToString("N"); 

       f = app.VBE.ActiveVBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_ClassModule); 

       //We must set the Instancing to 2-PublicNotCreatable 
       f.Properties.Item("Instancing").Value = 2; 
       f.Name = "TEMP_CLASS_" + tempGuid; 
       f.CodeModule.AddFromString(
        "Public Sub TempClassCall()\r\n" + 
        " Call " + function + "\r\n" + 
        "End Sub\r\n"); 

       //Step 2: Append a new standard module to the target Access file, and create a public function to instantiate the class and return it. 
       f2 = app.VBE.ActiveVBProject.VBComponents.Add(VBA.vbext_ComponentType.vbext_ct_StdModule); 
       f2.Name = "TEMP_MODULE_" + tempGuid 
       f2.CodeModule.AddFromString(string.Format(
        "Public Function instantiateTempClass_{0}() As Object\r\n" + 
        " Set instantiateTempClass_{0} = New TEMP_CLASS_{0}\r\n" + 
        "End Function" 
        ,tempGuid)); 

       //Step 3: Get a reference to a new TEMP_CLASS_* object 
       tempObject = app.Run("instantiateTempClass_" + tempGuid, 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, 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); 

       //Step 4: Call the method on the TEMP_CLASS_* object. 
       Microsoft.VisualBasic.Interaction.CallByName(tempObject, "TempClassCall", Microsoft.VisualBasic.CallType.Method); 
      } 
      catch (COMException e) 
      { 
       MessageBox.Show("A VBA Exception occurred in file:" + e.Message); 
      } 
      catch (Exception e) 
      { 
       MessageBox.Show("A general exception has occurred: " + e.StackTrace.ToString()); 
      } 
      finally 
      { 
       //Clean up 
       if (f != null) 
       { 
        app.VBE.ActiveVBProject.VBComponents.Remove(f); 
        Marshal.FinalReleaseComObject(f); 
       } 

       if (f2 != null) 
       { 
        app.VBE.ActiveVBProject.VBComponents.Remove(f2); 
        Marshal.FinalReleaseComObject(f2); 
       } 

       if (tempObject != null) Marshal.FinalReleaseComObject(tempObject); 

       if (app != null) 
       { 
        //Step 5: When you close the database, you call Application.Quit() with acQuitSaveNone, so none of the VBA code you just created gets saved. 
        app.Quit(Microsoft.Office.Interop.Access.AcQuitOption.acQuitSaveNone); 
        Marshal.FinalReleaseComObject(app); 
       } 

       GC.Collect(); 
       GC.WaitForPendingFinalizers(); 
      } 
     } 
    } 
} 

詳細:

Mike Rosenblumによってthe thread I had linked toによれば、CallByName()C#でオフィスコードを実行し、ことができ、トラップVBA例外(Application.Run()Application.Eval()ユーザーがデバッグウィンドウとやりとりした後にしか捕らえられないようです)。問題は、CallByName()がメソッドを呼び出すために[インスタンス化された]オブジェクトを必要とすることです。既定では、ExcelにはThisWorkbookというオブジェクトがあり、ブックを開くとインスタンス化されます。アクセスは、私が知る限り、アクセス可能な同様のオブジェクトを持っていません。

A subsequent post on the same threadは、Excelブックに動的にコードを追加し、標準モジュールでメソッドを呼び出すことを可能にすることを提案しています。 ThisWorkbookでこれを行うのは比較的簡単です.WorkWorkbookにコードビハインドがあり、自動的にインスタンス化されるためです。しかし、どうすればAccessでこれを行うことができますか?

ソリューションは、次のように上記2つの手法を組み合わせた:

  1. プログラムによるAccessデータベースに対象の関数を呼び出すためにどのと、ターゲット・アクセス・ファイルに新しい一時的クラスモジュールを作成します。クラスのInstancingプロパティは2 - PublicNotCreatableに設定する必要があります。つまり、そのクラスはそのプロジェクトの外では作成できませんが、公開されています。
  2. ターゲット・アクセス・ファイルに新しい標準モジュールを追加し、クラスをインスタンス化して戻すパブリック関数を作成します。
  3. 手順(2)でVBAコードを呼び出して、C#コード内のオブジェクトへの参照を取得します。これは、Access interopのApplication.Run()を使用して行うことができます。
  4. 標準モジュール内のメソッドを呼び出し、トラップ可能なCallByName--を使用して、(3)からオブジェクトのメソッドを呼び出します。
  5. Application.Quit()acQuitSaveNoneと呼んでデータベースを閉じると、作成したVBAコードは保存されません。

VBAエラーの説明を取得するには、 "e"がCOMExceptionオブジェクトである "e.Message"を使用します。

あなたはC#プロジェクトに次の.NETへの参照を追加していることを確認してください:

Microsoft.Office.Interop.Access 
Microsoft.Vbe.Interop 
Microsoft.VisualBasic 
3

私はそれが最良の方法であるかどうかわかりませんが、try/catchブロックでCOMExceptionを捕らえて、例外を調べて正確に何がスローされたのかを調べることができます。 Excelの相互運用機能を使用してこれを行います。

try { 
    DoSomething(); 
} catch (COMException ex) { 
    Console.WriteLine ex.ErrorCode.ToString(); 
    Console.WriteLine ex.Message; 
} 
+0

私はevalメソッドの周りのtry/catchを試してみましたが、完全にそれを無視しているようです。 Evalを使用してExcel関数を呼び出すのですか? – transistor1

+1

いいえ、私はそうではありません。私はinteropがそれを捕まえて消費者に泡立てると思っていたでしょう。 – squillman

+0

したがって、あなたのExcelブック内からVBA関数を呼び出すには、あなたのDoSomething()メソッドの中で何を使用しますか?私は何かが明らかに欠けていることを望んでいる! – transistor1

関連する問題