我正在尝试获取所有当前打开的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的第一个打开实例和最近打开的实例,因此在这两个实例之间打开的任何工作簿都不在列表中。
我也开始在blog link的答案中探索this SO question中描述的解决方案,并尝试使用博客条目中建议的代码访问运行对象表:
IBindCtx bc;
IRunningObjectTable rot;
CreateBindCtx(0, out bc);
bc.GetRunningObjectTable(out rot);
问题在于CreateBindCtx
实际上接受UCOMIBindCTX
而不是IBindCTX
,但UCOMIBindCTX
已经过时MSDN。
是否有更简单的方法来执行我要执行的操作:获取与所有打开的Excel书籍相对应的Workbook
个对象列表?
答案 0 :(得分:3)
好的,我找到了一种方法。描述解决方案的博客似乎不再可用,但有一个Google cached version。
我略微调整了代码,以便构造函数接受MainWindowHandle,因为我正在迭代所有进程的句柄。
课程如下。我已经留下了一些Andrew Whitechapel的评论来解释发生了什么,因为这段代码超出了我目前对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 :(得分: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;
}
答案 2 :(得分: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();
}
}
}
}