如何从 VBE 加载项中获取正确的活动应用程序?
Posted
技术标签:
【中文标题】如何从 VBE 加载项中获取正确的活动应用程序?【英文标题】:How to get the correct active Application from a VBE-Add-In? 【发布时间】:2018-08-06 09:11:57 【问题描述】:我正在为访问的 VBE 编写一个 COM 加载项,我想在单击命令栏按钮后从 C# 中执行一个 vba 函数。
所以我使用以下代码:
const string ApplicationObjectName = "Access.Application";
Microsoft.Office.Interop.Access.Application app = (Microsoft.Office.Interop.Access.Application)Marshal.GetActiveObject(ApplicationObjectName);
app.Run(functionName);
如果只有一个 ms-access-db 打开,这可以正常工作。但是如果有两个打开的数据库,“GetActiveObject”会得到错误的应用程序,并且在另一个数据库中调用该函数。不在命令栏按钮所在的那个。
那么,我如何获得正确的应用程序对象(= 单击按钮的那个)?
【问题讨论】:
【参考方案1】:目前我从这里使用 sn-p(仅限德语):
https://dotnet-snippets.de/snippet/laufende-com-objekte-abfragen/526
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace Rainbird.Tools.COMInterop
public class RunningObjectTable
private RunningObjectTable()
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx pctx);
public static object GetRunningCOMObjectByName(string objectDisplayName)
IRunningObjectTable runningObjectTable = null;
IEnumMoniker monikerList = null;
try
if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null) return null;
runningObjectTable.EnumRunning(out monikerList);
monikerList.Reset();
IMoniker[] monikerContainer = new IMoniker[1];
IntPtr pointerFetchedMonikers = IntPtr.Zero;
while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
IBindCtx bindInfo;
string displayName;
CreateBindCtx(0, out bindInfo);
monikerContainer[0].GetDisplayName(bindInfo, null, out displayName);
Marshal.ReleaseComObject(bindInfo);
if (displayName.IndexOf(objectDisplayName) != -1)
object comInstance;
runningObjectTable.GetObject(monikerContainer[0], out comInstance);
return comInstance;
catch
return null;
finally
if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
if (monikerList != null) Marshal.ReleaseComObject(monikerList);
return null;
public static IList<string> GetRunningCOMObjectNames()
IList<string> result = new List<string>();
IRunningObjectTable runningObjectTable = null;
IEnumMoniker monikerList = null;
try
if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null) return null;
runningObjectTable.EnumRunning(out monikerList);
monikerList.Reset();
IMoniker[] monikerContainer = new IMoniker[1];
IntPtr pointerFetchedMonikers = IntPtr.Zero;
while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
IBindCtx bindInfo;
string displayName;
CreateBindCtx(0, out bindInfo);
monikerContainer[0].GetDisplayName(bindInfo, null, out displayName);
Marshal.ReleaseComObject(bindInfo);
result.Add(displayName);
return result;
catch
return null;
finally
if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
if (monikerList != null) Marshal.ReleaseComObject(monikerList);
使用此代码(仅适用于 ms-access):
var activeProject = m_VBE.ActiveVBProject;
Microsoft.Office.Interop.Access.Application app = (Microsoft.Office.Interop.Access.Application)RunningObjectTable.GetRunningCOMObjectByName(activeProject.FileName);
app.Run(functionName);
但这个问题必须有更好的解决方案。
这里也讨论了类似的问题:How do I get the *actual* host application instance? 和这里:How to use Marshal.getActiveObject() to get 2 instance of of a running process that has two processes open
【讨论】:
以上是关于如何从 VBE 加载项中获取正确的活动应用程序?的主要内容,如果未能解决你的问题,请参考以下文章
无论应用程序状态如何,都可以从通知中正确启动 Activity