MFC应用程序崩溃在ProcessShellCommand()当文件打开指定在命令行 [英] MFC application crashing in ProcessShellCommand() when file to open specified on command line

查看:5181
本文介绍了MFC应用程序崩溃在ProcessShellCommand()当文件打开指定在命令行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要解决的问题是如何使用 InitInstance()中的MFC函数 ProcessShellCommand()









我有一个MFC MDI(多文档接口)应用程序,由另一个应用程序使用命令行启动 ShellExecute()包含要打开的文件的路径。当使用Visual Studio 2005编译时,我没有看到启动的应用程序的问题。当使用Visual Studio 2013编译时,启动的应用程序崩溃,我从未看到应用程序窗口。



在调试器中运行,我看到一个错误对话框,的Microsoft Visual C ++运行库,并显示错误消息Debug Assertion Failed!。指定mfc120ud.dll和src \mfc\filelist.cpp文件的行:221



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


NHPOSLM.exe中0x76EBC54F处的未处理异常:Microsoft C ++
异常:内存位置为0x0014F094的CInvalidArgException。


如果我点击继续按钮,我这次从src \mfc\filelist.cpp中得到另一个Debug Assertion Failed:234



进行源更改以执行 Sleep()以使用 Debug->附加到进程 Visual Studio 2013命令我能够使用调试器来查看各种数据区域和逐步执行代码。



一旦点击 ProcessShellCommand()函数并查看异常,当线程返回到语句后调用函数时,我使用set source line debugger命令将当前行设置回函数调用并跳过它再次。这一次没有异常,当我允许线程继续,应用程序提出了正确的文件打开。



然后我发现这篇文章, ProcessShellCommand和视图和框架窗口,其中说明了以下内容:文档文件时,会出现这种情况:p>


<
这些窗口存在,但没有办法访问它们,因为
框架窗口指针不保存到应用程序范围的变量,直到
文档打开之后。


本文提供的解决方案是调用 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应用程序,你会看到两个文档窗口,一个空文件新创建的空,一个实际



我还发现使用调试器附加到应用程序进程,然后慢慢地步进,如果我让启动的应用程序继续之后异常对话框,应用程序将完成提出请求的文件。但是,如果不在调试器中,则不会显示启动的应用程序的主窗口。



因此,似乎存在某种竞争条件,启动应用程序以使其运行时环境完全初始化。



有关 ProcessShellCommand()函数的说明, a href =https://msdn.microsoft.com/en-us/library/zatkyh2a.aspx =nofollow> CWinApp :: ProcessShellCommand ,它将命令行处理的基本过程描述为: / p>


  1. InitInstance 中创建后, CCommandLineInfo object is
    传递给 ParseCommandLine

  2. ParseCommandLine 然后重复调用 CCommandLineInfo :: ParseParam ,每个参数调用

  3. ParseParam 填充 CCommandLineInfo 对象,然后将
    传递给 ProcessShellCommand

  4. ProcessShellCommand 处理命令行参数和标记。

我们在 InitInstance()中使用的具体来源是:

  //注册应用程序的文档模板。文档模板
//用作文档,框架窗口和视图之间的连接。

CMultiDocTemplate * pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_NEWLAYTYPE,
RUNTIME_CLASS(CNewLayoutDoc),
RUNTIME_CLASS(CChildFrame),//自定义MDI子框架
RUNTIME_CLASS(CNewLayoutView / * CLeftView * /));
AddDocTemplate(pDocTemplate);

//创建主MDI框架窗口
CMainFrame * pMainFrame = new CMainFrame;
if(!pMainFrame-> LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;

//为标准shell命令,DDE,文件打开命令行
CLOMCommandLineInfo cmdInfo;
/ *初始化语言标识符为英语,所以我们没有垃圾如果没有语言
标志设置在命令行* /
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);
}
//在命令行上指定调度命令
if(!ProcessShellCommand(cmdInfo))
return FALSE;

//主窗口已初始化,因此显示和更新。
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()启动已启动应用程序的应用程序提供的命令行的正确路径。



注意:字符串中的每个反斜杠实际上是调试手表中的双反斜杠。所以要渲染在堆栈溢出正确我需要添加额外的反斜杠,如在LC:\\Users\\rchamber\\Documents\\ailan_221.dat,但双反斜杠似乎是什么



编辑3/23/2016 - 有关来源历史记录的注释 b

另外一点信息是这个应用程序的源历史。原始应用程序使用Visual Studio 6.0创建,然后移动到Visual Studio 2005. CWinApp InitInstance()

解决方案

使用Visual Studio 2013生成新的MFC MDI多文档接口)应用程序来比较我遇到问题启动的应用程序和新的生成的源代码,我有一个解决方案。



启动之间的主要区别正确和不正确启动似乎是初始化COM的要求。以下特定的源代码被放入正在启动的应用程序的 InitInstance()中,应用程序现在正常工作。部分源代码更改是调用初始化COM。

  //如果应用程序在Windows XP上需要InitCommonControlsEx 
// manifest指定使用ComCtl32.dll版本6或更高版本以启用
//视觉样式。否则,任何窗口创建将失败。
INITCOMMON CONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
//设置为包括你想在应用程序中使用
//的所有通用控制类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(& InitCtrls);

CWinApp :: InitInstance();

//初始化OLE库
if(!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}

AfxEnableControlContainer();

// AfxInitRichEdit2()是使用RichEdit控件所必需的
// AfxInitRichEdit2();

