AccessViolationException

AccessViolationException

我有一个C++/CLI DLL的C++客户端,它初始化了一系列C#dll。

这曾经工作。失败的代码未更改。引发异常之前,不会调用已更改的代码。我的编译环境发生了变化,但是在一台与我的旧环境类似的计算机上重新编译仍然失败。 (编辑:正如我们在答案中看到的那样,这并不完全正确,我只是在旧环境中重新编译了库,而不是库和客户端一起重新编译。客户端项目已升级,无法轻易返回。)

除了我之外,有人重新编译了库,然后我们开始遇到内存管理问题。 The pointer passed in as a String must not be in the bottom 64K of the process's address space.我重新编译了它,并且所有工作都很好,无需更改代码。 (警报1)最近,它已重新编译,并且出现了字符串字符串的内存管理问题,但这次并没有消失。新的错误是Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
我很确定问题不在我看到异常的地方,成功的构建和失败的构建之间的代码都没有改变,但是我们应该对其进行完整的检查。忽略事物的名称,我对这些字符串的功能设计没有太多控制权。很抱歉造成混淆,但是请注意_bridgebridge是不同的东西。由于此问题已经太长,因此缺少许多代码行。

在库中定义:

struct Config
{
    std::string aye;
    std::string bee;
    std::string sea;
};

extern "C" __declspec(dllexport) BridgeBase_I* __stdcall Bridge_GetConfiguredDefaultsImplementationPointer(
    const std::vector<Config> & newConfigs, /**< new configurations to apply **/
    std::string configFolderPath, /**< folder to write config files in **/
    std::string defaultConfigFolderPath, /**< folder to find default config files in **/
    std::string & status /**< output status of config parse **/
    );

在客户端功能中:
GatewayWrapper::Config bridge;
std::string configPath("./config");
std::string defaultPath("./config/default");
GatewayWrapper::Config gwtransport;
bridge.aye = "bridged.dll";
bridge.bee = "1.0";
bridge.sea = "";
configs.push_back(bridge);
_bridge = GatewayWrapper::Bridge_GetConfiguredDefaultsImplementationPointer(configs, configPath, defaultPath, status);

请注意,对崩溃的库的调用与 vector 声明,结构声明,字符串分配和 vector 推回的作用域相同
在本节代码中没有线程调用,但是其他线程正在运行以执行其他操作。这里没有指针数学,除了标准库内部之外,该区域中没有堆分配。

我可以在调试器中运行直到Bridge_GetConfiguredDefaultsImplementationPointer调用的代码,并且configs vector 的内容在调试器中看起来正确。

回到库的第一个子函数中,调试器不会发光,我将失败的语句分解为几个控制台打印。
System::String^ temp
List<CConfig^>^ configs = gcnew List<CConfig ^>((INT32)newConfigs.size());
for( int i = 0; i< newConfigs.size(); i++)
{
  std::cout << newConfigs[i].aye<< std::flush; // prints
  std::cout << newConfigs[i].aye.c_str() << std::flush; // prints
  temp = gcnew System::String(newConfigs[i].aye.c_str());
  System::Console::WriteLine(temp); // prints
  std::cout << "Testing string creation" << std::endl; // prints
  std::cout << newConfigs[i].bee << std::flush; // crashes here
}

如果将bee移到newConfigs[i].bee的分配上方,或注释掉列表声明/分配,则在访问temp时也会遇到相同的异常。

仅供引用, vector 的结构中的std::string应该已经到达目的地了
  • Is std::vector copying the objects with a push_back?
  • std::string in struct - Copy/assignment issues?
  • http://www.cplusplus.com/reference/vector/vector/operator=/
  • Assign one struct to another in C

  • 为什么我的try/catch没有捕获此异常

    https://stackoverflow.com/a/918891/2091951

    通用AccessViolationException相关问题
  • How to handle AccessViolationException
  • Programs randomly getting System.AccessViolationException
  • https://connect.microsoft.com/VisualStudio/feedback/details/819552/visual-studio-debugger-throws-accessviolationexception
  • finding the cause of System.AccessViolationException
  • https://msdn.microsoft.com/en-us/library/ms164911.aspx
  • Catching access violation exceptions?
  • AccessViolationException when using C++ DLL from C#

  • 以上问题的建议
  • 更改为.net 3.5,更改目标平台-这些解决方案在大型项目解决方案中可能会遇到严重问题。
  • HandleProcessCorruptedStateExceptions-在C++中不起作用,此修饰用于C#,无论如何,捕获此错误可能是一个非常糟糕的主意
  • 更改legacyCorruptedStateExceptionsPolicy-这是关于捕获错误,而不是防止错误
  • 安装.NET 4.5.2-不能,已经有4.6.1。安装4.6.2没有帮助。在未安装4.5或4.6的其他计算机上重新编译没有帮助。 (尽管这曾经用于在安装Visual Studio 2013之前在我的计算机上编译并运行,但这强烈表明.NET库是一个问题?)
  • VSDebug_DisableManagedReturnValue-我只看到有关调试器中特定崩溃的提及,并且Microsoft的帮助说其他AccessViolationException问题可能无关。 (http://connect.microsoft.com/VisualStudio/feedbackdetail/view/819552/visual-studio-debugger-throws-accessviolationexception)
  • 更改Comodo防火墙设置-我不使用此软件
  • 将所有代码更改为托管内存-不可选项。从C++到C++/CLI调用C#的总体设计难以改变。专门要求我设计这种方式,以利用现有C++代码中的现有C#代码。
  • 确保已分配内存-应该在C++客户端的堆栈上分配内存。我试图使 vector 不是引用参数,以强制 vector 复制到显式库控制的内存空间中,但没有帮助。
  • “冒泡到托管代码的非托管代码中的访问冲突始终包装在AccessViolationException中。” -事实,不是解决方案。
  • 最佳答案



    是的,这是VS中的黑字定律。不幸的是,您只是错过了VS2012内置的对策,它将该错误变成可诊断的链接器错误。以前(以及在VS2010中),CRT会使用HeapAlloc()分配自己的堆。现在(在VS2013中),它使用默认的进程堆,即GetProcessHeap()返回的堆。

    当您在Vista或更高版本上运行应用程序时,它本身足以触发AVE,从一个堆分配内存,然后从另一个堆释放内存,则在运行时触发AVE,在启用Debug Heap进行调试时,调试器中断。

    这不是结束之处,另一个重要问题是两个版本之间的std::string对象布局不相同。您可以通过一些测试程序发现一些东西:

    #include <string>
    #include <iostream>
    
    int main()
    {
        std::cout << sizeof(std::string) << std::endl;
        return 0;
    }
    
  • VS2010调试:32
  • VS2010版本:28
  • VS2013调试:28
  • VS2013版本:24

  • 我对Stephen Lavavej提到std::string对象的大小减小有一个模糊的内存,这是作为一种功能提供的,但是我找不到它。调试版本中多余的4个字节是由迭代器调试功能引起的,可以使用“预处理程序定义”中的_HAS_ITERATOR_DEBUGGING=0禁用它。这不是您很快想放弃的功能,但是它使EXE及其DLL的Debug和Release版本的混合使用非常致命。

    不用说,当在使用标准C++库的一个版本构建的DLL中创建Config对象并在另一个版本中使用Config对象时,不同的对象大小会严重占用字节。很多不幸的事,最基本的一个是代码将简单地从错误的偏移量读取Config::bee成员。 (几乎)有保证。当代码分配Config对象的较小样式但写入std::string的较大样式时,这会更加痛苦,这会随机破坏堆或堆栈帧。

    不要混

    08-06 12:40