delphidlladvanced-installerlpcstrlpwstr

Using Advanced Installer DLL with Delphi


I am trying to use the GetPropertyValue function in the Advanced Installer dll.

The syntax for the function is:

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
);

The parameters are:

propertyName - input parameter of property to read (Im using 'trialname' as test)

result - output parameter with starting address of buffer where value is written

resultLen - if function is successful contains size of data copied to result, else returns 234

The return values are:

0 (ERROR_SUCCESS) - The function succeeded

234 (ERROR_MORE_DATA) - The provided buffer was too small to hold the entire value and the terminating null character

I have set up the function as follows in 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;

The first time I call the function i am getting the error code 234 returned which according to documentation means to allocate the buffer then call the function again. But on second call to the function i am getting access violation. I have found this post:

Providing a buffer to receive an Out LPWSTR value

which has helped me to get to where my code is now, but I am having trouble going any further.


Solution

  • You are making several mistakes that lead to undefined behavior:

    With that said, try something more like this:

    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;
    

    If it is ever possible that the property value could change between the 2 function calls, then use a loop instead:

    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;