Flickering is a common issue with Windows applications. Different techniques exist to solve it, but they are more or less intrusive, and more or less efficient. Double-buffering for example is probably the best solution, but hard to put in place in a legacy code (even impossible sometimes). Using the WM_SETREDRAWmessage can be a light and efficient solution. Continue reading
When C++ Builder 6 fails to properly import COM Objects
In Borland C++ Builder 6, the code generated to import COM objects slightly differs from the one generated by previous versions of C++ Builder.
Specifically, the VARIANT date type is used instead of TVariant. Giving up its TVariant
“smart wrapper” was a good move from Borland, but it broke backward compatibility with output parameters of some COM methods, which don’t receive a value anymore.
For example, below is the code generated by C++ Builer 4 for the FarPoint TfpSpread COM object (from file FPSpreadADO_TLB.h) :
[code lang=cpp]
VARIANT_BOOL __fastcall GetText(long Col, long Row, TVariant* Var)
{
_TDispID _dispid(/* GetText */ DISPID(252));
TAutoArgs<3> _args;
_args[1] = Col /*[VT_I4:0]*/;
_args[2] = Row /*[VT_I4:0]*/;
_args[3] = Var /*[VT_VARIANT:1]*/;
OleFunction(_dispid, _args);
return _args.GetRetVariant();
}
[/code]
When generated using C++ Builder 6, the code becomes :
[code lang=cpp]
VARIANT_BOOL __fastcall GetText(long Col, long Row, VARIANT* Var)
{
_TDispID _dispid(/* GetText */ DISPID(252));
TAutoArgs<3> _args;
_args[1] = Col /*[VT_I4:0]*/;
_args[2] = Row /*[VT_I4:0]*/;
_args[3] = Var /*[VT_VARIANT:1]*/;
OleFunction(_dispid, _args);
return _args.GetRetVariant();
}
[/code]
Notice the Var
parameter, whose type was changed from TVariant* to VARIANT* : that’s the source of the problem.
The issue comes from the TAutoArgs
class, more specifically from its array of Variant
.
The Variant
assignment operator (=) behaves differently depending if its RHS is a TVariant*
or a VARIANT*
:
Variant = TVariant*
: TheVariant
is initialized with a reference to theTVariant*
object (vt = VT_BYREF|VT_VARIANT
, andpvarVal
receives a pointer to theTVariant*
Variant = VARIANT*
: theVariant
‘=’ operator assumes theVARIANT*
is aVT_BOOL
. Changes to theVariant
are not reflected on theVARIANT*
To fix the problem, one must explicitly set the Variant
as a reference to the VARIANT*
. For example, the previous example must be changed to :
[code lang=cpp]
VARIANT_BOOL __fastcall GetText(long Col, long Row, VARIANT* Var)
{
_TDispID _dispid(/* GetText */ DISPID(252));
TAutoArgs<3> _args;
_args[1] = Col /*[VT_I4:0]*/;
_args[2] = Row /*[VT_I4:0]*/;
_args[3].vt = VT_BYREF|VT_VARIANT /*[VT_VARIANT:1]*/;
_args[3].pvarVal = Var;
OleFunction(_dispid, _args);
return _args.GetRetVariant();
}
[/code]
How to compare C++ builder/Delphi DFM binary files with Tortoise SVN ?
With antique versions of Delphi or C++ builder (<= 4), the DFM files – which define the structure of the application forms – are stored as binary files.
Binary files are a hassle because they prevent any batch processing, search, etc… But only with version control do they become a real pain in the ass. Versions can’t be compared, which makes conflict solving almost impossible. And conflicts happen often because Delphi and C++ builder modifiy DFM files for any reason (ex : moving a form around).
If you use Tortoise SVN and strugle with DFM conflicts, here is a solution to automate DFM comparison : Continue reading
Why is “boolean val = (0 == 0 ? null : true)” causing a Java NullPointerException ?
This would be a funny interview question : why is the following Java code compiling but throwing a NullPointerException at runtime ?
[code lang=java]
boolean val = (0 == 0 ? null : true);
[/code]
Since boolean
is a primitive data type, it can’t be initialized with a null value. As a matter of fact, the compiler rejects the following code :
[code lang=java]boolean val = null;[/code]
So, one would expect the conditional/ternary operator assignement to also fail at compile time, whether because null
can’t be assigned to a boolean
, or because null
and true
don’t have the same data type, which is not allowed by the ternary operator.
That’s not the case, compilation runs fine, only at runtime do you get a NullPointerException.
Why ?
I guess this comes from Java autoboxing/autounboxing feature. Since the “then” and “else” parts of the operator must have the same type, Java autoboxes true
to Boolean.TRUE
, and assumes null
is a Boolean
. Knowing that Boolean
can be autounboxed to boolean
, the compiler will accept the statement.
Then, at runtime, null
will be autoboxed to boolean
, eventually causing the NullPointerException. Et voilĂ !