find所有打开的Excel工作簿
我试图获取所有当前打开的Excel工作簿的列表,以便用户可以select其中哪一个从中获取某些数据。
我试过这个:
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的第一个打开的实例和最近打开的实例,所以在这两者之间打开的任何工作簿都不在列表中。
我也开始探索在这个SO问题的答案中的博客链接中描述的解决scheme,并尝试使用博客条目中build议的代码访问Running Object Table:
IBindCtx bc; IRunningObjectTable rot; CreateBindCtx(0, out bc); bc.GetRunningObjectTable(out rot);
这里的问题是, CreateBindCtx
实际上接受一个UCOMIBindCTX
而不是IBindCTX
,但是每个MSDN UCOMIBindCTX
已经过时了。
有没有更简单的方法来做我想做的事情:获取与所有打开的Excel书籍相对应的Workbook
对象列表?
好吧,我find了一个方法来做到这一点。 描述该解决scheme的博客似乎不再可用,但有一个Googlecaching版本 。
我稍微调整了代码,使构造函数接受一个MainWindowHandle,因为我遍历所有进程的句柄。
这个class级如下。 我已经留下了一些Andrew Whitechapel的意见来解释发生了什么,因为这些代码超出了我目前对Windows操作系统pipe理的知识:
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; } } }
这个代码被放在一起使用资源(链接)在最后的评论和你提供的代码。 这应该拉入所有打开的工作簿名称。
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; }
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(); } } } }