プログラムでMessageBoxを強制終了する

あなたに背景を教えてください。

MessageBox.Show(....)をさまざまな場所(数百)で使用しているアプリケーション(中規模)があります。

これらのメッセージボックスは、ワークフローの一部であり、通知、警告、またはユーザーからの入力に使用されます。アクティビティがない場合、特定の時間が経過するとアプリケーションは自動的にログオフします。アプリケーションをログアウトする際には、セッションデータを消去するだけでなく、次回の起動時には時間のかかる起動プロセスを実行する必要がないように、セッションデータを消去し、表示をクリアして非表示にする必要があります。

すべてが正常に動作していますが、画面にメッセージボックスがあり、メッセージボックスに応答せずにユーザーがコンピュータを離れた後、ログアウトするためのアクティビティがないためにシナリオが発生しました。問題はメッセージボックスが消えないことです。

アプリケーションを隠している間に、開いているメッセージボックスがあればそれを閉じることができますか?

ありがとう。

14
私は MessageBox.Show(...)はモーダルだと思ったので、プログラムはどのようにキーを送信できますか?スレッド/タスクを使用していますか?
追加された 著者 Fischermaen,
多分キーの入力やescを送るでしょうか? :)
追加された 著者 Reniuz,
返信ありがとう。カスタマイズされたmsgボックスの使用を明確にすることは、リワークが非常に巨大であるためオプションではありません。アクティブなアプリケーションだけがコマンドを受信するため、ESCキーの送信も正しくありません。私はIDとmsgボックスのキャプションを渡すことによってMsgboxのハンドルを取得しているFIndWindowのアプローチを使用しています。ハンドラを取得した後、私は次のwin32 APIを使用して閉じています。 SendMessage(新しいHandleRef(null、msgbxcHandler)、WM_CLOSE、IntPtr.Zero、IntPtr.Zero); SendMessage(新しいHandleRef(null、msgbxcHandler)、WM_NCDESTROY、IntPtr.Zero、IntPtr.Zero);これまでのところうまく動作しています。
追加された 著者 NYK,

9 答え

UIAutomation (ク​​ールではあるがあまり使用されていないAPI)に基づいたコードです現在のプロセスのすべてのモーダルウィンドウ(MessageBoxで開いたウィンドウを含む)を閉じます。

    /// 
/// Attempt to close modal windows if there are any. ///
 
    public static void CloseModalWindows()
    {
       //get the main window
        AutomationElement root = AutomationElement.FromHandle(Process.GetCurrentProcess().MainWindowHandle);
        if (root == null)
            return;

       //it should implement the Window pattern
        object pattern;
        if (!root.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
            return;

        WindowPattern window = (WindowPattern)pattern;
        if (window.Current.WindowInteractionState != WindowInteractionState.ReadyForUserInteraction)
        {
           //get sub windows
            foreach (AutomationElement element in root.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window)))
            {
               //hmmm... is it really a window?
                if (element.TryGetCurrentPattern(WindowPattern.Pattern, out pattern))
                {
                   //if it's ready, try to close it
                    WindowPattern childWindow = (WindowPattern)pattern;
                    if (childWindow.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction)
                    {
                        childWindow.Close();
                    }
                }
            }
        }
    }

たとえば、あるボタン1を押したときにMessageBoxをポップアップするWinFormsアプリケーションがある場合、Windowsの「ウィンドウを閉じる」メニューを使用してアプリケーションを閉じることができます(タスクバーを右クリックします)。

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show("Don't click me. I want to be closed automatically!");
    }

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        const int WM_SYSCOMMAND = 0x0112;
        const int SC_CLOSE = 0xF060;

        if (m.Msg == WM_SYSCOMMAND)//this is sent even if a modal MessageBox is shown
        {
            if ((int)m.WParam == SC_CLOSE)
            {
                CloseModalWindows();
                Close();
            }
        }
        base.WndProc(ref m);
    }

