Ric*_*ers 5 c++ mfc windows-applications visual-studio-2013
我需要解决的问题是如何使用MFC函数ProcessShellCommand()在InitInstance()一个CWinApp处理一个文件打开与特定的路径时,打开文件的应用程序正在被另一个应用程序启动。
我有一个 MFC MDI(多文档界面)应用程序,它由另一个应用程序使用命令行启动,ShellExecute()其中包含要打开的文件的路径。使用 Visual Studio 2005 编译时,我没有发现启动的应用程序有问题。使用 Visual Studio 2013 编译时,启动的应用程序崩溃,我从未看到应用程序窗口。
在调试器中运行时,我看到一个错误对话框,其标题为“Microsoft Visual C++ 运行时库”,错误消息为“调试断言失败!” 指定 src\mfc\filelist.cpp 行的 mfc120ud.dll 和文件:221
此时我可以附加到应用程序进程,然后单击对话框的重试按钮。然后当我继续时,我看到一个来自未处理异常的 Visual Studio 错误对话框,该异常似乎是由KernelBase.dll.
NHPOSLM.exe 中 0x76EBC54F 处的未处理异常:Microsoft C++ 异常:内存位置 0x0014F094 处的 CInvalidArgException。
如果我单击“继续”按钮,这次我会从 src\mfc\filelist.cpp 行收到另一个“调试断言失败”:234
在进行源代码更改以Sleep()使用Debug->Attach to processVisual Studio 2013 命令后,我能够使用调试器查看各种数据区域并逐步执行代码。
有一次,在跳过ProcessShellCommand()函数看到异常后,当线程返回函数调用后的语句时,我使用set source line debugger命令将当前行设置回函数调用并再次跳过它。这次没有例外,当我允许线程继续时,应用程序打开了正确的文件。
然后我找到了这篇文章ProcessShellCommand and the View and Frame Windows,其中说明了以下内容:
问题是 ProcessShellCommand() 中的代码在完成创建框架和视图窗口之前打开文档文件。这些窗口存在但无法访问它们,因为框架窗口指针在文档打开之前不会保存到应用程序范围的变量中。
文章中提供的解决方案是ProcesShellCommand()像下面的代码段那样调用两次。
CCommandLineInfo cmdInfo;
if( !ProcessShellCommand( cmdInfo ) )
return FALSE;
ParseCommandLine( cmdInfo );
if( cmdInfo.m_nShellCommand != CCommandLineInfo::FileNew )
{
if (!ProcessShellCommand( cmdInfo ) )
return FALSE;
}
Run Code Online (Sandbox Code Playgroud)
我在我的应用程序中尝试过这种方法,它确实打开了文档并且似乎正确处理了所有内容。问题是,虽然这适用于 MDI(多文档界面)类型的 MFC 应用程序的 SDI(单文档界面)类型的 MFC 应用程序,但您将看到两个文档窗口,一个是由 File New 创建的空一个,另一个实际上是想要由文件打开创建。
我还发现使用调试器附加到应用程序进程,然后慢慢地单步执行,如果我让启动的应用程序在异常对话框后继续,应用程序将完成提出请求的文件。但是,如果不在调试器中,启动的应用程序的主窗口将不会显示。
因此,环境似乎存在某种竞争条件,以便为启动的应用程序完全初始化其运行时环境做好准备。
有关该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();
Run Code Online (Sandbox Code Playgroud)
我不喜欢ProcessShellCommand()两次调用文章中提供的解决方案,因为它看起来不整洁。它没有提供我需要的 MDI 应用程序。我不知道为什么这段代码在 VS 2005 中似乎可以正常工作并在 VS2013 中导致错误。
最后我在 codeproject 中看到了这个帖子,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" 但是双反斜杠似乎是调试器用来表示单个反斜杠的特点。
2016 年 3 月 23 日编辑 - 关于源历史记录的说明
另一位信息是此应用程序的源历史记录。原始应用程序是使用 Visual Studio 6.0 创建的,然后移至 Visual Studio 2005。自最初创建以来,该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();
Run Code Online (Sandbox Code Playgroud)
虽然 Visual Studio 2005 编译的应用程序没有演示此问题,但我确实希望保持 Visual Studio 2005 和 Visual Studio 2013 编译的源代码尽可能相似。我在 Visual Studio 2005 源代码树中进行了相同的源代码更改,它在 Visual Studio 2005 源代码树中也可以正常工作。
使用 Visual Studio 2005 并为 MDI 创建一个空的 MFC 应用程序会生成与上面类似的源代码。
| 归档时间: |
|
| 查看次数: |
5393 次 |
| 最近记录: |