【发布时间】:2017-06-16 23:15:47
【问题描述】:
我正在尝试在我的应用程序退出之前显示带有一些调试信息的MessageBox,以防出现特定错误。我需要将其显示为消息框而不是将其记录在文件中的原因是因为我需要它在错误发生时立即引起我的注意。 (使用静默日志记录,我可能会错过错误第一次开始发生的那一刻。)
而且,如果用户愿意,同时打开一个文件也很好。
所以我从全局声明的结构之一的析构函数中调用以下“vanila”代码。换句话说,它将在进程退出之前被调用:
int nRes = MessageBox(NULL,
L"Specific error occurred, do you want to open the log file?",
L"ERROR",
MB_YESNOCANCEL | MB_ICONERROR | MB_SYSTEMMODAL);
//'nRes' is returned as IDYES immediately w/o displaying a dialog
if(nRes == IDYES)
{
int nResOpen = (int)ShellExecute(NULL, L"open", L"path-to\\file.txt", NULL, NULL, SW_SHOWNORMAL);
BOOL bOpenedOK = nResOpen > 32;
//'nResOpen' is returned as 42 but the file doesn't open
}
如果我在仍然显示流程 UI 的情况下从其他任何地方调用上面的代码,它就可以正常工作。但是,当我在应用程序关闭之前从析构函数中调用它时,代码 cmets 中描述的行为就会发生。
知道如何让它在这种情况下工作吗?
PS。我正在 64 位 Windows 10 Pro 上对其进行测试。该项目构建为 x64 MFC/C++ 进程。
PS2。编辑:
调整了代码以遵循 cmets 中的建议。重现——将结构定义为:
struct TEST_STRUCT{
TEST_STRUCT()
{
}
~TEST_STRUCT()
{
//The call below only plays the error sound...
int nRes = MessageBox(NULL,
L"Specific error occurred, do you want to open the log file?",
L"ERROR",
MB_YESNOCANCEL | MB_ICONERROR | MB_SYSTEMMODAL);
//'nRes' is returned as IDYES immediately w/o displaying a dialog
if(nRes == IDYES)
{
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.lpFile = L"path-to\\file.txt";
sei.nShow = SW_SHOW;
sei.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
::SetLastError(0);
BOOL bOpenedOK = ::ShellExecuteEx(&sei);
int nErr = ::GetLastError();
if(bOpenedOK)
{
if(sei.hProcess)
{
DWORD dwR = ::WaitForSingleObject(sei.hProcess, INFINITE);
DWORD dwExitCode = 0;
if(::GetExitCodeProcess(sei.hProcess, &dwExitCode))
{
//Check error code
}
}
else
{
//When called before app's exit it gets here -- no process handle
//'nErr' == 0x8000000A
}
}
if(sei.hProcess)
CloseHandle(sei.hProcess);
}
}
};
然后我创建了一个基于 MFC 对话框的 GUI 项目,并在 CWinAppEx 派生变量之前添加了 TEST_STRUCT 的声明,如下所示:
然后使用 Visual Studio 对其进行调试。 (在我的例子中是VS 2008 SP1。)将断点放在析构函数上,运行应用程序并关闭它。断点应该触发。然后遍历上面的代码。我也能够在 Windows 8.1 上重现它。 (阅读代码中的cmets。)
【问题讨论】:
-
我无法复制。在全局结构的析构函数中调用
MessageBox()会按预期显示对话框。如果我必须猜测(请不要让人们猜测),可能消息队列中有WM_QUIT消息,MessageBox()正在其内部消息循环中提取它以取消对话。不过,这并不能解释为什么ShellExecute()无法打开文件。 -
ShellExecute()不擅长报告错误。请改用ShellExecuteEx()。open动词是否也属于需要 shell 实例才能正常工作的事物? -
The SEE_MASK_NOASYNC flag must be specified 如果调用 ShellExecuteEx 的线程没有消息循环,或者线程或进程将在 ShellExecuteEx 返回后很快终止。 ShellExecute 只是转发到
ShellExecuteEx。 -
听起来很像您应该安装和未处理的异常过滤器。您可能应该询问您正在尝试解决的问题,而不是您提出的解决方案。
-
@RemyLebeau:添加了更多详细信息。我也能够在 Win8.1 上重现它。也感谢其他人的建议。但到目前为止,它们都没有奏效。
标签: c++ windows winapi messagebox windows-shell