Wierd Delphi behavour [duplicate] - delphi

This question already has answers here:
What is the default value of 'Result' in Delphi?
(3 answers)
Closed 2 years ago.
Due to a bug in my code I stumbled upon this scenario by accident: -
function Bogus: string;
begin
end;
var
s: string;
begin
s:='Original value';
s:=Bogus;
MessageDlg(s, mtInformation, [mbOK], 0);
end.
Will show a dialog of Original value where the non-result of my function call has no effect on my var; I would have expected it to be empty.
Is this the expected behaviour? Can it be relied upon? Would anyone actually implement this on purpose??!?
I've been using Delphi since 1995 and have never encountered this before.
(Even to this day I still won't select text from the cursor placed by the IDE on a compile error as that used to crash Delphi 1.0!!!)

Behind the scenes, the string return value of Bogus() is passed using a hidden var parameter (not an out parameter, like you are thinking of), as if you had written your code like this instead:
procedure Bogus(var Result: string);
begin
end;
var
s: string;
begin
s:='Original value';
Bogus(s);
MessageDlg(s, mtInformation, [mbOK], 0);
end.
Since Bogus() does not assign anything to its Result, the original string value is preserved (undefined behavior), which is why you are able to see it in the MessageDlg() afterwards.
However, this is a widely known implementation detail that you should NOT rely on. You should always assign something to the Result of a function. The documentation merely says:
If the function exits without assigning a value to Result or the function name, then the function's return value is undefined.

Related

Delphi XE4 gives E2036 when accessing generic list items of 'object's

This is probably similar / continuation on the previous question below:
Why Delphi XE3 gives "E2382 Cannot call constructors using instance variables"?
Now I'm trying Delphi XE4 with the same code (with 'constructor' changed to 'procedure' as per the solution of the above question).
Now I have also these things in a generics list, i.e. I have
TCoordRect = object
public
function Something: Boolean;
end;
and then a list of these in a function parameter, which I loop through and try to access the items directly:
function DoSomething(AList: TList<TCoordRect>): Boolean;
var
i: Integer;
begin
Result := False;
for i := 0 to AList.Count - 1 do
begin
Result := Result or AList[i].Something; // <-- Here comes the compiler error!
end;
end;
This gives the compiler error "E2036 Variable required". However, if I don't access it directly, i.e put instead a local variable and use that first, then it works:
function DoSomething(AList: TList<TCoordRect>): Boolean;
var
i: Integer;
ListItem: TCoordRect;
begin
Result := False;
for i := 0 to AList.Count - 1 do
begin
ListItem := AList[i];
Result := Result or ListItem.Something; // <-- Now this compiles!
end;
end;
And another "workaround" is to remove all these 'object' types and change them to 'class', but I'm curious as to why this does not work like it used to? Is it again just something with "the compiler moving towards mobile development" or is there some more specific reason, or is this even a bug? BTW I also reported this as a QC issue, so will see if something comes from there.
It's a compiler bug, and it's present in all earlier versions of the compiler. The fault is not limited to XE4. Submitting a QC report is the correct response.
I would not be surprised if Embarcadero never attempt to fix it. That's because you are using deprecated object. Switch to using record and the code compiles.
The issue you have uncovered in this question is unrelated to the SO question you refer to at the top of your question.
Incidentally, this really is a case of old meets new. Legacy Turbo Pascal objects, and modern day generic containers. You are mixing oil and water!

Getting WPARAM in TWndMethod to return 4 bytes

I'm using AllocateHWnd in a class I'm writing to receive system messages with a TWndMethod and the messages I'm receiving need to handle a 4-byte WPARAM, which specifically references a pointer. But I'm only getting 2 bytes in return. How do I set up things so I can correctly receive these messages within the class?
Edit: Specific code. I'm setting a message event up using SHChangeNotifyRegister, based on a Microsoft sample I downloaded. The proc works enough to pull back events (in lEvent) that I can buy off on, but the code Microsoft used defines WParam to be Thandle and LParam to be DWord. The specific problem I have is that when the function IsItemNotificationEvent is true, SHGetPathFromIDList is AVing or pulling back garbage. I kept looking this over and am not really seeing a problem other than what the docs I have indicate in that WParam is a Word (probably old) and that GetLastError at the point I put in the code returns "The handle is invalid".
function IsItemNotificationevent(lEvent: Longint): boolean;
var
flagval: Longint;
begin
flagval := (lEvent and (SCHNE_UPDATEIMAGE or SHCNE_ASSOCCHANGED
or SHCNE_EXTENDED_EVENT or SHCNE_FREESPACE
or SHCNE_DRIVEADDGUI or SHCNE_SERVERDISCONNECT));
Result := (flagval > 0);
end;
procedure TShellNotifyHandler.WindowProc(Var msg: TMessage);
var
hNotifyLock: THandle;
lEvent: Longint;
pgpidl: PitemIDList;
psi1: array[1..MAX_PATH] of Char;
begin
if Msg.Msg = FShellMsg then
begin
hNotifyLock := SHChangeNotification_Lock(THandle(Msg.WParam),DWord(Msg.LParam),
pgpidl, lEvent);
writeln(SysErrorMessage(GetLastError));
if (hNotifyLock > 0) then
begin
if IsItemNotificationEvent(lEvent) then
// this limits events for this to what Microsoft defined in their example
begin
if (pgpidl <> nil) then
SHGetPathFromIDList(pgpidl, #psi1);
Writeln('Path #1: ', String(psi1));
end;
SHChangeNotification_Unlock(hNotifyLock);
end;
if Assigned(FOnShellNotify) then
FOnShellNotify(Self, LEvent);
end
else
FWndProc(Msg);
end;
The main thing that I see wrong with this code, and I've only really studied the call to SHChangeNotification_Lock, is that you are unconditionally calling GetLastError.
The documentation for that API function is inadequate because it does not specify how errors are signalled. However, I would strongly expect that errors to be signalled by the function returning NULL. Since the documentation does not say anything about calling GetLastError it is entirely possible that the API function does not set the last error value. No matter, even if you can be sure that GetLastError can be called, you should only do so after a failure, ie. if the call to SHChangeNotification_Lock returns NULL. If you call GetLastError after a successful API call you will get the error code for the most recent failed API call, which is unrelated to the current call.
The bottom line is that I'm sure WParam is carrying all 4 bytes and that your problem is not with that part of the process.
The upshot of all this is the I strongly believe that SHChangeNotification_Lock is succeeding, but the call to SHGetPathFromIDList is failing. You don't check the return value for that. I bet it returns FALSE.
Take a look at the C++ declarations for the two functions.
SHChangeNotification_Lock returns the ID list in a parameter typed liked this:
PIDLIST_ABSOLUTE **pppidl
SHGetPathFromIDList receives the ID list in a parameter typed liked this:
PCIDLIST_ABSOLUTE pidl
I don't know what your declaration of SHChangeNotification_Lock looks like, but the one supplied in my version of Delphi (XE2) looks plain wrong. It has this parameter declared like this:
out pppidl: array of PItemIDList
I honestly can't see how a Windows API function can return a Delphi open array as an out parameter. I think it should be declared so:
out pppidl: PPItemIDList
and you may need to declare PPItemIDList to be ^PItemIDList.
Now, pppidl is an array. It points to the first element of an array of PItemIDList. So you would obtain the path of the first element by calling:
SHGetPathFromIDList(pppidl^, #psi1);
This, I believe, is the real problem you have.
Finally I can't understand why you would test for success with hNotifyLock > 0. The correct test is hNotifyLock <> 0. Now, I know that some of the Delphi types have changed in recent versions, but if THandle was a signed value in your version of Delphi then you code would be wrong. No matter what, the correct logical test is <>0.
Okay, I got this answered. A number of problems all over the board, actually:
1) I had things wrong when it comes to IsItemNotificationEvent. To have valid PIDLs, I needed to make sure that the event WASN'T one of those, because no PIDL is valid to process against those.
if IsItemNotificationEvent(lEvent) then
2) "out" was necessary in the definition to SHChangeNotification_Lock and not "var" or a simple pointer reference. I don't have anything that indicates what "out" does specifically, so if anyone can help, please do. The fixed definition is below.
function SHChangeNotification_Lock(hChangeNotification: THandle; dwProcessID: DWord;
out pppidl: PSHNotifyStruct; out plEvent: Longint): THandle; stdcall;
3) In my documentation (including the source samples), it indicates that multiple pidls are possible for some event types. Which makes the suggested correction invalid in the QC report. The problem with using the original definition is probably as suggested. It's not quite right. Reference the definition above, and you'll see a different type. That definition is below. No events have more than two parms, so it would suffice.
TSHNotifyStruct = packed record
dw1: PItemIDList;
dw2: PItemIDList;
end;
PSHNotifyStruct = ^TSHNotifyStruct;
Got it working as I expect it to now. I just need to find a valid list of two parm events and code in to make it a little cleaner (i.e. not reference the second pitemid if known to be invalid). Some samples of output from my test program are below to illustrate:
Event received: $00001000 Parm 1: (C:) Local Disk // update directory
Event received: $00000008 Parm 1: ChangeNotifyWatcher // make directory
Event received: $00000002 Parm 1: ChangeNotifyWatcher // create file
Event received: $00000010 Parm 1: ChangeNotifyWatcher Parm 2: RECYCLER // remove directory
Thanks all for your help!

