在访问使用memcpy_s设置的指针时出现FatalExecutionEngineError

时间:2018-11-29 22:15:49

标签: clr marshalling memcpy void-pointers gsl

有关错误原因的猜测,请参见下面的更新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类对象的大小?无论如何,希望有人会对此有所帮助。

0 个答案:

没有答案