Получить сфокусированный в данный момент элемент управления не переднего окна? - PullRequest
0 голосов
/ 04 июня 2018

У меня есть инструмент, который автоматизирует 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.

1 Ответ

0 голосов
/ 05 июня 2018

Каждый поток получает свое собственное окно фокуса (предположительно), но вы не можете получить к нему доступ, когда окно не активно.GetGUIThreadInfo возвращает значения NULL, когда окно не активно:

HWND hWnd = FindWindow(...);
DWORD tid = GetWindowThreadProcessId(hWnd, NULL);
GUITHREADINFO gti;
gti.cbSize = sizeof(GUITHREADINFO);
if (GetGUIThreadInfo(tid, &gti))
{
    printf("hwndFocus=%p hwndActive=%p\n", gti.hwndFocus, gti.hwndActive);
}

На уровне Win32 код должен восстановить фокус до правильного управления вручную при переключении в приложение.: ... обработка сообщений WM_ACTIVATE и WM_SETFOCUS для сохранения фокуса при переключении пользователя из окна и обратно .

Вы можете попробовать UI Automation ,Office обычно имеет хорошую поддержку и может предоставлять информацию об активации / фокусировке для своих элементов управления.

...