【问题标题】:Get all MSACCESS application instances using C# Interop使用 C# Interop 获取所有 MSACCESS 应用程序实例
【发布时间】:2018-10-31 11:47:05
【问题描述】:

我需要获取系统(Windows)中所有当前打开的 MSACCESS 实例的列表,以便能够从我的应用程序中关闭它们中的任何一个。我对 EXCEL 和 WINWORD 没有问题,但无法连接 Access。

我使用 Office 2016,我看到 MSACCESS 为每个打开的数据库文件创建单独的进程。所以我想我必须从窗口句柄中获取应用程序实例。我已尝试修改此代码:How to iterate through instance of Excel c#

我能够获取所有 MSACCESS 进程,但 Excel 或 Word 代码不适用于 MSACCESS。代码行:

if (buf.ToString() == "EXCEL7")

总是给我 MsoCommandBarDock 值。 关于如何实现这一目标的任何想法?

【问题讨论】:

  • MS Access 主窗口的类名恰好是OMain(CS_OWNDC 风格)。为什么应该是EXCEL7?请注意,这在版本之间会有所不同,并且可能会在 MS 选择更新时随时更改。 MsoCommandBarDock 在窗口层次结构中太深了一两级。
  • 在Access中,可以直接从顶层窗口的hWnd中获取应用对象。在 Excel 中,您需要获取特定的嵌套窗口,并且可以从它的 hWnd 中获取应用程序对象。这意味着您应该对 Access 使用更简单的方法。您可以使用从(int)p.MainWindowHandle 获取的 hWnd 直接调用AccessibleObjectFromWindow。您可以废弃所有枚举子窗口的代码。我会写一个简短的答案,但是我的 C# 充其量只是初学者级别,而且我没有手边的测试环境,这应该可以帮助您入门。
  • 如果您只想关闭应用程序,为什么不直接按名称获取进程并终止它?
  • @krishKM 该应用程序应保存数据并正常关闭。简单的杀戮是不够的:(
  • @ErikvonAsmuth 就是这样!我必须省略子查找程序才能访问 MSAccess 应用程序类。请发布一个新答案,以便我接受。

标签: c# ms-access office-interop


【解决方案1】:

基于answer for Excel,Access版本类似:

const uint OBJID_NATIVEOM = 0xFFFFFFF0;

var procs = new List<Process>(Process.GetProcessesByName("MSACCESS.EXE"));

foreach (var p in procs)
{
    var mainHandle = (int)p.MainWindowHandle;
    if (mainHandle > 0)
    {
        var IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
        Microsoft.Office.Interop.Access.Application app = null;
        int res = AccessibleObjectFromWindow(mainHandle, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), ref app);
        if (res >= 0)
        {
            Debug.Assert(app.hWndAccessApp == mainHandle);
            Console.WriteLine(app.Name);
        }
    }
}

我在 Windows 10 上使用 Access 2016 对其进行了测试,en-us 语言环境。 主要区别在于访问的窗口层次结构不像Excel那样复杂,因此可以省略子窗口的迭代。

免责声明:这依赖于闭源 Windows 应用程序的内部结构。微软作为其供应商不鼓励这种技巧,原因很明显:他们可能会在内部结构(窗口层次结构)发生变化的任何时候发布和更新或发布新版本,从而破坏依赖于此的代码。此外,MS Access 曾经有一个单一的文档视图模式,这可能会在同一版本中为您提供两个版本的窗口层次结构。不要在商业产品/生产软件中这样做。

【讨论】:

    【解决方案2】:

    根据 Cee McSharpface 的 answer,在 2021 年(Microsoft Access for Microsoft 365 MSO (16.0.14326.20504) 64 位和 Windows 10 20H2)我必须按如下方式调整解决方案:

    [DllImport("oleacc.dll")]
    private static extern int AccessibleObjectFromWindow(
    int hwnd, uint dwObjectID, byte[] riid,
        ref Microsoft.Office.Interop.Access.Application ptr);  
    
    const uint OBJID_NATIVEOM = 0xFFFFFFF0;
    
    var procs = new List<Process>(Process.GetProcessesByName("MSACCESS"));
    
    foreach (var p in procs)
    {
        var mainHandle = (int)p.MainWindowHandle;
        if (mainHandle > 0)
        {
            var IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
            Microsoft.Office.Interop.Access.Application app = null;
            int res = AccessibleObjectFromWindow(mainHandle, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), ref app);
            if (res >= 0)
            {
                Debug.Assert(app.hWndAccessApp() == mainHandle);
                Console.WriteLine(app.Name);
            }
        }
    }
    

    请注意以下变化:

    GetProcessesByName 使用"MSACESS" 而不是"MSACCESS.EXE",根据this 文档:

    进程名称是进程的友好名称,例如 Outlook, 不包括 .exe 扩展名或路径

    AccessibleObjectFromWindow 使用 ref Microsoft.Office.Interop.Access.Application ptr,因为在 Excel 互操作中没有 Window 对象。

    【讨论】:

      【解决方案3】:

      有很多方法可以做到这一点,包括从 ROT (运行对象表)中检索 COM 对象。由于您需要“只是”才能关闭应用程序,因此以下代码应该可以正常工作。

      using System.Diagnostics;
      using System.Linq;
      
      Process.GetProcessesByName("MSACCESS").All(x => x.CloseMainWindow());
      

      这会向all发送关闭消息访问主窗口,类似于用户关闭应用程序。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-24
        • 2015-02-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多