C++ 如何显示另一进程的模式对话框窗口?

C++ 如何显示另一进程的模式对话框窗口?,c++,windows,winapi,mfc,modal-dialog,C++,Windows,Winapi,Mfc,Modal Dialog,我有一个32位MFC应用程序,它使用一个自定义库,重新编译成x64将是一场噩梦。一般来说,除了一种情况外,该应用程序实际上不需要以64位的方式运行,即呈现要在对话框窗口中显示的内容,这可以受益于更大的寻址空间 因此,我的目标是“模仿”另一个进程中的对话框 我将该对话框窗口构建为一个独立的基于x64 MFC对话框的应用程序。它以文件路径作为输入参数,在内部完成所有工作,并返回简单的用户选择:OK,Cancel 因此,我从主父进程执行以下操作: //Error checks omitted for

我有一个32位MFC应用程序,它使用一个自定义库,重新编译成x64将是一场噩梦。一般来说,除了一种情况外,该应用程序实际上不需要以64位的方式运行,即呈现要在对话框窗口中显示的内容,这可以受益于更大的寻址空间

因此,我的目标是“模仿”另一个进程中的对话框

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

因此,我从主父进程执行以下操作:

//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();
其中取
getHwndFromProcID

除以下情况外,这种方法有效:

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

(2) 我可以将焦点从子窗口切换到父窗口,反之亦然。在实际的模态对话框窗口中,当子对象打开时,不能切换回父对象。有办法吗

(3) 如果我开始与父级交互,它似乎“挂起”,操作系统甚至会在其标题栏上显示它


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

模态对话框有两个功能使其成为“模态”:

  • 对话框的“所有者”设置为父窗口
  • 父窗口被禁用
我对此进行了一些讨论,虽然您可以手动执行这些操作,但最简单的方法是将父窗口句柄传递给函数(或MFC中的
CDialog
构造函数)

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

  • 您需要将自己窗口的指针传递给子进程
  • 您需要在等待子进程时处理windows消息 退出<代码>WaitForSingleObject此处不可接受-需要使用
    MsgWaitForMultipleObjectsEx
  • 子进程必须在创建时将窗口设置为自拥有窗口 时间-您不需要呼叫
    SetParent
  • 有了这些,一切都会变得完美。在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

    中等待您尝试执行的操作无法安全完成。这篇博文解释说,安装跨进程所有者/所有者窗口关系会导致系统调用,并且——众所周知——会导致调用。这就创造了一个真正的死锁潜力。只有控制两个参与线程,才能安全地防止这种情况发生。因为至少有一个线程使用第三方应用程序框架(本例中为MFC),所以这是禁止的


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

    Wow,dude。它完全有效!谢谢不过,对代码的两个更正。(1) 为什么不使用MFC来传输消息,而不是
    PeekMessage
    while循环?还是严格遵守Win32。(2)您的方法不会从任务栏中删除子进程的图标。要在
    MyDialogBasedApp.exe
    过程中执行此操作,您需要从
    WM_INITDIALOG
    处理程序中的自窗口中删除
    WS_EX_APPWINDOW
    扩展样式。@c0000fd-1)是-您可以使用
    while(PeekMessage(&msg,NULL,0,0,PM\u NOREMOVE))App->PumpMessage()而不是
    while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){..}
    2)在自己的测试中,我使用MessageBoxW对话框,它不创建任务栏的图标。我不使用WS_EX_APPWINDOW。当然,我不知道您在
    MyDialogBasedApp.exe
    @c0000fd中使用了什么-在这里实现消息循环没有什么特别的区别,因为模态对话框有自己的消息循环。如果说你直接从你的应用程序中调用
    MessageBox
    ,当它运行时,内部消息循环将在
    MessageBox
    中执行,但不会在你的MFC循环中执行。main-UI线程必须永久调用进程windows消息的
    PeekMessage
    GetMessage
    ,否则UI挂起在某个时候会中断,并且您无法修复此问题,因为MFC不在您的控制范围内。您无法可靠地实现跨流程所有者/所有者窗口关系。抱歉,-1甚至没有提到这一点。评论不用于扩展讨论;这段对话已经结束。我感谢你的意见。所以在我们进一步讨论之前,让我问一下这个问题。陈的文章讨论的是一个“陌生人”的儿童过程,或者我们无法控制的其他过程。在我的情况下,我控制这两个过程,并可以根据需要调整它们。也是MFC框架,并不陌生。我们有它的源代码,我们知道幕后发生了什么。这里没有秘密。我将静态链接到它,这样它也不会随着Windows更新而改变。除此之外,我不会对这些窗口进行编码,以便彼此发送消息。那么,你能用这个特殊的例子说明为什么它不起作用吗?我在XP到Win10的操作系统上测试了它,而我唯一似乎有问题的是XP。出于某种原因,两个w