【问题标题】:How to get instance using the Process object如何使用 Process 对象获取实例
【发布时间】:2014-08-15 17:19:22
【问题描述】:

我想做的是编写一个方法来遍历所有打开的 excel 实例并确定是否已经打开了特定的 excel 文件。如果是,则将带有打开文件的 excel 实例传递出去以进行进一步的工作。

我的难题是如何将流程对象转换为 Excel.Application,以便我可以循环浏览实例中的工作簿。

我确实阅读了类似帖子 How to get Excel instance or Excel instance CLSID using the Process ID? 中的答案。但是,Mike 在他的回答中提到的 Andrew Whitechapel 的文章 Getting the Application Object in a Shimmed Automation Add-in 已经无法访问了。

示例代码:

using System;
using System.Diagnostics;
using Excel = Microsoft.Office.Interop.Excel;
namespace test
{
    public class try
    {
        private bool IsExcelOpened(string wbookname, out Excel.Application exApp)
        {
            bool isOpened = false;
            Excel.Application exl=new Excel.Application();

            Process[] allopenexcel = Process.GetProcessesByName("EXCEL");
            foreach (Process excl in allopenexcel)
            {

                exl=MagicConversionFromProcessToExcel(excl)   // struggle here, how to do this

                foreach (Excel.Workbook wb in exl.Workbooks)
                {
                    if (wb.Name == wbookname)
                    {
                        isOpened = true;
                        break;
                    }
                 }
                 if (isOpened) break;
             }

            exApp = exl;
            return isOpened;
        }
    }
}

【问题讨论】:

标签: c# excel


【解决方案1】:

我创建了一个类,它可以遍历所有当前运行的 Excel 实例,还可以通过 ProcessID、Hwnd 或 Process 对象查找 Application 对象。代码如下,但请查看链接以获取更多说明。

http://www.codeproject.com/Tips/1080611/Get-a-Collection-of-All-Running-Excel-Instances

这尚未在所有版本的 Excel 或 Windows 上进行测试。


代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;

//Don't add the entire interop namespace, it will introduce some naming conflicts.
using xlApp = Microsoft.Office.Interop.Excel.Application;
using xlWin = Microsoft.Office.Interop.Excel.Window;


namespace ExcelExtensions {

    /// <summary>
    /// Collection of currently running Excel instances.
    /// </summary>
    public class ExcelAppCollection : IEnumerable<xlApp>  {

        #region Constructors

        /// <summary>Initializes a new instance of the 
        /// <see cref="ExcelAppCollection"/> class.</summary>
        /// <param name="sessionID">Windows sessionID to filter instances by.
        /// If not assigned, uses current session.</param>
        public ExcelAppCollection (Int32? sessionID = null) {
            if (sessionID.HasValue && sessionID.Value < -1)
                throw new ArgumentOutOfRangeException("sessionID");

            this.SessionID = sessionID
                ?? Process.GetCurrentProcess().SessionId;
        }

        #endregion

        #region Properties

        /// <summary>Gets the Windows sessionID used to filter instances.
        /// If -1, uses instances from all sessions.</summary>
        /// <value>The sessionID.</value>
        public Int32 SessionID { get; private set; }

        #endregion

        #region Accessors

        /// <summary>Gets the Application associated with a given process.</summary>
        /// <param name="process">The process.</param>
        /// <returns>Application associated with process.</returns>
        /// <exception cref="System.ArgumentNullException">process</exception>
        public xlApp FromProcess(Process process) {
            if (process == null)
                throw new ArgumentNullException("process");
            return InnerFromProcess(process);
        }

        /// <summary>Gets the Application associated with a given processID.</summary>
        /// <param name="processID">The process identifier.</param>
        /// <returns>Application associated with processID.</returns>
        public xlApp FromProcessID(Int32 processID) {
            try {
                return FromProcess(Process.GetProcessById(processID));
            }
            catch (ArgumentException) {
                return null;
            }
        }

        /// <summary>Get the Application associated with a given window handle.</summary>
        /// <param name="mainHandle">The window handle.</param>
        /// <returns>Application associated with window handle.</returns>
        public xlApp FromMainWindowHandle(Int32 mainHandle) {
            return InnerFromHandle(ChildHandleFromMainHandle(mainHandle));
        }

        /// <summary>Gets the main instance. </summary>
        /// <remarks>This is the oldest running instance.
        /// It will be used if an Excel file is double-clicked in Explorer, etc.</remarks>
        public xlApp PrimaryInstance {
            get {
                try {
                    return Marshal.GetActiveObject(MarshalName) as xlApp;
                }
                catch (COMException) {
                    return null;
                }
            }
        }

        /// <summary>Gets the top most instance.</summary>
        /// <value>The top most instance.</value>
        public xlApp TopMostInstance {
            get {
                var topMost = GetProcesses() //All Excel processes
                    .Select(p => p.MainWindowHandle) //All Excel main window handles
                    .Select(h => new { h = h, z = GetWindowZ(h) }) //Get (handle, z) pair per instance
                    .Where(x => x.z > 0) //Filter hidden instances
                    .OrderBy(x => x.z) //Sort by z value
                    .First(); //Lowest z value

                return FromMainWindowHandle(topMost.h.ToInt32());
            }
        }

