Каков рекомендуемый способ обмена данными между библиотеками DLL? - PullRequest
0 голосов
/ 16 сентября 2018

Поток

У меня есть DLL для чтения, написанная на C++.
У меня также есть DLL писателя, написанная на каком-то языке (не на C++).
DLL работают в одном и том же процессе синхронно.

  1. Reader DLL вызывает API DLL писателя, GetData
  2. Writer DLL подготавливает данные, загружая их, распаковывая и т. Д.
  3. Reader DLL читает и использует данные

Вопрос

Каков рекомендуемый способ обмена данными между библиотеками DLL?


Подход 1

Reader DLL передает аргумент пути к файлу в Writer DLL и считывает данные из файла.

Против

Я бы хотел избежать записи данных на диск. Даже если это самое надежное решение, я бы хотел изучить различные варианты, поскольку мне не очень удобно записывать данные на диск, когда они вам не нужны.


Подход 2

Writer DLL будет выделять буфер в куче и возвращать адрес и размер в DLL читателя.

Против

Reader DLL должна освободить память. Это возможно? удалить память по адресу и размеру?

Кроме того, это, вероятно, большой буфер распределения и освобождения NO-NO между модулями / языками


Подход 3

Разделите GetData () на два вызова.

  1. Считыватель DLL вызывает GetDataSize ()
  2. Reader DLL выделяет буфер и передает адрес в Writer DLL
  3. Writer DLL заполняет буфер
  4. Reader DLL использует буфер
  5. Reader DLL освобождает буфер

Это приемлемый подход WINAPI.

Против

Я предполагаю, что Writer DLL способен знать размер данных до записи, но это не всегда так.


Подход 4

Использовать сопоставление файлов Windows

Против

Аналогичные минусы с подходами 2 и 3.

  • Кто будет создавать сопоставление файлов?
  • Кто разархивирует?
  • Отображение файла не имеет динамического размера. Вы должны определить размер при его создании.

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

Примечание: поскольку мы говорим о передаче данных между двумя разными языками, я предполагаю, что речь идет о «необработанных» данных (примитивных типах, POD и других), которыене нужно никакого особого отношения к уничтожению;если это не так, пожалуйста, сообщите мне в комментариях.

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

  2. Выполнимо и регулярно выполняется;проблема, которую вы обычно должны обойти, состоит в том, что часто куча «по умолчанию» данного модуля является частной 1 , поэтому выделение из одного и освобождение от другого - это большое «нет-нет».Есть два типичных способа реализовать это:

    • пройти через кучу, которая, безусловно, доступна для обоих модулей;в Win32 вы часто найдете LocalAlloc / LocalFree (или другие предоставленные Win32 API примитивы кучи), используемые для этого, так как они логически «ниже» всего кода пользовательского режима и предоставляют общую кучу, доступную для всехмодули в текущем процессе;Итак, одна сторона знает, что она должна распределить, используя LocalAlloc, другая сторона знает, что эти данные должны быть освобождены, используя LocalFree;все работает нормально;
    • модуль выделения также предоставляет функцию освобождения памяти, которую он выделяет;клиентский код знает, что все, что он получил, выделенный модулем A, должно быть освобождено с использованием функции A_free().Это, в свою очередь, вероятно, просто обернет вашу функцию освобождения языка, которая будет использоваться в качестве аналога распределений, которые вы делаете в экспортируемых функциях «бизнес-логики».Кстати, может быть полезно иметь A_malloc(), чтобы отмечать выделения, которые, как ожидается, будут освобождены к A_free() - даже если они могут быть простыми malloc / free сегодня, вас могут заинтересоватьв изменении этого позже.
  3. Обычно также делается;часто в Win32 API есть какая-то специальная форма вызова, которая позволяет получить необходимый размер для выделения;может быть трудоемким в использовании или реализации, если такой размер не может быть легко вычислен без фактической попытки сделать то, что должна делать функция, или если такой размер колеблется (в голову приходят API-интерфейсы Win32 для извлечения данных процессов, где вам, возможно, придется хранить циклыувеличение ассигнований в случае, если объем данных для извлечения фактически увеличивается между одним вызовом и другим).

  4. Можно ли сделать , хотя я никогда не видел, чтобы это было сделано длявнутрипроцессные данные;накладные расходы на выделение будут больше, чем у любой «обычной» функции кучи, но ничего подобного записи в файл;в общем, это более громоздко, чем решение LocalAlloc / LocalFree без особой выгоды, поэтому я бы не стал беспокоиться.

