我要做的是编写一个方法来遍历所有打开的excel实例,并确定是否已经打开了特定的excel文件。如果是,则将excel实例与打开的文件一起传递出去进行进一步的工作。
我的奋斗是如何将进程对象转换为Excel.Application,以便我可以遍历实例中的工作簿。
我确实在类似的帖子How to get Excel instance or Excel instance CLSID using the Process ID?中阅读了答案。但是,迈克在他的回答中提到的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;
}
}
}
答案 0 :(得分:2)
我创建了一个可以遍历所有当前运行的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
}
}
答案 1 :(得分:0)
首先,您可以放心地说,如果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();
}
}