2012-12-10 74 views
4

我试图获取所有当前打开的Excel工作簿的列表,以便用户可以选择其中哪一个从中获取某些数据。查找所有打开的Excel工作簿

我尝试这样做:

List<string> excelList = new List<string>(); 
Process[] processList = Process.GetProcessesByName("excel"); 
foreach (Process p in processList) 
{ 
excelList.Add(p.MainWindowTitle); 
Console.WriteLine(p.MainWindowTitle); 
} 

但是只得到Excel的第一个打开的实例和最近打开的情况下,所以这两个之间被打开,任何工作簿是未在列表中。

我也开始探索在回答this SO questionblog link描述的解决方案,并试图获得与在博客中建议的代码访问运行对象表:这里

IBindCtx bc; 
IRunningObjectTable rot; 
CreateBindCtx(0, out bc); 
bc.GetRunningObjectTable(out rot); 

问题是, CreateBindCtx实际上接受UCOMIBindCTX而不是IBindCTX,但UCOMIBindCTX已过时MSDN

有没有更简单的方法来做我想做的事情:获取对应于所有打开的Excel书籍的Workbook对象的列表?

回答

3

好的,我找到了一种方法来做到这一点。描述解决方案的博客似乎不再可用,但有一个Google cached version

我稍微调整了代码,使构造函数接受一个MainWindowHandle,因为我遍历所有进程的句柄。

该课程如下。我已经留下了一些安德鲁白教堂的评论中解释发生了什么,因为这个代码是超出了我目前的Windows操作系统管理的知识:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Runtime.InteropServices; 
using Excel = Microsoft.Office.Interop.Excel; 

namespace DTEExcel 
{ 
    class ExcelApplicationRetriever 
    { 
     [DllImport("Oleacc.dll")] 
     public static extern int AccessibleObjectFromWindow(
       int hwnd, uint dwObjectID, byte[] riid, 
       ref Microsoft.Office.Interop.Excel.Window ptr); 

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

     [DllImport("User32.dll")] 
     public static extern bool EnumChildWindows(
       int hWndParent, EnumChildCallback lpEnumFunc, 
       ref int lParam); 

     public delegate bool EnumChildCallback(int hwnd, ref int lParam); 
     private EnumChildCallback cb; 
     public Excel.Application xl; 

     public ExcelApplicationRetriever(int winHandle) 
     { 
      // 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 (winHandle != 0) 
      { 
       int hwndChild = 0; 
       cb = new EnumChildCallback(EnumChildProc); 
       EnumChildWindows(winHandle, 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}"); 
        Excel.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 Excel Application (using the implicit 
         // cast operator supplied in the PIA). 
         xl = ptr.Application; 
        } 
       } 
      } 
     } 

     public bool EnumChildProc(int hwndChild, ref int lParam) 
     { 
      StringBuilder buf = new StringBuilder(128); 
      GetClassName(hwndChild, buf, 128); 
      if (buf.ToString() == "EXCEL7") 
      { 
       lParam = hwndChild; 
       return false; 
      } 
      return true; 
     } 

    } 
} 
+1

我没有看到你超越Excel类的第一个实例。您需要继续循环查看最后一个excel窗口的z索引处的其余窗口。 – Sorceri

+0

对,构造函数'ExcelApplicationRetriever()'只查看单个实例。我在程序中使用它来获取所有实例的方式是调用GetProcesses()来获取所有正在运行的进程的列表,然后将每个进程的句柄传递给ExcelApplicationRetriever(),并返回Excel应用程序实例, ExcelApplicationRetriever。xl'不是'null'。 – sigil

+0

谷歌缓存版本已经死亡。万岁https://web.archive.org/web/20060110230904/http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx – ThunderFrame

0

此代码是使用上次评论中的资源(链接)和您提供的代码放在一起的。这应该拉入所有打开的工作簿名称。

using Excel = Microsoft.Office.Interop.Excel;   

[DllImport("User32")] 
public static extern int GetClassName(
    int hWnd, StringBuilder lpClassName, int nMaxCount); 


