我需要解决的问题是如何使用MFC功能ProcessShellCommand()
in the InitInstance()
of a CWinApp
当另一个应用程序启动打开文件的应用程序时,处理具有特定路径的文件打开。
我有一个 MFC MDI(多文档界面)应用程序,该应用程序由另一个应用程序使用命令行启动ShellExecute()
包含要打开的文件的路径。当使用 Visual Studio 2005 编译时,我没有发现启动的应用程序有问题。使用 Visual Studio 2013 编译时,启动的应用程序崩溃,并且我从未看到应用程序窗口。
在调试器中运行时,我看到一个错误对话框,标题为“Microsoft Visual C++ Runtime Library”,错误消息为“调试断言失败!”指定 mfc120ud.dll 和文件 src\mfc\filelist.cpp 行:221
此时,我可以附加到应用程序进程,然后单击对话框的“重试”按钮。然后,当我继续时,我看到一个来自未处理异常的 Visual Studio 错误对话框,该异常似乎是由KernelBase.dll
.
NHPOSLM.exe 中 0x76EBC54F 处未处理的异常:Microsoft C++
异常:内存位置 0x0014F094 处的 CInvalidArgException。
如果单击“继续”按钮,这次我会从 src\mfc\filelist.cpp 行收到另一个“调试断言失败”:234
进行源更改后执行Sleep()
为了使用Debug->Attach to process
Visual Studio 2013 命令我能够使用调试器查看各种数据区域并逐步执行代码。
有一次,跨过ProcessShellCommand()
函数并看到异常,当线程返回到函数调用后的语句时,我使用 set source line debugger 命令将当前行设置回函数调用并再次单步跳过它。这次没有例外,当我允许线程继续时,应用程序打开了正确的文件。
然后我找到了这篇文章,ProcessShellCommand 以及视图和框架窗口其中规定如下:
问题是 ProcessShellCommand() 中的代码打开
在完成创建框架和视图窗口之前保存文档文件。
这些窗口存在,但无法访问它们,因为
直到之后框架窗口指针才会保存到应用程序范围的变量中
该文档已打开。
文章中提供的解决方案是调用ProcesShellCommand()
是以下代码段的两倍。
CCommandLineInfo cmdInfo;
if( !ProcessShellCommand( cmdInfo ) )
return FALSE;
ParseCommandLine( cmdInfo );
if( cmdInfo.m_nShellCommand != CCommandLineInfo::FileNew )
{
if (!ProcessShellCommand( cmdInfo ) )
return FALSE;
}
我已经在我的应用程序中尝试过这种方法,它确实打开了文档并且似乎可以正确处理所有内容。问题是,虽然这适用于 SDI(单文档接口)类型的 MFC 应用程序,但对于 MDI(多文档接口)类型的 MFC 应用程序,您将看到两个文档窗口,一个是由 File New 创建的空窗口,另一个是实际创建的窗口。想要通过文件打开创建。
我还发现,使用调试器附加到应用程序进程,然后慢慢地逐步执行,如果我让启动的应用程序在异常对话框后继续,应用程序将完成生成请求的文件。但是,如果不在调试器中,则不会显示启动的应用程序的主窗口。
因此,环境似乎存在某种竞争条件,以便为启动的应用程序完全初始化其运行时环境做好准备。
为了解释ProcessShellCommand()
功能见CWinApp::ProcessShellCommand其中描述了命令行处理的基本过程:
- 创建后于
InitInstance
, the CCommandLineInfo
对象是
传递给ParseCommandLine
.
-
ParseCommandLine
然后打电话CCommandLineInfo::ParseParam
反复,
每个参数一次。
-
ParseParam
填充CCommandLineInfo
对象,然后传递
到ProcessShellCommand
.
-
ProcessShellCommand
处理命令行参数和标志。
我们在中使用的具体来源InitInstance()
is:
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views.
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_NEWLAYTYPE,
RUNTIME_CLASS(CNewLayoutDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CNewLayoutView/*CLeftView*/));
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
// Parse command line for standard shell commands, DDE, file open
CLOMCommandLineInfo cmdInfo;
/*initialize language identifier to English so we wont have garbage if no language
flag is set on teh command line*/
cmdInfo.lang = LANG_ENGLISH;
cmdInfo.sublang = SUBLANG_ENGLISH_US;
//CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
BOOL success = pMainFrame->ProcessCmdLineLang(cmdInfo.lang, cmdInfo.sublang);
if(!success){
AfxMessageBox(IDS_CMDLINE_LANG_NF,MB_OK,0);
}
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it.
pMainFrame->ShowWindow(SW_SHOWNORMAL);
pMainFrame->UpdateWindow();
我不喜欢调用文章中提供的解决方案ProcessShellCommand()
两倍,看起来不整洁。它没有提供 MDI 应用程序所需的内容。我不知道为什么这段代码在 VS 2005 中运行良好,但在 VS2013 中却出现错误。
最后我在 codeproject 中看到了这篇文章,调试断言错误 Visual Studio 2010,这表明涉及 src\mfc\filelist.cpp 的类似断言错误被追踪到,当文件路径包含星号时,将文件路径添加到“最近的文件列表”中。
当我使用调试器查看cmdInfo
对象有一个成员,(*((CCommandLineInfo*)(&(cmdInfo)))).m_strFileName
,其中包含值 L"C:\Users\rchamber\Documents\ailan_221.dat"。这是启动启动应用程序的应用程序提供的命令行的正确路径ShellExecute()
.
Note:字符串中的每个反斜杠实际上是调试监视中的双反斜杠。因此,为了正确渲染堆栈溢出,我需要添加额外的反斜杠,如 L"C:\\Users\\rchamber\\Documents\\ailan_221.dat" 中所示,但是双反斜杠似乎是调试器用来表示单个反斜杠的内容特点。
编辑 2016 年 3 月 23 日 - 有关源历史记录的注释
另一项信息是该应用程序的源历史记录。原始应用程序是使用 Visual Studio 6.0 创建的,然后迁移到 Visual Studio 2005。InitInstance()
的方法CWinApp
自最初创建以来未进行任何程度的修改。