23

したがって、この質問をする前に、グーグルとSOを検索しました。基本的に、フォームがコンパイルされたDLLがあります。フォームは、画面に情報を表示するために使用されます。最終的には非同期になり、dll で多くのカスタマイズが公開されます。とりあえずちゃんと表示してほしい。私が抱えている問題は、Powershell セッションにロードして dll を使用することです。そのため、フォームを表示して一番上に来てフォーカスを取得しようとすると、他のすべてのアプリの上に表示しても問題ありませんが、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 セッションにフォーカスしやすくなりますか?


@ジョエル、情報をありがとう。あなたの提案に基づいて私が試したことは次のとおりです。

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

フォームは引き続きPowershell セッションの下に表示されます。糸通しを進めていきます。以前にスレッドを生成したことはありますが、親スレッドが子スレッドと通信する必要がある場所でスレッドを生成したことはありません。

これまでのすべてのアイデアに感謝します。


わかりました、スレッド化で問題は解決しました。@Quarrelsome、私はそれらの両方を試しました。どちらも(または両方とも)機能しませんでした。スレッドを使用することの何が悪いのでしょうか? 私は Application.Run を使用していませんが、まだ問題はありません。親スレッドと子スレッドの両方がアクセスできるメディエーター クラスを使用しています。そのオブジェクトでは、ReaderWriterLock を使用して、子スレッドが作成するフォームに表示するメッセージを表す 1 つのプロパティをロックしています。親はプロパティをロックしてから、表示すべきものを書き込みます。子スレッドはプロパティをロックし、フォームのラベルを何に変更するかを読み取ります。子はポーリング間隔 (デフォルトでは 500 ミリ秒) でこれを行う必要がありますが、これについてはあまり満足していませんが、プロパティが変更されたことを子スレッドに知らせるイベント ドリブンの方法を見つけることができませんでした。

4

8 に答える 8

17

また、ウィンドウをアクティブにして前面に表示するのにも問題がありました。これが最終的に私のために働いたコードです。それがあなたの問題を解決するかどうかはわかりません。

基本的に、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);
    }
}
于 2008-09-05T15:49:22.937 に答える
16

これは、私が数年間何らかのフォームで使用してきたコードです。別のアプリのウィンドウをポップアップさせるには、いくつかの落とし穴があります。ウィンドウ ハンドルを取得したら、次の操作を行います。

      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;
于 2008-09-18T17:23:23.273 に答える
3

ShowDialog()は、 Show ()とは異なるウィンドウ動作をしていませんか?

あなたが試してみたらどうですか:

msgFrm.Show();
msgFrm.BringToFront();
msgFrm.Focus();
于 2008-09-05T18:22:28.187 に答える
3

TopMost = true; 。活性化() ?

どちらでもいいですか?

それを独自のスレッドに分割することは、 Application.Run で呼び出さないと正しく機能せず、スレッドを飲み込んでしまうため、少し悪いことです。最悪のシナリオでは、それを別のプロセスに分離し、ディスクまたは WCF 経由で通信できると思います。

于 2008-09-05T18:24:29.320 に答える
2

次のソリューションは、要件を満たす必要があります。

  1. アセンブリを PowerShell にロードし、メイン クラスをインスタンス化できます
  2. このインスタンスの ShowMessage メソッドが呼び出されると、新しいウィンドウが表示され、アクティブになります。
  3. ShowMessage を複数回呼び出すと、この同じウィンドウがタイトル テキストを更新し、アクティブになります。
  4. ウィンドウの使用を停止するには、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 の両方で期待どおりに機能します。このソリューションがどのように機能するかについて質問がある場合は、このエントリを更新して詳細な議論を行うことができます. 今のところ、コードが自明であると期待しています。

于 2009-01-13T13:41:30.147 に答える
1

本当にありがとうございました。
私はそれを少し短くしたと思います、これが私が別のスレッドに置いたものであり、うまく機能しているようです。

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

私はわざわざ定義を繰り返す必要はありません...

于 2009-04-04T17:12:59.600 に答える
0

ダイアログを呼び出しフォームの子にしたいだけではありませんか?

これを行うには、呼び出し元のウィンドウでパスが必要になり、ShowDialog( IWin32Window owner ) メソッドを使用します。

于 2008-09-30T22:18:53.097 に答える
0

このために win32 関数をインポートする必要はありません。.Focus() が十分でない場合、フォームには使用できる .BringToFront() メソッドも必要です。それが失敗した場合は、.TopMost プロパティを true に設定できます。いつまでも true のままにしたくないので、Application.DoEvents を呼び出して、フォームがそのメッセージを処理し、false に戻すことができるようにします。

于 2008-09-05T16:16:39.220 に答える