Modify

Opened 10 months ago

Last modified 10 months ago

#3697 new Bug

_WinAPI_GetOverlappedResult fails to set the [out] parameter $iBytes in some cases

Reported by: AlanParry Owned by:
Milestone: Component: Standard UDFs
Version: 3.3.14.5 Severity: None
Keywords: _WinAPI_GetOverlappedResult $ERROR_MORE_DATA Cc:

Description

_WinAPI_GetOverlappedResult does not set the $iBytes [out] parameter in the case where GetOverlappedResult() returns False.

However the $iBytes has a meaningful value in the case when it returns False and GetLastError()==ERROR_MORE_DATA
which can occur when using Named Pipes in message mode (see here if you wish to read about named pipes:[ https://docs.microsoft.com/en-us/windows/desktop/ipc/named-pipe-client]).

Note though that it should return the value for all cases regardless of the reason for the error.

The code back 3.3.8.1 was correct
In at least the following versions it isn't: 3.3.9.22, 3.3.12.0, 3.3.13.19, 3.3.14.5, 3.3.15.0, 3.3.15.1

The incorrect code is

Func _WinAPI_GetOverlappedResult($hFile, $tOverlapped, ByRef $iBytes, $bWait = False)
	Local $aResult = DllCall("kernel32.dll", "bool", "GetOverlappedResult", "handle", $hFile, "struct*", $tOverlapped, "dword*", 0, _
			"bool", $bWait)
	If @error Or Not $aResult[0] Then Return SetError(@error, @extended, False)

	$iBytes = $aResult[3]
	Return $aResult[0]
EndFunc   ;==>_WinAPI_GetOverlappedResult

The old 3.3.8.1 CORRECT code (though missing the struct* change) was

Func _WinAPI_GetOverlappedResult($hFile, $pOverlapped, ByRef $iBytes, $fWait = False)
	Local $aResult = DllCall("kernel32.dll", "bool", "GetOverlappedResult", "handle", $hFile, "ptr", $pOverlapped, "dword*", 0, "bool", $fWait)
	If @error Then Return SetError(@error, @extended, False)
	$iBytes = $aResult[3]
	Return $aResult[0]
EndFunc   ;==>_WinAPI_GetOverlappedResult

Regards
Alan Parry

Attachments (0)

Change History (8)

comment:1 Changed 10 months ago by AlanParry

Sorry - should have set component to Standard UDFs

comment:2 Changed 10 months ago by Jpm

Hi,
I cannot undzrstand why passing a ptr instead of a struct can set $iBytes. But well can you post an example that reproduce the pb so I can analyse more carefully
Thanks for the help

comment:3 Changed 10 months ago by Jpm

  • Component changed from AutoIt to Standard UDFs

comment:4 Changed 10 months ago by anonymous

The Struct/ptr code change is not the problem (that change since 3.3.8.1 is fine and wanted)

The problem is that:

If @error Then Return SetError(@error, @extended, False)

was changed to

If @error Or Not $aResult[0] Then Return SetError(@error, @extended, False)

So if $aResult[0] is False then the $iBytes = $aResult[3] statement doesn't get done.

The issue is just that the ByRef parameter needs to be assigned whatever is set by the DllCall in all cases, not just when the result is True.

An example would be long and complex but could be done, but hoping you don't need it.
Alan

comment:5 Changed 10 months ago by Jpm

Thanks,
the MSDN DOC does not describe how the ibyte parameter is set <hen the return result is set to zero.
So after the _WinAPI_GetOverlappedResult() return false you call the Getlasterror==ERROR_MORE_DATA then you need to use the nbbyte you use in the function that initiated the transfert _WinAPI-ReadFile() or other related function you use.
Perhaps it was working with the old coding but as it is not described in the MSDN DOC I will not change it

Cheers

comment:6 Changed 10 months ago by anonymous

Indeed MSDN doc is lacking <- nothing new there.
In the MSDN example at the end of what I linked to in the first post, just how many chars does the _tprintf statement output anyway???

BUT:- as MSDN does NOT document how and when it sets the $iBytes parameter, why should the UDF decide to discard the value set by Microsoft, based on some unnecessary and arbitrary logic in the UDF. Why discard the value if the return is False but use it when it returns True. Surely the UDF should pass back whatever the Dll call sets!

A look at Wine's implementation https://source.winehq.org/source/dlls/kernel32/file.c#0610 shows that it always sets the $iBytes value regardless of the return code or GetLastError() value.

But it's up to you whether you fix this bug or not. AS you say there are workarounds.

comment:7 Changed 10 months ago by Jpm

Hi,
For me it is not a workaraound as if MS make some internal changes the UDF still work
I leave to Jon the final answer on this Ticket
Cheers

comment:8 Changed 10 months ago by anonymous

Not sure why I'm persevering but it has occurred to me that what you said about MS not documenting it is not actually true.

The MSDN documentation says:

lpNumberOfBytesTransferred - A pointer to a variable that receives the number of bytes that were actually transferred by a read or write operation.

It doesn't say "but only in the case when GetOverlappedResult is True otherwise it can't be bothered to tell you how many bytes were successfully transferred"

When GetOverlappedResult returns False and GetLastError()==ERROR_MORE_DATA The Read/Write operation has transferred a whole buffer, and *lpNumberOfBytesTransferred is set to tell us how many that is - exactly as MSDN documents.

The UDF then discards the value leaving junk in $iBytes

(Also if an operation is aborted part way through, then it tells you how many where transferred - though I can't believe that's very reliable for Write).

Anyway this is absolutely my last comment on the matter, a workaround is too easy for all this effort.
Alan

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as new The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.