C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト


(過去ログ 149 を表示中)
■86798 / )  Re[4]: メッセージボックスが出ない
□投稿者/ 魔界の仮面弁士 (1594回)-(2018/03/16(Fri) 21:58:21)
No86795 (にゃるら さん) に返信
> Enterキーを押しっぱなしにしてみてください。
> メッセージ2が出るとは思いますが、勝手に閉じるのが見えると思います。

勝手に閉じたというよりは、メッセージボックスの default button が
Enter キーによって押下されたということではないでしょうか。


No86796 (にゃるら さん) に返信
> うーん、こういうことなのでしょうか?


以下、私の認識。(多分に推測を含みます)


.NET Framework のソースコードを読んでみると明らかですが、
MessageBox.Show メソッドの内部実装は、最終的に MessageBox API を呼び出します。

そして MessageBox API は、第一引数にオーナーウィンドウの ハンドルを渡す
仕様になっています。(ただし、この第一引数は NULL でも構いません)
https://msdn.microsoft.com/en-us/library/windows/desktop/ms645505.aspx


そして、今回問題になっているコードですが、試しに
 var ret1 = MessageBox.Show("1");
 var e1 = Marshal.GetLastWin32Error();
 var ret2 = MessageBox.Show("2 ret1=" + ret1.ToString());
 var e2 = Marshal.GetLastWin32Error();
として実行すると、"1" が表示されない場合には、
 e1 が 6 (すなわち ERROR_INVALID_HANDLE)
 e2 が 0 (すなわち NO_ERROR)
という値が返される状況になっていました。


MessageBox.Show の内部実装を見る限りでは、owner を null にした場合、
GetActiveWindow API で得られたウィンドウが使われるようなので、今回の場合、
おそらくこの段階で invalid な HWND が使われてしまったのではないでしょうか。

そしてその結果、既に破棄されたウィンドウハンドルが
MessageBox API の第一引数に渡されることになり、この P/Invoke が
ERROR_INVALID_HANDLE を理由に失敗していたのだと推察します。
親ハンドルが不正のため、"1" のメッセージはそもそも表示されません。


また、呼び出しが失敗した場合、MessageBox API は固定値 0 を返す仕様になっています。
ですから、呼び出されなかった場合には
 var ret1 = (DialogResult)0;
に相当する処理結果になりそうなのですが…実際には
DialogResult.None ではなく、DialogResult.No が返されています。

何故、この段階で None ではなく No が返されるのかといえば、
 var ret1 = Win32ToDialogResult(0);
という処理が行われるためのようです。
このメソッドの実装は下記をご覧ください。
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/MessageBox.cs,bbe9ce90a8f7d475


この推察が正しいなら、回避策としては、正しいオーナーを渡せば良いわけですから、
Azulean さんの No86789 のコードが、解決策の一つになるかと思います。


もしくは、MessageBox API は、オーナーの HWND を NULL にすることを認めていますので、
API の第一引数に対して、意図的に IntPtr.Zero が渡るようにすることでも、
今回の問題を回避することができます。

 private class NullWindow : IWin32Window
 {
  // IntPtr IWin32Window.Handle => IntPtr.Zero;
  IntPtr IWin32Window.Handle { get { return IntPtr.Zero; } }
 }
 public UserControl1()
 {
  var nw = new NullWindow();
  var ret1 = MessageBox.Show(nw, "1");
  var ret2 = MessageBox.Show(nw, "2 ret1=" + ret1.ToString());
  Controls.Add(new Button());
 }


とはいえ、NULL ハンドルな IWin32Window を作るだけなら、
わざわざ上記のようなクラスを自作せずとも、
No86791 に書いた NativeWindow を利用できます。
返信 編集キー/


管理者用

- Child Tree -