我需要解决的问题是,当另一个应用程序正在启动打开文件的应用程序时,如何在ProcessShellCommand()InitInstance()中使用MFC函数CWinApp来处理具有特定路径的文件打开。

我有一个MFC MDI(多文档界面)应用程序,该应用程序由另一个应用程序通过命令行启动,使用ShellExecute()包含要打开的文件的路径。使用Visual Studio 2005进行编译时,启动的应用程序没有出现问题。使用Visual Studio 2013进行编译时,启动的应用程序崩溃,并且我再也看不到应用程序窗口。

在调试器中运行,我看到一个错误对话框,标题为“Microsoft Visual C++运行时库”,错误消息为“调试断言失败!”指定src\mfc\filelist.cpp的mfc120ud.dll和文件行:221

此时,我可以附加到应用程序过程,然后单击对话框的“重试”按钮。然后,当我继续时,我看到一个来自未处理异常的Visual Studio错误对话框,该异常似乎是由KernelBase.dll生成的。



如果单击“继续”按钮,这一次我会从src\mfc\filelist.cpp行获得另一个“调试断言失败”的提示:234

在更改源代码以执行Sleep()以便使用Debug->Attach to process Visual Studio 2013命令之后,我能够使用调试器查看各种数据区域并逐步执行代码。

一方面,在遍历ProcessShellCommand()函数并看到异常之后,当线程在函数调用之后返回到语句时,我使用set source line debugger命令将当前行设置回函数调用,并再次遍历它。这次没有异常(exception),当我允许线程继续运行时,应用程序打开了正确的文件。

然后我发现这篇文章ProcessShellCommand and the View and Frame Windows指出以下内容:



本文提供的解决方案是调用ProcesShellCommand()两次,如以下代码段所示。

CCommandLineInfo cmdInfo;

if( !ProcessShellCommand( cmdInfo ) )
    return FALSE;

ParseCommandLine( cmdInfo );

if( cmdInfo.m_nShellCommand != CCommandLineInfo::FileNew )
{
    if (!ProcessShellCommand( cmdInfo ) )
        return FALSE;
}

我已经在我的应用程序中尝试了这种方法,并且确实确实打开了文档并且似乎可以正确处理所有内容。问题是,虽然这对于MFC应用程序的MDI(多文档界面)类型的MFC应用程序的SDI(单文档界面)类型有效,但是您将看到两个文档窗口,一个是由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中导致错误。

    最终,我在代码项目Debug Assertion Error Visual Studio 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-有关源历史记录的注释

    另外一点信息是此应用程序的源历史记录。最初的应用程序是使用Visual Studio 6.0创建的,然后移至Visual Studio2005。InitInstance()CWinApp方法自最初创建以来未作任何修改。

    最佳答案

    使用Visual Studio 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 Studio 2005编译的应用程序未演示此问题,但我确实希望尽可能使Visual Studio 2005和Visual Studio 2013编译的源代码相似。我在Visual Studio 2005源代码树中进行了相同的源代码更改,并且在Visual Studio 2005源代码树中也可以正常工作。

    使用Visual Studio 2005并为MDI创建一个空的MFC应用程序会生成类似于上面的源代码。

    关于c++ - 在命令行上打开文件时,MFC应用程序在ProcessShellCommand()中崩溃,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36164837/

    10-13 07:46
    查看更多