        #endregion

        #region Methods

        /// <summary>Returns an enumerator that iterates through the collection.</summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> 
        /// that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<xlApp> GetEnumerator() {
            foreach (var p in GetProcesses())
                yield return FromProcess(p);
        }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }

        /// <summary>Gets all Excel processes in the current session.</summary>
        /// <returns>Collection of all Excel processing in the current session.</returns>
        public IEnumerable<Process> GetProcesses() {

            IEnumerable<Process> result = Process.GetProcessesByName(ProcessName);

            if (this.SessionID >= 0)
                result = result.Where(p => p.SessionId == SessionID);

            return result;
        }

        #endregion

//------------Implementation

        #region Methods

        private static xlApp InnerFromProcess(Process p) {
            return InnerFromHandle(ChildHandleFromMainHandle(p.MainWindowHandle.ToInt32()));
        }

        private static Int32 ChildHandleFromMainHandle(Int32 mainHandle) {
            Int32 handle = 0;
            EnumChildWindows(mainHandle, EnumChildFunc, ref handle);
            return handle;
        }

        private static xlApp InnerFromHandle(Int32 handle) {
            xlWin win = null;
            Int32 hr = AccessibleObjectFromWindow(handle, DW_OBJECTID, rrid.ToByteArray(), ref win);
            return win.Application;
        }

        private static Int32 GetWindowZ(IntPtr handle) {
            var z = 0;
            for (IntPtr h = handle; h != IntPtr.Zero; h = GetWindow(h, GW_HWNDPREV))
                z++;
            return z;
        }

        private static Boolean EnumChildFunc(Int32 hwndChild, ref Int32 lParam) { 
            var buf = new StringBuilder(128); 
            GetClassName(hwndChild, buf, 128); 
            if (buf.ToString() == ComClassName) { 
                lParam = hwndChild; 
                return false; 
            } 
            return true; 
        }

        #endregion

        #region Extern Methods

        [DllImport("Oleacc.dll")] 
        private static extern Int32 AccessibleObjectFromWindow(
            Int32 hwnd, UInt32 dwObjectID, Byte[] riid, ref xlWin ptr); 

        [DllImport("User32.dll")] 
        private static extern Boolean EnumChildWindows(
            Int32 hWndParent, EnumChildCallback lpEnumFunc, ref Int32 lParam); 

        [DllImport("User32.dll")] 
        private static extern Int32 GetClassName(
            Int32 hWnd, StringBuilder lpClassName, Int32 nMaxCount); 

        [DllImport("User32.dll")] 
        private static extern IntPtr GetWindow(IntPtr hWnd, UInt32 uCmd);

        #endregion

        #region Constants & delegates

        private const String MarshalName = "Excel.Application";

        private const String ProcessName = "EXCEL";

        private const String ComClassName = "EXCEL7";

        private const UInt32 DW_OBJECTID = 0xFFFFFFF0;

        private const UInt32 GW_HWNDPREV = 3;
        //3 = GW_HWNDPREV
        //The retrieved handle identifies the window above the specified window in the Z order.
        //If the specified window is a topmost window, the handle identifies a topmost window.
        //If the specified window is a top-level window, the handle identifies a top-level window.
        //If the specified window is a child window, the handle identifies a sibling window.

        private static Guid rrid = new Guid("{00020400-0000-0000-C000-000000000046}");

        private delegate Boolean EnumChildCallback(Int32 hwnd, ref Int32 lParam);
        #endregion
    }
} 

【讨论】:

    【解决方案2】:

    首先,您可以放心地说,如果 Excel 未打开,那么您正在检查的文件未打开,对吧?

    因此,您的流程应该是检查 Excel 是否正在运行。如果没有,请返回false

    如果 Excel 正在运行,只需为该进程创建一个新的 Excel 实例并循环访问工作簿。无需尝试将流程变成实例。

    Process[] allopenexcel = Process.GetProcessesByName("EXCEL");
    if(allopenexcel.count==0)
    {
        isOpened = false;
        break;
    }
    else
    {
        foreach (System.Diagnostics.Process excl in allopenexcel)
        {
            Excel.Application exl = new Excel.Application();
            for (int i = 1; i <= exl.Workbooks.Count; i++)
            {
                if (exl.Workbooks(i).FullName == wbookname)
                {
                    isOpened = true;
                    break;
                }
            }
            exl.Quit();
        }
    }
    

    【讨论】:

    • 您的逻辑分支不是必需的(即使是,break 语句也不是必需的) - 如果 allopenexcel 不包含任何元素,foreach 循环将迭代零次。更重要的是,您实际上没有对 Process 实例做任何事情 - 您正在创建一个独立于 Process 实例的 Excel.Application 的新实例。
    • 感谢赛斯的建议。但是,我同意普雷斯顿的评论。您的代码实际上不适用于 Process 数组。
    猜你喜欢
    • 2014-04-02
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    相关资源
    最近更新 更多