2018/03/19(Mon) 12:00:02 編集(投稿者)
2018/03/19(Mon) 11:59:57 編集(投稿者)
<pre><pre>■No86798 (魔界の仮面弁士 さん) に返信
MessageBoxがFormとは異なる実装とは思っていましたが、
似たようなことをしていると思い、MessageBoxクラスまでは
追いかけていませんでした。。。
public static DialogResult Show(string text)
{
return ShowCore(null, text, String.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, false);
}
であれば、オーナーを指定していないときはnull扱いなので、
GetActiveWindowsを呼び出す形でMessageBoxAPIに置き換えたソースコードを書いてみました(文末)。
実行結果ですが、環境は以下になります。
Windows7 SP1 (日本語) 64bit
Visual Studio 2017
まずは質問者様と同じForms.MessageBoxを使った場合は、
1回目は、
Result = No
LastError(1150): ???
で2回目以降は
Result = No
LastError(1400): ERROR_INVALID_WINDOW_HANDLE
でした。
一方で、MessageBoxAPIに置き換えた方は、
1回目は、
AciveWindow(788866):System.Windows.Forms.Application+ParkingWindow
Result = None
LastError(1400): ERROR_INVALID_WINDOW_HANDLE
で2回目からもParkingwindowのハンドルは違うものが出ます。
ただ、何回かしていると(時には2回目から)
AciveWindow(919896):WindowsFormsApp9.Form1
Result = OK
LastError(0): ???
となりました。
私の呼び出し方に問題がもしなかったとすれば、GetActiveWindow/MessageBox以外の
API呼び出し等も影響を与えていそうです。
ただ、なんにしても「(呼び出し側にとって)不特定なハンドル」が親ウィンドウになってしまうことが
原因ということになんら変わらないわけなので、
・メインとなるダイアログクラス以外は、
- 親ハンドルを渡す(Azulean さんの No86789 のコード)
- NativeWindowを渡す(魔界の仮面弁士 さんの No86791のNativeWindow指定) <-- これはGetWindowLongレベルの知識が必要なので微妙
・サービスであればMessageBoxOptions.ServiceNotificationを指定する(魔界の仮面弁士 さんの No86791のServiceNotification指定)
としておくのが良いのかなと個人的には思いました。
自分(もしくは周りの人)でMessageBoxのオーナーを指定しない実装がなかったので、結構面白い内容でした!
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows.Forms;
namespace WindowsFormsApp9
{
public class Form1 : Form
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public Form1()
{
Controls.Add(new UserControl1());
Menu = new MainMenu(new MenuItem[] { new MenuItem("&File...", (sender, e) =>
{
var control = Controls[0];
Controls.Remove(control);
control.Dispose();
Controls.Add(new UserControl1());
})});
}
class UserControl1 : UserControl
{
static bool first = true;
public UserControl1()
{
if (first)
{
first = false;
}
else
{
#if true
var ret1 = System.Windows.Forms.MessageBox.Show("1");
#else
var activeWindow = GetActiveWindow();
var c = Control.FromHandle(activeWindow);
System.Diagnostics.Debug.WriteLine($"AciveWindow({activeWindow}):{(c != null? c.GetType().FullName: "null")}");
var ret1 = (DialogResult)MessageBox(new HandleRef(null, activeWindow), "出ないはずメッセージ", "確認", MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL);
#endif
System.Diagnostics.Debug.WriteLine($"Result = {ret1}");
GetLastErrorCheck();
var ret2 = System.Windows.Forms.MessageBox.Show("2 ret1=" + ret1.ToString());
}
Controls.Add(new Button());
}
}
[DllImport("kernel32.dll")]
static extern int GetLastError();
[DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto),
SuppressMessage("Microsoft.Usage", "CA2205:UseManagedEquivalentsOfWin32Api")]
[ResourceExposure(ResourceScope.None)]
public static extern int MessageBox(HandleRef hWnd, string text, string caption, int type);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.Process)]
public static extern IntPtr GetActiveWindow();
const int MB_OK = 0x00000000;
const int MB_ICONEXCLAMATION = 0x00000030;
const int MB_APPLMODAL = 0x00000000;
const int ERROR_INVALID_HANDLE = 6;
const int ERROR_INVALID_WINDOW_HANDLE = 1400;
private static void GetLastErrorCheck()
{
var lastError = GetLastError();
switch (lastError)
{
case ERROR_INVALID_HANDLE:
System.Diagnostics.Debug.WriteLine($"LastError({lastError}): ERROR_INVALID_HANDLE");
break;
case ERROR_INVALID_WINDOW_HANDLE:
System.Diagnostics.Debug.WriteLine($"LastError({lastError}): ERROR_INVALID_WINDOW_HANDLE");
break;
default:
System.Diagnostics.Debug.WriteLine($"LastError({lastError}): ???");
break;
}
}
}
}
</pre></pre>