C# Force Form Focus

前端 未结 8 1961
不思量自难忘°
不思量自难忘° 2020-12-02 14:46

So, I did search google and SO prior to asking this question. Basically I have a DLL that has a form compiled into it. The form will be used to display information to the sc

8条回答
  •  佛祖请我去吃肉
    2020-12-02 15:18

    The following solution should meet your requirements:

    1. Assembly can be loaded into PowerShell and main class instantiated
    2. When ShowMessage method on this instance is called, a new window is shown and activated
    3. If you call ShowMessage multiple times, this same window updates its title text and is activated
    4. To stop using the window, call Dispose method

    Step 1: Let's create a temporary working directory (you can naturally use your own dir)

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

    Step 2: Now let's define class that we will be interacting with in 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();
        }
    }
    

    As well as the Form that will be shown:

    // 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;
        }
    }
    

    Step 3: Let's compile the assembly...

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

    Step 4: ... and load the assembly in PowerShell to have fun with it:

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

    A green-ish window with title 'Hello, world' should now pop-up and be active. If you reactivate the PowerShell window and enter:

    $a.ShowMessage('Stack overflow')
    

    The Window's title should change to 'Stack overflow' and the window should be active again.

    To stop working with our window, dispose the object:

    $a.Dispose()
    

    This solution works as expected in both Windows XP SP3, x86 and Windows Vista SP1, x64. If there are question about how this solution works I can update this entry with detailed discussion. For now I'm hoping the code if self-explanatory.

提交回复
热议问题