问题
I am injecting a piece of VBA code into a Microsoft Access database from .Net.
It is just a single line of code, which runs a Macro. All the Macro does is run a block of VBA code inside a module.
The issue I am having is that this all happens in a new MSAccess session, which I can't even see, instead of the session the user currently has open.
Is it possible to, instead, have this interact with the users current MSAccess session? The whole point of this is to open a particular form inside the MSAccess session on every occurence of a .Net event. My C# code is below:
using Microsoft.Office.Interop.Access;
var msAccess = new Application();
msAccess.OpenCurrentDatabase(@"x:\foo\bar.accdb", false);
msAccess.DoCmd.RunMacro("macCTI");
msAccess.CloseCurrentDatabase();
Thanks
回答1:
If you have a single instance of Access running you could use Marshal.GetActiveObject:
using Microsoft.Office.Interop.Access;
using System.Runtime.InteropServices
...
try
{
var msAccess = (Application)Marshal.GetActiveObject("Access.Application");
msAccess.DoCmd.RunMacro("macCTI");
}
catch (COMException ex)
{
// handle error
}
Alternatively, if more than one are running, and no two instances have the same database open, you could use Marshal.BindToMoniker:
var msAccess = (Application) Marshal.BindToMoniker(@"x:\foo\bar.accdb");
Note: This Microsoft knowledge base article has this to say:
Whether a COM server is Single Use (Multiple Instances) or Multiuse (Single Instance) might affect your decision to use GetActiveObject to get reference to that server. Because potentially more than one instance of Word, Excel, or Microsoft Access can be running, GetActiveObject on a particular server may return an instance that you did not expect. The instance that is first registered in the ROT is typically the instance that is returned by GetActiveObject. If you want to get an Automation Reference to a specific running instance of Word, Excel, or Microsoft Access, use BindToMoniker with the name of the file that is opened in that instance. For a Multiuse (Single Instance) server like PowerPoint, it does not matter, because the automation reference points to the same running instance.
回答2:
To not get a new Access instance you will have to see if an instance already exists. Use Marshal.GetActiveObject
as Matt suggested:
Application GetAccessApplication() {
var application = (Application) Marshal.GetActiveObject("Access.Application");
return application ?? new Application();
}
Notice that there is a slight disconnect here because you have to use the ProgID to get an existing instance of Access but to create a new instance you use the interop assembly.
You can then make the Access application visible:
var msAccess = GetAccessApplication();
msAccess.Visible = true;
来源:https://stackoverflow.com/questions/10397702/interop-with-microsoft-access-does-not-interact-with-users-session