FatalExecutionEngineError при доступе к указателю, установленному с помощью memcpy_s - PullRequest
0 голосов
/ 30 ноября 2018

См. Обновление 1 ниже, чтобы понять, почему происходит ошибка

Я пытаюсь разработать приложение с некоторыми C # / WPF и C ++.У меня проблема на стороне C ++ в части кода, которая включает в себя оптимизацию объекта с использованием функций оптимизации GNU Scientific Library (GSL).Я буду избегать включения любого кода C # / WPF / GSL, чтобы сделать этот вопрос более общим и потому что проблема в моем коде C ++.

Для минимального, полного и проверяемого примера ниже, вот чтоЯ имею.У меня есть класс Foo.И Оптимизатор класса.Объект класса Optimizer является членом класса Foo, так что объекты Foo могут оптимизировать себя, когда это требуется.

Способ, которым функции оптимизации GSL принимают внешние параметры, - через пустой указатель.Сначала я определяю структуру Params для хранения всех необходимых параметров.Затем я определяю объект Params и преобразую его в пустой указатель.Копия этих данных создается с помощью memcpy_s, и указатель на недействительный член optimParamsPtr класса Optimizer указывает на него, чтобы он мог получить доступ к параметрам при вызове оптимизатора для запуска позднее.Когда CostFn () обращается к optimParamsPtr, я получаю следующую ошибку:

Помощник по управляемой отладке 'FatalExecutionEngineError': 'Во время выполнения обнаружена фатальная ошибка.Адрес ошибки был 0x6f25e01e, в потоке 0x431c.Код ошибки 0xc0000005.Эта ошибка может быть ошибкой в ​​CLR или в небезопасных или не поддающихся проверке частях пользовательского кода.Распространенные источники этой ошибки включают в себя ошибки пользовательского маршалинга для COM-взаимодействия или PInvoke, которые могут повредить стек. '

Просто чтобы убедиться в правильности созданного мною указателя void, я вызываю CostFn () встрока 81 с указателем void *, переданным в качестве аргумента InitOptimizer (), и все работает.Но в строке 85, когда тот же CostFn () вызывается с optimParamsPtr, указывающим на данные, скопированные memcpy_s, я получаю сообщение об ошибке.Итак, я предполагаю, что что-то не так с шагом 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 : пытаясь отладить это, я заподозрил, что получаю размер paramsArg в строке 109. Похоже, яполучаю размер paramsArg как размер int cost_scaling плюс размер памяти, в которой хранится адрес FooObj, а не размер памяти, в которой хранится сам FooObj.Я понял это после того, как наткнулся на этот ответ на другой пост .Я подтвердил это, проверив значение paramsArg после добавления новых фиктивных двойных членов в класс Foo.Как и ожидалось, значение paramsArg не меняется.Я полагаю, это объясняет, почему я получаю ошибку.Решением было бы написать код для правильного вычисления размера объекта класса Foo и установить для него значение paramsArg вместо использования sizeof.Но это оказывается слишком сложным и, вероятно, само по себе другим вопросом.Например, как получить размер объекта класса ref?В любом случае, надеюсь, кто-то найдет это полезным.

...