Laden...

Laufende COM-Objekte abfragen

Erstellt von Rainbird vor 16 Jahren Letzter Beitrag vor 11 Jahren 13.601 Views
Rainbird Themenstarter:in
3.728 Beiträge seit 2005
vor 16 Jahren
Laufende COM-Objekte abfragen

Beschreibung:

Viele bekannte Anwendungen (z.B. Excel, Word, Access, Outlook oder Visual Studio) bieten Automatisierungsmöglichkeiten über COM-Schnittstellen. Manchmal möchte man eine bereits geöffnete Instanz einer solchen Anwendung fernsteuern. Wenn nun z.B. aber gleich mehrere Excel-Sheets gleichzeitig geöffnet sind? Wie findet man die richtige Instanz?

Das geht über die "Running Object Table". Dort werden laufende Objekte registiert, die als COM-Server fungieren. Jedes laufende Objekt bekommt einen Namen. Oft ist es der Dateiname des Dokuments oder eine GUID (Das hängt von der Anwendung ab).

Das folgende Snippet gibt alle laufenden COM-Objekte aus der Running Object Table zurück und kann direkt auf die einzelnen Objekte über ihren Namen zugreifen.

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

namespace Rainbird.Tools.COMInterop
{
    /// <summary>
    /// Ermöglicht .NET-Anwendungen direkten Zugriff auf die Running Object Table (Tabelle mit allen momentan laufenden COM-Objekte)
    /// </summary>
    public class RunningObjectTable
    {
        /// <summary>
        /// Privater Standardkonstruktor.
        /// </summary>
        private RunningObjectTable() { }

        // Win32-API-Aufruf zum lesen der ROT
        [DllImport("ole32.dll")]
        private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);

        // Win32-API-Aufruf zum erstellen von Bindungen
        [DllImport("ole32.dll")]
        private static extern int CreateBindCtx(uint reserved, out IBindCtx pctx);

        /// <summary>
        /// Gibt einen Verweis auf eine laufendes COM-Objekt anhand ihres Anzeigenamens zurück.
        /// </summary>
        /// <param name="objectDisplayName">Anzeigename einer COM-Instanz</param>
        /// <returns>Verweis auf COM-Objekt, oder null, wenn kein COM-Objekt mit dem angegbenen Namen läuft</returns>
        public static object GetRunningCOMObjectByName(string objectDisplayName)
        {
            // ROT-Schnittstelle
            IRunningObjectTable runningObjectTable = null;

            // Moniker-Auflistung
            IEnumMoniker monikerList = null;

            try
            {
                // Running Object Table abfragen und nichts zurückgeben, wenn keine COM-Objekte laufen
                if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null) return null;

                // Moniker abfragen
                runningObjectTable.EnumRunning(out monikerList);

                // An den Anfang der Auflistung springen
                monikerList.Reset();

                // Array für Moniker-Abfrage erzeugen
                IMoniker[] monikerContainer = new IMoniker[1];

                // Zeiger auf die Anzahl der tatsächlich abgefragten Moniker erzeugen
                IntPtr pointerFetchedMonikers = IntPtr.Zero;

                // Alle Moniker durchlaufen
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    // Objekt für Bindungsinformationen
                    IBindCtx bindInfo;

                    // Variable für den Anzeigenamen des aktuellen COM-Objekts
                    string displayName;

                    // Bindungsobjekt erzeugen
                    CreateBindCtx(0, out bindInfo);

                    // Anzeigename des COM-Objekts über den Moniker abfragen
                    monikerContainer[0].GetDisplayName(bindInfo, null, out displayName);

                    // Bindungsobjekt entsorgen
                    Marshal.ReleaseComObject(bindInfo);

