DOBON.NET DOBON.NETプログラミング掲示板過去ログ

Windows7においてのメッセージボックスの強制終了

環境/言語:[環境(Windows 7)、Visual  Studio2008、使用言語(C#)、.NET Frameworkのバージョン(2.0)]
分類:[.NET]

いつもお世話になっております。

http://www.divakk.co.jp/blog/aoyagi/archive/2004/09/22/591.aspx
上記サイトを参考に保存ダイアログを強制的に10秒後に閉じる、というのを作りました。
しかし、Windows7において、保存ダイアログで既存のファイルに上書きする場合に、確認メッセージが表示され、
その確認メッセージを表示したまま10秒が経過すると、確認メッセージが消えずに残ってしまう という現象が発生しました。
Windows XPでは確認メッセージも一緒に強制的に閉じるようです。

Windows7においても、上書き確認メッセージごと強制的に閉じるようにするには、どうしたらよいのでしょうか?

どうぞよろしくお願い致します。

*************************************************************************
// 使い方:TimeoutDialog.ShowDialog();
using System;
using System.Collections.Generic;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
using System.Text;
using System.Diagnostics;

public class TimeoutDialog
{
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();

private delegate int EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

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

[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, [Out] StringBuilder lpClassName, int nMaxCount);

[DllImport("user32.dll")]
private static extern bool IsWindowEnabled(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

/// <summary>
/// 別スレッドでタイムアウトを監視するためのクラスです。
/// </summary>
private class TimerThread
{
private DateTime timeoutTime;
private uint currentThreadId;
private bool terminateFlag;
private Thread thread;

/// <summary>
/// コンストラクタです。
/// メッセージボックスのタイムアウト監視を開始します。
/// </summary>
/// <param name="timeoutMillisec">タイムアウト値(ミリ秒)。</param>
public TimerThread(int timeoutMillisec)
{
this.timeoutTime = DateTime.Now.AddMilliseconds(timeoutMillisec);
this.currentThreadId = GetCurrentThreadId();
this.terminateFlag = false;
this.thread = new Thread(new ThreadStart(this.ThreadProc));
this.thread.Start();
}

/// <summary>
/// スレッド関数です。
/// </summary>
private void ThreadProc()
{
while (!this.terminateFlag)
{
Thread.Sleep(100);
if (DateTime.Now > this.timeoutTime)
{
// タイムアウトが発生
// EnumWindows API を使ってメッセージボックスウインドウを探す
EnumWindows(new EnumWindowsProc(this.EnumWindowsProc), new IntPtr(0));
return;
}
}
}

/// <summary>
/// メッセージボックスウインドウを探して、見つかった場合は閉じます。
/// </summary>
/// <param name="hWnd"></param>
/// <param name="lParam"></param>
/// <returns></returns>
private int EnumWindowsProc(IntPtr hWnd, IntPtr lParam)
{
uint processId;
uint threadId;
threadId = GetWindowThreadProcessId(hWnd, out processId);

if (processId == Process.GetCurrentProcess().Id)
{
StringBuilder className = new StringBuilder("", 256);
GetClassName(hWnd, className, 256);

// メッセージボックスを閉じる
if (className.ToString() == "#32770")
{
const int WM_COMMAND = 0x111;
PostMessage(hWnd, WM_COMMAND, new IntPtr(2), new IntPtr(0)); // キャンセルボタン
PostMessage(hWnd, WM_COMMAND, new IntPtr(7), new IntPtr(0)); // Noボタン
return 1;
}
}
return 1;
}

/// <summary>
/// タイムアウト監視用スレッドを終了させます。
/// </summary>
public void Terminate()
{
this.terminateFlag = true;
this.thread.Join();
}
}

/// <summary>
/// 保存ダイアログを表示します。
/// </summary>
/// <returns><see cref="DialogResult"/></returns>
public static DialogResult ShowDialog()
{
TimerThread tt = new TimerThread(10000); // 10秒後に、保存ダイアログを強制的に閉じる
try
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
return saveFileDialog1.ShowDialog();
}
finally
{
tt.Terminate();
}
}
}

************************************************************************
■No27725に返信(ぱんだこぱんださんの記事)
> Windows7においても、上書き確認メッセージごと強制的に閉じるようにするに
> は、どうしたらよいのでしょうか?

単にメッセージも探して閉じたら良いのでは?


> 上記サイトを参考に保存ダイアログを強制的に10秒後に閉じる、というのを
> 作りました。

個人的には備わっていない機能を外から無理矢理付け加えることは避けるべきだと思っています。
理由は、新しい Windows ではうまく動作しない可能性が高い、将来的な更新プログラムによって挙動が変化する可能性があることからです。

今回のように仕様で明言されていない”現在の実装に依存した”実装をされている場合に Windows のバージョン違いで発生した問題はその実装した人が責任を負うべきです。
Windows XP でうまく動いて、Windows 7 でまずいことになるのは Windows XP での実装に依存した実装をした結果だと思いますので、Windows 7 で出てくる問題は Windows 7 の実装に依存してふさぐ実装が必要でしょう。

おそらく、これが最適・適切・標準的といったような答えはありません。
@
FindWindowExを使って、ダイアログのタイトル「名前を付けて保存の確認」を直接指定して
ダイアログのハンドルを取得した場合も、ダイアログは消えませんでした。
****************************************************************************
[DllImport("user32.dll")]
private static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpszClass, string lpszWindow);

IntPtr child = IntPtr.Zero;
// Windows7 での「名前を付けて保存」ダイアログ上に表示される
// ファイル上書きメッセージ「名前を付けて保存の確認」メッセージを強制的に消したい!
child = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "名前を付けて保存の確認");
if (child != IntPtr.Zero)
{
PostMessage(child, WM_COMMAND, new IntPtr(7), new IntPtr(0)); // Noボタン
}
****************************************************************************

A
SendKeysを使ってみたのですが、これもダイアログが消えませんでした。
****************************************************************************
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool SetForegroundWindow(IntPtr hWnd);

if (child != IntPtr.Zero)
{
SetForegroundWindow(child);
SendKeys.SendWait("N");
}
****************************************************************************


PostMessage の使い方が違うとか、
Win 7においては、ダイアログにはPostMessageやSendKeys以外のものを使うとか
そういった事なのでしょうか?
■No27732に返信(ぱんだこぱんださんの記事)
未確認ですが、閉じるボタン、あるいは はい/いいえ ボタンを拾って、
IAccessible.DoDefaultAction メソッドを呼び出してみるのは如何でしょう。
■No27732に返信(ぱんだこぱんださんの記事)
>SendKeys.SendWait("N");

大文字の N が何をするか調べてみましたか?


正解を書いてしまうと、Shift + n という組み合わせになります。
じゃあ、どうすれば良いかは言わずともおわかりいただけると思っています。
下記で消すことができました。
ありがとうございました。

**************************************************************************
// メッセージボックスを閉じる
if (className.ToString() == "#32770")
{
const int WM_COMMAND = 0x111;
PostMessage(hWnd, WM_COMMAND, new IntPtr(2), new IntPtr(0)); // キャンセルボタン
PostMessage(hWnd, WM_COMMAND, new IntPtr(7), new IntPtr(0)); // Noボタン
SetForegroundWindow(hWnd);
SendKeys.SendWait("n");

return 1;
}
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板