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

zqry0prt  于 2022-11-18  发布在  Windows
关注(0)|答案(3)|浏览(232)

我有一个32位的MFC应用程序,它使用了一个自定义库,如果重新编译成x64,那将是一场噩梦。一般来说,该应用程序实际上并不需要以64位运行,只有一种情况例外--那就是呈现要在对话框窗口中显示的内容,这可以从更大的寻址空间中受益。
因此我的目标是“模仿”CDialog::DoModal方法但在另一个进程中进行对话。
我将该对话框窗口构建为一个独立的基于x64 MFC对话框的应用程序。它将文件路径作为输入参数,在内部完成所有工作,并返回简单的用户选择:一个月一个月一个月,一个月二个月。
因此,我从我的主父进程执行以下操作:

//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)如果我开始与父级交互,它似乎被“挂起”,操作系统甚至会在其标题栏上显示它。
所以我很好奇,有没有办法解决这些问题?

ruarlubt

ruarlubt1#

1.你需要把自己窗口的指针传递给子进程
1.当你等待子进程退出时,你需要处理windows消息。此处不能接受WaitForSingleObject-需要使用MsgWaitForMultipleObjectsEx
1.子进程必须在创建时将您的窗口设置为自身所有者窗口-您不需要调用SetParent
有了这个,一切都将完美地工作。2在你32位MFC应用程序中,你需要使用下面的代码:

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中,让我们使用MessageBox作为演示对话框。我们将使用您MFC窗口作为它的第一个参数。

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);
}

使用以下代码:
(1)主应用程序的任务栏上只有一个图标
(2)您不能将焦点从子窗口切换到父窗口,反之亦然所有窗口都作为实际模式对话框窗口工作
(3)父进程未“挂起”,因为它正在处理Windows消息(MsgWaitForMultipleObjectsEx)-您的代码已“挂起”,因为您未执行此操作,而是在WaitForSingleObject中等待

0mkxixxg

0mkxixxg2#

胁迫回应对话方块会执行两项动作,使其成为“胁迫回应”:

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

我对此进行了一些尝试,虽然您可以手动执行这些操作,但最简单的方法是将父窗口句柄传递给DialogBox函数(或MFC中的CDialog构造函数)。
您的子进程可以使用FindWindow(或类似的机制)来获取父窗口句柄,并使用该句柄来显示对话框,而不是在父进程中执行ShellExecuteEx之后的所有工作。

lnxxn5zx

lnxxn5zx3#

您尝试的操作无法安全地完成。blog条目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位进程进行显示。

相关问题