2010-08-11 2 views
3

Я хочу создать DLL, которая экспортирует функции, возвращающие строку. Эта DLL должна работать с другими языками программирования! Я нашел для себя всевозможные неприятные решения/взломы, лучше всего заставить мою функцию вернуть Pchar, а затем вызвать другую функцию, содержащуюся в той же DLL (назовем ее ReleaseMemory), чтобы освободить память, зарезервированную для PChar.Delphi DLL, совместимый с другими языками программирования

В любом случае, недавно я обнаружил библиотеку FastShareMem. В нем говорится, что он может делать именно то, что я хочу БЕЗ вызова ReleaseMemory. С другой стороны, FastMM, похоже, делает то же самое как LONG, поскольку как DLL, так и приложение использует FastMM в качестве менеджера памяти. Это мгновенно убьет шанс использовать FastMM в качестве ящика памяти для моей универсальной DLL. Правильно?

====================

FastShareMem (http://www.codexterity.com/fastsharemem.htm), Delphi 7, Windows XP 32 бита, Windows 7 64 бит

ответ

6

Вы смешиваете два разных сценария:

  1. приложений Delphi с использованием Delphi библиотеки DLL
  2. Любое приложение, использующее Delphi библиотеки DLL

В первом случае, если вы не смешивать Delphi версию или сделать что-то странное , менеджер памяти тот же, и компилятор. Таким образом, есть способы совместного использования диспетчера памяти, а затем компилятор может правильно обрабатывать распределения/деаллокации. Это сценарий, в котором работают FastMM и FastShareMem, и только этот.

Во втором случае приложение и DLL будут использовать разные менеджеры памяти, возможно, очень разные, и, как правило, нет возможности их разделить. В такой ситуации лучший подход никогда не должен возвращать PChar, выделенный внутри DLL, даже если вы предоставляете функцию деаллокации, потому что вы не можете быть уверены, что язык вызова будет делать позже с вашим PChar сам по себе, и если у вызывающего есть возможность вызвать надлежащую процедуру разворачивания перед компилятором/интерпретатором. COM работает несколько так, как вы сказали, но он обеспечивает распределение/освобождение памяти через собственный диспетчер памяти, тем самым он безопасен. Вы не можете принудительно применять его с помощью обычных DLL, тем самым, это небезопасно.

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

+0

+1. Я действительно надеялся, что смогу вернуть PChar, но ваш аргумент кажется твердым. – Ampere

+1

Вы можете применять такие же действия, как и COM. Если вы не используете COM-функции для освобождения памяти, выделенной COM, вы получаете ошибки. Вот как это «принуждает» вещи. Вы также можете использовать свой собственный код управления памятью DLL таким же образом. Нет никакой опасности в распределении и возврате указателей из вашей DLL. Существует риск того, что EXE будет неправильно использовать память, но всегда есть риск, что в другом коде будут ошибки. Это не твоя проблема. –

+2

Проблема в том, что те языки, которые не знают о указателях, могут передавать буферы, и эти буферы автоматически управляются. IMHO намного лучше разрешить вызывающей программе управлять своей памятью и просто манипулировать уже выделенным буфером, а не выделять ее и просить звонящего освободить ее, особенно если вы не можете принудительно использовать свои типы данных onw, такие как COM. У кода могут быть ошибки, но некоторые методы более склонны вводить ошибки, чем другие. – 2010-08-12 07:19:53

8

Если вы возвращаете Delphi string, тогда ваша DLL не будет работать с другими языками программирования, потому что ни один другой язык программирования не использует тип строки Delphi. Не имеет значения, как вы выделяете память, если типы не совпадают. Если вы работаете с текстом, следуйте модели Windows API и используйте простые старые указатели на символы.

Решение, которое вы нашли - чтобы вернуть указатель, а затем предоставить другую функцию для вашей DLL, чтобы освободить память - это не взломать и совсем не противно. Это совершенно обычное решение, и никто, кто использует вашу DLL, не заметит глаз, когда они это видят. Функция API FormatMessage использует подобную модель: она выделяет для вас строку, и она указывает, что строки, которые она выделяет, должны быть освобождены с помощью LocalFree.

На самом деле не имеет значения, какой менеджер памяти вы используете до тех пор, пока вы согласны, и пользователи вашей DLL могут его использовать. Один из подходов - указать функции Windows API для выделения и освобождения строк, таких как LocalAlloc и LocalFree, или SysAllocString и SysFreeString. Другой способ - никогда ничего не выделять - если вызывающему нужно вернуть строку, вызывающий обеспечивает буфер и сообщает, насколько он большой. Если буфер слишком мал, тогда вы возвращаете требуемый размер, чтобы вызывающий мог перераспределить буфер и повторно вызвать функцию. Например, см. GetLongPathName.

FastSharemem дает длинное объяснение how Delphi's memory manager works, а затем он говорит, что вы можете избежать всех проблем, просто используя это устройство в своей программе. Но помните, что я сказал выше: потребители вашей DLL должны иметь возможность использовать тот же менеджер памяти, который вы используете. Когда потребитель вашей DLL не написан в Delphi, он не может использовать блок FastSharemem. FastSharemem отлично подходит для однородной среды Delphi, но при использовании в смешанной среде он испытывает все те же проблемы, что и любой другой менеджер памяти.

+0

Извините. Конечно, никакой другой язык программирования не имеет такого удивительного типа, как Delphi STRING. Поэтому мне нужно что-то более совместимое. Я забыл упомянуть, что я намерен заменить функцию возврата строки на возвращаемые функции PChar. – Ampere

+0

Он говорит, что он возвращает PChar, а затем выпускает его с помощью DllReleaseString. Таким образом, типы прекрасны, просто диспетчер памяти не избавит его от необходимости вызывать DllReleaseString, если вызывающий абонент не использует тот же менеджер памяти. Поскольку менеджеры памяти Delphi работают только в Delphi, приложения, написанные на всех других языках, даже не смогут использовать этих менеджеров памяти. – himself

+1

Обратите внимание, что управление версиями Delphi также может вызвать проблемы. D2009 + ansistring отличается от D2007-one, хотя я не знаю, вызывает ли это проблемы с двоичными интерфейсами. –

2

Что происходит, в основном это. Каждый фрагмент кода, скомпилированный отдельно (DLL или EXE), содержит свой собственный код, который выделяет память из системы и управляет ею, называется менеджером памяти. Проще говоря, когда этот фрагмент кода инициализируется, он выделяет большой блок памяти из системы. Позже, когда он делает GetMem или выделяет строки, массивы и так далее, менеджер памяти отмечает части этого большого блока, как он используется. Когда вы FreeMem/deallocate их, они помечены как неиспользуемые.

Теперь представьте, что у вас есть EXE и DLL, как со своими менеджерами памяти. EXE вызывает DLL-процедуру, DLL выделяет строку (PChar), тем самым выделяя часть своего большого блока памяти в качестве используемого. Затем он возвращает указатель на EXE, который использует его, а затем решает освободить. EXE дает указатель на свой собственный менеджер памяти и просит освободить его, но его даже не от огромного блока памяти EXE!Менеджер памяти EXE не знает, как «освободить» чужую память.

Вот почему вам нужно вызвать DllReleaseString(), возвращая при этом заимствованный указатель памяти на DLL и позволяя его встроенному диспетчеру внутренней памяти.

Теперь, что делают Менеджеры Памяти памяти, они соединяются друг с другом. Менеджер памяти в вашем DLL и диспетчере памяти в вашем EXE умеет разговаривать друг с другом, а когда вы указываете память DLL на менеджер памяти EXE, он понимает, что это из DLL и позволяет диспетчеру памяти DLL освободить его. Конечно, это возможно только тогда, когда BOTH DLL и EXE-серверы памяти построены из одного и того же кода менеджера памяти (иначе они не узнают друг друга!). Если ваш менеджер памяти DLL является общим, а ваш менеджер памяти EXE - это что-то еще, менеджер памяти DLL не сможет «спросить» EXE о выпуске памяти, и диспетчер памяти EXE даже не попытается (он не используется).

Поэтому, если вы хотите, чтобы ваша библиотека была универсальной, вы не можете полагаться на менеджеров памяти, разговаривающих друг с другом. Ваша DLL может использоваться с EXE или DLL, которые полагаются на другой менеджер памяти, возможно, написанный на другом языке. Совместное использование диспетчеров памяти возможно только тогда, когда вы контролируете все части своего проекта и можете явно настроить один и тот же менеджер во всем мире.

+0

Но вот оно. Этот менеджер памяти Delphi говорит, что это можно сделать (прочитайте последние две строки): http://www.codexterity.com/memmgr.htm . Я понимаю это неправильно или это действительно может сделать это? – Ampere

+0

+1 для вашего полного ответа! – Ampere

+1

Я не вижу, где говорится, что решение будет работать, даже если одна сторона (EXE или DLL) не использует FastSharemem. – himself

3

Я автор FastSharemem, и я хотел бы внести свой вклад в 2 цента. Роб прав, FastSharemem предполагает, что все модули будут записаны в Delphi. Передача данных между модулями на разных языках может быть сложной, особенно с динамическими данными, такими как строки.

Именно поэтому API Windows часто является болью для работы при работе со сложными структурами данных, и это также одна из причин того, что COM (OLE) Microsoft предоставляет свои собственные функции управления памятью и специальные типы; Целью является двоичная совместимость между модулями, составленными из разных источников.

Итак, поскольку Windows уже делала это раньше, вы можете использовать один из двух способов, которым Windows это делает.Либо:

1) Выведите API-интерфейс C (PChars и т. Д.) И укажите API в кропотливой детали. Вы можете либо открыть процедуры распределения памяти, которые клиенты должны будут вызвать, либо предоставить клиентам распределение. API Windows работает в разное время. Клиентам также может понадобиться SDK для удобного общения с вашим модулем и не забудьте использовать соглашение о вызове stdcall равномерно.

Или

2) Типы использования COM и передавать данные в и. Delphi имеет превосходную, почти прозрачную поддержку COM. Например, для строк вы можете использовать BSTR COM (WideString в Delphi).

Надеюсь, это поможет.

Смежные вопросы