他の(一般的なウィンドウのブラックボックス)アプリケーションをポップアップとして起動し、それらの完了を待つMFCアプリケーションがあります。親と子の間のコミュニケーション/相互作用は必要ではなく、避けるべきです。「子アプリは親アプリのモーダルダイアログとして動作する」動作のみが必要です。これを行う正しい方法は何ですか?
「別のアプリケーションを子ウィンドウとして起動する」の例は、次の場所にあります。http://www.codeproject.com/Articles/18724/Hosting-exe-applications-につながる別のアプリケーションの子/ポップアップとしてアプリケーションをアクティブ化するinto-a-dialog(これは私が望むものではなく、モーダルポップアップ動作が必要です)
簡単にするために、起動するアプリケーションと起動するアプリケーションの両方に、ウィンドウの単一の「スタック」(独自のモーダルダイアログを持つことができるモーダルダイアログを持つ1つのメインウィンドウ)があると想定できます。
私の現在の擬似コード(簡単にするためにエラー処理とコールバック関数は省略されています)
//get the current MFC dialog of launcher program we are launching the other app from
parentWnd = AfxGetMainWnd()->GetActiveWindow();
parentHwnd = parentWnd->GetSafeHwnd(); //HWND
// launch child and retrieve basic info from PROCESSINFO structure
CreateProcess(childExecutable); // => childProcessHandle, childProcessId
//get the "main" window of child application
EnumWindows(EnumProc_That_Retrieves_TopLevelWindow_With_childProcessId); // => childHwnd
//link the child window as popup
SetWindowLong(childHwnd, GW_OWNER, parentHwnd);
//disable input into parent window
parentWnd->EnableWindow(FALSE);
//remove taskbar entry for child
SetWindowLong(child, GWL_EXSTYLE, GetWindowLong(child, GWL_EXSTYLE)&~WS_APPWINDOW);
//now keep waiting for the child process termination and process parent messages (e.g. WM_PAINT)
while (MsgWaitForMultipleObjects(childProcessHandle and process QS_ALLINPUT) {
while (PeekMessage(PM_NOREMOVE)) AfxGetApp()->PumpMessage();
}
//re-enable input into parent window
parentWnd->EnableWindow(TRUE);
さて、私の小さな問題は、前景の視覚スタイル(たとえば、前景=青いタイトルバーと背景=灰色のタイトルバー)とキーボード入力のフォーカス動作に関するものです。
1)子の最初の削除WS_APPWINDOWスタイルは、子アプリから前景のビジュアルと入力フォーカスを削除します。その時点で焦点を当てているアプリケーションはありません。
2)ユーザーが親アプリケーションウィンドウをクリックすると、子の前景の視覚スタイルが切り替わります。キーボードフォーカスは子アプリケーションで保持されます。例:子アプリにフォアグラウンド+フォーカスがあります->親を1回クリック->子がフォアグラウンドを失い、フォーカスを保持します->親を2回クリックします->子がフォアグラウンドを取得し、フォーカスを保持します->など。
予想される動作(「通常の」MFCポップアップの動作):子アプリに前景+フォーカスがあります->親をクリックします->子タイトルバーが短時間点滅して前景を保持します+フォーカス子アプリに前景+フォーカスがありません->親をクリックします->子タイトルバーのゲインフォアグラウンドとキーボードフォーカス
そして今、これは最悪の問題です:3)個別に起動すると、ユーザーが「モーダルダイアログのスタック」A-> B-> C-> D-> Eを開くことができる、ウィンドウの所有権が完全に一致するMFCアプリケーションに遭遇しました。これ(EはDが所有し、DはCが所有するなど)。しかし、MFCアプリケーション(M)から開くと、所有権はM-> A-> B-> C、D、E(C、D、EはすべてBが所有、BはAが所有、AはAが所有)のようになります。私のアプリウィンドウでM)。これにより、「サポートなしのスタック」http://blogs.msdn.com/b/oldnewthing/archive/2005/02/24/379635.aspxの問題が発生します。この動作は削除すると消えるSetWindowLong(childHwnd, GW_OWNER, parentHwnd)
ので、所有権をいじると子アプリケーションの望ましくない動作が引き起こされる可能性がありますが、それがないと「一方が他方の上に配置される」ことを保証できないようです。
したがって、再び壮大な質問があります。このタスクを実行し、私が説明した問題を回避する正しい方法は何ですか。
編集
これまでの解決策
@mfcが以下に示すように、所有者が所有する構造をいじらないようにする必要があります。したがって、タスクは基本的に、別の方法で親子ペアだけにウィンドウマネージャーのこの側面を再実装することです。Windowsフックを使用してソリューションの一部のプロトタイプを作成しました。ただし、完了するのは非常に複雑で面倒なように思われるため、別の原始的なアプローチを採用することにしました(締め切り、締め切り)。例として、両方の基本的な考え方を説明します。
フックソリューション
免責事項:親フォーカスフックのみが機能することが確認されており、残りは理論的作成です。よりクリーンで軽量な実装があるかもしれませんが、実際のWindowsウィンドウマネージャーの実装からインスピレーションを得ることができます(ウィンドウマネージャーでは正常に機能するGW_OWNERの設定を避けることが重要ですが、子ブラックボックスアプリの動作を壊す可能性があります)。
- 子アプリがウィンドウなしで(断続的に)実行されている場合の親メッセージループに「子の実行中に入力を無視する」を追加します
- 呼び出しごとに[parentPid、parentHwnd、childPid]を保持する共有メモリと構造を作成します
- [親の所有されていないウィンドウのリスト、それらのUIスレッド、子フック]のDLLインスタンスメモリを作成します
- システム全体をWH_CBT->HCBT_CREATEWNDにフックし、childPidが一致する場合は、ウィンドウをリストに登録し、まだ存在しない場合は、その子スレッド専用に別のフックHCBT_ACTIVATEを登録します。
- システム全体をWH_CBT->HCBT_DESTROYWNDにフックし、childPidが一致する場合は、リスト内のウィンドウの登録を解除し、これが特定のスレッドの最後のウィンドウである場合はHCBT_ACTIVATEフックの登録を解除し、これが子アプリの最後のウィンドウである場合は、親HCBT_ACTIVATEフックとフォーカスの親をフック解除します
- 親スレッドHCBT_ACTIVATEフックは、フォーカスの取得を防ぎ、代わりにEnumWindowsを使用して子アプリにフォーカスします。
- 子スレッドHCBT_ACTIVATEフックは、ターゲットが親である場合にフォーカスの喪失を防ぎ、親を子のすぐ下にZオーダーで保持します
- 子プロセスを一時停止して作成し、フックが配置されている場合にのみ再開します
- どこでもフックを外すことを忘れないでください
原始的なアプローチ
基本的に、前のソリューションの最初のポイントでフォーカススイッチを適用します。親がクリックされると、フォーカスが前後に移動するときに子の上でちらつきます。
- 子アプリの実行中の親メッセージループで「子の実行中に入力を無視する」(さまざまなキー押下、クリックなどのメッセージを破棄)、代わりにEnumWindowsを使用して子アプリにフォーカスします。