4

有谁知道如何Application使用 C# 访问 Visio 2010 的多个实例的 VSTO 对象?Marshal.GetActiveObject()只返回活动实例。

论坛上有关于如何Application为所有 Excel 实例获取多个对象的帖子——作者使用该技术按进程遍历每个 Excel 实例,从 Excel 实例的子窗口获取本机对象模型,最后获取Application对象。

此技术适用于 Excel,但我无法从对允许我引用父对象的 Visio 窗口句柄的调用中获取有效的本机 OM(IDispatch或)。IAccessibleAccessibleObjectFromWindow()Application

这是我的代码片段:

using VISIO = Microsoft.Office.Interop.Visio;

Process[] processes = Process.GetProcessesByName(Visio.APP_PROCESS_NAME);
foreach (Process process in processes)
{
    int hwnd = (int)process.MainWindowHandle;

    // We need to enumerate the child windows to find one that
    // supports accessibility. To do this, instantiate the
    // delegate and wrap the callback method in it, then call
    // EnumChildWindows, passing the delegate as the 2nd arg.
    if (hwnd != 0)
    {
        int hwndChild = 0;
        cb = new EnumChildCallback(EnumChildProc);
        EnumChildWindows(hwnd, cb, ref hwndChild);

        // If we found an accessible child window, call
        // AccessibleObjectFromWindow, passing the constant
        // OBJID_NATIVEOM (defined in winuser.h) and
        // IID_IDispatch - we want an IDispatch pointer
        // into the native object model.
        if (hwndChild != 0)
        {
            const uint OBJID_NATIVEOM = 0xFFFFFFF0;
            Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
            EXL.Window ptr = null;

            int hr = AccessibleObjectFromWindow(
                hwndChild, OBJID_NATIVEOM,
                IID_IDispatch.ToByteArray(), ref ptr);
            if (hr >= 0)
            {
                // If we successfully got a native OM
                // IDispatch pointer, we can QI this for
                // an Viso Application (using the implicit
                // cast operator supplied in the PIA).
                VISIO.Application app = (VISIO.Application)ptr.Application;
                allInstances.Add(app);
            }
        }
    }
}
4

1 回答 1

0

我找到了一个解决方案,它遍历运行时对象表 (ROT) 以获取与 Visio 文档 (".vdx") 对应的所有名字对象并获取它们对应的 VSO.Document 对象(使用 VSO=Microsoft.Office.Interop.Visio);使用 VSO.Document 对象,我可以从 Document.Application 属性中获取 VSO.Application 对象。

从 Andrew Baker 的这篇文章:http ://www.vbusers.com/codecsharp/codeget.asp?ThreadID=69&PostID= 1,我创建了一个 ROTUtil 实用程序/帮助类:

/// <summary>
/// The COM running object table utility class.
/// </summary>
public class ROTUtil
{
    #region APIs

    [DllImport("ole32.dll")]
    private static extern int GetRunningObjectTable(int reserved,
        out UCOMIRunningObjectTable prot);

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