                    // Wenn der Anzeigename mit dem gesuchten übereinstimmt ...
                    if (displayName.IndexOf(objectDisplayName) != -1)
                    {
                        // Variable für COM-Objekt
                        object comInstance;

                        // COM-Objekt über den Anzeigenamen abrfragen
                        runningObjectTable.GetObject(monikerContainer[0], out comInstance);

                        // COM-Objekt zurückgeben
                        return comInstance;
                    }
                }
            }
            catch
            {
                // Nichts zurückgeben
                return null;
            }
            finally
            {
                // Ggf. COM-Verweise entsorgen
                if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
                if (monikerList != null) Marshal.ReleaseComObject(monikerList);
            }
            // Nichts zurückgeben
            return null;
        }

        /// <summary>
        /// Gibt eine Liste mit Anzeigenamen aller momentan laufenden COM-Objekte zurück.
        /// </summary>
        /// <returns>Liste mit Anzeigenamen</returns>
        public static IList<string> GetRunningCOMObjectNames()
        {
            // Auflistung der Anzeigenamen erzeugen
            IList<string> result = new List<string>();

            // Informationsobjekt der laufenden COM-Instanzen
            IRunningObjectTable runningObjectTable = null;

            // Moniker-Auflistung
            IEnumMoniker monikerList = null;

            try
            {
                // Running Object Table abfragen und nichts zurückgeben, wenn keine COM-Objekte laufen
                if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null) return null;

                // Moniker abfragen
                runningObjectTable.EnumRunning(out monikerList);

                // An den Anfang der Auflistung springen
                monikerList.Reset();

                // Array für Moniker-Abfrage erzeugen
                IMoniker[] monikerContainer = new IMoniker[1];

                // Zeiger auf die Anzahl der tatsächlich abgefragten Moniker erzeugen
                IntPtr pointerFetchedMonikers = IntPtr.Zero;

                // Alle Moniker durchlaufen
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    // Objekt für Bindungsinformationen
                    IBindCtx bindInfo;

                    // Variable für den Anzeigenamen des aktuellen COM-Objekts
                    string displayName;

                    // Bindungsobjekt erzeugen
                    CreateBindCtx(0, out bindInfo);

                    // Anzeigename des COM-Objekts über den Moniker abfragen
                    monikerContainer[0].GetDisplayName(bindInfo, null, out displayName);

                    // Bindungsobjekt entsorgen
                    Marshal.ReleaseComObject(bindInfo);

                    // Anzeigenamen der Auflistung zufügen
                    result.Add(displayName);
                }
                // Auflistung zurückgeben
                return result;
            }
            catch
            {
                // Nichts zurückgeben
                return null;
            }
            finally
            {
                // Ggf. COM-Verweise entsorgen
                if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
                if (monikerList != null) Marshal.ReleaseComObject(monikerList);
            }
        }
    }
}

Schlagwörter: ROT,Running Object Table,Office,Excel,COM,COM-Interop,Access,Word,Visio,PowerPoint,Outlook,GetObject,COM-Server,OLE

Quelle: .NET-Snippets

T
17 Beiträge seit 2007
vor 11 Jahren
Laufende COM-Objekte abfragen

Hallo Rainbird,

Vielen Dank für deine Klasse RunningObjectTable!
habe soeben eine Excel-Datei mit deiner Funktion GetRunningCOMObjectByName als object zurückerhalten. Nun schaff ichs einfach nicht dieses Object in ein Excel.Application-Objekt zu verwandeln, damit ich ich dann die Datei auch schliessen oder anderes damit anstellen kann.

Herzliche Grüsse
Tecla

Gelöschter Account
vor 11 Jahren

Aufgrund einer Frage im Office Forum habe ich eine modifizierte Variante erstellt.
Diese arbeitet nicht nach Display-Name in der ROT sondern vielmehr nach ClassID bzw. Komponentenname und Klassenname(die ClassID setzt sich üblicherweise daraus zusammen) Damit ist eine ähnliche Verwendung wie Marshal.GetActiveObject und Co. möglich.

