如何从另一个进程显示模式对话框窗口?

时间:2016-12-27 01:33:07

标签: c++ windows winapi mfc modal-dialog

我有一个32位MFC应用程序,它使用自定义库,这将成为重新编译为x64的噩梦。一般来说,应用程序并不需要以64位运行,除非在一种情况下 - 这就是渲染内容以在对话窗口中显示,这可以从更大的寻址空间中受益。

所以我的目标是"模仿" CDialog::DoModal方法,但用于另一个进程中的对话框。

我将该对话框窗口构建为独立的基于x64 MFC对话框的应用程序。它将文件路径作为输入参数,在内部完成所有工作,并返回简单的用户选择:OKCancel

所以我从我的主要父进程中执行以下操作:

//Error checks omitted for brevity
CString strCmd = L"D:\\C++\\MyDialogBasedApp.exe";

HWND hParWnd = this->GetSafeHwnd();

SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS;
sei.nShow = SW_SHOW;
sei.lpVerb = _T("open");
sei.lpFile = strCmd.GetBuffer();
sei.hwnd = hParWnd;

BOOL bInitted = SUCCEEDED(::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));

ShellExecuteEx(&sei);

DWORD dwProcID = ::GetProcessId(sei.hProcess);

//Try to get main Wnd handle for the child process
HWND hMainChildWnd = NULL;
for(;; ::Sleep(100))
{
    hMainChildWnd = getHwndFromProcID(dwProcID);
    if(hMainChildWnd)
        break;
}

HWND hPrevParWnd = ::SetParent(hMainChildWnd, hParWnd);
if(hPrevParWnd)
{
    //Wait for child process to close
    ::WaitForSingleObject(sei.hProcess, INFINITE);

    //Reset parent back
    ::SetParent(hMainChildWnd, hPrevParWnd);
}

::CloseHandle(sei.hProcess);

if(bInitted)
    ::CoUninitialize();

getHwndFromProcIDfrom here

除了以下内容之外,这种方式有效:

(1)任务栏上有两个图标:一个用于我的主应用程序,另一个用于子应用程序。有没有办法不显示儿童图标?

(2)我可以将焦点从子窗口切换到父窗口,反之亦然。在实际的模态对话框窗口中,当子项打开时,无法切换回父项。有没有办法做到这一点?

(3)如果我开始与父母互动,它似乎是“挂断”"操作系统甚至会在标题栏上显示它。

所以我很好奇是否有办法解决所有这些问题?

3 个答案:

答案 0 :(得分:3)

  1. 您需要将自己窗口的指针传递给子进程
  2. 在等待子进程时,您需要进程窗口消息 出口。 //seekbar change listener seekbar.setMax(music.getDuration()); seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // TODO Auto-generated method stub if(fromUser) music.seekTo(progress); } }); //music playing public void button(View v){ if(music.isPlaying()){ music.stop(); try { music.prepare(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } music.seekTo(0); button.setText(R.string.start); seekbar.setProgress(0); }else{ music.start(); button.setText(R.string.stop); Thread(); } } //Thread public void Thread(){ Runnable task = new Runnable(){ public void run(){ while(music.isPlaying()){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } seekbar.setProgress(music.getCurrentPosition()); } } }; Thread thread = new Thread(task); thread.start(); } 在这里无法接受 - 需要使用 WaitForSingleObject
  3. 子进程必须在创建时将窗口设置为自己的所有者窗口 时间 - 您不需要致电MsgWaitForMultipleObjectsEx
  4. 这一切都将完美无缺。在32位MFC应用程序中,您需要使用下一个代码:

    SetParent
    BOOL DoExternalModal(HWND hwnd, PCWSTR ApplicationName) { STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; WCHAR CommandLine[32]; swprintf(CommandLine, L"*%p", hwnd); if (CreateProcessW(ApplicationName, CommandLine, 0, 0, 0, 0, 0, 0, &si, &pi)) { CloseHandle(pi.hThread); MSG msg; for (;;) { switch (MsgWaitForMultipleObjectsEx(1, &pi.hProcess, INFINITE, QS_ALLINPUT, 0)) { case WAIT_OBJECT_0: CloseHandle(pi.hProcess); return TRUE; case WAIT_OBJECT_0 + 1: while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } continue; default: __debugbreak(); } } } return FALSE; } 中的

    让我们使用MyDialogBasedApp.exe作为演示对话框。我们将使用您的MFC窗口作为它的第一个参数。

    MessageBox

    使用此代码:

    (1)主应用程序的任务栏上只有一个图标

    (2)您无法将焦点从子窗口切换到父窗口,反之亦然。所有工作作为实际的模态对话窗口

    (3)父母没有“挂断”,因为它处理了Windows消息(void ExeEntry() { int ec = -1; if (PWSTR CommandLine = GetCommandLine()) { if (CommandLine = wcschr(CommandLine, '*')) { HWND hwnd = (HWND)(ULONG_PTR)_wcstoi64(CommandLine + 1, &CommandLine, 16); if (hwnd && !*CommandLine && IsWindow(hwnd)) { ec = MessageBoxW(hwnd, L"aaa", L"bbb", MB_OK); } } } ExitProcess(ec); } ) - 您的代码被“挂断”,因为您没有这样做,而是等待MsgWaitForMultipleObjectsEx

答案 1 :(得分:0)

模态对话框会执行两项使其成为“模态”的内容:

  • 对话框的“所有者”设置为父窗口。
  • 父窗口已停用。

我玩了一下,虽然你可以手动完成这些操作,但最简单的方法是将父窗口句柄传递给DialogBox函数(或MFC中的CDialog构造函数)

您的子进程可以使用FindWindow(或类似的机制)来获取父窗口句柄并使用它来显示对话框,而不是在父进程中ShellExecuteEx之后完成所有工作。

答案 2 :(得分:0)

您要做的事情无法安全地完成。博客条目Is it legal to have a cross-process parent/child or owner/owned window relationship?解释说,安装跨进程所有者/拥有的窗口关系会导致系统调用AttachThreadInput,并且 - 我们都知道 - AttachThreadInput is like taking two threads and pooling their money into a joint bank account, where both parties need to be present in order to withdraw any money。这为死锁创造了非常真实的潜力。如果您控制两个参与的线程,则只能安全地防止这种情况发生。由于至少有一个线程使用第三方应用程序框架(在这种情况下为MFC),因此这是不受限制的。

由于我们已经确定您提出的解决方案无法安全实施,因此您需要研究替代方案。一种解决方案可能是将工作委托给64位进程进行计算,并将结果传递回32位进程进行显示。