C Runtime объекты, границы DLL - PullRequest
6 голосов
/ 27 июня 2009

Каков наилучший способ разработки C API для dll, который решает проблему передачи «объектов», зависящих от времени выполнения C (FILE *, указатель, возвращаемый malloc и т. Д.).Например, если две dll связаны с другой версией среды выполнения, я понимаю, что вы не можете безопасно передать FILE * из одной dll в другую.

Это единственное решение для использования Windows-зависимого API(которые гарантированно работают через dll)?C API уже существует и является зрелым, но в основном был разработан для PIX Unix (и, конечно, все еще должен работать на Unix).

Ответы [ 4 ]

2 голосов
/ 29 июня 2009

Вы просили C, а не решение C ++.

Обычные методы для подобных вещей в C:

  • Разработайте API модулей, чтобы просто не требовать ЭЛТ-объектов. Получите информацию, передаваемую в необработанных C-типах - то есть, чтобы потребитель загрузил файл и просто передал вам указатель. Или попросите получателя передать полностью определенное имя файла, которое открывается, читается и закрывается внутри.

  • Подход, используемый другими модулями c, SD-кабинетом MS и частями библиотеки OpenSSL iirc, заставляет потребляющее приложение передавать указатели на функции на функцию инициализации. Таким образом, любой API, которому вы передаете FILE *, в какой-то момент во время инициализации получал бы указатель на структуру с указателями на функции, совпадающими с сигнатурами fread, fopen и т. Д. При работе с внешними FILE * dll всегда использует переданный в функции, а не функции CRT.

С помощью таких простых приемов вы можете сделать свой интерфейс C DLL совершенно независимым от CRT хостов - или фактически потребовать, чтобы хост вообще был написан на C или C ++.

1 голос
/ 27 июня 2009

Ни один из существующих ответов не является правильным: Учитывая следующее в Windows: у вас есть две библиотеки DLL, каждая из которых статически связана с двумя разными версиями стандартных библиотек C / C ++.

В этом случае не следует передавать указатели на структуры, созданные стандартной библиотекой C / C ++ в одной DLL, в другую. Причина в том, что эти структуры могут различаться в двух реализациях стандартной библиотеки C / C ++.

Еще одна вещь, которую вы не должны делать, это освободить указатель, выделенный new или malloc, из одной DLL, которая была выделена в другой. Менеджер кучи также может быть реализован по-разному.

Обратите внимание, вы можете использовать указатели между библиотеками DLL - они просто указывают на память. Это проблема бесплатна.

Теперь вы можете обнаружить, что это работает, но если это работает, то вам просто повезло. Это может вызвать проблемы в будущем.

Одним из возможных решений вашей проблемы является динамическое соединение с CRT . Например, вы можете динамически ссылаться на MSVCRT.DLL. Таким образом, ваши DLL всегда будут использовать один и тот же CRT.

Обратите внимание, я полагаю, что не рекомендуется передавать структуры данных ЭЛТ между библиотеками DLL. Возможно, вы захотите посмотреть, сможете ли вы учесть вещи лучше.

Обратите внимание, я не эксперт по Linux / Unix - но у вас будут такие же проблемы и в этих ОС.

0 голосов
/ 27 июня 2009

Если C API существует и является зрелым, обход CRT внутри с использованием чистого Win32 API поможет вам на полпути. Другая половина уверена, что пользователь DLL использует соответствующие функции Win32 API. Это сделает ваш API менее переносимым как в использовании, так и в документации. Кроме того, даже если вы пойдете по этому пути с распределением памяти, где функции CRT и функции Win32 имеют дело с void *, у вас все еще есть проблемы с файловыми ресурсами - Win32 API использует дескрипторы и ничего не знает о структуре FILE.

Я не совсем уверен, каковы ограничения ФАЙЛА *, но я предполагаю, что проблема та же, что и при распределении ЭЛТ между модулями. MSVCRT использует Win32 для обработки файловых операций, и основной дескриптор файла можно использовать из каждого модуля в одном и том же процессе. Что может не сработать, так это закрытие файла, который был открыт другим модулем, что предполагает освобождение структуры FILE на возможно другой CRT.

Что бы я сделал, если изменение API все еще является опцией, это экспортные функции очистки для любого возможного «объекта», созданного в DLL. Эти функции очистки будут обрабатывать удаление данного объекта так, как оно было создано в этой DLL. Это также сделает DLL абсолютно переносимой с точки зрения использования. Единственное, о чем вы будете беспокоиться, это убедиться, что пользователь DLL действительно использует ваши функции очистки, а не обычные функции CRT. Это можно сделать, используя несколько приемов, которые заслуживают другого вопроса ...

0 голосов
/ 27 июня 2009

Проблема с разными средами выполнения не решаема, потому что структура FILE * принадлежит в одну среду выполнения в системе Windows.

Но если вы напишете небольшую оболочку интерфейса, то все готово, и это на самом деле не помешает.

stdcall IFile* IFileFactory(const char* filename, const char* mode);

class IFile {

  virtual fwrite(...) = 0;
  virtual fread(...) = 0;

  virtual delete() = 0; 
}

Это безопасно для передачи через границы dll повсюду и на самом деле не причиняет вреда.

P.S .: Будьте осторожны, если вы начинаете бросать исключения через границы dll. Это будет работать тихо, если вы выполните некоторые дизайнерские требования в ОС Windows, но не получится в некоторых других.

...