People may think that BOOL and Boolean are the same types in Delphi since they can just contain true and false. Right? Yes and No. They seem to be the same, however there is a little difference how these types are handled in an assignment. To see the difference we have to open up the CPU window and dig into assembler code. Don’t stop reading since it is really easy to understand.
You can also pack a boolean value into an integer. In fact the Windows Headers define it that way so we going to look at it as well.
The following assembler lines were read from the Delphi 7 CPU window:
value := Integer(True); mov [value],$00000001
value := true; mov [value],$ffffffff
value := true; mov byte ptr [value],$01
I don’t want to philosophize about the good and bad of these lines. It’s all about the result we can see here. The Integer and Boolean assignment simply moves the value one into the variable value. On the other side, the BOOL/LongBool assignment looks different – and it is in fact in fully contrast. It moves the value $FFFFFFFF – which is obviously the negative presentation of the value one. So the result of variable value is -1. You can verify it with this code. The variable i will contain the negative number.
Why is it so important to understand these differences? Well, some Win32API functions check for the incoming value and may return with an error 87 (invalid paramater) or behave strangely in the worst case. As you could see, every time a BOOL type value is set to true, Delphi internally sets it to -1. If a Win32API function casts this value into a simple integer it obtains -1 ($ffffffff) and fails because the function may only accept one and zero.
The journey just starts here…. The whole JEDI API uses BOOL as a type that resolves into Delphi’s LongBool which – as we’ve already seen – defines the value “-1″ as true. In most cases Win32API functions don’t thoroughly check it and we are fine. They just check for a zero (false) and otherwise recognize it as true. Because I do not want to change such a big once made decision, so I decided to leave BOOL alone in the JEDI API. Furthermore changing BOOL to Integer means that you can’t assign true and false without excplicitly casting a boolean value to BOOL (or Integer).
In the ending it is just about compability and convenience. However the other side of the story is that you should remember that the Delphi type BOOL may be the source of a 87 (invalid parameter) error.
|X||BOOL in Delphi||BOOL in C||Boolean in Delphi|
|original Type||LongBool||int||compiler magic|
|size in bytes||4||4||1|
|false = ?||0||0||0|
|true = ?||-1 ($FFFFFFFF)||1||1|
The following list contains functions that may collide with the different declaration because they check the value. Send me function names that behave strange with a BOOL type.
|1.||QueryServiceConfig2(W/A)||JwaWinSVC.pas||Happened with struct TServiceDelayedAutoStartInfo (VISTA)|
Solutions you may find in JEDI API.
You may find different solutions for this problem in the JEDI source code. This reason is that the code was written by many persons with different knowledge during a long period of time. So if you find an struct that as a BOOL declaration like SERVICE_DELAYED_AUTO_START_INFO you should make sure that the function which uses this struct behaves correctly.
And here are two possible solution…you’d think.
The first declaration seems to be correct, however as you know it isn’t for the reasons described in the beginning. The second one may work, but it does only work by accident. Be aware that BOOL in C has 4 bytes (=sizeof(int)) and that type Boolean consists only of one byte. Actually we map one byte into four bytes without caring about the other three bytes. So the target function (here: ChangeServiceConfig2) “thinks” that the record member fDelayedAutoStart consists of four bytes (sizeof(int)) and also uses the three bytes behind our declaration. Since we don’t know the values of them (don’t care) they may be filled with random values. The result is a new number that isn’t just zero or one. Since ChangeServiceConfig2 may only succeed for one or zero values, we get the error 87 (invalid parameter).
So there are two real solutions:
I prefer the second solution and you should too! Why? If you hadn’t read the article above you would have thought that the first solution is quite handy because you just could have assigned true or false to it – like C programmers. However as I already mentioned the Pad variable has to be initialized first. So it is obvious to use something like this:
But it won’t work! Why? Again the solution is simple. Many Delphi programmers find their solution in real C sources. They just convert the C syntax to Delphi without knowing the details. As a matter of fact you won’t find a ZeroMemory call in such a C source! The C programmer just sets fDelayedAutoStart to TRUE and nothing more. So don’t you agree that solution numer 2 is the right one?
That’s the reason why you’ll find only the following record in the recent JED API version:
The compiler will fail if you assign true to the record member. However this is the best case I can think of.
So there is nothing better than working code…