有关错误原因的猜测,请参见下面的更新1
我正在尝试使用某些C#/ WPF和C ++开发应用程序。我在C ++端的一部分代码中遇到问题,该问题涉及使用GNU科学库(GSL)优化函数优化对象。我将避免包括任何C#/ WPF / GSL代码,以使这个问题更通用,并且因为问题在我的C ++代码之内。
对于下面的最小,完整和可验证的示例,这是我所拥有的。我有班Foo。和一个类优化器。 Optimizer类的对象是Foo类的成员,因此Foo的对象可以在需要时进行自身优化。
GSL优化函数采用外部参数的方式是通过void指针。我首先定义一个结构Params来保存所有必需的参数。然后,我定义一个Params对象,并将其转换为void指针。使用memcpy_s创建此数据的副本,Optimizer类的成员无效指针optimParamsPtr指向该数据,以便在调用优化器以使其稍后运行时可以访问参数。通过CostFn()访问optimParamsPtr时,出现以下错误。
托管调试助手'FatalExecutionEngineError':'运行时 遇到致命错误。错误的地址在 0x6f25e01e,在线程0x431c上。错误代码为0xc0000005。这个错误 可能是CLR中或bug的不安全或不可验证部分中的错误 用户代码。此错误的常见来源包括用户封送处理错误 用于COM-interop或PInvoke,这可能会破坏堆栈。'
只是为了确保我制作的void指针的有效性,我在第81行调用CostFn(),并将void *指针作为参数传递给InitOptimizer(),并且一切正常。但是在第85行中,当使用optimParamsPtr指向由memcpy_s复制的数据调用相同的CostFn()时,出现了错误。所以我猜测memcpy_s步骤出了问题。任何人对什么有任何想法吗?
#include "pch.h"
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace std;
// An optimizer for various kinds of objects
class Optimizer // GSL requires this to be an unmanaged class
{
public:
double InitOptimizer(int ptrID, void *optimParams, size_t optimParamsSize);
void FreeOptimizer();
void * optimParamsPtr;
private:
double cost = 0;
};
ref class Foo // A class whose objects can be optimized
{
private:
int a; // An internal variable that can be changed to optimize the object
Optimizer *fooOptimizer; // Optimizer for a Foo object
public:
Foo(int val) // Constructor
{
a = val;
fooOptimizer = new Optimizer;
}
~Foo()
{
if (fooOptimizer != NULL)
{
delete fooOptimizer;
}
}
void SetA(int val) // Mutator
{
a = val;
}
int GetA() // Accessor
{
return a;
}
double Optimize(int ptrID); // Optimize object
// ptrID is a variable just to change behavior of Optimize() and show what works and what doesn't
};
ref struct Params // Parameters required by the cost function
{
int cost_scaling;
Foo ^ FooObj;
};
double CostFn(void *params) // GSL requires cost function to be of this type and cannot be a member of a class
{
// Cast void * to Params type
GCHandle h = GCHandle::FromIntPtr(IntPtr(params));
Params ^ paramsArg = safe_cast<Params^>(h.Target);
h.Free(); // Deallocate
// Return the cost
int val = paramsArg->FooObj->GetA();
return (double)(paramsArg->cost_scaling * val);
}
double Optimizer::InitOptimizer(int ptrID, void *optimParamsArg, size_t optimParamsSizeArg)
{
optimParamsPtr = ::operator new(optimParamsSizeArg);
memcpy_s(optimParamsPtr, optimParamsSizeArg, optimParamsArg, optimParamsSizeArg);
double ret_val;
// Here is where the GSL stuff would be. But I replace that with a call to CostFn to show the error
if (ptrID == 1)
{
ret_val = CostFn(optimParamsArg); // Works
}
else
{
ret_val = CostFn(optimParamsPtr); // Doesn't work
}
return ret_val;
}
// Release memory used by unmanaged variables in Optimizer
void Optimizer::FreeOptimizer()
{
if (optimParamsPtr != NULL)
{
delete optimParamsPtr;
}
}
double Foo::Optimize(int ptrID)
{
// Create and initialize params object
Params^ paramsArg = gcnew Params;
paramsArg->cost_scaling = 11;
paramsArg->FooObj = this;
// Convert Params type object to void *
void * paramsArgVPtr = GCHandle::ToIntPtr(GCHandle::Alloc(paramsArg)).ToPointer();
size_t paramsArgSize = sizeof(paramsArg); // size of memory block in bytes pointed to by void pointer
double result = 0;
// Initialize optimizer
result = fooOptimizer->InitOptimizer(ptrID, paramsArgVPtr, paramsArgSize);
// Here is where the loop that does the optimization will be. Removed from this example for simplicity.
return result;
}
int main()
{
Foo Foo1(2);
std::cout << Foo1.Optimize(1) << endl; // Use orig void * arg in line 81 and it works
std::cout << Foo1.Optimize(2) << endl; // Use memcpy_s-ed new void * public member of Optimizer in line 85 and it doesn't work
}
只需重申一下,我需要将参数复制到优化器中的成员,因为优化器将在Foo对象的整个生命周期中运行。因此,只要Optimizer对象存在,它就必须存在,而不仅限于Foo :: Optimize()
需要在项目属性中选择/ clr支持,以便编译代码。在x64解决方案平台上运行。
更新1 :在尝试调试该代码时,我对第109行获得paramsArg大小的方式感到怀疑。看起来我将paramsArg的大小作为int cost_scaling的大小加上存储到FooObj地址的内存大小,而不是存储FooObj本身的内存大小。在绊倒this answer to another post之后,我意识到了这一点。我通过在Foo类中添加一些新的虚拟双精度成员后,通过检查paramsArg的值来确认这一点。不出所料,paramsArg的值没有改变。我想这解释了为什么我得到错误。一种解决方案是编写代码以正确计算Foo类对象的大小并将其设置为paramsArg而不是使用sizeof。但这事实太复杂了,可能本身就是另一个问题。例如,如何获取ref类对象的大小?无论如何,希望有人会对此有所帮助。