如何从 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 加载项中获取正确的活动应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

Word 加载项中的绑定用法

无论应用程序状态如何,都可以从通知中正确启动 Activity

如何从RecyclerView项中获取Value

如何从另一个活动中编辑和获取 editText 输入字段的更新?

Android - 无法从列表项中增加或减少值

从onListItemClick获取解析对象ID