もちろん、あなたのコードのどこかにCloseModalWindowsを使うことができます。これは単なるサンプルです。

7
追加された
しかし、これについては、私は、.net APIに言及していた...
追加された 著者 Erik Funkenbusch,
System.Windows.Automation名前空間はWPF用だと言います。 msdn.microsoft.com/en-us/library /ms747327(v=vs.110).aspx "Microsoft UI Automationは、Windows Presentation Foundation(WPF)をサポートするすべてのオペレーティングシステムで利用可能な、Microsoft Windows用の新しいアクセシビリティフレームワークです。"
追加された 著者 Erik Funkenbusch,
WPF用のUIAutomationではありませんか?質問はWinForms
追加された 著者 Erik Funkenbusch,
@ErikFunkenbusch - あなたが "WPF"の意味を知らない。 UIAutomationはあらゆる種類のアプリケーションをサポートします。
追加された 著者 Simon Mourier,
おそらくUIAutomationは、クライアントとしても、サーバーとしても、WPFに限定されません。同じ記事では、「WPF以外のフレームワーク向けのUIオートメーションプロバイダの開発者」とも言われています。
追加された 著者 Simon Mourier,
ありがとう、私の多くを助けた。
追加された 著者 digitguy,

このリンク MSDNフォーラムでは、 FindWindow を使用してメッセージボックスを閉じ、 WM_CLOSE メッセージを送信する方法を示します。問題は.NET/WindowsCEについて尋ねられましたが、あなたの問題を解決するかもしれません。

6
追加された
リンクされたページがどのように問題を解決するかについての情報を少なくともほんの少し追加することをお勧めします。私は簡単な説明を追加しても構わないと思います。 +1とにかく、これは参考になるリソースです。
追加された 著者 MartinStettner,
まあ、マーティン、あなたは絶対に正しいです。時間の複雑さのために私はこの情報を追加することができませんでした。しかし、編集のおかげで。 :)
追加された 著者 Bravo,

最初の質問:メッセージボックスがワークフローの一部として使用される場合、プログラムによってメッセージボックスを閉じないと、フローが変更/継続されますか?

3つの選択肢があると思います

  1. メッセージボックスクラスの独自のバージョンを作成します。

  2. プログラム的にメッセージボックスを閉じるために、C#でこれを実装します。 http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx

  3. メッセージボックスがワークフローに干渉しないようにします。メッセージボックスをプログラムで閉じると、ワークフローが継続/変更され、おそらく別のメッセージボックスが表示されてしまう可能性があるため、これはおそらく最良の解決策です。しかし、根本的な問題を明らかに修正するのが最適かもしれませんが、いつも最も簡単な方法はありません。

1と2は別のスレッドから行う必要があるので、メッセージボックスがブロックされていることを示すために、その意味を考える必要があります。

3
追加された
ポイント3の+1!私も、ユーザーが気付いたかどうかわからなくても、開いているメッセージボックスを単に「確認」するだけの良い気分はありません。
追加された 著者 MartinStettner,

私の例をSendKeysでテストしています。

私たちはbackgroundworkerとフォームのボタンを持っていると言うことができます。ボタンをクリックした後、ワーカーを開始してメッセージボックスを表示します。労働者のDoWorkイベントが5秒間スリープ状態になり、enter key - メッセージボックスを閉じて送信します。

private void button1_Click(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
    MessageBox.Show("Close this message!");
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Thread.Sleep(5000);
    SendKeys.SendWait("{Enter}");//or Esc
}
2
追加された
このソリューションは、ユーザーがフォーカスを別のアプリに変更しない場合にのみ機能します。アプリにまだフォーカスがあることを確認することはできません。そして、Windowsは、プログラムによって他のアプリからフォーカスを取得することをアプリが防止します。
追加された 著者 Alex,
ソリューションのために、実際にメッセージボックスが開いているかどうかを確認することが非常に重要であることを指摘したいと思います。ユーザーインターフェイスの他の部分に送信されたEnterキーは、望ましくない結果を招く可能性があります...
追加された 著者 MartinStettner,
はい、あなたのポイントは良いです。その迅速な回避策は、キーを入力することは明らかに良いキーではなく、より良いesc - in UIを使用する方がキャンセルを表すべきです。
追加された 著者 Reniuz,

