更新:何らかの理由で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つの手法を組み合わせた:
- プログラムによるAccessデータベースに対象の関数を呼び出すためにどのと、ターゲット・アクセス・ファイルに新しい一時的クラスモジュールを作成します。クラスの
Instancing
プロパティは2 - PublicNotCreatable
に設定する必要があります。つまり、そのクラスはそのプロジェクトの外では作成できませんが、公開されています。
- ターゲット・アクセス・ファイルに新しい標準モジュールを追加し、クラスをインスタンス化して戻すパブリック関数を作成します。
- 手順(2)でVBAコードを呼び出して、C#コード内のオブジェクトへの参照を取得します。これは、Access interopのApplication.Run()を使用して行うことができます。
- 標準モジュール内のメソッドを呼び出し、トラップ可能なCallByName--を使用して、(3)からオブジェクトのメソッドを呼び出します。
Application.Quit()
をacQuitSaveNone
と呼んでデータベースを閉じると、作成したVBAコードは保存されません。
VBAエラーの説明を取得するには、 "e"がCOMExceptionオブジェクトである "e.Message"を使用します。
あなたはC#プロジェクトに次の.NETへの参照を追加していることを確認してください:
Microsoft.Office.Interop.Access
Microsoft.Vbe.Interop
Microsoft.VisualBasic
私はevalメソッドの周りのtry/catchを試してみましたが、完全にそれを無視しているようです。 Evalを使用してExcel関数を呼び出すのですか? – transistor1
いいえ、私はそうではありません。私はinteropがそれを捕まえて消費者に泡立てると思っていたでしょう。 – squillman
したがって、あなたのExcelブック内からVBA関数を呼び出すには、あなたのDoSomething()メソッドの中で何を使用しますか?私は何かが明らかに欠けていることを望んでいる! – transistor1