附加到现有 Excel 实例,即使 VBA 由 Excel 实例打开
Attach To Existing Excel Instance even if VBA is Open by the Excel Instance
这是关于为这个问题选择的答案:
How to iterate through instance of Excel c#
此代码非常适合获取 Excel 的所有实例,除非其中一个实例打开了 VBA 编辑器。代码在尝试为 EXCEL7
查找 class 时中断。这是工作簿 subwindow 的 class。调试时我确定在枚举子windows时,找不到EXCEL7子window。 类 喜欢 VbaWindow
而不是出现。我什至试图为这个 vba window class 获取带有 window 句柄的 Excel.Window 但它失败了。我怎样才能仍然获得方法 AccessibleObjectFromWindow
来引用 Excel.Window,然后我可以使用它来引用应用程序。这是我修改后的方法(我已经有了 Excel 进程 ID...为了便于阅读,省略了所有其他声明):
internal static Excel.Application GetExcelInstance(int procID)
{
EnumChildCallback cb;
Process p = Process.GetProcessById(procID);
if (p != null)
{
if ((int)p.MainWindowHandle > 0)
{
int childWindow = 0;
cb = new EnumChildCallback(EnumChildProc);
EnumChildWindows((int)p.MainWindowHandle, cb, ref childWindow);
if (childWindow > 0)
{
const uint OBJID_NATIVEOM = 0xFFFFFFF0;
// GUIDs used by the OLE Automation Protocol:
// https://msdn.microsoft.com/en-us/library/cc237842.aspx
Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
Excel.Window window = null;
int res = AccessibleObjectFromWindow
(
childWindow,
OBJID_NATIVEOM,
IID_IDispatch.ToByteArray(),
ref window
);
if (res >= 0)
{
return window.Application;
}
}
}
}
return null;
}
// If VBA is open this method will fail when enumerating
// all child windows of the excel process
// EXCEL7 will not be found in child windows but other windows
// will be found like the window for class "VbaWindow"
private static bool EnumChildProc(int hwndChild, ref int lParam)
{
StringBuilder buf = new StringBuilder(128);
GetClassName(hwndChild, buf, 128);
// More info on excel classes:
// http://www.mrexcel.com/forum/excel-questions/54007-worksheet-class-findwindow-api.html
if (buf.ToString() == "EXCEL7")
{
lParam = hwndChild;
return false;
}
return true;
}
我遇到了完全相同的问题。我通过以下方式解决了它:
- 获取所有 Excel 进程 ID 的列表
- 遍历所有顶级 windows 并检查其进程 ID 是否在 Excel ID 列表中。如果是这样,则将其添加到另一个列表。 (使用 EnumWindows 和 GetWindowThreadProcessID)
- 然后遍历这个 Excel hwnd 列表,基本上做你之前做的事情,即搜索他们的 child windows 找到 class名称是 EXCEL7
下面是一些示例代码:
public class ExcelInstances
{
HashSet<int> _ExcelProcessIDs;
List<int> _ExcelTopLevelWindowHwnds;
List<Excel.Application> _XLInstances;
public Excel.Application[] GetExcelInstances()
{
_XLInstances = new List<Excel.Application>();
_ExcelProcessIDs = new HashSet<int>();
_ExcelTopLevelWindowHwnds = new List<int>();
foreach (Process p in Process.GetProcessesByName("EXCEL")) _ExcelProcessIDs.Add(p.Id); //find all process ids related to Excel
int hwnd = 0;
var cb = new WinAPI.WindowEnumProc(GetAllExcelTopLevelWindowHwnds);
WinAPI.EnumWindows(cb, ref hwnd);
foreach (var hwnd2 in _ExcelTopLevelWindowHwnds)
{
var excelHwnd = 0;
var cb2 = new WinAPI.WindowEnumProc(GetExcelWorkbooksFromExcelWindowHandles);
WinAPI.EnumChildWindows(hwnd2, cb2, ref excelHwnd);
}
return _XLInstances.ToArray();
}
private bool GetAllExcelTopLevelWindowHwnds(int hwnd, ref int lParam)
{
int id = 0;
WinAPI.GetWindowThreadProcessId(hwnd, ref id);
if (_ExcelProcessIDs.Contains(id))
{
if (hwnd > 0)
{
_ExcelTopLevelWindowHwnds.Add(hwnd);
}
}
return true;
}
private bool GetExcelWorkbooksFromExcelWindowHandles(int hwndChild, ref int lParam)
{
int id = 0;
WinAPI.GetWindowThreadProcessId(hwndChild, ref id);
StringBuilder buf = new StringBuilder(128);
WinAPI.GetClassName(hwndChild, buf, 128);
string clsName = buf.ToString();
if (clsName == "EXCEL7")
{
lParam = hwndChild;
var wb = UsefulStaticMethods.GetActiveWorkbookFromExcelHandle(hwndChild);
if (wb != null) _XLInstances.Add(wb.Parent);
}
return true;
}
}
这是关于为这个问题选择的答案:
How to iterate through instance of Excel c#
此代码非常适合获取 Excel 的所有实例,除非其中一个实例打开了 VBA 编辑器。代码在尝试为 EXCEL7
查找 class 时中断。这是工作簿 subwindow 的 class。调试时我确定在枚举子windows时,找不到EXCEL7子window。 类 喜欢 VbaWindow
而不是出现。我什至试图为这个 vba window class 获取带有 window 句柄的 Excel.Window 但它失败了。我怎样才能仍然获得方法 AccessibleObjectFromWindow
来引用 Excel.Window,然后我可以使用它来引用应用程序。这是我修改后的方法(我已经有了 Excel 进程 ID...为了便于阅读,省略了所有其他声明):
internal static Excel.Application GetExcelInstance(int procID)
{
EnumChildCallback cb;
Process p = Process.GetProcessById(procID);
if (p != null)
{
if ((int)p.MainWindowHandle > 0)
{
int childWindow = 0;
cb = new EnumChildCallback(EnumChildProc);
EnumChildWindows((int)p.MainWindowHandle, cb, ref childWindow);
if (childWindow > 0)
{
const uint OBJID_NATIVEOM = 0xFFFFFFF0;
// GUIDs used by the OLE Automation Protocol:
// https://msdn.microsoft.com/en-us/library/cc237842.aspx
Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
Excel.Window window = null;
int res = AccessibleObjectFromWindow
(
childWindow,
OBJID_NATIVEOM,
IID_IDispatch.ToByteArray(),
ref window
);
if (res >= 0)
{
return window.Application;
}
}
}
}
return null;
}
// If VBA is open this method will fail when enumerating
// all child windows of the excel process
// EXCEL7 will not be found in child windows but other windows
// will be found like the window for class "VbaWindow"
private static bool EnumChildProc(int hwndChild, ref int lParam)
{
StringBuilder buf = new StringBuilder(128);
GetClassName(hwndChild, buf, 128);
// More info on excel classes:
// http://www.mrexcel.com/forum/excel-questions/54007-worksheet-class-findwindow-api.html
if (buf.ToString() == "EXCEL7")
{
lParam = hwndChild;
return false;
}
return true;
}
我遇到了完全相同的问题。我通过以下方式解决了它:
- 获取所有 Excel 进程 ID 的列表
- 遍历所有顶级 windows 并检查其进程 ID 是否在 Excel ID 列表中。如果是这样,则将其添加到另一个列表。 (使用 EnumWindows 和 GetWindowThreadProcessID)
- 然后遍历这个 Excel hwnd 列表,基本上做你之前做的事情,即搜索他们的 child windows 找到 class名称是 EXCEL7
下面是一些示例代码:
public class ExcelInstances
{
HashSet<int> _ExcelProcessIDs;
List<int> _ExcelTopLevelWindowHwnds;
List<Excel.Application> _XLInstances;
public Excel.Application[] GetExcelInstances()
{
_XLInstances = new List<Excel.Application>();
_ExcelProcessIDs = new HashSet<int>();
_ExcelTopLevelWindowHwnds = new List<int>();
foreach (Process p in Process.GetProcessesByName("EXCEL")) _ExcelProcessIDs.Add(p.Id); //find all process ids related to Excel
int hwnd = 0;
var cb = new WinAPI.WindowEnumProc(GetAllExcelTopLevelWindowHwnds);
WinAPI.EnumWindows(cb, ref hwnd);
foreach (var hwnd2 in _ExcelTopLevelWindowHwnds)
{
var excelHwnd = 0;
var cb2 = new WinAPI.WindowEnumProc(GetExcelWorkbooksFromExcelWindowHandles);
WinAPI.EnumChildWindows(hwnd2, cb2, ref excelHwnd);
}
return _XLInstances.ToArray();
}
private bool GetAllExcelTopLevelWindowHwnds(int hwnd, ref int lParam)
{
int id = 0;
WinAPI.GetWindowThreadProcessId(hwnd, ref id);
if (_ExcelProcessIDs.Contains(id))
{
if (hwnd > 0)
{
_ExcelTopLevelWindowHwnds.Add(hwnd);
}
}
return true;
}
private bool GetExcelWorkbooksFromExcelWindowHandles(int hwndChild, ref int lParam)
{
int id = 0;
WinAPI.GetWindowThreadProcessId(hwndChild, ref id);
StringBuilder buf = new StringBuilder(128);
WinAPI.GetClassName(hwndChild, buf, 128);
string clsName = buf.ToString();
if (clsName == "EXCEL7")
{
lParam = hwndChild;
var wb = UsefulStaticMethods.GetActiveWorkbookFromExcelHandle(hwndChild);
if (wb != null) _XLInstances.Add(wb.Parent);
}
return true;
}
}