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

С# Force Form Focus

Итак, я искал google и SO, прежде чем задавать этот вопрос. В основном у меня есть DLL, которая имеет форму, скомпилированную в нее. Форма будет использоваться для отображения информации на экране. В конце концов он будет асинхронным и выставит много настроек в dll. Пока я просто хочу, чтобы он отображался правильно. Проблема, с которой я сталкиваюсь, заключается в том, что я использую dll, загружая ее в сеанс Powershell. Поэтому, когда я пытаюсь отобразить форму и заставить ее прийти в себя и сосредоточиться, у нее нет проблем с отображением всех других приложений, но я не могу на всю жизнь заставить ее отображать окно Powershell, Вот код, который я сейчас использую, чтобы попытаться отобразить его. Я уверен, что большинство из них не потребуется, как только я это выясню, это просто представляет все, что я нашел через Google.

CLass Blah
{
        [DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
        public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);

        [DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("User32.dll", EntryPoint = "ShowWindowAsync")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
        private const int WS_SHOWNORMAL = 1;

    public void ShowMessage(string msg)
    {
            MessageForm msgFrm = new MessageForm();
            msgFrm.lblMessage.Text = "FOO";
            msgFrm.ShowDialog();
            msgFrm.BringToFront();
            msgFrm.TopMost = true;
            msgFrm.Activate();

            SystemParametersInfo((uint)0x2001, 0, 0, 0x0002 | 0x0001);
            ShowWindowAsync(msgFrm.Handle, WS_SHOWNORMAL);
            SetForegroundWindow(msgFrm.Handle);
            SystemParametersInfo((uint)0x2001, 200000, 200000, 0x0002 | 0x0001);
    }
}

Как я уже сказал, я уверен, что большинство из них либо не нужны, либо даже ошибочны, я просто хотел показать вещи, которые я пробовал. Кроме того, как я уже упоминал, я планирую, что это будет асинхронно отображаться в какой-то момент, который, как я подозреваю, приведет к необходимости отдельного потока. Разделение формы на ее собственную нить облегчит заставить ее сосредоточиться на сеансе Powershell?


@Joel, спасибо за информацию. Вот что я пробовал на основе вашего предложения:

msgFrm.ShowDialog();
msgFrm.BringToFront();
msgFrm.Focus();
Application.DoEvents();

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

Thnks для всех идей до сих пор люди.


Хорошо, потокование позаботилось о проблеме. @Quarrelsome, я попробовал оба из них. Ни один (ни один вместе) не работал. Мне любопытно, что такое зло в использовании потоков? Я не использую Application.Run, и у меня еще есть проблема. Я использую класс посредника, к которому имеют доступ как родительский поток, так и дочерний поток. В этом объекте я использую ReaderWriterLock для блокировки одного свойства, которое представляет сообщение, которое я хочу отобразить в форме, созданной дочерним потоком. Родитель блокирует свойство, затем записывает то, что должно отображаться. Детский поток блокирует свойство и читает, что он должен изменить на этикетке в форме. Ребенок должен сделать это на интервале опроса (я по умолчанию его до 500 мс), которого я не очень доволен, но я не мог найти способ, связанный с событиями, чтобы позволить дочернему потоку узнать, что проперти изменилась, m застрял с опросом.

4b9b3361

Ответ 1

Мне также не удалось активировать и вывести окно на передний план. Вот код, который в конечном итоге работал у меня. Я не уверен, что это решит вашу проблему.

В принципе, вызовите ShowWindow(), затем SetForegroundWindow().

using System.Diagnostics;
using System.Runtime.InteropServices;

// Sets the window to be foreground
[DllImport("User32")]
private static extern int SetForegroundWindow(IntPtr hwnd);

// Activate or minimize a window
[DllImportAttribute("User32.DLL")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_RESTORE = 9;

private void ActivateApplication(string briefAppName)
{
    Process[] procList = Process.GetProcessesByName(briefAppName);

    if (procList.Length > 0)
    {
        ShowWindow(procList[0].MainWindowHandle, SW_RESTORE);
        SetForegroundWindow(procList[0].MainWindowHandle);
    }
}

Ответ 2

Вот какой код, который я использовал в той или иной форме в течение нескольких лет. Есть несколько ошибок, связанных с созданием окна в другом приложении. Как только у вас есть дескриптор окна, выполните следующие действия:

      if (IsIconic(hWnd))
        ShowWindowAsync(hWnd, SW_RESTORE);

      ShowWindowAsync(hWnd, SW_SHOW);

      SetForegroundWindow(hWnd);

      // Code from Karl E. Peterson, www.mvps.org/vb/sample.htm
      // Converted to Delphi by Ray Lischner
      // Published in The Delphi Magazine 55, page 16
      // Converted to C# by Kevin Gale
      IntPtr foregroundWindow = GetForegroundWindow();
      IntPtr Dummy = IntPtr.Zero;

      uint foregroundThreadId = GetWindowThreadProcessId(foregroundWindow, Dummy);
      uint thisThreadId       = GetWindowThreadProcessId(hWnd, Dummy);

      if (AttachThreadInput(thisThreadId, foregroundThreadId, true))
      {
        BringWindowToTop(hWnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
        AttachThreadInput(thisThreadId, foregroundThreadId, false);
      }

      if (GetForegroundWindow() != hWnd)
      {
        // Code by Daniel P. Stasinski
        // Converted to C# by Kevin Gale
        IntPtr Timeout = IntPtr.Zero;
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, Timeout, 0);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Dummy, SPIF_SENDCHANGE);
        BringWindowToTop(hWnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Timeout, SPIF_SENDCHANGE);
      }

Я не буду публиковать весь блок, так как он делает другие вещи, которые не актуальны но вот константы и импорт для вышеуказанного кода.

//Win32 API calls necesary to raise an unowned processs main window

[DllImport("user32.dll")]

private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll")]
static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll")] 
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, Int32 nMaxCount);
[DllImport("user32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, ref Int32 lpdwProcessId);
[DllImport("User32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd); 

private const int SW_HIDE = 0;
private const int SW_SHOWNORMAL = 1;
private const int SW_NORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_MAXIMIZE = 3;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_SHOWMINNOACTIVE = 7;
private const int SW_SHOWNA = 8;
private const int SW_RESTORE = 9;
private const int SW_SHOWDEFAULT = 10;
private const int SW_MAX = 10;

private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
private const int  SPIF_SENDCHANGE = 0x2;

Ответ 3

Не отличается ShowDialog() поведение окна, чем просто Показать()?

Что делать, если вы попытались:

msgFrm.Show();
msgFrm.BringToFront();
msgFrm.Focus();

Ответ 4

TopMost = true; .Activate()?

Либо любой из них хорош?

Разделить его на собственный поток немного злобно, так как он не будет работать нормально, если вы не назовете его Application.Run, и это проглотит поток. В худшем случае, я думаю, вы могли бы разделить его на другой процесс и общаться через диск или WCF.

Ответ 5

Следующее решение должно соответствовать вашим требованиям:

  • Сборка может быть загружена в PowerShell, а основной экземпляр класса
  • Когда вызывается метод ShowMessage для этого экземпляра, отображается и активируется новое окно
  • Если вы вызываете ShowMessage несколько раз, это же окно обновляет текст заголовка и активируется
  • Чтобы остановить использование окна, вызовите метод Dispose

Шаг 1. Позвольте создать временный рабочий каталог (вы можете, естественно, использовать свой собственный каталог)

(powershell.exe)
mkdir C:\TEMP\PshWindow
cd C:\TEMP\PshWindow

Шаг 2. Теперь давайте определим класс, с которым мы будем взаимодействовать в PowerShell:

// file 'InfoProvider.cs' in C:\TEMP\PshWindow
using System;
using System.Threading;
using System.Windows.Forms;

namespace PshWindow
{
    public sealed class InfoProvider : IDisposable
    {
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            lock (this._sync)
            {
                if (!this._disposed)
                {
                    this._disposed = true;
                    if (null != this._worker)
                    {
                        if (null != this._form)
                        {
                            this._form.Invoke(new Action(() => this._form.Close()));
                        }
                        this._worker.Join();
                        this._form = null;
                        this._worker = null;
                    }
                }
            }
        }

        public void ShowMessage(string msg)
        {
            lock (this._sync)
            {
                // make sure worker is up and running
                if (this._disposed) { throw new ObjectDisposedException("InfoProvider"); }
                if (null == this._worker)
                {
                    this._worker = new Thread(() => (this._form = new MyForm(this._sync)).ShowDialog()) { IsBackground = true };
                    this._worker.Start();
                    while (this._form == null || !this._form.Created)
                    {
                        Monitor.Wait(this._sync);
                    }
                }

                // update the text
                this._form.Invoke(new Action(delegate
                {
                    this._form.Text = msg;
                    this._form.Activate();
                }));
            }
        }

        private bool _disposed;
        private Form _form;
        private Thread _worker;
        private readonly object _sync = new object();
    }
}

Также как и форма, которая будет показана:

// file 'MyForm.cs' in C:\TEMP\PshWindow
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace PshWindow
{
    internal sealed class MyForm : Form
    {
        public MyForm(object sync)
        {
            this._sync = sync;
            this.BackColor = Color.LightGreen;
            this.Width = 200;
            this.Height = 80;
            this.FormBorderStyle = FormBorderStyle.SizableToolWindow;
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            this.TopMost = true;

            lock (this._sync)
            {
                Monitor.PulseAll(this._sync);
            }
        }

        private readonly object _sync;
    }
}

Шаг 3: скомпилируйте сборку...

(powershell.exe)
csc /out:PshWindow.dll /target:library InfoProvider.cs MyForm.cs

Шаг 4:... и загрузите сборку в PowerShell, чтобы получить удовольствие от нее:

(powershell.exe)
[System.Reflection.Assembly]::LoadFile('C:\TEMP\PshWindow\PshWindow.dll')
$a = New-Object PshWindow.InfoProvider
$a.ShowMessage('Hello, world')

Окно зеленого цвета с заголовком "Hello, world" должно теперь всплывать и быть активным. Если вы активируете окно PowerShell и введите:

$a.ShowMessage('Stack overflow')

Заголовок окна должен измениться на "Переполнение стека", и окно должно быть активным снова.

Чтобы прекратить работу с нашим окном, удалите объект:

$a.Dispose()

Это решение работает как в Windows XP SP3, x86, так и в Windows Vista SP1, x64. Если есть вопросы о том, как это решение работает, я могу обновить эту запись с подробным обсуждением. Пока я надеюсь, что код будет само собой разумеющимся.

Ответ 6

Огромное спасибо людям.
Я думаю, что я сделал это немного короче, вот что я надел на отдельную нить и, кажется, работает нормально.

private static void StatusChecking()
{
    IntPtr iActiveForm = IntPtr.Zero, iCurrentACtiveApp = IntPtr.Zero;
    Int32 iMyProcID = Process.GetCurrentProcess().Id, iCurrentProcID = 0;
    IntPtr iTmp = (IntPtr)1;

    while (bIsRunning)
    {
        try
        {
            Thread.Sleep(45);
            if (Form.ActiveForm != null)
            {
                iActiveForm = Form.ActiveForm.Handle;
            }
            iTmp = GetForegroundWindow();
            if (iTmp == IntPtr.Zero) continue;
            GetWindowThreadProcessId(iTmp, ref iCurrentProcID);
            if (iCurrentProcID == 0)
            {
                iCurrentProcID = 1;
                continue;
            }
            if (iCurrentProcID != iMyProcID)
            {
                SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, IntPtr.Zero, 0);
                SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, IntPtr.Zero, SPIF_SENDCHANGE);
                BringWindowToTop(iActiveForm);
                SetForegroundWindow(iActiveForm);
            }
            else iActiveForm = iTmp;
        }
        catch (Exception ex)
        {
            Definitions.UnhandledExceptionHandler(ex, 103106);
        }
    }
}

Я не потрудился переписать определения...

Ответ 7

Вам не нужно импортировать никакие функции win32 для этого. Если .Focus() недостаточно, форма также должна иметь метод .BringToFront(), который вы можете использовать. Если это не удается, вы можете установить для него свойство .TopMost значение true. Вы не хотите оставлять его истинным навсегда, поэтому вызовите Application.DoEvents, чтобы форма могла обработать это сообщение и вернуть его в значение false.

Ответ 8

Разве вы не хотите, чтобы диалог был дочерним элементом вызывающей формы?

Для этого вам понадобится пройти в вызывающем окне и используйте метод ShowDialog (IWin32Window owner).