result in simple code when variables are uninitialized

Today one of my friend ask me about below code :
var
a: Integer;
begin
ShowMessage(IntToStr(a));
end;
This is local variable and not been initialized , ok ?
Put code in OnClick event of a button component and then run code in three diffrent ways below :
Click on the button and see result , result = 1635841
Press Enter key and see result , result = 1
Press Space key and see result , reuslt = 1636097
I test code in two diffrent computer & see same result , any idea about this ?
Since the variable is not initialized, its value can be anything. Since your result is 'something', there is nothing unusual at all going on here.
procedure TForm1.Button1Click(Sender: TObject);
var
a: Integer;
begin
ShowMessage(IntToStr(Integer(a)));
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(IntToStr(Integer(Pointer(TButtonControl(Button1)))));
end;
on my machine this code produces same message as compiler uses ebx register for a variable, while TButtonControl.WndProc uses ebx to store pointer to Self(as EAX will be overwritten after WinAPI function calls from TbuttonControl.WndProc) which is button1 before calling the actual handler Button1Click. So alas, on Delphi 2007 message text is too predictable.
[edited]
You can see what's happening inside VCL while debugging if you turn on Use debug DCUs option in your project compiler options Compiler->Debugging->Use debug DCUs.
See this similar Stackoverflow question.
In Delphi local variables are not initialised by default. The programmer is responsible for that and should always set a value before reading it. The value of an unitialised variable depends on the content of the actual allocated memory cells used for that variable. So any value is possible here.