public class RunningObjectTable
{
 // Win32-API-call for reading ROT
        [DllImport("ole32.dll")]
        private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);

        // Win32-API-call to create binding
        [DllImport("ole32.dll")]
        private static extern int CreateBindCtx(uint reserved, out IBindCtx pctx);

        /// <summary>
        /// Gibt eine laufene Instanz aus der Running Object Table zurück
        /// </summary>
        /// <param name="componentName">Einfache Komponentenangabe z.B. "Excel" oder "Outlook"</param>
        /// <param name="className">Name der Klasse z.B. "Application" oder "Workbook"</param>
        /// <returns>Den ersten COM Proxy in der RunngingObject Table der den Eingangsparametern entspricht oder null</returns>
        private static object GetApplicationInstanceFromROT(string componentName, string className)
        {
            IEnumMoniker monikerList = null;
            IRunningObjectTable runningObjectTable = null;
            try
            {
                // query table and returns null if no objects runnings
                if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null)
                    return null;

                // query moniker & reset 
                runningObjectTable.EnumRunning(out monikerList);
                monikerList.Reset();

                IMoniker[] monikerContainer = new IMoniker[1];
                IntPtr pointerFetchedMonikers = IntPtr.Zero;

                // fetch all moniker
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    // create binding object
                    IBindCtx bindInfo;
                    CreateBindCtx(0, out bindInfo);

                    // query com proxy info       
                    object comInstance = null;
                    runningObjectTable.GetObject(monikerContainer[0], out comInstance);

                    string name = TypeDescriptor.GetClassName(comInstance);
                    string component = TypeDescriptor.GetComponentName(comInstance, false);

                    if ((component == componentName) && (name == className))
                    {
                        Marshal.ReleaseComObject(bindInfo);
                        return comInstance;
                    }
                    else if ((component == "Micrcosoft " + componentName) && (name == className))
                    {
                        // nur manche office anwendungen führen je nach version "Microsoft" im Komponentennamen
                        Marshal.ReleaseComObject(bindInfo);
                        return comInstance;
                    }
                    else
                        Marshal.ReleaseComObject(comInstance);

                    Marshal.ReleaseComObject(bindInfo);
                }

                // not running 
                return null;
            }
            finally
            {
                // release proxies
                if (runningObjectTable != null)
                    Marshal.ReleaseComObject(runningObjectTable);
                if (monikerList != null)
                    Marshal.ReleaseComObject(monikerList);
            }
        }

        /// <summary>
        /// Gibt eine oder mehrere laufende Instanzen aus der Running Object Table zurück
        /// </summary>
        /// <param name="componentName">infache Komponentenangabe z.B. "Excel" oder "Outlook"</param>
        /// <param name="className">Name der Klasse z.B. "Application" oder "Workbook"</param>
        /// <returns>Alle COM Proxies in der RunngingObject Table die den Eingangsparametern entsprechen</returns>
        private static List<object> GetApplicationInstancesFromROT(string componentName, string className)
        {
            IEnumMoniker monikerList = null;
            IRunningObjectTable runningObjectTable = null;
            List<object> resultList = new List<object>();
            try
            {
                // query table and returns null if no objects runnings
                if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null)
                    return null;

                // query moniker & reset 
                runningObjectTable.EnumRunning(out monikerList);
                monikerList.Reset();

                IMoniker[] monikerContainer = new IMoniker[1];
                IntPtr pointerFetchedMonikers = IntPtr.Zero;

                // fetch all moniker
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    // create binding object
                    IBindCtx bindInfo;
                    CreateBindCtx(0, out bindInfo);

                    // query com proxy info       
                    object comInstance = null;
                    runningObjectTable.GetObject(monikerContainer[0], out comInstance);

                    string name = TypeDescriptor.GetClassName(comInstance);
                    string component = TypeDescriptor.GetComponentName(comInstance, false);

                    if ((component == componentName) && (name == className))
                    {
                        Marshal.ReleaseComObject(bindInfo);
                        resultList.Add(comInstance);
                    }
                    else if ((component == "Micrcosoft " + componentName) && (name == className))
                    {
                        // nur manche office anwendungen führen je nach version "Microsoft" im Komponentennamen
                        Marshal.ReleaseComObject(bindInfo);
                        resultList.Add(comInstance);
                    }
                    else
                        Marshal.ReleaseComObject(comInstance);

                    Marshal.ReleaseComObject(bindInfo);
                }

                // not running 
                return resultList;
            }
            finally
            {
                // release proxies
                if (runningObjectTable != null)
                    Marshal.ReleaseComObject(runningObjectTable);
                if (monikerList != null)
                    Marshal.ReleaseComObject(monikerList);
            }
        }
}

Der Aufruf ist vergleichsweise simpel:

foreach(object proxy in RunningObjectTable.GetApplicationInstancesFromROT("Excel", "Application"))
{
   Excel.Application app = proxy as Excel.Application; // Interop Beispiel
    
   Marshal.ReleaseComObject(proxy);
}
N
54 Beiträge seit 2011
vor 11 Jahren

Hallo,

Vielen Dank an Rainbird und Sebastian, habt mir sehr geholfen.

gruß

mfg