    [DllImport("ole32.dll", PreserveSig = false)]
    private static extern void CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);

    [DllImport("ole32.dll", PreserveSig = false)]
    private static extern void CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);

    [DllImport("ole32.dll")]
    private static extern int ProgIDFromCLSID([In()]ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)]out string lplpszProgID);

    #endregion

    #region Public Methods

    /// <summary>
    /// Converts a COM class ID into a prog id.
    /// </summary>
    /// <param name="progID">The prog id to convert to a class id.</param>
    /// <returns>Returns the matching class id or the prog id if it wasn't found.</returns>
    public static string ConvertProgIdToClassId(string progID)
    {
        Guid testGuid;
        try
        {
            CLSIDFromProgIDEx(progID, out testGuid);
        }
        catch
        {
            try
            {
                CLSIDFromProgID(progID, out testGuid);
            }
            catch
            {
                return progID;
            }
        }
        return testGuid.ToString().ToUpper();
    }

    /// <summary>
    /// Converts a COM class ID into a prog id.
    /// </summary>
    /// <param name="classID">The class id to convert to a prog id.</param>
    /// <returns>Returns the matching class id or null if it wasn't found.</returns>
    public static string ConvertClassIdToProgId(string classID)
    {
        Guid testGuid = new Guid(classID.Replace("!", ""));
        string progId = null;
        try
        {
            ProgIDFromCLSID(ref testGuid, out progId);
        }
        catch (Exception)
        {
            return null;
        }
        return progId;
    }

    /// <summary>
    /// Get a snapshot of the running object table (ROT).
    /// </summary>
    /// <returns>A hashtable mapping the name of the object in the ROT to the corresponding object
    /// <param name="filter">The filter to apply to the list (nullable).</param>
    /// <returns>A hashtable of the matching entries in the ROT</returns>
    public static Hashtable GetActiveObjectList(string filter)
    {
        Hashtable result = new Hashtable();

        int numFetched;
        UCOMIRunningObjectTable runningObjectTable;
        UCOMIEnumMoniker monikerEnumerator;
        UCOMIMoniker[] monikers = new UCOMIMoniker[1];

        GetRunningObjectTable(0, out runningObjectTable);
        runningObjectTable.EnumRunning(out monikerEnumerator);
        monikerEnumerator.Reset();

        while (monikerEnumerator.Next(1, monikers, out numFetched) == 0)
        {
            UCOMIBindCtx ctx;
            CreateBindCtx(0, out ctx);

            string runningObjectName;
            monikers[0].GetDisplayName(ctx, null, out runningObjectName);

            if (filter == null || filter.Length == 0 || runningObjectName.IndexOf(filter) != -1)
            {
                object runningObjectVal;
                runningObjectTable.GetObject(monikers[0], out runningObjectVal);
                result[runningObjectName] = runningObjectVal;
            }
        }

        return result;
    }

    /// <summary>
    /// Returns an object from the ROT, given a prog Id.
    /// </summary>
    /// <param name="progId">The prog id of the object to return.</param>
    /// <returns>The requested object, or null if the object is not found.</returns>
    public static object GetActiveObject(string progId)
    {
        // Convert the prog id into a class id
        string classId = ConvertProgIdToClassId(progId);

        UCOMIRunningObjectTable prot = null;
        UCOMIEnumMoniker pMonkEnum = null;
        try
        {
            int Fetched = 0;
            // Open the running objects table.
            GetRunningObjectTable(0, out prot);
            prot.EnumRunning(out pMonkEnum);
            pMonkEnum.Reset();
            UCOMIMoniker[] pmon = new UCOMIMoniker[1];

            // Iterate through the results
            while (pMonkEnum.Next(1, pmon, out Fetched) == 0)
            {
                UCOMIBindCtx pCtx;

                CreateBindCtx(0, out pCtx);

                string displayName;
                pmon[0].GetDisplayName(pCtx, null, out displayName);
                Marshal.ReleaseComObject(pCtx);
                if (displayName.IndexOf(classId) != -1)
                {
                    // Return the matching object
                    object objReturnObject;
                    prot.GetObject(pmon[0], out objReturnObject);
                    return objReturnObject;
                }
            }
            return null;
        }
        finally
        {
            // Free resources
            if (prot != null)
                Marshal.ReleaseComObject(prot);
            if (pMonkEnum != null)
                Marshal.ReleaseComObject(pMonkEnum);
        }
    }

然后这是我的类中获取实例的方法:

    /// <summary>
    /// This strategy of getting the VSO.Application instances uses the appropriate file monikers
    /// from the Runtime Object Table to get the VSO.Document object and hence the VSO.Document.Application
    /// property.
    /// </summary>
    /// <param name="allInstances"></param>
    protected void GetInstancesROTStrategy(IList<VSO.Application> allInstances)
    {
        // Iterate through all the objects in the ROT
        string filter = ".vdx";
        Hashtable runningObjects = ROTUtil.GetActiveObjectList(filter);
        Hashtable appInstances = new Hashtable();

        // Display the object ids
        foreach (DictionaryEntry de in runningObjects)
        {
            // get visio document file monikers
            string progId = de.Key.ToString();
            object getObj = ROTUtil.GetActiveObject(progId);
            if (getObj != null)
            {
                // try to cast the object to a VSO.Document
                VSO.Document doc = getObj as VSO.Document;
                if (doc != null)
                {
                    VSO.Application app = doc.Application;
                    // only add to results IList if not duplicate
                    if (!appInstances.ContainsKey(app.WindowHandle32))
                    {
                        appInstances.Add(app.WindowHandle32, app);
                        allInstances.Add(app);
                    }
                }
            }
        }
    }
于 2012-09-26T02:17:57.397 回答