// Callback passed to EnumChildWindows to find any window with the 
// registered classname "paneClassDC" - this is the class name of 
// PowerPoint's accessible document window. 
public bool EnumChildProc(int hwnd, ref int lParam) 
{ 
    StringBuilder windowClass = new StringBuilder(128); 
    GetClassName(hwnd, windowClass, 128); 
    s += windowClass.ToString() + "\n"; 
    if (windowClass.ToString() == "EXCEL7") 
    { 
     lParam = hwnd; 
    } 
    return true; 
} 

public delegate bool EnumChildCallback(int hwnd, ref int lParam); 


[DllImport("User32")] 
public static extern bool EnumChildWindows(
    int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam); 


[DllImport("User32")] 
public static extern int FindWindowEx(
    int hwndParent, int hwndChildAfter, string lpszClass, 
    int missing); 


// AccessibleObjectFromWindow gets the IDispatch pointer of an object 
// that supports IAccessible, which allows us to get to the native OM. 
[DllImport("Oleacc.dll")] 
private static extern int AccessibleObjectFromWindow(
    int hwnd, uint dwObjectID, 
    byte[] riid, 
    ref Excel.Window ptr); 


// Get the window handle for a running instance of PowerPoint. 
internal List<String> GetAccessibleObject() 
{ 

    List<String> workbookNames = new List<String>(); 
    try 
    { 
     // Walk the children of the desktop to find PowerPoint’s main 
     // window. 
     int hwnd = FindWindowEx(0, 0, "XLMAIN", 0); 
     while(hwnd != 0) 
     if (hwnd != 0) 
     { 
      // Walk the children of this window to see if any are 
      // IAccessible. 
      int hWndChild = 0; 
      EnumChildCallback cb = 
       new EnumChildCallback(EnumChildProc); 
      EnumChildWindows(hwnd, cb, ref hWndChild); 


      if (hWndChild != 0) 
      { 
       // OBJID_NATIVEOM gets us a pointer to the native 
       // object model. 
       uint OBJID_NATIVEOM = 0xFFFFFFF0; 
       Guid IID_IDispatch = 
        new Guid("{00020400-0000-0000-C000-000000000046}"); 
       Excel.Window ptr = null; 
       int hr = AccessibleObjectFromWindow(
        hWndChild, OBJID_NATIVEOM, 
        IID_IDispatch.ToByteArray(), ref ptr); 
       if (hr >= 0) 
       { 
        Excel.Application eApp = ptr.Application; 
        if (eApp != null) 
        { 
         foreach (Excel.Workbook wb in eApp.Workbooks) 
         { 
          workbookNames.Add(wb.FullName); 
         } 
         Marshal.ReleaseComObject(eApp); 
         GC.WaitForPendingFinalizers(); 
         GC.Collect(); 
        } 
       } 

       hwnd = FindWindowEx(0, hwnd, "XLMAIN", 0); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     Debug.WriteLine(ex.ToString()); 
    } 
    return workbookNames; 
} 
+0

只是尝试这样做。它不起作用,因为创建Excel的新实例时,eApp.Workbooks'为空;它不包括其集合中当前打开的工作簿。 – sigil

+0

@sigil更新以获取当前excel实例 – Sorceri

+0

这不会获得Excel的所有实例 - 尝试打开3个Excel实例,它只返回2个实例。 – sigil

0
public void closeOpenedFile(string file_name) 
{ 
    //Excel Application Object 
    Microsoft.Office.Interop.Excel.Application oExcelApp; 
    //Get reference to Excel.Application from the ROT. 
    if (Process.GetProcessesByName("EXCEL").Count() > 0) 
    { 
     oExcelApp = (Microsoft.Office.Interop.Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"); 

     foreach (Microsoft.Office.Interop.Excel.Workbook WB in oExcelApp.Workbooks) 
     { 
      //MessageBox.Show(WB.FullName); 
      if (WB.Name == file_name) 
      { 
       WB.Save(); 
       WB.Close(); 
       //oExcelApp.Quit(); 
      } 
     } 
    } 
}