当在命令行上指定要打开的文件时,MFC 应用程序在 ProcessShellCommand() 中崩溃

2023-11-25

我需要解决的问题是如何使用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 processVisual 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其中描述了命令行处理的基本过程:

  1. 创建后于InitInstance, the CCommandLineInfo对象是 传递给ParseCommandLine.
  2. ParseCommandLine然后打电话CCommandLineInfo::ParseParam反复, 每个参数一次。
  3. ParseParam填充CCommandLineInfo对象,然后传递 到ProcessShellCommand.
  4. 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自最初创建以来未进行任何程度的修改。


在使用 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 应用程序会生成与上面类似的源代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当在命令行上指定要打开的文件时,MFC 应用程序在 ProcessShellCommand() 中崩溃 的相关文章

  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • 实例化类时重写虚拟方法

    我有一个带有一些虚函数的类 让我们假设这是其中之一 public class AClassWhatever protected virtual string DoAThingToAString string inputString retu
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 如何将 GIT 调用的输出获取到批处理脚本中的变量中?

    我有一个 git 命令来获取当前存储库的最新 SHA 如下所示 git log pretty format H n 1 我有一个 Windows 批处理脚本 我想按如下方式使用它 SET CURRENT SHA 但我不知道如何将从 git
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template

随机推荐