2

他の(一般的なウィンドウのブラックボックス)アプリケーションをポップアップとして起動し、それらの完了を待つ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を使用して子アプリにフォーカスします。
4

2 に答える 2

0

自分に属していない他のウィンドウの親子関係を変更するのは注意が必要で、エラーが発生しやすくなります。ランチャープログラムが起動されたプログラムと通信せず、主な目的がランチャーとのUIを回避することである場合、ShowWindow(SW_HIDE)を使用して、セカンダリプログラムが正常に起動された後にランチャーを非表示にすることができます。非表示モードでは、起動されたプログラムを引き続き監視し、セカンダリプログラムが終了したときに自分自身を再表示します。

于 2012-08-19T01:43:11.930 に答える
-3

API関数「ShellExecute()」を使用してみてください。

于 2015-01-17T08:06:13.270 に答える