Надеюсь, у вас уже есть код в вашем приложении для правильного удаления всех динамически распределенных записей, которые вы New()
в FillStructureForDLL()
. Я считаю этот код очень сомнительным, но давайте предположим, что это сокращенный код только для демонстрации проблемы. В любом случае, DLL, которой вы передаете экземпляр записи, не заботится о том, насколько велик кусок памяти, он все равно получит только указатель на нее. Таким образом, вы можете увеличить размер записи, чтобы освободить место для строки Pascal, которая теперь является временным экземпляром в стеке в версии Unicode:
type
PStructureForSomeCDLL = ^TStructureForSomeCDLL;
TStructureForSomeCDLL = record
pName: PAnsiChar;
// ... other parts of the record
pNameBuffer: string;
end;
И функция:
function FillStructureForDLL: PStructureForSomeDLL;
begin
New(Result);
// there may be a bug here, can't test on the Mac... idea should be clear
Result.pNameBuffer := Utf8ToAnsi(UTF8Encode(SomeObject.SomeString));
Result.pName := Result.pNameBuffer;
end;
Кстати: у вас даже не было бы этой проблемы, если бы запись, передаваемая в DLL, была переменной стека в процедуре или функции, которая вызывает функцию DLL. В этом случае временные строковые буферы будут необходимы только в версии Unicode, если нужно передать более одного PAnsiChar
(в противном случае вызовы преобразования будут повторно использовать временную строку). Попробуйте изменить код соответствующим образом.
Изменить:
Вы пишете в комментарии:
Это было бы лучшим решением, если бы была возможна модификация структур DLL.
Вы уверены, что не можете использовать это решение? Дело в том, что из POV DLL структура не изменяется вообще. Возможно, я не прояснил себя, но DLL будет не заботиться о том, является ли переданная ей структура именно такой, какой она была объявлена . Будет передан указатель на структуру, и этот указатель должен указывать на блок памяти, который по крайней мере такой же большой, как структура, и должен иметь ту же структуру памяти. Однако это может быть блок памяти, который на больше , чем исходная структура, и содержит дополнительные данные.
На самом деле это используется во многих местах в Windows API. Вы когда-нибудь задумывались, почему в Windows API есть структуры, которые в первую очередь содержат порядковое значение, дающее размер структуры? Это ключ к развитию API, сохраняя обратную совместимость. Всякий раз, когда для работы функции API требуется новая информация, она просто добавляется к существующей структуре, и объявляется новая версия структуры. Обратите внимание, что макет памяти старых версий структуры сохраняется. Старые клиенты DLL могут по-прежнему вызывать новую функцию, которая будет использовать член структуры размера, чтобы определить, какая версия API вызывается.
В вашем случае не существует разных версий структуры, если речь идет о DLL. Однако вы можете объявить его большим для своего приложения, чем он есть на самом деле, при условии, что структура реальной структуры памяти сохранена, а дополнительные данные только добавлены . Единственный случай, когда это не сработает, - это когда последняя часть структуры была записью с переменным размером, примерно как структура Windows BITMAP
- фиксированный заголовок и динамические данные. Однако ваша запись выглядит так, как будто она имеет фиксированную длину.