Showing posts with label Delphi. Show all posts
Showing posts with label Delphi. Show all posts

Tuesday, September 20, 2011

Forms (Windows) Interface Font

Delphi 7, while being very powerful IDE for Windows development, has some deviations from standards set by Microsoft. One of them is that by default, VCL forms use MS Sans Serif, 8pt font for all controls. This may look good on developer's machine, but on some target machines, this may look very bad because of lack of this font and substituting it for some another.
Standard behavior would be to use default OS Dialog font.
To overcome this, in each form's OnCreate event, invoke the following procedure:

procedure SetFormFontToSystemDefaultFont(Form: TForm);
var
  NonClientMetrics: TNonClientMetrics;
begin
  NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0);
  Form.Font.Handle := CreateFontIndirect(NonClientMetrics.lfMessageFont);
  if Form.Scaled then
  begin
    Form.Font.Height := NonClientMetrics.lfMessageFont.lfHeight;
  end;
end;

(Set ParentFont property to True for all controls on the Form for this to have effect).

Sunday, September 18, 2011

Delphi Inactive Forms Behavior

In Delphi 7, there is one drawback in VCL, which appears as lack of any indication of which window is active when user clicks with mouse on inactive window. Standard behavior of compliant windows application is to flash the title bar of active modal window in that case, but Delphi forms do not respond to this action.

To remedy this, add the following code to your modal forms:
type
  TMyForm = class(TForm)
  ...
  protected
    procedure CreateParams(var Para: TCreateParams); override;
  ....
  end;
.... 
procedure TMyForm.CreateParams(var Para: TCreateParams);
begin
  inherited;
  Para.WndParent := GetActiveWindow;//to allow title flashing of child modal dialogs
end;

Monday, September 12, 2011

64-bit Delphi version is ready

Embarcadero has finally released so long-awaited Delphi IDE version with 64-bit target compilation mode support. By far, I have been using Delphi 7 for about 10 years, and it still allows one to create first-class windows apps. I reluctantly considered an idea of upgrading to newer versions of IDE because of large amount of source code written during these years and of old habit. But I feel that now it is time to upgrade. For me, three main reasons are:

1. 64-bit compilation support

For example, if you have MS Office Add-In, clients will inevitably ask for support of 64-bit MS Office. You have to create 64-bit DLL in order to be able to integrate with any of 64-bit MS Office application, 32-bit, no matter how good it is, will not work. The only plausible solution before was to create a 64-bit stub DLL in Microsoft Visual Studio. Now one can provide 64-bit support without using another IDE.

2. Native Unicode support

Although using of WideString type instead of AnsiString everywhere, and components such as TNT Unicode Controls, in general solves the problem, there still remains some artefacts. For example, you can not register COM server with regsvr32 command, if DLL resides in a path that contains characters which are not representable in the default system Ansi Code Page. And WideStrings was designed to be compatible with system OLE interface calls, and thus they are not reference-counted and use a special system memory allocation routine, which degrade performance. In
contrast, since Delphi 2009, the new native UnicodeString type was introduced, which is reference-counted and uses internal memory manager to allocate space, more efficiently than system-wide does. TNT Unicode controls are also, while work, look a bit like a crutch.

3. Bugs fixing in the IDE

Delphi 7 is convenient IDE, but some intricate bugs sometimes annoy. These include "internal compiler error", AVs, and inability to customize the IDE watches and debug windows in arbitrary way (I constantly got AV after effort to do this). The same applies to C++ Builder 5.5 which I also used for several years.
Embarcadero seems to pay much attention to quality in last years, I hope that their latest products are much more stable.

Unfortunately, C++ Builder XE2 does not yet include 64-bit compilation mode, but its next version anyway seems to fix that. All what is said above, applies also to C++ Builder (I used it for several years).

During last years, Embarcadero are making great efforts to rehabilitate Delphi & Builder IDEs reputation in comparison with MS Visual Studio and open-source competitors. I hope that when demand of support for ARM CPUs will become evident, they will add it in time.

Saturday, September 10, 2011

Hard-to-find lack of variable initialization issue

Another bug which I once encountered, raised from using local (i.e., stack) method (or procedure/function) variables of record type. When you forget to assign some value to simple local variable, and then try to use it, Delphi compiler warns about it:
Variable "VarName" might not have been initialized
But when you have record-type local variable, and somewhere passes it as a parameter to another function, the Delphi (7) compiler does not check that you have initialized all fields. It will silently use whatever values was in memory at the call stack on the location that was taken for such record field. So you must be especially careful when using local record vars. Doing ZeroMemory on all such records at the very beginning of the function (or procedure/method), seems to be a good habit, otherwise it is a big chance that you will lose some valuable time trying to understand why the program behaves so strange.

To illustrate what has been said, take a look at the following code:

type
  TFoo = record
    a, b: Integer;
  end;

procedure A(Foo: TFoo);
begin
end;

procedure B;
var
  Foo: TFoo;
begin
  A(Foo);
end;

Compiler will not say anything on A(Foo) source code string.
The following string
ZeroMemory(@Foo, SizeOf(Foo));
before A(Foo), if will not make your program free of logical errors, but at least will make the program behavior predictable and reproducible.

Hard-to-find object variable overwriting issue

I write software programs in Delphi, version 7 (also in C++ Builder, but this post is about Delphi). Once I decided to optimize the code and to add "const" specifier to all parameter declarations of methods which are not changed in the method body itself. This is recommended for better readability and for performance reasons. For variables of WideString type, if you do not use "const" specifier in function parameters list, the compiler will create new copy of  passed WideString variable upon function entry and release it after function will return. This is because WideStrings are not reference-counted, as opposed to AnsiStrings. But when "const" is specified, the compiler passes pointer to string content (2-byte-wide chars) as is. So, I added "const" specifier everywhere when applicable, and did not receive any copiler warning. But during following testing, it turned out that something had became broken after this change. Strange error sometimes appears, which resulted in wrong value in some object field of type WideString. This error did not appear always, and only on one of two tested MS Windows versions.
I started to seek for the reason, and finally it traced to using WideString-type field of the object which passed as now "const" parameter. The code was something like:

procedure TFoo.SetProperty(const APropValue: WideString);
...
begin
...
  FPropValue := APropValue;
end;

....

procedure TFoo.DoSomething(const APropValue: WideString;...);
...
begin
...
  SetProperty(APropValue);
  DoSomethingElse(APropValue);
end;

procedure TFoo.DoSomething2(...);
...
begin
...
  DoSomething(FPropValue);
...
end;


The compiler did not detect any issues, yet the problem was
that after invoking of SetProperty, the previous pointer to WideChars array contained in FPropValue field, and in APropValue parameter, becomes invalid, but this old pointer value was used when invoking DoSomethingElse method.
I fixed the problem by eliminating "const" specifier from DoSomething declaration.

Be aware of such a pitfall!