Передача управляемого обратного вызова в функцию DllImport (ed) - PullRequest
2 голосов
/ 24 июня 2011

У меня был фрагмент кода, который выдавал исключение в отношении делегата, собирающего мусор, вызываемого неуправляемой функцией. Это код:

// Setup Callback functions
errorCode = gsapi_set_stdio(ghostScriptPtr, new StdioMessageEventHandler(RaiseStdInCallbackMessageEvent), new StdioMessageEventHandler(RaiseStdOutCallbackMessageEvent), new StdioMessageEventHandler(RaiseStdErrCallbackMessageEvent));

if (errorCode >= 0)
{
    try
    {
        //GC.SuppressFinalize(this);
        // Init the GhostScript interpreter
        errorCode = gsapi_init_with_args(ghostScriptPtr, commandParameters.Length, commandParameters);

        // Stop the Ghostscript interpreter
        gsapi_exit(ghostScriptPtr);
    }
    finally
    {
        // Release the Ghostscript instance handle
        gsapi_delete_instance(ghostScriptPtr);
    }
}

_Raise ... переменные, переданные в функцию, располагаются перед вызовом функцией.

Я не знаю, что со мной произошло, но я изменил обратные вызовы на переменные:

var _RaiseStdInCallbackMessageEventHandler = new StdioMessageEventHandler(RaiseStdInCallbackMessageEvent);
var _RaiseStdOutCallbackMessageEventHandler = new StdioMessageEventHandler(RaiseStdOutCallbackMessageEvent);
var _RaiseStdErrCallbackMessageEventHandler = new StdioMessageEventHandler(RaiseStdErrCallbackMessageEvent);
// Setup Callback functions
errorCode = gsapi_set_stdio(ghostScriptPtr, _RaiseStdInCallbackMessageEventHandler, _RaiseStdOutCallbackMessageEventHandler, _RaiseStdErrCallbackMessageEventHandler);

и, наконец, блокировать до:

    finally
    {
        // Release the Ghostscript instance handle
        gsapi_delete_instance(ghostScriptPtr);
        _RaiseStdInCallbackMessageEventHandler = _RaiseStdOutCallbackMessageEventHandler = _RaiseStdErrCallbackMessageEventHandler = null;
    }

и это решило проблему. Зачем? Я не знаю. Возможно, это просто совпадение. У меня есть ощущение, что использование переменных в блоке finally приводит к тому, что объект переменной не удаляется рано (поскольку он используется в блоке finally). Это правда? В любом случае, это правильный подход для предоставления функции dllimported с управляемыми обратными вызовами?

Спасибо, Pawel

Ответы [ 2 ]

4 голосов
/ 24 июня 2011

Да, вы на правильном пути с этим. Не совсем. Сборщик мусора не может знать, что нативный код имеет ссылку на делегат. Он похоронен в толще, сгенерированном Marshal.GetFunctionPointerForDelegate (), вне досягаемости GC. Поэтому необходимо, чтобы у вас была еще одна ссылка на делегата, которую GC может см.

Вы сделали это частично с помощью локальной переменной, GC также просматривает стек и регистры ЦП и может видеть ссылку на делегат. Однако это происходит неправильно, когда вы запускаете свой код в режиме Release без отладчика. С подключенным отладчиком джиттер сообщает время жизни локальных переменных до конца метода. Что делает отладку намного проще. Он больше не делает это без отладчика. Даже при установке переменной в null в блоке finally оптимизатор дрожания удаляет это назначение. Оптимизатор включен в сборке выпуска.

Лучше всего хранить ссылки делегатов в поле вашего класса. И убедитесь, что ваш объект класса живет достаточно долго, чтобы выдержать обратный вызов. Следующим лучшим вариантом является использование GC.KeepAlive () для локальных переменных.

0 голосов
/ 24 июня 2011

Ваше сообщение не содержит всей необходимой информации, чтобы понять, что происходит: прототип неуправляемой функции, объявления PInvoke. В любом случае, глядя на заголовок вопроса, я вижу, что вам нужно использовать метод Marshal.GetFunctionPointerForDelegate - это способ передачи управляемого обратного вызова в неуправляемый код.

...