|
Post by Fabio Siciliano on Jul 29, 2020 9:55:21 GMT -5
Hi folks, sometimes I experience randomly the follwing problem.
I faced it also in previous versions of Liberty BASIC and this looks completely incomprehensible to me.
For instance, I call a custom function passing it two string variables. The function then makes a CallDLL to a Windows API. Sometimes, randomly, that call fails. I put a NOTICE at the beginning of my function just to see what the hell the function receives. Well, when it fails (Dynamic Link Library call error), it receives two very long and strange numbers instead of the two string values the call passes.
Another example is the following error.log:
Error log timestamp Wednesday 29/07/20 16:28:10
Runtime error: Dynamic Link Library call error
Error(Exception)>>defaultAction
Error(Exception)>>activateHandler: <anUndefinedObject>
Error(Exception)>>handle
Error(Exception)>>signal
Error class(Exception class)>>signal: <'Dynamic Link Library...'>
BasicRunProgram(Object)>>error: <'Dynamic Link Library...'>
BasicRunProgram(BasicProgram)>>terminateRun: <anError>
[] in BasicProgram>>errorHandlerBlock
ExceptionHandler>>evaluateResponseBlock: <aBlockClosure> for: <anError>
[] in ExceptionHandler>>handle:
ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure>
BlockClosure>>invisibleEnsure: <aBlockClosure>
ExceptionHandler>>handle: <anError>
ExceptionHandler>>findHandler: <anError>
Error(Exception)>>activateHandler: <anExceptionHandler>
Error(Exception)>>handle
Error(Exception)>>signal
Error class(Exception class)>>signal: <'Dynamic Link Library...'>
BasicRunProgram(Object)>>error: <'Dynamic Link Library...'>
DynamicLinkLibrary class>>program: <aBasicRunProgram> primitiveCall: <aDynamicLinkLibrary> proc: <'RegQueryInfoKeyA '> arguments: <anArray> types: <anArray> returns: <2>
DynamicLinkLibrary>>program: <aBasicRunProgram> call: <'RegQueryInfoKeyA'> arguments: <anArray> types: <anArray> returns: <#long>
CalldllCommand>>value
[] in BasicRunProgram>>begin
ExceptionHandler>>evaluateProtectedBlock: <aBlockClosure>
[] in ExceptionHandler>>activateDuring:
ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure>
BlockClosure>>invisibleEnsure: <aBlockClosure>
ExceptionHandler>>activateDuring: <aBlockClosure>
ExceptionHandler class>>handle: <anError class> with: <aBlockClosure> during: <aBlockClosure>
BlockClosure>>on: <anError class> do: <aBlockClosure>
BasicRunProgram>>begin
BasicRunProgram(BasicProgram)>>run
BasicOnDemandCompiler class>>readTknFile: <'H:\_ LBB Projects\my...'> callingProgram: <anUndefinedObject> commandLine: <''>
Basic class>>start
Message>>perform
NotificationManager>>empty
NotificationManager>>runPendingEvents
NotificationManager>>runEventLoop
Message>>perform
Message>>evaluate
Process>>safelyEvaluate: <aMessage>
Process>>evaluate: <aMessage>
Well, the very strange stuff is, I previously received this error message from a different function, so I put a NOTICE at the beginning of it just to see what the function receives.
But, once I did it, the error moved to another function, I mean the previous one where I put the NOTICE was fine, that time.
Well, I can't figure out what is really going on.
Did you ever face such a strange behaviour?
Did you understand why?
And how did you solve it?
Thank you all in advance.
Fabio
|
|
|
Post by Rod on Jul 29, 2020 10:47:51 GMT -5
Well the code would have been more informative than the error.log iIf it works sometimes and not others I am pretty sure it is because you are using type LONG to specify handles. You must always use type ULONG whenever you pass a value that describes a Windows resource handle.
|
|
|
Post by Carl Gundel on Jul 29, 2020 10:56:54 GMT -5
Well the code would have been more informative than the error.log iIf it works sometimes and not others I am pretty sure it is because you are using type LONG to specify handles. You must always use type ULONG whenever you pass a value that describes a Windows resource handle. Agreed. We need to see the code. Keep in mind that if you call APIs or use DLLs, even the tiniest programming mistake can produce unpredictable results.
|
|
|
Post by Fabio Siciliano on Jul 29, 2020 14:09:02 GMT -5
No, the issue is different. I was talking about custom functions which receive strings as input values. Strings, not handles (which I always code as ULONG as you wrote)... about the code of the specific functions, it's not important, because the problem I noticed happens randomly on different functions. Sometimes, I noticed, strange enough, a dummy "print" command at the beginning of the function itself seems to be able to make the problem disappearing. So, I wonder, maybe there is a strange way SMALLTALK under Liberty BASIC uses to pass string variables to functions? Obviously my guess could be stupid but, I really don't understand that random strange behaviour. Nobody noticed it before?
|
|
|
Post by Fabio Siciliano on Jul 29, 2020 14:15:32 GMT -5
Okay, this is an example of the functions I was talking about just above:
[copyFile]
function copyFile(lpExistingFileName$, lpNewFileName$, bFailIfExists)
notice "copyFile(";lpExistingFileName$;", ";lpNewFileName$;", ";bFailIfExists;")"
calldll #kernel32, "CopyFileA", lpExistingFileName$ as ptr, lpNewFileName$ as ptr, bFailIfExists as boolean, r as boolean
if r = 0 then calldll #kernel32, "GetLastError", r as long if r = 2 then r$ = "CopyFileA (kernel32) = "; str$(r); cr2$; translate$("file"); ": "; word$(lpExistingFileName$, howmany(lpExistingFileName$, "\", 0, 0) + 1, "\") else r$ = "CopyFileA (kernel32) = "; str$(r); cr$; translate$("from"); ": "; lpExistingFileName$; cr$; translate$("to"); ": "; lpNewFileName$ end if notice caption$; formatMessage$(r$, r) copyFile = 1 end if
end function
Yesterday the NOTICE I put just at the beginning of the function exposed two numeric values (many digits) instead of the full paths and names of the source and destination files... and then, obviously, my program chashed when called the CopyFileA (kernel32) API function.
|
|
|
Post by Fabio Siciliano on Jul 29, 2020 14:25:52 GMT -5
About the log I posted above, there is a strange thing that is not shown here but you can see in Notepad++... in the following row:
DynamicLinkLibrary class>>program: <aBasicRunProgram> primitiveCall: <aDynamicLinkLibrary> proc: <'RegQueryInfoKeyA '> arguments: <anArray> types: <anArray> returns: <2>
the blank after RegQueryInfoKeyA is a NUL character.
Not sure whether it could be a signal of something important or not.
|
|
|
Post by Fabio Siciliano on Jul 29, 2020 14:31:23 GMT -5
This is the code of the above function (the one enlisted in the log):
[regQueryInfoKey]
function regQueryInfoKey(hKey)
open "advapi32" for dll as #advapi32
calldll #advapi32, "RegQueryInfoKeyA", _ hKey as ulong, _ 0 as long, _ ' lpClass 0 as long, _ ' lpcClass 0 as long, _ ' lpReserved lpcSubKeys as struct, _ lpcMaxSubKeyLen as struct, _ 0 as long, _ ' lpcMaxClassLen lpcValues as struct, _ lpcMaxValueNameLen as struct, _ lpcMaxValueLen as ptr, _ 0 as long, _ ' lpcbSecurityDescriptor 0 as long, _ ' lpftLastWriteTime (FILETIME structure) regQueryInfoKey as long
close #advapi32
if regQueryInfoKey then r$ = "RegQueryInfoKeyA (advapi32) = "; str$(regQueryInfoKey) if ERROR.MORE.DATA then r$ = r$; cr2$; translate$("output buffer too small"); "." else r$ = formatMessage$(r$, regQueryInfoKey) end if notice caption$; r$ end if
end function
|
|
|
Post by Chris Iverson on Jul 29, 2020 16:17:06 GMT -5
Still looking at RegQueryInfoKeyA, but as for CopyFile, the wrong datatypes are being used, although the confusion is understandable.
The bFailIfExists parameter and the return value for CopyFile are defined as BOOL in the Windows API. You've got that translated as the "boolean" datatype in LB. While understandable, this is actually incorrect, due to historical platform changes in Windows.
The boolean datatype in LB is 2 bytes long; the BOOL is defined in the Windows API as 4 bytes long. Not enough bytes are being copied to or from the function, which is causing the stack corruption and crash.
Change the "boolean" to "long", and it should work fine.
As for RegQueryInfoKeyA, while I've still not tested it, I note that you've got the other pointers you're using defined as STRUCTs, but for lpcMaxValueLen, you've got that defined as a ptr. LB can't pass a pointer to a numeric value directly.
Also, have you got those structs actually defined? And how are they defined?
|
|
|
Post by Fabio Siciliano on Jul 29, 2020 18:10:21 GMT -5
Hi Chris, very interesting stuff about boolean vs. long. So the "boolean" type should never used in LB? Sure I will give it a try, even if I don't understand the random behaviour. About lpcMaxValueLen (struct vs. ptr), yes, you're right, I noticed it after I placed here my message, then I corrected it but nothing changed. About structs, they are defined in a header file: as you know, structs are globals. I use Liberty BASIC Builder, so I can organize my projects in different files (general header files, specific main file, general function files) and merge them depending on the needed code. However, about RegQueryInfoKeyA, I discovered there is an issue in my code, so that is normal it fails in some specific circumstances. So, by now, my first question about string values passed to custom functions that sometimes, in absolute randomic way, are received in the functions as numbers still remains open.
|
|
|
Post by Fabio Siciliano on Jul 29, 2020 18:12:51 GMT -5
About my structs for RegQueryInfoKeyA:
'** RegQueryInfoKeyA (advapi32) struct lpcSubKeys, lpcSubKeys as long struct lpcMaxSubKeyLen, lpcMaxSubKeyLen as long struct lpcValues, lpcValues as long struct lpcMaxValueNameLen, lpcMaxValueNameLen as long struct lpcMaxValueLen, lpcMaxValueLen as long
Good night. Fabio
|
|
|
Post by Chris Iverson on Jul 29, 2020 18:58:52 GMT -5
Hi Chris, very interesting stuff about boolean vs. long. So the "boolean" type should never used in LB? Pretty much, yes. I don't believe there's currently an API or DLL to interact with that uses 2-byte booleans. According to this: docs.microsoft.com/en-us/windows/win32/winprog/windows-data-typesWindows API definitions that use BOOL are internally ints, which are 32-bit, or 4-byte. Windows API definitions that use BOOLEAN(all caps) are BYTEs, which internally is an 'unsigned char'. This is an 8bit, or single byte, value. According to this: docs.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=vs-2019The size of the native C++ data type 'bool' is a single byte. (C didn't have a native 'bool' datatype; hence why the Windows API declares their own values for it.) Sure I will give it a try, even if I don't understand the random behaviour. I'd be very surprised if anyone could understand it completely. LB is randomly corrupting memory every time you call the mistaken version of the function. Even if you're lucky enough to not immediately crash, you're going to run into some problems somewhere. I'll take another look at RegQueryInfoKey().
|
|
|
Post by Chris Iverson on Jul 30, 2020 0:17:00 GMT -5
Well, I can't find any problem with RegQueryInfoKey().
I edited out the final error check(as I don't have the functions tied to those), I edited out the open and close advapi32 lines, and I dumped that function into one of my registry key test files. (I included your versions of the structs, as well.) It works fine, and I've yet to see a crash with it.
|
|
|
Post by Rod on Jul 30, 2020 3:00:11 GMT -5
I am assuming that this is still a random error and that your code works sometimes, I think that is what you said. In your struct
'** RegQueryInfoKeyA (advapi32) struct lpcSubKeys, lpcSubKeys as long struct lpcMaxSubKeyLen, lpcMaxSubKeyLen as long struct lpcValues, lpcValues as long struct lpcMaxValueNameLen, lpcMaxValueNameLen as long struct lpcMaxValueLen, lpcMaxValueLen as long
The first value lpcSubKeys is a windows handle to a resource, it must be ULONG else you will get occasional errors when the number is passed inside Liberty and is transformed to a signed integer. If you look at the value of the LONG handle when the function errors you will see it has a negative value. To explain the randomness, mostly Windows issues handles (numbers) in the range it used to do in the past. But now it can also assign very large numbers as a handle. When it does this and the number is passed inside Liberty it gets interpreted as a negative value and so corrupts the original representation of the number. Only occasionally are very large numbers assigned as handles so it only occasionally matters.
|
|
|
Post by Rod on Jul 30, 2020 3:38:01 GMT -5
When we are chasing down something as complex as this it helps to have a runnable set of code that displays the error. Liberty has been around for a while, we know most of its bugs. This ULONG handle issue is the only "bug" I am aware of. If it isn't that then it comes down to having the correct types for the API call, and the correct receivers, structs etc.
A runnable set of code would help us get closer, just an extract, but complete and runnable.
|
|
|
Post by Fabio Siciliano on Jul 30, 2020 18:11:27 GMT -5
Okay, I spent the whole day testing and testing and testing again... and here you can find my results.
First of all, I have to say that it is very difficult for me to give you here a "runnable set of code that displays the error" (I mean, something you could test by yourself), because the program I am working on is quite complex (it is an installation procedure that involves writing in the Windows registry, making folders and copying files) and however the wrong behaviour I am talking about happens in absolute random way: I mean, if you run it exactly the same way, I say, for instance, 10 times choosing each time exactly the same user options, the result would be okay 7 times and wrong 3 times.
Anyway, I'm going to explain you the problem better.
Following your above suggestions, first I changed all "boolean" occurences to "long" all over my software and also changed the lpcSubKeys field type in the same name struct of RegQueryInfoKeyA (advapi32) from long to ulong.
Then I made several tests but nothing changed.
Currently I face two kinds of random errors: the first one is a "Runtime error: "isEmpty" not understood" and the second one is the one I was talking about in my first message, I mean, passing strings to custom functions, randomly those functions receive long numbers instead of strings and the result is obviously an error.
Here you can find two examples of the above.
1. Runtime error: "isEmpty" not understood:
Error log timestamp Friday 31/07/20 00:18:17
Runtime error: "isEmpty" not understood
MessageNotUnderstood>>defaultAction MessageNotUnderstood(Exception)>>activateHandler: <anUndefinedObject> MessageNotUnderstood(Exception)>>handle MessageNotUnderstood(Exception)>>signal MessageNotUnderstood class>>message: <aMessage> UndefinedObject(Object)>>doesNotUnderstand: <aMessage> BasicRunProgram(BasicProgram)>>terminateRun: <aMessageNotUnderstood> [] in BasicProgram>>errorHandlerBlock ExceptionHandler>>evaluateResponseBlock: <aBlockClosure> for: <aMessageNotUnderstood> [] in ExceptionHandler>>handle: ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure> BlockClosure>>invisibleEnsure: <aBlockClosure> ExceptionHandler>>handle: <aMessageNotUnderstood> ExceptionHandler>>findHandler: <aMessageNotUnderstood> MessageNotUnderstood(Exception)>>activateHandler: <anExceptionHandler> MessageNotUnderstood(Exception)>>handle MessageNotUnderstood(Exception)>>signal MessageNotUnderstood class>>message: <aMessage> LargePositiveInteger(Object)>>doesNotUnderstand: <aMessage> [] in FilesCommand>>using: [] in BasicRunProgram>>begin ExceptionHandler>>evaluateProtectedBlock: <aBlockClosure> [] in ExceptionHandler>>activateDuring: ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure> BlockClosure>>invisibleEnsure: <aBlockClosure> ExceptionHandler>>activateDuring: <aBlockClosure> ExceptionHandler class>>handle: <anError class> with: <aBlockClosure> during: <aBlockClosure> BlockClosure>>on: <anError class> do: <aBlockClosure> BasicRunProgram>>begin BasicRunProgram(BasicProgram)>>run BasicOnDemandCompiler class>>readTknFile: <'H:\_ LBB Projects\my...'> callingProgram: <anUndefinedObject> commandLine: <''> Basic class>>start Message>>perform NotificationManager>>empty NotificationManager>>runPendingEvents NotificationManager>>runEventLoop Message>>perform Message>>evaluate Process>>safelyEvaluate: <aMessage> Process>>evaluate: <aMessage>
2. A custom function receives two input strings but randomly the strings values are replaced by long numbers.
Here you can find a tipycal example (please notice the NOTICE instruction I put at the beginning of the function in order to debug the problem):
[copyFile]
function copyFile(lpExistingFileName$, lpNewFileName$, bFailIfExists)
if val(lpExistingFileName$) > 0 or val(lpNewFileName$) > 0 then notice "copyFile("; lpExistingFileName$; ", "; lpNewFileName$; ", "; bFailIfExists; ")"
calldll #kernel32, "CopyFileA", lpExistingFileName$ as ptr, lpNewFileName$ as ptr, bFailIfExists as long, r as long
if r = 0 then calldll #kernel32, "GetLastError", r as long if r = 2 then r$ = "CopyFileA (kernel32) = "; str$(r); cr2$; translate$("file"); ": "; word$(lpExistingFileName$, howmany(lpExistingFileName$, "\", 0, 0) + 1, "\") else r$ = "CopyFileA (kernel32) = "; str$(r); cr$; translate$("from"); ": "; lpExistingFileName$; cr$; translate$("to"); ": "; lpNewFileName$ end if notice caption$; formatMessage$(r$, r) copyFile = 1 end if
end function
In one of my last tests the above NOTICE reported the following long numbers instead of the two expected paths and filenames:
This resulted in the crash of the "CopyFileA" API with the following error messages in the "error.log" file:
Error log timestamp Friday 31/07/20 00:20:07
Runtime error: Dynamic Link Library call error
Error(Exception)>>defaultAction Error(Exception)>>activateHandler: <anUndefinedObject> Error(Exception)>>handle Error(Exception)>>signal Error class(Exception class)>>signal: <'Dynamic Link Library...'> BasicRunProgram(Object)>>error: <'Dynamic Link Library...'> BasicRunProgram(BasicProgram)>>terminateRun: <anError> [] in BasicProgram>>errorHandlerBlock ExceptionHandler>>evaluateResponseBlock: <aBlockClosure> for: <anError> [] in ExceptionHandler>>handle: ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure> BlockClosure>>invisibleEnsure: <aBlockClosure> ExceptionHandler>>handle: <anError> ExceptionHandler>>findHandler: <anError> Error(Exception)>>activateHandler: <anExceptionHandler> Error(Exception)>>handle Error(Exception)>>signal Error class(Exception class)>>signal: <'Dynamic Link Library...'> BasicRunProgram(Object)>>error: <'Dynamic Link Library...'> KernelDLL class(DynamicLinkLibrary class)>>program: <aBasicRunProgram> primitiveCall: <aKernelDLL> proc: <'CopyFileA '> arguments: <anArray> types: <anArray> returns: <2> KernelDLL(DynamicLinkLibrary)>>program: <aBasicRunProgram> call: <'CopyFileA'> arguments: <anArray> types: <anArray> returns: <#long> CalldllCommand>>value [] in BasicRunProgram>>begin ExceptionHandler>>evaluateProtectedBlock: <aBlockClosure> [] in ExceptionHandler>>activateDuring: ProtectedFrameMarker(BlockClosure)>>setUnwind: <aBlockClosure> BlockClosure>>invisibleEnsure: <aBlockClosure> ExceptionHandler>>activateDuring: <aBlockClosure> ExceptionHandler class>>handle: <anError class> with: <aBlockClosure> during: <aBlockClosure> BlockClosure>>on: <anError class> do: <aBlockClosure> BasicRunProgram>>begin BasicRunProgram(BasicProgram)>>run BasicOnDemandCompiler class>>readTknFile: <'H:\_ LBB Projects\my...'> callingProgram: <anUndefinedObject> commandLine: <''> Basic class>>start Message>>perform NotificationManager>>empty NotificationManager>>runPendingEvents NotificationManager>>runEventLoop Message>>perform Message>>evaluate Process>>safelyEvaluate: <aMessage> Process>>evaluate: <aMessage>
I noticed this strange behaviour several times in these years and frankly this is very frustrating, because to me it seems that LB works very fine when a program is relatively small and simple, but these random issues grow when a program becames more long and complex. I would like to be completely wrong about it (I like LB very, very much), but I couldn't figure out why, till now.
Any idea?
|
|