Как мне вернуть строку из DLL в Inno Setup Pascal Script

У меня есть две функции C в DLL, которые определены в файле определения и экспортированы для использования в Inno Setup.

char* __stdcall GetName()
{
        return "Kishore";
}
void __stdcall getName(char* strName)
{
     strcpy(strName, "Kishore");
}

Код Inno Setup загрузит пользовательскую DLL и вызовет функцию / процедуру, чтобы вернуть имена

{ Inno Setup script }
[Code]
procedure  getName(MacAddress: String);
external 'getName@files:MyDll.dll stdcall setuponly';

function  GetName():PAnsiChar;
external 'GetName@files:MyDll.dll stdcall setuponly';

function NextButtonClick(CurPage: Integer): Boolean;
var
  StrName: String;
begin
  SetLength(StrName,15);    
  getName(StrName); { displaying only single character }
  StrName := GetName(); { this call is crashing }
end

Как я могу получить имя в скрипте Inno Setup без сбоев?

1 ответ

Решение

GetName должен вернуть const char * Однако в остальном все хорошо, как написано. Однако обратите внимание, что возвращение такой строки может работать только для констант литералов, как показано выше. Вы не можете использовать этот шаблон для возврата вычисленного строкового значения (если вы попытаетесь это сделать, он, скорее всего, потерпит крах или выдаст поврежденные данные); таким образом getName неправильно.

Также обратите внимание, что в то время как C чувствителен к регистру, Pascal нет, поэтому getName а также GetName та же функция в сценарии Inno. Возможно, вам это сойдет с рук в приведенном выше случае, потому что параметры разные, но я бы не стал на это полагаться - вы должны дать им разные имена. (Не используйте одно и то же имя на стороне C, так как экспорт DLL иногда также ищется без учета регистра).

Чтобы вернуть вычисленную строку, вы должны использовать шаблон, подобный этому:

Код DLL:

void __stdcall CalculateName(char *buffer, size_t size)
{
    strncpy(buffer, "whatever", size);
    buffer[size-1] = 0;
}

Код Inno:

procedure CalculateName(Buffer: AnsiString; Max: Cardinal);
external 'CalculateName@files:my.dll stdcall';

...
Max := 16;
Buffer := StringOfChar(#0, Max);
CalculateName(Buffer, Max);
SetLength(Buffer, Pos(#0, Buffer) - 1);
...

Существует несколько приемлемых вариантов, например, вы можете заставить функцию DLL возвращать количество символов, фактически записанных в буфер, и использовать это в последующем SetLength вместо вызова Pos, чтобы найти нулевой терминатор.

Но вы должны:

  • Убедитесь, что обе стороны используют одинаковые строковые типы, либо ANSI, либо оба Unicode.
    • ANSI Inno Setup поддерживает только строки ANSI со своими String тип.
    • Unicode Inno Setup поддерживает строки ANSI с AnsiString или Unicode строки с String,
  • При использовании строк Unicode убедитесь, что обе стороны согласны с тем, указывается ли Max и / или возвращаемое значение в символах или байтах (в примере кода предполагается, что оно в символах).
  • Перед вызовом функции используйте либо SetLength, либо StringOfChar, чтобы убедиться, что размер буфера соответствует требуемой максимально возможной длине результата.
  • Убедитесь, что вызываемая функция не пытается записать после этой максимальной длины (что легче, если это предоставляется в качестве параметра функции).
  • Убедитесь, что если вы используете Pos, вызываемая функция должна убедиться, что значение завершено нулем (или вам нужно быть более осторожным, чем показано в примере).
  • Убедитесь, что после вызова вы усекаете строку до фактической длины, либо используя возвращаемое значение, либо найдя нулевой терминатор.

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

Другие вопросы по тегам