我需要解决的问题是,当要打开文件的应用程序由另一个应用程序启动时,如何在ProcessShellCommand()
的InitInstance()
中使用MFC函数CWinApp
来处理具有特定路径的文件打开。
我有一个MDI (多文档接口)应用程序,它是由另一个应用程序使用包含要打开的文件的路径的命令行启动的。使用Visual 2005编译时,我不认为已启动的应用程序有任何问题。使用Visual 2013编译时,启动的应用程序会崩溃,而我从未看到应用程序窗口。
在调试器中运行时,我看到一个错误对话框,其标题为"Microsoft C++运行时库“,其中错误消息为”调试断言失败!“指定mfc120ud.dll和src\mfc\filelist.cpp行的文件: 221
此时,我可以附加到应用程序进程,然后单击对话框的Retry按钮。然后,当我继续时,我看到一个Visual错误对话框来自一个未处理的异常,该异常似乎是由KernelBase.dll
生成的。
NHPOSLM.exe中0x76EBC54F处的未处理异常: C++异常:内存位置为0x0014F094的CInvalidArgException。
如果我单击“继续”按钮,我将得到另一个“调试断言失败”,这一次来自src\mfc\filelist.cpp行: 234
在进行了源更改以执行Sleep()
(以便使用Debug->Attach to process
Visual 2013命令)之后,我能够使用调试器查看各种数据区域并逐步执行代码。
有一次,在跨过ProcessShellCommand()
函数并看到异常后,当线程在函数调用后返回到语句时,我使用set源行调试器命令将当前行设置为函数调用,并再次遍历它。这一次没有例外,当我允许线程继续时,应用程序打开了正确的文件。
然后,我找到了这篇文章,ProcessShellCommand与视图和框架窗口,它声明如下:
问题是ProcessShellCommand()中的代码在文档文件完成创建框架和视图窗口之前打开。这些窗口存在,但无法访问它们,因为框架窗口指针在打开文档之前不会保存到应用程序范围的变量。
本文中提供的解决方案是调用ProcesShellCommand()
,比下面的代码段多一倍。
CCommandLineInfo cmdInfo;
if( !ProcessShellCommand( cmdInfo ) )
return FALSE;
ParseCommandLine( cmdInfo );
if( cmdInfo.m_nShellCommand != CCommandLineInfo::FileNew )
{
if (!ProcessShellCommand( cmdInfo ) )
return FALSE;
}
我已经在我的应用程序中尝试过这种方法,它确实打开了文档,并且似乎正确地处理了一切。问题是,对于MDI (多文档接口)类型的MFC应用程序的SDI (单文档接口)类型的MFC应用程序,您将看到两个文档窗口,一个是由File New创建的空窗口,另一个是由File Open创建的文档窗口。
我还发现,使用调试器附加到应用程序进程,然后缓慢地完成,如果我让启动的应用程序在异常对话框之后继续运行,应用程序将完成所请求的文件。但是,如果没有在调试器中,启动的应用程序的主窗口将不会显示。
因此,似乎存在某种竞争条件,使得启动的应用程序能够完全初始化其运行时环境。
有关ProcessShellCommand()
函数的说明,请参阅CWinApp::ProcessShellCommand,它将命令行处理的基本过程描述为:
InitInstance
中创建之后,CCommandLineInfo
对象被传递给ParseCommandLine
。ParseCommandLine
对每个参数重复调用CCommandLineInfo::ParseParam
一次。ParseParam
填充CCommandLineInfo
对象,然后传递给ProcessShellCommand
。ProcessShellCommand
处理命令行参数和标志.我们在InitInstance()
中使用的特定源是:
// 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 2010中看到了这篇文章,它指出,当文件路径包含星号时,跟踪涉及src\mfc\filelist.cpp的类似断言错误是将文件路径添加到最近的文件列表中。
当我使用调试器查看cmdInfo
对象时,有一个成员(*((CCommandLineInfo*)(&(cmdInfo)))).m_strFileName
,它包含一个L"C:\Users\rchamber\Documents\ailan_221.dat“的值。这是使用ShellExecute()
启动启动应用程序的应用程序提供的命令行的正确路径。
注意:字符串中的每一个反斜杠实际上都是调试表中的双反斜杠。因此,为了正确地呈现堆栈溢出,我需要添加额外的反斜杠(如L"C:\Users\rchamber\Documents\ailan_221.dat“中的反斜杠),但是双反斜杠似乎是调试器用来表示单个反斜杠字符的方法。
编辑3/23/2016 -关于源历史的说明
另一项信息是此应用程序的源历史记录。最初的应用程序是使用VisualStudio6.0创建的,然后移到Visual 2005。自从最初创建InitInstance()
方法以来,CWinApp
的方法还没有进行任何程度的修改。
发布于 2016-03-23 15:50:38
在使用Visual 2013生成新的MFC MDI (多文档接口)应用程序以比较我在启动时遇到问题的应用程序和新生成的源代码之后,我有了一个解决方案。
正确启动和不正确启动之间的主要区别似乎是初始化COM的要求。以下特定的源代码已放入正在启动的应用程序的InitInstance()
中,并且该应用程序现在正在成功工作。源代码更改的一部分是调用初始化COM。
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
// AfxInitRichEdit2() is required to use RichEdit control
// AfxInitRichEdit2();
虽然Visual 2005编译应用程序没有演示此问题,但我确实希望尽可能保持Visual 2005和Visual 2013编译的源代码类似。我在Visual 2005源代码树中做了相同的源更改,它在Visual 2005源代码树中也正常工作。
使用Visual 2005并为MDI创建一个空的MFC应用程序将生成类似于上面的源代码。
发布于 2016-12-16 16:34:48
我只是在Windows 10和Visual 2013以及一个MDI应用程序中遇到了同样的问题。在这里,提供的两次调用ProcessShellCommand()的解决方案仍然导致崩溃。解决方案是在解释命令行之前创建窗口。对我起作用了。我尝试了CoInitialize()变体,这也有效(将它放在下面代码之前的某个地方):
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
// The main window has been initialized, so show and update it.
// This needs to be up really before parsing the command line,
// so that any FileOpen command has something to render in.
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if(!ProcessShellCommand(cmdInfo))
return FALSE;
发布于 2018-11-15 16:09:44
我正在将桌面应用程序从VC++更新到Visual 2017,并在用户试图从资源管理器双击打开文件时遇到了同样的问题。在我的例子中,我只需添加以下代码:
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox("Could not open the file! \nTry open CS Setup first and then open the file using the menu \"File->Open...\".", MB_ICONERROR);
return FALSE;
}
https://stackoverflow.com/questions/36164837
复制相似问题