あなたが呼び出しているコードを編集できるという前提で MessageBox.Show()メソッドを使用することをお勧めします メッセージボックス。代わりに、独自のカスタムフォームを使用して、 ShowDialog() 基本的にはMessageBoxクラスと同じことを行うために使用します。次にあなた フォーム自体のインスタンスを持っていて、その上で Close()を呼び出すことができます インスタンスを閉じます。

良い例はここです。

1
追加された
ああ。そして、人々がメッセージボックスを再実装したときの恐ろしいことは、何度も見られます。 msg は、あなたが本当のメッセージボックスを持っていて、遅れてそれを閉じることができることを示しています...
追加された 著者 Joey,
@ジョイ:はい、あなたは正しい...それはちょうどアイデア、おそらく最高ではなかった。あなたは、 msgは、実際のメッセージボックスを持ち、遅れてそれを閉じることができることを示しています ... "msg"は何ですか?申し訳ありませんが、私は理解していない、私はあなたの恩赦を頼む
追加された 著者 Marco,

私はあなたのメッセージボックスのフォームを自分のように実装することが最もクリーンな方法だと思う

class MyMessageBox : Form {
  private MyMessageBox currentForm;//The currently active message box

  public static Show(....) {//same as MessageBox.Show
   //...
  }

  public static Show(...) {//define additional overloads
  }

  public static CloseCurrent() {
    if (currentForm != null)
      currentForm.Close();
  }

 //...
}

私の大規模なプロジェクトの中には、このアプローチが他の目的(エラーメッセージの自動ロギングなど)にも役立つことがわかりました。

2番目のアイデアは、アプリケーションの現在のトップレベルウィンドウを取得し、 WM_CLOSE メッセージを送信するために GetTopWindow()(または他のWIN32関数)を使用することですそれに。

1
追加された
ユーザーがaltタブを押すか、目的のメッセージボックスの上に表示される他のメッセージボックスを押すとどうなりますか?そのシナリオでは、私はそれが望ましくないものを閉じるだろうと思う。
追加された 著者 Vivekh,

Refer to DmitryG post in "Close a MessageBox after several seconds"

タイムアウトに達するとMessageBoxを自動的に閉じる

using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

    public class AutoClosingMessageBox
    {
        System.Threading.Timer _timeoutTimer;
        string _caption;
        AutoClosingMessageBox(string text, string caption, int timeout)
        {
            _caption = caption;
            _timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
                null, timeout, System.Threading.Timeout.Infinite);
            MessageBox.Show(text, caption);
        }
        public static void Show(string text, string caption, int timeout)
        {
            new AutoClosingMessageBox(text, caption, timeout);
        }
        void OnTimerElapsed(object state)
        {
            IntPtr mbWnd = FindWindow(null, _caption);
            if (mbWnd != IntPtr.Zero)
                SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
            _timeoutTimer.Dispose();
        }
        const int WM_CLOSE = 0x0010;
        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    }

それを介して呼び出し

AutoClosingMessageBox.Show("Content", "Title", TimeOut);
1
追加された

私は.net 2と同じトリックで2つのアプローチを使用しました。

Open the MessageBox from stub-Form with MessageBox.Show(this,"message")

フォームが表示されていないか、実際にUIがない場合。

  1. Keep the form handler and close it with:

    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    

    or

  2. holding the form as class parameter and using FormX.Close().

フォームはMessageBoxの所有者であるため、閉じるとMessageBoxが閉じます。

1
追加された

これに対して独自のコントロールを作成し、そこにあるような動作を実装します。オプションとして、このMessageBoxを閉じるタイマーがあります。

0
追加された