Подтвердить что ты не робот

Сохранить окно сверху и украсть фокус в WinForms

Я понимаю, что это будет ПОЛНОСТЬЮ плохая практика в нормальных ситуациях, но это только для тестового приложения, которое должно принимать входные данные от сканера штрих-кода (эмуляция клавиатуры). Проблема в том, что мне нужно запустить некоторые скрипты во время сканирования, поэтому мне нужно окно, чтобы восстановить фокус непосредственно после того, как я нажму на script, чтобы запустить его. Я попытался использовать Активировать(), BringToFront(), Focus(), а также некоторые вызовы Win32, такие как SetForegroundWindow(), Setcapture() и SetActiveWindow()... однако лучшее, что я могу сделать, это сделать сделайте элемент панели задач мигающим, чтобы сказать мне, что он хочет сосредоточиться, но что-то останавливает его. BTW, я запускаю это на XP SP2 и использую .NET 2.0.

Возможно ли это?

Изменить: Чтобы уточнить, я запускаю скрипты, дважды щелкнув по ним в проводнике. Поэтому мне нужно, чтобы украсть фокус обратно из проводника и тестового приложения.

4b9b3361

Ответ 1

Видимость

Сделайте окно "самым большим". Так Менеджер задач может оставаться поверх других окон. Это свойство Form, и вы делаете форму самой верхней (плавающей над другими окнами), устанавливая значение true.

Вам не нужно переопределять поведение "активного окна" с самой верхней настройкой.

Фокус

Я задал аналогичный вопрос fooobar.com/questions/241241/..., и ответ решит вашу проблему. Вы можете заставить приложение использовать низкоуровневый входной крючок и получить уведомление о кодах клавиш, поступающих со сканера. Таким образом, ваше приложение всегда получает эти ключи, даже если приложение не имеет фокуса.

Вам может потребоваться усилить решение для сквоша ключей-кодов, чтобы они не передавались в приложение "в фокусе" (например, блокнот).

Начиная с Windows 2000, нет официального механизма приложения для захвата фокуса без прямого вмешательства пользователя. Подглядывание на входные потоки через крюк RawInputDevices - единственный разумный путь.

Несколько статей могут помочь (реализации С#)

Ответ 2

Я долгое время боролся с подобной проблемой. После долгих экспериментов и угадываний я решил это:

// Get the window to the front.
this.TopMost = true;
this.TopMost = false;

// 'Steal' the focus.
this.Activate();

Ответ 3

У меня была аналогичная проблема, и я нашел следующее, чтобы сделать трюк. Адаптирован к С# из здесь

        // force window to have focus
        uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
        uint appThread = GetCurrentThreadId();
        const uint SW_SHOW = 5;
        if (foreThread != appThread)
        {
            AttachThreadInput(foreThread, appThread, true);
            BringWindowToTop(form.Handle);
            ShowWindow(form.Handle, SW_SHOW);
            AttachThreadInput(foreThread, appThread, false);
        }
        else
        {
            BringWindowToTop(form.Handle);
            ShowWindow(form.Handle, SW_SHOW);
        }
        form.Activate();

EDIT: Вот необходимые определения PInvoke для С#:

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();

/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(HandleRef hWnd);

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);

Ответ 4

То, как я подошел к этой проблеме, состоял в том, чтобы создать еще один поток, целью которого было только то, что Form является TopMost и всегда имеет фокус. Этот код сделает все остальные приложения непригодными для использования во время работы, что я и нуждаюсь в своих конкретных приложениях. Вы можете добавить в Sleep in keepFocus или вызвать другое событие.

using System.Threading;          // be sure to include the System.Threading namespace

    //Delegates for safe multi-threading.
    delegate void DelegateGetFocus();
    private DelegateGetFocus m_getFocus;

    //Constructor.
    myForm()
    {
        m_getFocus = new DelegateGetFocus(this.getFocus);   // initialise getFocus
        InitializeComponent();
        spawnThread(keepFocus);                             // call spawnThread method
    }

    //Spawns a new Thread.
    private void spawnThread(ThreadStart ts)
    {
        try
        {
            Thread newThread = new Thread(ts);
            newThread.IsBackground = true;
            newThread.Start();
        }
        catch(Exception e)
        {
            MessageBox.Show(e.Message, "Exception!", MessageBoxButtons.OK, 
                MessageBoxIcon.Error);
        }
    }

    //Continuously call getFocus.
    private void keepFocus()
    {
        while(true)
        {
            getFocus();
        }
    }

    //Keeps Form on top and gives focus.
    private void getFocus()
    {
        //If we need to invoke this call from another thread.
        if (this.InvokeRequired)
        {
            try
            {
                this.Invoke(m_getFocus, new object[] { });
            }
            catch (System.ObjectDisposedException e)
            {
                // Window was destroyed. No problem but terminate application.
                Application.Exit();
            }
        }
        //Otherwise, we're safe.
        else
        {
            this.TopMost = true;
            this.Activate();
        }
    }       
}

Ответ 5

Вы можете попробовать сосредоточиться на определенном входе или попробовать установить свойство .TopMost в значение true (а затем снова отменить его).

Но я подозреваю, что ваша проблема заключается в том, что эти методы просто размещают сообщения в очереди событий Windows, и ваша программа должна дождаться завершения всех существующих событий до того, как она обработает это, и сосредоточьте приложение.