У меня есть инструмент, который автоматизирует Excel, запрашивая обновление Excel с помощью Application.Calculate
- проблема в том, что (на удивление) вызов Microsoft.Office.Interop.Excel.Application.Calculate
выведет пользователя из текущего действия, если он находится в середине редактирования ячейки,переименование листа, ввод в ленточный элемент управления (например, поле со шрифтом) и т. д. (эта проблема описана здесь: Вызов приложения. Вычисляется формула разрывов )
Чтобы противостоять этомуЯ смог использовать вызовы 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-вкладывает в браузер, чтобы что-то гуглить, я не хочу отменять приостановку автоматизации, или их наполовину набранная формула будет потеряна.
Я почти уверен, что мне нужно дальшеэто некоторая функция winapi, похожая на «GetFocus», но она получает «активный» или «сфокусированный» элемент управления для окна приложения, которое в данный момент не находится на переднем плане.
Я пытаюсь избежатьследить за каждым действием пользователя, отслеживать его выход и повторно входить в приложение Excel.Я ищу как можно более легкую и не имеющую состояния проверку, чтобы определить, находится ли пользователь в любой момент выполнения операции редактирования в приложении Excel.