Использование DLL Advanced Installer с Delphi

Я пытаюсь использовать функцию GetPropertyValue в dll Advanced Installer.

Синтаксис функции следующий:

UINT GetPropertyValue(
  __in    LPCWSTR propertyName,      // name of the licensing property to be read
  __out   LPWSTR result,             // buffer where the property value will be written
  __inout DWORD * resultLen          // capacity
);

Параметры:

propertyName — входной параметр свойства для чтения (я использую «trialname» в качестве теста)

result - выходной параметр с начальным адресом буфера, куда записывается значение

resultLen - если функция выполнена успешно, содержит размер данных, скопированных в результат, иначе возвращает 234

Возвращаемые значения:

0 (ERROR_SUCCESS) — функция выполнена успешно.

234 (ERROR_MORE_DATA) — предоставленный буфер слишком мал для хранения всего значения и завершающего нулевого символа.

Я настроил функцию в Delphi следующим образом:

function GetPropertyValue(propertyName : LPCWSTR; Result : PLPWSTR; ResultLen : PDWORD64):Int32; stdcall; external DLLDirectory;

procedure TForm3.btn2Click(Sender: TObject);
Var
  asReturn : PWideString;
  intSize : DWORD64;
  TestValue : LPCWSTR;
  theint : Integer;
begin
  TestValue := LPCWSTR('TrialName');
  theint := GetPropertyValue(TestValue, @asReturn, @intsize);
  ShowMessage(theint.ToString);
  ShowMessage(intsize.ToString);
  if theint = 234 then begin
    GetPropertyValue(TestValue,@asReturn,@intsize);
  end;
end;

При первом вызове функции я получаю возвращенный код ошибки 234, что, согласно документации, означает выделение буфера, а затем повторный вызов функции. Но при втором вызове функции я получаю нарушение доступа. Я нашел этот пост:

Предоставление буфера для получения значения Out LPWSTR

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

Я считаю, что ожидается, что вы предварительно выделите указатель asReturn на определенный размер и укажете этот размер в аргументе intSize. Если это правда, то вам не следует использовать @ перед asReturn.

500 - Internal Server Error 27.06.2024 22:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы совершаете несколько ошибок, которые приводят к неопределенному поведению:

  • __out LPWSTR НЕ переводится в PLPWSTR (указатель на указатель на WideChar), как у вас есть. На самом деле вместо этого он переводится как LPWSTR (указатель на WideChar). __out в C/C++ — это НЕ то же самое, что out в Delphi.

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

  • PWideString — это указатель на WideString, который несовместим ни с PLPWSTR, ни с LPWSTR в том виде, в каком вы его используете.

  • функция ожидает указатель на DWORD, но вместо этого вы передаете ей указатель на DWORD64. DWORD — 4 байта, тогда как DWORD64 — 8 байт.

  • вы не инициализируете свою intsize переменную с фактической емкостью, которую asReturn может содержать. Вам повезло, что intSize получил случайное значение, меньшее, чем ожидает функция, поэтому она возвращает 234 при первом вызове. Первый вызов вполне мог бы потерпеть неудачу, если бы intSize вместо этого удерживал большее значение.

С учетом вышесказанного попробуйте что-нибудь еще вроде этого:

function GetPropertyValue(propertyName : LPCWSTR; Result : LPWSTR; ResultLen : PDWORD): Integer; stdcall; external DLLDirectory;

procedure TForm3.btn2Click(Sender: TObject);
var
  sPropName, sValue : UnicodeString;
  dwSize : DWORD;
  intResult : Integer;
begin
  sPropName := 'TrialName';
  dwSize := 0;
  intResult := GetPropertyValue(PWideChar(sPropName), nil, @dwSize);
  if intResult = 234 then begin
    SetLength(sValue, dwSize-1); // -1 to ignore null terminator
    intResult := GetPropertyValue(PWideChar(sPropName), PWideChar(sValue), @dwSize);
  end;
  if intResult = 0 then
    ShowMessage('Value: ' + sValue)
  end
    ShowMessage('Error: ' + intResult.ToString);
end;

Если когда-либо возможно, что значение свойства может измениться между двумя вызовами функции, вместо этого используйте цикл:

function GetPropertyValue(propertyName : LPCWSTR; Result : LPWSTR; ResultLen : PDWORD): Integer; stdcall; external DLLDirectory;

procedure TForm3.btn2Click(Sender: TObject);
var
  sPropName, sValue : UnicodeString;
  dwSize : DWORD;
  intResult : Integer;
begin
  sPropName := 'TrialName';
  dwSize := 0;
  repeat
    intResult := GetPropertyValue(PWideChar(sPropName), PWideChar(sValue), @dwSize);
    if intResult <> 234 then Break;
    SetLength(sValue, dwSize-1); // -1 to ignore null terminator
  until False;
  if intResult = 0 then begin
    SetLength(sValue, dwSize); // does not include null terminator
    ShowMessage('Value: ' + sValue);
  end else
    ShowMessage('Error: ' + intResult.ToString);
end;

Спасибо @RemyLebau

Shawn Vance 01.07.2024 14:07

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