Лично я бы выбрал вариант 2 -это тривиально реализовать и не требует больших изменений в том, как вы обычно пишете эти вещи на C - единственное отличие состоит в том, что вы должны использовать определенную пару функций выделения / освобождения при работе с этими данными.

Дополнительная возможность, которая приходит на ум, - это заставить вашу функцию принимать функцию выделения в качестве параметра обратного вызова (и, возможно, также параметр функции деллокации, если это необходимо для вашего алгоритма - на ум приходят динамически растущие массивы);это будет вызывающая сторона для ее предоставления, поэтому вызываемая DLL будет выделять любую кучу, которая нравится вызывающей стороне.


Примечания

  1. , хотя она может для совместного использования, например, если два модуля динамически связаны с одной и той же средой выполнения C, то, вероятно, это так;ОТО, если два модуля написаны на разных языках, это маловероятно.
0 голосов
/ 16 сентября 2018

Все DLL работают в одном и том же процессе и адресном пространстве. Таким образом, они могут делиться любыми данными прямо в памяти. Задача состоит только в том, чтобы предоставить доступ к данным, особенно если вы используете разные языки.

  • Вариант 1 прост, потому что вам просто нужно передать общее имя файла читателю. Но почему это накладные расходы? Есть более легкий строковый вариант : если вам удастся передать имя файла в виде строки, вы также можете позволить автору сериализовать данные в строку и передать их читателю

  • Вариант 2 более тонкий. Это нормально, если память выделяется / освобождается на той же стороне DLL или если вы выделяете свой буфер с помощью Windows API. В противном случае это может быть непросто, поскольку выделение памяти с трудом преодолевает барьеры DLL (не из-за адресного пространства, а из-за риска использования разных куч и разных процедур выделения / освобождения). Кроме того, вы не можете быть уверены, что вызывающая программа правильно управляет жизненным циклом объекта C ++ (если вы используете какой-либо дизайн RAII на стороне C ++). Таким образом, это вариант, только если читатель управляет жизненным циклом буфера:

    • вызывающая сторона просит читателя выделить, затем вызывающая сторона предоставляет записывающему устройству адрес буфера, затем вызывающая сторона снова вызывает считывающее устройство для обработки данных и освобождения буфера.
    • фиксированный размер буфера приемлем, то есть размер daa известен.
  • Вариант 3 - это вариант 2, выполненный хорошо

  • Опция 4 по-прежнему требует дополнительной нагрузки на диск, если вы используете Ввод / вывод сопоставленного файла , с дополнительным вопросом: можете ли вы дважды сопоставить один и тот же файл в одном и том же процессе? Если вы соблазнитесь этой опцией, взгляните еще раз на строковый вариант, который я предложил для варианта 1 выше: общая строка играет роль отображения памяти без неудобств для файла.

Строковый вариант кажется простой альтернативой для преодоления языковых барьеров со сложными структурами данных. Почти на каждом языке есть строки. Производитель может построить свою строку без необходимости заранее знать размер данных. Наконец, даже если строки обрабатываются по-разному в разных языках, всегда есть способ получить к ним доступ только для чтения .

Но мой предпочтительный вариант состоял бы в том, чтобы организовать все так, чтобы писатель (или основная программа, выступающая в качестве посредника) вызывал функции обработки считывателя по мере необходимости (когда части данных доступны), предоставляя данные как аргументы четко определенных типов для вызовов функций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...