System call level is not correct - delphi

I'm using SHFileOperation to delete files to Recycle bin. But sometimes I received a "System call level is not correct" error. It not happens every time or every file. Just some random files at random time. Anyone knows the reason? Thanks.
Update: Here it the code I am using:
function DeleteToRecycleBin(const ADir : WideString) : Integer;
var
op : SHFILEOPSTRUCTW;
begin
ZeroMemory(#op, sizeof(op));
op.pFrom := PWideChar(ADir + #0#0);
op.wFunc := FO_DELETE;
op.fFlags := FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOERRORUI or FOF_ALLOWUNDO;
Result := SHFileOperationW(op);
end;

You are receiving error code 124 (0x7C). Win32 error code 124 is ERROR_INVALID_LEVEL. However, if you read the documentation for SHFileOperation(), some of its error codes pre-date Win32 and thus do not have the same meaning as the same Win32 error codes. Error code 124 is one of those values. In the context of SHFileOperation(), error 124 actually means:
DE_INVALIDFILES 0x7C
The path in the source or destination or both was invalid.
Update: try this:
function DeleteToRecycleBin(const ADir : WideString) : Integer;
var
  op : SHFILEOPSTRUCTW;
sFrom: WideString;
begin
// +1 to copy the source string's null terminator.
// the resulting string will have its own null
// terminator, effectively creating a double
// null terminated string...
SetString(sFrom, PWideChar(ADir), Length(ADir)+1);
 
ZeroMemory(#op, sizeof(op));
  op.pFrom := PWideChar(sFrom);
  op.wFunc := FO_DELETE;
  op.fFlags := FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOERRORUI or FOF_ALLOWUNDO;
 
Result := SHFileOperationW(op);
end;

Related

Impossible to inspect (one) local string variable

(Based on comments I have edited the question to focus on the actual code part causing the issue, and added a section at the end)
This is the local variables window in my Delphi 10.3.4 Win32 app, note that lData is missing:
The breakpoint corresponding to that screenshot is on the if EmptyJSONArray(lData) line at the end of the next code snippet:
function TLoketAPI.GetOneBatch(AJSONToXML: TJSONToXML; const ASelProps: TSelectionProps; var ABatchCount, AStatusCode: Integer): Boolean;
var
lURL,
lNextID,
lData : String;
lLogHTTP : TLogHTTP;
begin
Result := false;
if FAbort then Exit;
lLogHTTP := TLogHTTP.Create(nil);
Result := false;
if ABatchCount = 0 then
lNextID := ASelProps.GetNextIDValue
else
lNextID := ASelProps.GetSameIDValue;
lURL := StringReplace(ASelProps.URL,'{id}',lNextID,[rfReplaceAll,rfIgnoreCase]);
if ABatchCount <> 0 then
if Pos('?',lURL) = 0 then lURL := lURL + '?pageNumber=' + IntToStr(ABatchCount+1)
else lURL := lURL + '&pageNumber=' + IntToStr(ABatchCount+1);
try
SetHTTPJSONProperties(lLogHTTP);
try
lLogHTTP.Get(lURL);
if FAbort then
Exit;
lData := lLogHTTP.TransferredData;
if FSaveJSON then
begin
lBatchCount := ABatchCount;
TThread.Synchronize(nil,
procedure
begin
if lNextID = '' then
SaveToJSONFile(Format('%s batch (%d)',[ASelProps.TextID,lBatchCount]),lData)
else
SaveToJSONFile(Format('%s (%s) batch (%d)',[ASelProps.TextID,lNextID,lBatchCount]),lData);
end);
end;
if EmptyJSONArray(lData) then
lData contains valid data returned from the logHTTP call.
At the breakpoint (or anywhere else in the routine), lData does not show up in the local variables and cannot be inspected or watched.
The IDE gives:
E2003 Undeclared identifier: 'lData'
This stuff is running in the main thread, optimization is off.
If I comment out the TThread.Synchronize, lData can again be inspected/watched.
What is going on here?
(Added)
In the comments, Remy and Dalija suggested that anonymous procedure as the reason, and linked this to the Embarcadero issue RSP-22924 Watch shows undeclared identifier for captured local variables (also reported under RSP-21917).
I still don't get it though: RSP-22924 was edited to change its type to Feature. Feature? It looks like a bug to me.
And: (How) can this behavior be prevented? I'd really like to inspect lData.

Has function initialization code changed from Seattle to Tokyo?

I am in the process of upgrading code from Delphi 10 Seattle to Delphi 10.2 Tokyo and get a lot of H2077 hints Value assigned to ... never used on assignments.
(Even in places where these were explicitly added in the past to get rid of 'may not have a value' warnings).
These are all function initialized like:
Result := 0;
...
Or:
Result := ftType1; // where ftType1 is an enumerated type
...
Did the compiler get smarter in detecting these or has something changed regarding the initial return values of functions?
We have always had these hints 'on', and I always build (not compile).
Example function (1) that builds without hints in Seattle,
but gives the hint H2077 Value assigned to 'GetDatabaseDialect' not used on the first Result := 0 line in Tokyo.
function GetDatabaseDialect(DBName, User, Pswd: string) : integer;
var
status: array[1..19] of longint;
szDbName, szDbParam: PANSIChar;
dbHandle : pointer;
rslt: longint;
lDPBBuffer : ANSIString;
lDPBLength : integer;
cItem: ANSIChar;
szRslt: PANSIChar; //array[0..IBResultBufferSize-1] of ANSIChar;
begin
Result := 0;
dbHandle := nil;
// init database parameter block with version number
lDPBBuffer := '';
SetLength(lDPBBuffer, 1);
lDPBBuffer[1] := ANSIChar(isc_dpb_version1);
lDPBLength := 1;
// fill Database Parameter Buffer with user name/password
lDPBBuffer := lDPBBuffer +
ANSIChar(isc_dpb_user_name) +
ANSIChar(Length(User)) +
ANSIString( User );
Inc(lDPBLength, 2 + Length(User));
lDPBBuffer := lDPBBuffer +
ANSIChar(isc_dpb_password) +
ANSIChar(Length(Pswd)) +
ANSIString( Pswd );
Inc(lDPBLength, 2 + Length(Pswd));
//Pointers naar naam + buffer
szDbName := PANSIChar(ANSISTring(DBName));
szDbParam := PANSIChar( lDPBBuffer );
// attach to the database and set dialect
rslt := isc_attach_database(#status, 0, szDbName, #dbHandle, lDPBLength, szDbParam);
if rslt <> 0 then
raise EDatabaseError.Create('Error attaching database! ISC# ' + IntToStr(rslt));
//Haal sql dialect op
szRslt := AllocMem(1000);
try
FillChar( szRslt^, 1000, 0);
cItem := ANSIChar( isc_info_db_SQL_dialect );
rslt := isc_database_info(#status, #DBHandle, 1, #cItem, 1000, szRslt);
if rslt <> 0 then
raise EDatabaseError.Create('Error retrieving database info ! ISC# ' + IntToStr(rslt));
Result := Ord(szRslt[3]); //3e positie is dialect
finally
FreeMem(szRslt);
end;
// Drop the connection to the database
rslt := isc_detach_database(#status, #dbHandle);
if rslt <> 0 then
raise EDatabaseError.Create('Error detaching database! ISC# ' + IntToStr(rslt));
end;
Example (2) from a third party library that does not seem to be optimized for Tokyo,
illustrating the case with enumerated types:
H2077 Value assigned to 'TppTemplate.StreamType' not used
Note that changing the assignment to Result := ftASCII; does not make the hint go away (my initial assumption that it was associated with the first enumeration value was incorrect).
type TppFormatType = (ftBinary, ftASCII);
function TppTemplate.StreamType(aStream: TStream): TppFormatType;
var
lSavePos: Integer;
begin
{save stream position}
lSavePos := aStream.Position;
Result := ftBinary;
try
ComputeOffsetFromStream(aStream);
aStream.Seek(FOffset, soBeginning);
if IsValidASCIISignature(aStream) then
Result := ftASCII
else if IsValidBinarySignature(aStream) then
Result := ftBinary
else
raise EInvalidTemplateError.Create(ppLoadStr(49));
finally
{restore stream position}
aStream.Seek(lSavePos, soBeginning);
end;
end; {function, StreamType}
The common denominator seems to be the Result assignments being in try/finally blocks.
Consider this code with a minimal reproduction of your scenario:
function Bar: Boolean;
begin
Result := Random<0.5;
end;
function Foo: Integer;
begin
Result := 0;
if Bar then
Result := 1
else
raise Exception.Create('');
end;
The compiler, even older versions, emits the following hint:
[dcc32 Hint]: H2077 Value assigned to 'Foo' never used
This is reasonable. The first assignment to Result is pointless and can be removed.
Now consider this variation:
function Foo: Integer;
begin
Result := 0;
try
if Bar then
Result := 1
else
raise Exception.Create('');
finally
end;
end;
Older versions of the compiler no longer emit the hint, but the latest version of the compiler does. This should be considered a compiler defect, for older versions. The two variants of Foo shown above are semantically identical. The compiler would be justified in generating identical code.
As you surmise, the assignment being inside the try/finally block is necessary to trigger the defect in previous versions.
We can conclude that the Embarcadero developers have fixed a defect in Tokyo. You can resolve the hints by removing the spurious initial assignments.
Of course, if your code is to be compiled by older versions of the compiler, as well as by new versions, then you are in a bind. With the code as it stands now, a hint is emitted by new versions of the compiler. Remove the initial assignment and a hint is emitted by old versions of the compiler.

Writing all instances of strings between two other strings to logfile

After looking at Delphi extract string between to 2 tags and trying the code given there by Andreas Rejbrand I realized that I needed a version that wouldn't stop after one tag - my goal is to write all the values that occur between two strings in several .xml files to a logfile.
<screen> xyz </screen> blah blah <screen> abc </screen>
-> giving a logfile with
xyz
abc
... and so on.
What I tried was to delete a portion of the text read by the function, so that when the function repeated, it would go to the next instance of the desired string and then write that to the logfile too until there were no matches left - the boolean function would be true and the function could stop - below the slightly modified function as based on the version in the link.
function ExtractText(const Tag, Text: string): string;
var
StartPos1, StartPos2, EndPos: integer;
i: Integer;
mytext : string;
bFinished : bool;
begin
bFinished := false;
mytext := text;
result := '';
while not bFinished do
begin
StartPos1 := Pos('<' + Tag, mytext);
if StartPos1 = 0 then bFinished := true;
EndPos := Pos('</' + Tag + '>', mytext);
StartPos2 := 0;
for i := StartPos1 + length(Tag) + 1 to EndPos do
if mytext[i] = '>' then
begin
StartPos2 := i + 1;
break;
end;
if (StartPos2 > 0) and (EndPos > StartPos2) then
begin
result := result + Copy(mytext, StartPos2, EndPos - StartPos2);
delete (mytext, StartPos1, 1);
end
So I create the form and assign a logfile.
procedure TTagtextextract0r.FormCreate(Sender: TObject);
begin
Edit2.Text:=(TDirectory.GetCurrentDirectory);
AssignFile(LogFile, 'Wordlist.txt');
ReWrite(LogFile);
CloseFile(Logfile);
end;
To then get the files in question, I click a button which then reads them.
procedure TTagtextextract0r.Button3Click(Sender: TObject);
begin
try
sD := TDirectory.GetCurrentDirectory;
Files:= TDirectory.GetFiles(sD, '*.xml');
except
exit
end;
j:=Length(Files);
for k := 0 to j-1 do
begin
Listbox2.Items.Add(Files[k]);
sA:= TFile.ReadAllText(Files[k]);
iL:= Length(sA);
AssignFile(LogFile, 'Wordlist.txt');
Append(LogFile);
WriteLn(LogFile, (ExtractText('screen', sA)));
CloseFile (LogFile);
end;
end;
end.
My problem is that without the boolean loop in the function, the application only writes the one line per file and then stops but with the boolean code the application gets stuck in an infinite loop - but I can't quite see where the loop doesn't end. Is it perhaps that the "WriteLn" command can't then output the result of the function? If it can't, I don't know how to get a new line for every run of the function - what am I doing wrong here?
First you need to get a grip on debugging
Look at this post for a briefing on how to pause and debug a program gone wild.
Also read Setting and modifying breakpoints to learn how to use breakpoints. If you would have stepped through your code, you would soon have seen where you go wrong.
Then to your problem:
In older Delphi versions (up to Delphi XE2) you could use the PosEx() function (as suggested in comments), which would simplify the code in ExtractText() function significantly. From Delphi XE3 the System.Pos() function has been expanded with the same functionality as PosEx(), that is, a third parameter Offset: integer
Since you are on Delphi 10 Seattle you can use interchangeably either System.StrUtils.PosEx() or System.Pos().
System.StrUtils.PosEx
PosEx() returns the index of SubStr in S, beginning the search at
Offset
function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer; inline; overload;
The implementation of ExtractText() could look like this (with PosEx()):
function ExtractText(const tag, text: string): string;
var
startPos, endPos: integer;
begin
result := '';
startPos := 1;
repeat
startPos := PosEx('<'+tag, text, startpos);
if startPos = 0 then exit;
startPos := PosEx('>', text, startPos)+1;
if startPos = 1 then exit;
endPos := PosEx('</'+tag+'>', text, startPos);
if endPos = 0 then exit;
result := result + Copy(text, startPos, endPos - startPos) + sLineBreak;
until false;
end;
I added sLineBreak (in unit System.Types) after each found text, otherwise it should work as you intended it (I believe).

Error: No overloaded versoin of 'IntToStr'

When compiling the following code:
procedure TMainWin.FormActivate(Sender: TObject);
var LineRaw : String;
LinesFile : TextFile;
i, i2 : integer;
tempChar : String;
CurTempCharPos : integer;
begin
AssignFile(LinesFile, 'Lines.txt');
Reset(LinesFile);
i := 0;
tempChar := '';
CurTempCharPos := 1;
while not EoF(LinesFile) do begin
i := i+1; //ticker
ReadLn(LinesFile, LineRaw);
for i2 := 0 to 4 do begin
tempChar := LineRaw[CurTempCharPos] + LineRaw[CurTempCharPos +1];
Lines[i,i2] := IntToStr(tempChar);
tempChar := '';
CurTempCharPos := CurTempCharPos + 3;
end;
end;
CloseFile(LinesFile);
end;
With Lines being defined in another form:
unit uGlobal;
interface
type
aLines = array[1..5] of integer;
aLinesFinal = array of aLines;
var
Lines : aLinesFinal;
implementation
end.
I get the following error: There is no overloaded version of 'IntToStr' that can be called with these arguments. The error points to the line:
Lines[i,i2] := IntToStr(tempChar);
Here is the declaration of tempChar:
tempChar : String;
It is a string. And here is the call that the compiler rejects:
Lines[i,i2] := IntToStr(tempChar);
The IntToStr function, which has various overloads, accepts integer input parameters and returns strings. You cannot pass a string to IntToStr. Perhaps you meant to write:
Lines[i,i2] := StrToInt(tempChar);
Some other comments:
I doesn't look like you initialised Lines. This means that whilst the code might compile, it will fail at runtime.
Since you declared aLines as array[1..5] of integer, the valid values for i2 are 1 to 5 inclusive. You use 0 to 4 inclusive. Again, that's going to bite at runtime.
You really should enable range checking as a matter of urgency, since when you start executing this code that setting will reveal the errors above, and no doubt more besides.
In my view tempChar is a poor name for something that can hold more than a single character.
As #TLama points out, OnActivate seems to be an unusual place to execute this code. This event will run multiple times. Perhaps you should be executing this code at start up. In any case, code like this should not be in an event handler and should be moved to a separate method which an event handler can call.

BlockWrite I/O Error 1784

I get an I/O Error 1784 due to blockwrite in the following code
which overwrites 3 times a file.
I presume I/O Error 1784 means ERROR_INVALID_USER_BUFFER.
I don't know why. The error appears sometimes, not at each run...
Could you help me ?
procedure overwrite_files_3_times(iPath : string);
var
numwritten : integer;
iFileSize, iPosition : int64;
InFile : File of byte;
ipBufBlock : pTBUFFER;
k : integer;
begin
if not FileExists(iPath) then
exit;
FileMode := fmOpenRead or fmOpenWrite or fmShareDenyNone;
AssignFile(InFile, iPath);
Reset(InFile);
iFileSize := getfilesize2(iPath); // retrieve the filesize
iPosition := 0;
// 3 overwrites
for k:= 0 to 3-1 do
begin
Seek(InFile, 0);
iPosition := 0;
///////////////////
// on écrit
while iPosition + sizeOf(TBuffer) < iFileSize do
begin
BlockWrite(InFile,ipBufBlock^,sizeOf(TBuffer),numwritten);
iPosition := iPosition + sizeOf(TBuffer);
end;
// the end
if iPosition <= iFileSize -1 then
begin
BlockWrite(InFile,ipBufBlock^,iFileSize-iPosition,numwritten); //-->> generate I/O Error 1784
end;
end;
////////////////
CloseFile(InFile);
end;
Assuming pTBUFFER is a pointer to TBUFFER, where is ipBufBlock initialized? If it isn't, ipBufBlock may point to anything - even memory that cannot be read and thus not be written to the file.
Someone else had a similair error, so this might apply to your case too:
WriteFile returning error 1784
-- Arjan

Resources