虽然Visual Studio 2005编译的应用程序没有演示这个问题,由Visual Studio 2005和Visual Studio 2013编译尽可能相似。我在Visual Studio 2005源代码树中做了相同的源代码更改,并且在Visual Studio 2005源代码树中也正常工作。



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


The problem I need to solve is how to use the MFC function ProcessShellCommand() in the InitInstance() of a CWinApp to process a File Open with a specific path when the application to open the file is being launched by another application.

I have an MFC MDI (Multiple Document Interface) application that is started by another application with a command line using ShellExecute() containing the path to a file to open. When compiled with Visual Studio 2005, I do not see a problem with the launched application. When compiled with Visual Studio 2013, the application that is started crashes and I never see the application window.

Running in the debugger, I see an error dialog that has a title of "Microsoft Visual C++ Runtime Library" with an error message of "Debug Assertion Failed!" specifing the mfc120ud.dll and file of src\mfc\filelist.cpp line: 221

At this point I can attach to the application process then click the Retry button of the dialog. Then when I continue I see a Visual Studio error dialog from an unhandled exception that seems to be generated by KernelBase.dll.

Unhandled exception at 0x76EBC54F in NHPOSLM.exe: Microsoft C++ exception: CInvalidArgException at memory location 0x0014F094.

If I click the Continue button, I get another "Debug Assertion Failed" this time from src\mfc\filelist.cpp line: 234

After making a source change to perform a Sleep() in order to use the Debug->Attach to process Visual Studio 2013 command I was able to use the debugger to look at various data areas and step through code.

At one point, after stepping over the ProcessShellCommand() function and seeing the exception, when the thread returned to the statement after the function call, I used the set source line debugger command to set the current line back to the function call and stepped over it again. This time there was no exception and when I allowed the thread to continue, the application came up with the correct file open.

Then I found this article, ProcessShellCommand and the View and Frame Windows which states the following:

The problem is that the code in ProcessShellCommand() opens the document file before it finishes creating the frame and view windows. Those windows exist but there is no way to access them because the frame window pointer is not saved to an app-wide variable until after the document is open.

The solution provided in the article is to call ProcesShellCommand() twice as in the following code segment.

CCommandLineInfo cmdInfo;

if( !ProcessShellCommand( cmdInfo ) )
    return FALSE;

ParseCommandLine( cmdInfo );

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

I have tried this approach in my application and it does indeed open the document and seems to process everything correctly. The problem is that while this works for an SDI (Single Document Interface) type of MFC application for an MDI (Multiple Document Interface) type of MFC application you will see two document windows, an empty one as created by File New and the one actually wanted created by File Open.

I have also found that using the debugger to attach to the application process and then stepping through slowly, if I let the launched application Continue after the exception dialogs, the application will finish coming up with the requested file. However if not in the debugger, the launched application's main window will not display.

So there appears to be some kind of a race condition for the environment to be ready for the launched application to have its run time environment fully initialized.

For an explanation of the ProcessShellCommand() function see CWinApp::ProcessShellCommand which describes the basic process for command line processing as:

  1. After being created in InitInstance, the CCommandLineInfo object is passed to ParseCommandLine.
  2. ParseCommandLine then calls CCommandLineInfo::ParseParam repeatedly, once for each parameter.
  3. ParseParam fills the CCommandLineInfo object, which is then passed to ProcessShellCommand.
  4. ProcessShellCommand handles the command-line arguments and flags.

The specific source we are using in the 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();

I do not like the solution provided in the article of calling ProcessShellCommand() twice as it appears untidy. It does not provide what I need for an MDI application. I do not know why this code seems to work fine with VS 2005 and cause an error in VS2013.

Finally I came across this posting in codeproject, Debug Assertion Error Visual Studio 2010, which indicated that a similar assertion error involving src\mfc\filelist.cpp was traced to adding a file path to the Recent File List when the file path contained an asterisk.

When I use the debugger to look at the cmdInfo object there is a member, (*((CCommandLineInfo*)(&(cmdInfo)))).m_strFileName, which contains a value of L"C:\Users\rchamber\Documents\ailan_221.dat". This is the correct path from the command line provided by the application that is starting the launched application with ShellExecute().

Note: each of the back slashes in the string are actually double backslashes in the debug watch. So to render in stack overflow properly I need to add additional backslashes as in L"C:\\Users\\rchamber\\Documents\\ailan_221.dat" however the double backslash seems to be what the debugger uses to represent a single backslash character.

Edit 3/23/2016 - note concerning source history

One additional bit of information is the source history for this application. The original application was created using Visual Studio 6.0 then moved to Visual Studio 2005. The InitInstance() method of the CWinApp has not been modified to any degree since it was originally created.

解决方案

After using Visual Studio 2013 to generate a new MFC MDI (Multiple Document Interface) application to compare between the application with which I am having problems launching and the new, generated source code, I have a solution.

The main difference between launching properly and not launching properly seems to be a requirement for initializing COM. The following specific source code was put into the InitInstance() of the application being launched and the application is now working successfully. Part of the source code changes is a call to initialize 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();

While the Visual Studio 2005 compiled application did not demonstrate this problem, I do want to keep the source as compiled by Visual Studio 2005 and Visual Studio 2013 as similar as possible. I made the same source change in the Visual Studio 2005 source tree and it works properly in the Visual Studio 2005 source tree as well.

Using Visual Studio 2005 and creating an empty MFC application for MDI generates source code similar to the above.

这篇关于MFC应用程序崩溃在ProcessShellCommand()当文件打开指定在命令行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