我有一个工具可以通过Application.Calculate
请求Excel刷新来自动执行excel - 问题是(令人惊讶的是)调用Microsoft.Office.Interop.Excel.Application.Calculate
会使用户退出当前操作,如果他们处于中间位置编辑单元格,重命名工作表,键入功能区控件(如字体框)等...(此处描述的问题:Calling Application.Calculate breaks formula being edited)
为了解决这个问题,我能够使用WinApi调用来检测是否有几个"编辑"控件是活动的。如果我检测到Excel用户是"忙碌",我只是暂停自动化以避免中断他们的编辑。
public static bool IsExcelBusy(Application xlApp, out string reason)
{
IntPtr excelHwnd = (IntPtr)xlApp.Hwnd;
uint excelThreadId = NativeMethods.GetWindowThreadProcessId(excelHwnd, out uint excelProcessId);
// Get the handle of whatever window is in the foreground (system-wide)
IntPtr foreground = NativeMethods.GetForegroundWindow();
// Problem: If a non-excel-owned process has focus, we cannot get the focused control
uint foregroundThreadId = NativeMethods.GetWindowThreadProcessId(foreground, out uint foregroundProcessId);
if (foregroundProcessId != excelProcessId)
return false; // How can we know what control has focus?
// Otherwise, the following works:
try
{
// We need to attach the thread that owns this window to get the focused control
uint thisThreadId = NativeMethods.GetCurrentThreadId();
NativeMethods.AttachThreadInput(foregroundThreadId, thisThreadId, true);
IntPtr focusedControlHandle = NativeMethods.GetFocus();
if (focusedControlHandle != IntPtr.Zero)
{
// Get the class name of the control that the user is currently interacting with (if any)
StringBuilder classNameResult = new StringBuilder(256);
NativeMethods.GetClassName(focusedControlHandle, classNameResult, 256);
string className = classNameResult.ToString();
// Determine if this control is at risk of being interrupted by a recalculations
switch (className)
{
case "EXCEL6":
reason = "User is editing a cell";
return true;
case "EXCEL<":
reason = "User is editing in the formula bar";
return true;
case "RICHEDIT60W":
reason = "User is editing a ribbon control";
return true;
case "Edit":
isActivitySensitive = true;
reason = "User is in the named range box";
return true;
case "EXCEL=":
isActivitySensitive = true;
reason = "User is renaming a sheet";
return true;
}
}
}
finally
{
NativeMethods.AttachThreadInput(foregroundThreadId, thisThreadId, false);
}
return false;
}
问题(如上面的评论中所强调的)是GetFocus()
WinApi调用仅适用于当前前景窗口。我真正想知道的是控件在主Excel应用程序窗口中的重点,无论该窗口当前是否处于活动状态。
例如,如果用户正在键入公式(计算暂停)并且用户使用alt-tabs到浏览器谷歌某些东西,我不想取消暂停自动化或他们的一半 - 配方将丢失。
我非常确定我接下来需要的是一些类似于&#34; GetFocus&#34;但是这得到了活跃的&#34;或者&#34;专注于&#34;控制当前不在前台的应用程序窗口。
我试图避免在他们离开并重新进入excel应用程序时监控用户要跟踪的每个操作。我正在寻找尽可能轻量级和无状态的检查,以确定在任何给定的实例中,用户是否处于Excel应用程序中的编辑操作中。
答案 0 :(得分:1)
Each thread gets its own focus window(据说)但是当窗口不活动时你无法真正访问它。当窗口未激活时,GetGUIThreadInfo
返回NULL值:
HWND hWnd = FindWindow(...);
DWORD tid = GetWindowThreadProcessId(hWnd, NULL);
GUITHREADINFO gti;
gti.cbSize = sizeof(GUITHREADINFO);
if (GetGUIThreadInfo(tid, >i))
{
printf("hwndFocus=%p hwndActive=%p\n", gti.hwndFocus, gti.hwndActive);
}
在切换到应用程序时,在Win32级别the code has to restore the focus手动设置正确的控件:... handling of the WM_ACTIVATE and WM_SETFOCUS messages to preserve the focus when the user switches away from the window and back。
您可以尝试UI Automation,Office通常会对其提供良好支持,并可能会为其控件公开激活/焦点信息。