我有一个32位MFC应用程序,它使用自定义库,这将成为重新编译为x64的噩梦。一般来说,应用程序并不需要以64位运行,除非在一种情况下 - 这就是渲染内容以在对话窗口中显示,这可以从更大的寻址空间中受益。
所以我的目标是"模仿" CDialog::DoModal
方法,但用于另一个进程中的对话框。
我将该对话框窗口构建为独立的基于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
取from here。
除了以下内容之外,这种方式有效:
(1)任务栏上有两个图标:一个用于我的主应用程序,另一个用于子应用程序。有没有办法不显示儿童图标?
(2)我可以将焦点从子窗口切换到父窗口,反之亦然。在实际的模态对话框窗口中,当子项打开时,无法切换回父项。有没有办法做到这一点?
(3)如果我开始与父母互动,它似乎是“挂断”"操作系统甚至会在标题栏上显示它。
所以我很好奇是否有办法解决所有这些问题?
答案 0 :(得分:3)
//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
MsgWaitForMultipleObjectsEx
这一切都将完美无缺。在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位进程进行显示。