Получить несколько запущенных COM-объектов одного типа - PullRequest
0 голосов
/ 06 декабря 2018

Я пытаюсь найти первый видимый экземпляр Word.Я нашел здесь полезный код и немного его изменил.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

using Microsoft.Office.Interop.Word;

namespace TestConsole
{
    internal class Program
    {
        [DllImport("ole32.dll")]
        private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);

        private static void Main(string[] args)
        {
            Application word1 = new Application();
            word1.Visible = false;
            Application word2 = new Application();
            word2.Visible = true;
            Application word3 = new Application();
            word3.Visible = false;

            int index = 0;
            while (true)
            {
                Application application = Program.GetRunningCOMObjectOfType<Application>(++index);
                if (application != null)
                {
                    Console.WriteLine($"{index}) IsVisible: {application.Visible}");
                    Debug.WriteLine($"{index}) IsVisible: {application.Visible}");
                }
                else
                {
                    break;
                }
            }

            Console.WriteLine("############# End of program #############");
            Console.ReadLine();
        }

        public static T GetRunningCOMObjectOfType<T>(int index)
        {
            IRunningObjectTable runningObjectTable = null;
            IEnumMoniker monikerList = null;

            try
            {
                if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null)
                {
                    return default(T);
                }

                runningObjectTable.EnumRunning(out monikerList);
                monikerList.Reset();
                IMoniker[] monikerContainer = new IMoniker[1];
                IntPtr pointerFetchedMonikers = IntPtr.Zero;
                int counter = 0;
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    runningObjectTable.GetObject(monikerContainer[0], out object comInstance);
                    if (comInstance is T castedInstance)
                    {
                        if (index == ++counter)
                        {
                            return castedInstance;
                        }
                    }
                }
            }
            finally
            {
                if (runningObjectTable != null)
                {
                    Marshal.ReleaseComObject(runningObjectTable);
                }

                if (monikerList != null)
                {
                    Marshal.ReleaseComObject(monikerList);
                }
            }

            return default(T);
        }
    }
}

Результат этого кода выглядит следующим образом:

1) IsVisible: False
2) IsVisible: False
3) IsVisible: False

Я ожидаю, что для одного экземпляра Visible должен вернуть true.Кажется, всегда возвращается первый экземпляр.Если word1 сделан видимым, true возвращается для всех экземпляров.

1 Ответ

0 голосов
/ 08 декабря 2018

После долгих попыток заставить это работать, оказалось, что это невозможно при таком общем подходе, потому что некоторые компоненты регистрируют разные экземпляры с одинаковыми ключами в Таблице запущенных объектов (ROT).IRunningObjectTable.GetObject возвращает первый зарегистрированный экземпляр в этом случае.Например, Word делает это таким образом с экземплярами ApplicationClass.

Решение:

Похоже, что нет никакого чистого и общего решения, но что-то, что сработало для меня,Word также регистрирует экземпляры Document в ROT.Таким образом, мы можем легко получить эти документы, и оттуда мы получаем приложение.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

using Microsoft.Office.Interop.Word;

namespace ConsoleApp
{
    internal class Program
    {
        #region public methods

        private static void Main(string[] args)
        {
            Application word1 = new Application();
            word1.Visible = false;
            word1.Documents.Add();

            Application word2 = new Application();
            word2.Visible = true;
            word2.Documents.Add();
            word2.Documents.Add();

            Application word3 = new Application();
            word3.Visible = false;
            word3.Documents.Add();
            word3.Documents.Add();
            word3.Documents.Add();

            List<(IMoniker moniker, IBindCtx bindingContext, object instance)> x = Program.GetRunningComObjects();
            foreach ( (IMoniker moniker, IBindCtx bindingContext, object instance)  in x)
            {
                // get only the instances that 
                if (instance is Document doc && doc.Application.ActiveDocument == doc)
                {
                    moniker.GetDisplayName(bindingContext, moniker, out string displayName);
                    Console.WriteLine($"{displayName}");

                    Application wordInstance = doc.Application;
                    Console.WriteLine($"\tVisible:\t{wordInstance.Visible}");
                    Console.WriteLine($"\tDocumentCount:\t{wordInstance.Documents.Count}");
                }
            }

            word1.Quit(false);
            word2.Quit(false);
            word3.Quit(false);

            Console.WriteLine();
            Console.WriteLine("##############      End of program     ##############");
            Console.WriteLine("############## press enter to continue ##############");
            Console.ReadLine();
        }

        [DllImport("ole32.dll")]
        private static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);

        [DllImport("ole32.dll")]
        private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);

        private static List<(IMoniker moniker, IBindCtx bindingContext, object instance)> GetRunningComObjects()
        {
            List<(IMoniker, IBindCtx, object)> result = new List<(IMoniker, IBindCtx, object)>();
            IRunningObjectTable runningObjectTable = null;
            IEnumMoniker monikerList = null;

            try
            {
                if (Program.GetRunningObjectTable(0, out runningObjectTable) != 0
                    || runningObjectTable == null)
                {
                    return result;
                }

                runningObjectTable.EnumRunning(out monikerList);
                monikerList.Reset();
                IMoniker[] monikerContainer = new IMoniker[1];
                IntPtr pointerFetchedMonikers = IntPtr.Zero;
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    Program.CreateBindCtx(0, out IBindCtx bindingContext);
                    runningObjectTable.GetObject(monikerContainer[0], out object comInstance);
                    result.Add((monikerContainer[0], bindingContext, comInstance));
                }
            }
            finally
            {
                if (runningObjectTable != null)
                {
                    Marshal.ReleaseComObject(runningObjectTable);
                }

                if (monikerList != null)
                {
                    Marshal.ReleaseComObject(monikerList);
                }
            }

            return result;
        }

        #endregion
    }
}

Этот код дает следующий результат:

Dokument1
        Visible:        False
        DocumentCount:  1
Dokument3
        Visible:        True
        DocumentCount:  2
Dokument6
        Visible:        False
        DocumentCount:  3

ОК, это немного подозрительно.Проблема такого подхода в том, что экземпляры, у которых нет документа, не могут быть найдены.

...