C++ 如何显示另一进程的模式对话框窗口?
我有一个32位MFC应用程序,它使用一个自定义库,重新编译成x64将是一场噩梦。一般来说,除了一种情况外,该应用程序实际上不需要以64位的方式运行,即呈现要在对话框窗口中显示的内容,这可以受益于更大的寻址空间 因此,我的目标是“模仿”另一个进程中的对话框 我将该对话框窗口构建为一个独立的基于x64 MFC对话框的应用程序。它以文件路径作为输入参数,在内部完成所有工作,并返回简单的用户选择: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
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) 如果我开始与父级交互,它似乎“挂起”,操作系统甚至会在其标题栏上显示它
所以我很好奇是否有办法解决所有这些问题?模态对话框有两个功能使其成为“模态”:
- 对话框的“所有者”设置为父窗口李>
- 父窗口被禁用
CDialog
构造函数)
您的子进程可以使用(或类似机制)获取父窗口句柄并使用该句柄显示对话框,而不是在父进程中执行ShellExecuteEx之后执行所有工作
MsgWaitForMultipleObjectsEx
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
中,让我们使用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