トップ回答者
最前面状態(TopMost)が効かなくなる

質問
-
はじめて利用させていただきます。よろしくお願いいたします。
MainForm1と、MainForm2というフォームコントロールを存在させます。
ここで、MainForm2は、MainForm1よりも前面とします。(TopMost=true)
次に、MainForm2をオーナーとする、SubForm1と、SubForm2があります。
SubForm1.Show(MainForm2) にて、SubForm1を表示させます。
SubForm1は、MainFrame1とMainFrame2の両方より前面に表示されます。
次に、SubForm2.TopMost=falseを明示的にコーディングしてから、SubForm2.Show(MainFrame2)として、SubForm2を表示させます。
この段階では、SubForm2は、SubForm1と同様に、MainFram1とMainFrame2より前面に表示されます。
最後に、SubForm1とSubForm2をいったん閉じてから、再表示させると、MainFrame2がMainFram1の背面となってしまい、
TopMost=falseとした様な振る舞いが見られます。
文章のみで少し解りづらいかもしれませんが、非常に不可解な動作です。
SubForm2は元々TopMost=falseにもかかわらず、コード上でTopMost=falseと明示的に指定しただけで、最前面のはずのMainFrame2が背面となってしまします。
Spy++で監視してみると、TopMost=falseのタイミングで、MainFrame2の順序が大きく下の方へ移動しているようでした。
以上になりますが、ご教授賜れれば幸いです。
よろしくお願いいたします。
回答
-
上記の.NETのソースをもとにWin32の下記のコードを書いてみて試したところ、こちらのプログラムでも再現しました。
#include <windows.h> TCHAR szMainClassName[] = TEXT("Main"); TCHAR szSubClassName[] = TEXT("Sub"); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hChild; switch (msg) { case WM_CREATE: { WNDCLASS wndclass = { 0, DefWindowProc, 0, 0, ((LPCREATESTRUCT)lParam)->hInstance, 0, 0, 0, 0, szSubClassName }; RegisterClass(&wndclass); hChild = CreateWindow(szSubClassName, TEXT("Window"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 256, 256, hWnd, 0, ((LPCREATESTRUCT)lParam)->hInstance, 0); CreateWindow(TEXT("BUTTON"), TEXT("表示"), WS_VISIBLE | WS_CHILD, 10, 10, 256, 32, hWnd, (HMENU)100, ((LPCREATESTRUCT)lParam)->hInstance, 0); CreateWindow(TEXT("BUTTON"), TEXT("非表示"), WS_VISIBLE | WS_CHILD, 10, 50, 256, 32, hWnd, (HMENU)101, ((LPCREATESTRUCT)lParam)->hInstance, 0); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case 100: SetWindowPos(hChild, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); ShowWindow(hChild, SW_SHOW); break; case 101: ShowWindow(hChild, SW_HIDE); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPSTR pCmdLine, int nCmdShow) { MSG msg; WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, 0, LoadCursor(0,IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), 0, szMainClassName }; RegisterClass(&wndclass); HWND hWnd = CreateWindowEx( WS_EX_TOPMOST, szMainClassName, TEXT("メインウィンドウ"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance, 0 ); ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
「表示」ボタンと押すと、TOPMOST だったメインウィンドウが TOPMOST ではなくなる。 これは .NET や言語に依存する問題ではなく Windows で起こる問題のようです。
ネットで検索したところ、本事象のことが書かれていると思われるページを見つけました。
https://support.microsoft.com/ja-us/kb/2733420 (日本語)
https://support.microsoft.com/en-us/kb/2733420 (英語)ただ、私の環境では上記の修正プログラムを当てることができませんでした。
上記のページにも記載しているようにこれ以上はカスタマーサポートなどに問い合わせを行うしかなさそうですね。。。- 編集済み kenjinoteMVP 2016年6月21日 13:53
- 回答としてマーク CureBouz 2016年6月22日 6:44
すべての返信
-
こんにちは。
ご質問の内容を試したところ私の環境(Windows 10 x64、 Visual Studio 2015、.NET 4.5.2)では再現しないようでした。MainForm2は常にMainForm1の手前に表示されます。(再現手順か再現環境が異なる?)
ちなみに、再現環境のOS、Visual Studioのバージョン、.NETのバージョンは何になりますでしょうか?
- 編集済み kenjinoteMVP 2016年6月21日 9:44
-
早速のご返信感謝いたします。
すみません、環境のご説明が漏れておりました。以下の通りとなります。
Windows7Pro x64 VisualStudio2012Pro .NetFramework4.5
>>MainFrame2は常にMainFrame1の手前に表示されます。
これは、MainForm1とMainForm2が重なる位置で、MainForm1をクリックしても、MainForm2が前面のままということでしょうか。
当方環境では、MainForm2がアクティブな時は前面にはなりますが、MainForm1をアクティブとすると、MainForm2が背面に隠れてしまいます。
kenjinote様の.NET4.5.2のバージョンが気になりますが、当方環境はすぐにバージョンアップできない状況です。
ただ、この問題は.NetFramewowkより深い部分、ウインドウハンドルの問題のような気がしています。
プログラムの作り非常にシンプルです。
フォームコントロールを計4つ、デザイナでプロパティは変更せずデフォルトのままです。
MainForm1に、ボタンを2つ配置して、SubForm1とSubForm2の表示用に使用します。
MainForm1のLoadイベントで、MainForm2.TopMost=trueとして、MainForm2.Show()でオーナー無し表示します。
配置したボタン1で、SubForm1.Show(MainForm2);
ボタン2で、SubForm2.TopMost=falseとして、SubForm2.Show(MainForm2);
これで、SubFormをそれぞれ表示→閉じる→再表示すると症状が発症します。
この時、閉じるの意味は、Disposeせずに、インスタンスは使い回してください。毎回Newすると症状は発生しません。
以上になりますが、何かおわかりになりましたら、ご返信いただけると助かります。
よろしくお願いいたします。
-
閉じるの意味は、Disposeせずに、インスタンスは使い回してください。毎回Newすると症状は発生しません。
情報ありがとうございます。
サブのフォームを開くときに毎回Newしていたところをインスタンスを使いまわすようにしたところ再現しました。Spy++で確認したところ MainForm2 の WS_EX_TOPMOST スタイルがなくなりました。(Win7 x64、Win10 x64、それぞれ.NET 4.5および.NET 4.5.2で確認)
なぜか明示的に「SubForm1.TopMost = false」と記述するとオーナーのウィンドウタイルが変わってしまう。恐らくこの現象は不具合じゃないかと思いました。少しだけネットで調べてみましたが有効そうな情報は見つかりませんでした。 もう少し追ってみようと思います。まずはご報告まで。
- 編集済み kenjinoteMVP 2016年6月21日 11:08
-
上記の.NETのソースをもとにWin32の下記のコードを書いてみて試したところ、こちらのプログラムでも再現しました。
#include <windows.h> TCHAR szMainClassName[] = TEXT("Main"); TCHAR szSubClassName[] = TEXT("Sub"); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hChild; switch (msg) { case WM_CREATE: { WNDCLASS wndclass = { 0, DefWindowProc, 0, 0, ((LPCREATESTRUCT)lParam)->hInstance, 0, 0, 0, 0, szSubClassName }; RegisterClass(&wndclass); hChild = CreateWindow(szSubClassName, TEXT("Window"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 256, 256, hWnd, 0, ((LPCREATESTRUCT)lParam)->hInstance, 0); CreateWindow(TEXT("BUTTON"), TEXT("表示"), WS_VISIBLE | WS_CHILD, 10, 10, 256, 32, hWnd, (HMENU)100, ((LPCREATESTRUCT)lParam)->hInstance, 0); CreateWindow(TEXT("BUTTON"), TEXT("非表示"), WS_VISIBLE | WS_CHILD, 10, 50, 256, 32, hWnd, (HMENU)101, ((LPCREATESTRUCT)lParam)->hInstance, 0); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case 100: SetWindowPos(hChild, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); ShowWindow(hChild, SW_SHOW); break; case 101: ShowWindow(hChild, SW_HIDE); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPSTR pCmdLine, int nCmdShow) { MSG msg; WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, 0, LoadCursor(0,IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), 0, szMainClassName }; RegisterClass(&wndclass); HWND hWnd = CreateWindowEx( WS_EX_TOPMOST, szMainClassName, TEXT("メインウィンドウ"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance, 0 ); ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
「表示」ボタンと押すと、TOPMOST だったメインウィンドウが TOPMOST ではなくなる。 これは .NET や言語に依存する問題ではなく Windows で起こる問題のようです。
ネットで検索したところ、本事象のことが書かれていると思われるページを見つけました。
https://support.microsoft.com/ja-us/kb/2733420 (日本語)
https://support.microsoft.com/en-us/kb/2733420 (英語)ただ、私の環境では上記の修正プログラムを当てることができませんでした。
上記のページにも記載しているようにこれ以上はカスタマーサポートなどに問い合わせを行うしかなさそうですね。。。- 編集済み kenjinoteMVP 2016年6月21日 13:53
- 回答としてマーク CureBouz 2016年6月22日 6:44
-
kenjinote様
お世話になっております。
当方の環境へ修正パッチKB2733420を当ててみましたが、残念ながら改善はみられませんでした。
内容的には本件にマッチしているように思ったのですが。(このパッチ自体にまだバグがあるなんてことはないでしょうかね・・)
もうこのレベルの問題となると、仰る通りカスタマーサポートへ問い合わせるしかないようですね。
いろいろとご丁寧にご対応いただきましてありがとうございました。とても助かりました。
とりあえず本件は仮対応として、MainForm2のスタイルを監視して、WS_EX_TOPMOSTが外れていたら、TopMost=trueを明示的に行う作りでごまかそうとしています。。。
また何か進捗がありましたら、ご連絡させていただきたいと思います。
それでは失礼いたします。