Я пытаюсь использовать неуправляемый Cll для загрузки данных изображения в приложение C #. Библиотека имеет довольно простой интерфейс, в котором вы передаете структуру, содержащую три обратных вызова: один для получения размера изображения, другой для получения каждой строки пикселей и, наконец, один для вызова после завершения загрузки. Вот так (управляемое определение C #):
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct st_ImageProtocol
{
public st_ImageProtocol_done Done;
public st_ImageProtocol_setSize SetSize;
public st_ImageProtocol_sendLine SendLine;
}
Типами, начинающими st_ImageProtocol, являются delgates:
public delegate int st_ImageProtocol_sendLine(System.IntPtr localData, int rowNumber, System.IntPtr pixelData);
При использовании тестового файла, который я использую, SetSize должен вызываться один раз, затем SendLine будет вызываться 200 раз (по одному разу для каждой строки пикселей в изображении), и, наконец, вызывается обратный вызов Done. На самом деле происходит то, что SendLine вызывается 19 раз, а затем выдается AccessViolationException, утверждая, что библиотека пыталась получить доступ к защищенной памяти.
У меня есть доступ к коду библиотеки C (хотя я не могу изменить функциональность), и во время цикла, когда он вызывает метод SendLine, он не выделяет и не освобождает новую память, поэтому я предполагаю, что делегат проблема сама по себе, и мне нужно закрепить ее, прежде чем передать (в настоящее время у меня нет кода внутри самого делегата, кроме счетчика, чтобы увидеть, как часто он вызывается, поэтому я сомневаюсь, что что-то сломалось на управляемой стороне) , Проблема в том, что я не знаю, как это сделать; метод, который я использовал для объявления структур в неуправляемом пространстве, не работает с делегатами (Marshal.AllocHGlobal ()), и я не могу найти другой подходящий метод. Сами делегаты являются статическими полями в классе Program, поэтому их не следует собирать, но я думаю, что среда выполнения могла бы их перемещать.
Эта запись в блоге Криса Брамма говорит, что делегаты не должны быть закреплены перед передачей в неуправляемый код:
Очевидно, что указатель неуправляемой функции должен ссылаться на фиксированный адрес. Было бы катастрофой, если бы GC переместил это! Это приводит к тому, что многие приложения создают дескриптор закрепления для делегата. Это совершенно не нужно. Указатель неуправляемой функции фактически ссылается на заглушку собственного кода, которую мы динамически генерируем для выполнения перехода и маршалинга. Эта заглушка существует в фиксированной памяти вне кучи GC.
Но я не знаю, верно ли это, когда делегат является частью структуры. Это означает, что их можно закрепить вручную, и меня интересует, как это сделать, или какие-либо более полезные предложения относительно того, почему цикл будет выполняться 19 раз, а затем внезапно завершится неудачей.
Спасибо.
Отредактировано для ответа на вопросы Йохана ...
Код, который выделяет структуру, выглядит следующим образом:
_sendLineFunc = new st_ImageProtocol_sendLine(protocolSendLineStub);
_imageProtocol = new st_ImageProtocol()
{
//Set some other properties...
SendLine = _sendLineFunc
};
int protocolSize = Marshal.SizeOf(_imageProtocol);
_imageProtocolPtr = Marshal.AllocHGlobal(protocolSize);
Marshal.StructureToPtr(_imageProtocol, _imageProtocolPtr, true);
Где переменные _sendLineFunc и _imageProtocol являются статическими полями класса Program. Если я правильно понимаю внутренности этого, это означает, что я передаю неуправляемый указатель на копию переменной _imageProtocol в библиотеку C, но эта копия содержит ссылку на статический _sendLineFunc. Это должно означать, что GC не трогает копию, поскольку она неуправляема, и делегат не будет собран, поскольку он все еще находится в области действия (статическая).
Структура фактически передается в библиотеку как возвращаемое значение из другого обратного вызова, но как указатель:
private static IntPtr beginCallback(IntPtr localData, en_ImageType imageType)
{
return _imageProtocolPtr;
}
По существу, существует другой тип структуры, который содержит имя файла изображения и указатель на функцию для этого обратного вызова, библиотека выясняет, какой тип изображения хранится в файле, и использует этот обратный вызов для запроса правильной структуры протокола для данного типа. Моя структура имени файла объявляется и управляется так же, как и протокол, описанный выше, поэтому, вероятно, содержит те же ошибки, но поскольку этот делегат вызывается только один раз и вызывается быстро, у меня еще не было проблем с ним.
Изменено для обновления
Спасибо всем за ответы, но, потратив еще пару дней на проблему и не добившись прогресса, я решил отложить ее. На случай, если кому-то будет интересно, я пытался написать инструмент для пользователей приложения рендеринга Lightwave 3D, и хорошей особенностью была бы возможность просмотра всех различных форматов изображений, которые поддерживает Lightwave (некоторые из которых довольно экзотичны). Я подумал, что лучший способ сделать это - написать оболочку C # для архитектуры плагинов, которую Lightwave использует для манипулирования изображениями, чтобы я мог использовать их код для фактической загрузки файлов. К сожалению, после того, как я попробовал несколько плагинов для моего решения, у меня было множество ошибок, которые я не мог понять или исправить, и я предполагаю, что Lightwave не вызывает методы для плагинов стандартным способом, вероятно, для повышения безопасности. запуска внешнего кода (дикий удар в темноте, я признаю). Пока я собираюсь отбросить функцию изображения, и если я решу восстановить ее, я подойду к ней по-другому.
Еще раз спасибо, я многому научился благодаря этому процессу, хотя и не получил желаемого результата.