我有一个C++客户端连接到一个C++/CLI DLL,该DLL初始化了一系列的C# dll。
这曾经是有效的。发生故障的代码没有改变。在抛出异常之前没有调用已更改的代码。我的编译环境已更改,但在与以前相似的环境中重新编译仍然失败。(编辑:正如我们在答案中看到的那样,这不完全正确,我只是在旧环境中重新编译库,而不是库和客户端一起升级。客户端项目已经升级,不能轻松回退。)
除了我以外的其他人重新编译了库,我们开始遇到内存管理问题。作为字符串传递的指针不能位于进程地址空间的底部64K。
我重新编译它,所有东西都没有进行代码更改,一切顺利。(警报#1)最近它被重新编译,并且与字符串相关的内存管理问题再次出现,但这次它们无法消失。新错误为未处理的异常:System.AccessViolationException:企图读取或写入受保护的内存。这通常是其他内存损坏的指示。
我很确定问题不在我看到异常的地方,代码在成功和失败的构建之间没有改变,但我们应该审查以完成。忽略那些名称,我对它正在使用这些字符串的设计没有太多控制。对于混淆表示歉意,注意_bridge
和bridge
是不同的事物。由于这个问题已经太长了,许多行代码都被省略了。
库中定义:
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);
请注意,崩溃的库调用位于与向量声明、结构声明、字符串赋值和向量推入相同的作用域中。此代码段中没有线程调用,但有其他正在执行其他任务的线程。此处没有指针计算,在该区域内除了标准库之外可能没有堆分配。我可以在调试器中运行代码,直到Bridge_GetConfiguredDefaultsImplementationPointer调用,并且调试器中configs向量的内容看起来正确。回到库中,在第一个子函数中,调试器无法解决,我已将失败语句分解为多个控制台打印。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
}
如果我将newConfigs[i].bee
移动到temp
的赋值上面,或者注释列表声明/赋值,则在访问bee
时会发生相同的异常。
仅供参考,一个在向量中的结构体中的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://dev59.com/z3RB5IYBdhLWcg3w9Lvn#918891
通用AccessViolationException相关问题
- 如何处理AccessViolationException
- 程序随机收到System.AccessViolationException
- https://connect.microsoft.com/VisualStudio/feedback/details/819552/visual-studio-debugger-throws-accessviolationexception
- 查找System.AccessViolationException的原因
- https://msdn.microsoft.com/en-us/library/ms164911.aspx
- 捕获访问冲突异常?
- 从C#使用C++ DLL时出现AccessViolationException
上述问题中的建议
- 更改为.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++ / CLI从C++调用C#的整体设计是不易更改的。我被特别要求以这种方式设计它,以便从现有的C++代码中利用现有的C#代码。
- 确保分配内存 - 内存应在C++客户端的堆栈上分配。我试图使向量不是引用参数,强制将向量副本复制到显式库控制的内存空间中,但没有帮助。
- "未受管理代码中的访问冲突会在受管理代码中显示为AccessViolationException。" - 事实,而不是解决方案。
std::string
定义很可能与你的C++/CLI代码使用的std::string
定义不同。确保两个项目都使用/MD
,参见这里和这里获取更多信息。 - Lucas Trzesniewski