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.
You are making several mistakes that lead to undefined behavior:
__out LPWSTR
DOES NOT translate to PLPWSTR
(pointer to pointer to WideChar) as you have it. It actually translates to LPWSTR
(pointer to WideChar) instead. __out
in C/C++ is NOT the same thing as out
in Delphi.
The function expects you to pass in a pointer to a pre-allocated buffer, which it then fills in. But you are expecting the function to allocate the memory for you, which it doesn't. You are not allocating the buffer at all, even though you know you are supposed to (you said as much in your question), which is why the 2nd call is crashing when it tries to write to invalid memory.
PWideString
is a pointer to a WideString
, which is not compatible with either PLPWSTR
or LPWSTR
, the way you are using it.
the function expects a pointer to a DWORD
, but you are giving it a pointer to a DWORD64
instead. DWORD
is 4 bytes, whereas DWORD64
is 8 bytes.
you are not initializing your intsize
variable to the actual capacity that asReturn
can hold. You are getting lucky that intSize
happens to receive a random value that is smaller than the function is expecting, which it why it returns 234 on the 1st call. The 1st call could very well have crashed if intSize
were holding a larger value instead.
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;