如何从 VBE 插件中获取正确的活动应用程序? [英] How to get the correct active Application from a VBE-Add-In?

查看:47
本文介绍了如何从 VBE 插件中获取正确的活动应用程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为访问的 VBE 编写一个 COM 加载项,我想在单击命令栏按钮后从 C# 中执行一个 vba 函数.

I'm writing a COM add-in for the VBE of access and I want to execute a vba-function out of C# after clicking a commandbar button.

所以我使用以下代码:

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"会得到错误的应用程序,并且在另一个数据库中调用该函数.不是命令栏按钮所属的那个.

This works fine if there is only one ms-access-db open. But if there are two open databases, ´GetActiveObject´ gets the wrong application and the function is called in the other database. Not in the one the commandbar button is part of.

那么,我如何获得正确的应用程序对象(=点击按钮的那个)?

So, how do I get the correct application object (= the one the button is clicked in)?

推荐答案

目前我使用这里的代码片段(仅限德语​​):

At the moment I use the snippet from here (german only):

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):

with this code (works only for 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);

但是必须有更好的解决方案来解决这个问题.

But there has to be a better solution to this problem.

这里也讨论了类似的问题:我如何获得*实际*主机应用程序实例?和这里:如何使用 Marshal.getActiveObject() 获取两个进程打开的正在运行的进程的 2 个实例

Similar issues are also discussed here: How do I get the *actual* host application instance? and here: How to use Marshal.getActiveObject() to get 2 instance of of a running process that has two processes open

这篇关于如何从 VBE 插件中获取正确的活动应用程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