How do I stop this Variant memory leak?

I'm using an old script engine that's no longer supported by its creators, and having some trouble with memory leaks. It uses a function written in ASM to call from scripts into Delphi functions, and returns the result as an integer then passes that integer as an untyped parameter to another procedure that translates it into the correct type.
This works fine for most things, but when the return type of the Delphi function was Variant, it leaks memory because the variant is never getting disposed of. Does anyone know how I can take an untyped parameter containing a variant and ensure that it will be disposed of properly? This will probably involve some inline assembly.
procedure ConvertVariant(var input; var output: variant);
begin
output := variant(input);
asm
//what do I put here? Input is still held in EAX at this point.
end;
end;
EDIT: Responding to Rob Kennedy's question in comments:
AnsiString conversion works like this:
procedure VarFromString2(var s : AnsiString; var v : Variant);
begin
v := s;
s := '';
end;
procedure StringToVar(var p; var v : Variant);
begin
asm
call VarFromString2
end;
end;
That works fine and doesn't produce memory leaks. When I try to do the same thing with a variant as the input parameter, and assign the original Null on the second procedure, the memory leaks still happen.
The variants mostly contain strings--the script in question is used to generate XML--and they got there by assigning a Delphi string to a variant in the Delphi function that this script is calling. (Changing the return type of the function wouldn't work in this case.)
Have you tried the same trick as with the string, except that with a Variant, you should put UnAssigned instead of Null to free it, like you did s := ''; for the string.
And by the way, one of the only reasons I can think of that requires to explicitly free the strings, Variants, etc... is when using some ThreadVar.

Initialise string function result?

I've just been debugging a problem with a function that returns a string that has got me worried. I've always assumed that the implicit Result variable for functions that return a string would be empty at the start of the function call, but the following (simplified) code produced an unexpected result:
function TMyObject.GenerateInfo: string;
procedure AppendInfo(const AppendStr: string);
begin
if(Result > '') then
Result := Result + #13;
Result := Result + AppendStr;
end;
begin
if(ACondition) then
AppendInfo('Some Text');
end;
Calling this function multiple times resulted in:
"Some Text"
the first time,
"Some Text"
"Some Text"
the second time,
"Some Text"
"Some Text"
"Some Text"
the third time, etc.
To fix it I had to initialise the Result:
begin
Result := '';
if(ACondition) then
AppendInfo('Some Text');
end;
Is it necessary to initialise a string function result? Why (technically)? Why does the compiler not emit a warning "W1035 Return value of function 'xxx' might be undefined" for string functions? Do I need to go through all my code to make sure a value is set as it is not reliable to expect an empty string from a function if the result is not explicitly set?
I've tested this in a new test application and the result is the same.
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
S := GenerateInfo;
ShowMessage(S); // 5 lines!
end;
This is not a bug, but "feature":
For a string, dynamic array, method
pointer, or variant result, the
effects are the same as if the
function result were declared as an
additional var parameter following the
declared parameters. In other words,
the caller passes an additional 32-bit
pointer that points to a variable in
which to return the function result.
I.e. your
function TMyObject.GenerateInfo: string;
Is really this:
procedure TMyObject.GenerateInfo(var Result: string);
Note "var" prefix (not "out" as you may expect!).
This is SUCH un-intuitive, so it leads to all kind of problems in the code. Code in question - just one example of results of this feature.
See and vote for this request.
We've run into this before, I think maybe as far back as Delphi 6 or 7. Yes, even though the compiler doesn't bother to give you a warning, you do need to initialize your string Result variables, for precisely the reason you ran into. The string variable is getting initialized -- it doesn't start as a garbage reference -- but it doesn't seem to get reinitialized when you expect it to.
As for why it happens... not sure. It's a bug, so it doesn't necessarily need a reason. We only saw it happen when we called the function repeatedly in a loop; if we called it outside a loop, it worked as expected. It looked like the caller was allocating space for the Result variable (and reusing it when it called the same function repeatedly, thus causing the bug), rather than the function allocating its own string (and allocating a new one on each call).
If you were using short strings, then the caller does allocate the buffer -- that's long-standing behavior for large value types. But that doesn't make sense for AnsiString. Maybe the compiler team just forgot to change the semantics when they first implemented long strings in Delphi 2.
This is not a Bug. By definition no variable inside function is initialized, including Result.
So your Result is undefind on first call, and can hold anything. How it is implemented in compiler is irrelevant, and you can have different results in different compilers.
It seems like your function should be simplified like this:
function TMyObject.GenerateInfo: string;
begin
if(ACondition) then
Result := 'Some Text'
else
Result := '';
end;
You typically don't want to use Result on the right side of an assignment in a function.
Anyway, strictly for illustrative purposes, you could also do this, though not recommended:
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
begin
S := ''; // Clear before you call
S := GenerateInfo;
end;
ShowMessage(S); // 5 lines!
end;
This looks like a bug in D2007. I just tested it in Delphi 2010 and got the expected behavior. (1 line instead of 5.)
If you think that some automatic management of strings are made to make your life easier, you're only partly right. All such things are also done to make string logic consistent and side-effects free.
In plenty of places there are string passed by reference, passed by value, but all these lines are expecting VALID strings in whose memory-management counter is some valid, not a garbage value. So in order to keep strings valid the only thing for sure is that they should be initialized when they firstly introduced. For example, for any local variable string this is a necessity since this is the place a string is introduced. All other string usage including function(): string (that actually procedure(var Result: string) as Alexander correctly pointed out) just expects valid strings on the stack, not initialized. And validness here comes from the fact that (var Result: string) construction says that "I'm waiting for a valid variable that definetly was introduced before". UPDATE: Because of that the actual contents of Result is unexpected, but due to the same logic, if it's the only call to this function that has a local variable at the left, the emptiness of the string in this case is guaranteed.
Alex's answer is nearly always right and it answers why I was seeing the strange behaviour that I was, but it isn't the whole story.
The following, compiled without optimisation, produces the expected result of sTemp being an empty string. If you swap the function out for the procedure call you get a different result.
There seems to be a different rule for the actual program unit.
Admittedly this is a corner case.
program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils;
function PointlessFunction: string;
begin
end;
procedure PointlessProcedure(var AString: string);
begin
end;
var
sTemp: string;
begin
sTemp := '1234';
sTemp := PointlessFunction;
//PointlessProcedure(sTemp);
WriteLn('Result:' + sTemp);
ReadLn;
end.

Resources