2012-12-10 72 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(); 
      } 
     } 
    } 
}