October 29, 2013

Delphi XE5 update 1 is available

You already know that the update is available if you turned on auto-updates in your Delphi setup. If you didn't, now you know there is a new update...

Delphi and C++Builder XE5 Update 1 is available as an MSI patch or an ISO.

You can also download the update from the registered users web page.

--
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
This article is available from http://francois-piette.blogspot.be

October 15, 2013

Where is that MenuItem displayed ?

I had the need to display a form when the user clicks on an item in a menu. Easy! But the form, which
is small had to be displayed where the MenuItem was displayed. After Googleling a little bit, I did not
find what I needed. So I wrote it. Here is the result.

In Delphi, a TMenuItem has no property to tell where is has been displayed. I had to find how to get
that information. Among TMenuItem events is OnDrawItem. This event is called when the menu has
the OwnDraw property set to true. The corresponding event handler has a TCanvas property and a
TRect property. That was enough. A TCanvas has a handle which can be used to get hand on the
window where it is drawn. And the rectangle gives the position and size of the menu item in that
window. I had everything I needed.

But wait! When the OwnerDraw property of a menu is set to true, all menu items having an
OnDrawItem event assigned are no more drawn. That was annoying because I want it to be drawn as
usual.

Looking at TMenuItem source code, I found that drawing an item is done by AdvancedDrawItem
method. So I had to call it. There was a problem: this is a protected method and I cannot call it from
my TForm1 class. I can’t? Not really.

A protected class member can only be accessed by the class itself or any derived class, or any class in
the same source code. This feature has been considered as a bug by OOP purist but it is “as
designed”. In recent Delphi version, there is now a new “strict” keyword which restrict the
accessibility like OOP purist want it. This isn’t used for AdvancedDrawItem. To access a protected
method, you just have to declare a class deriving from the target class without changing anything. If
that declaration is located in the same source file as you code that need to call it, it can be called.

That empty class is declared like this:
  THackMenuItem = class(TMenuItem)
    // Intentionally left empty
  end;

The final code is the following:
procedure TForm1.ServiceMnuClick(Sender: TObject);
begin
    Form2.Top  := FServiceMnuRect.Top;
    Form2.Left := FServiceMnuRect.Left;
    Form2.ShowModal;
end;

procedure TForm1.ServiceMnuDrawItem(
    Sender   : TObject;
    ACanvas  : TCanvas;
    ARect    : TRect;
    Selected : Boolean);
var
    State    : TOwnerDrawState;
    MenuItem : TMenuItem;
    Handler  : TMenuDrawItemEvent;
begin
    MenuItem := Sender as TMenuItem;
    GetWindowRect(WindowFromDc(ACanvas.Handle), FServiceMnuRect);
    FServiceMnuRect.Top := FServiceMnuRect.Top  + ARect.Top;

    Handler := MenuItem.OnDrawItem;
    try
        MenuItem.OnDrawItem := nil;
        if Selected then
            State := [odSelected]
        else
            State := [];
        THackMenuItem(MenuItem).AdvancedDrawItem(ACanvas, ARect, State, FALSE);
    finally
        MenuItem.OnDrawItem := Handler;
    end;
end;

ServiceMnuClick is where the secondary form is displayed by calling ShowModal. His position is first
set from the top-left corner of FServiceMnuRect (A member variable of TForm1).

ServiceMnuDrawItem has the responsibility of initializing FServiceMnuRect. Basically, it make use of
the canvas handle (which is a “HDC” or Handle to Device Context in Windows terminology) to get the
rectangle of the window behind the canvas. I used two Windows API calls: WindowFromDc and
GetWindowRect. WindowFromDc fetch the window handle given an HDC and GetWindowRect fetch
the bounding rectangle of a given window.
The bounding rectangle has to be fixed by the drawing rectangle passed to the OnDrawItem event
handler. This drawing rectangle is where the menu item is really drawn.

To call AdvancedDrawItem which drawn the menu item, I used the trick explained above. But there
was another issue: Normally AdvancedDrawItem will call OnDrawItem event handler, so we end up
with a recursive call and a nice stack overflow. So before calling AdvancedDrawItem, I save and clear
the event handler and restore it after.

Complete source file is as follow:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics,
  Controls, Forms, Dialogs, Menus, Unit2;

type
  THackMenuItem = class(TMenuItem)
    // Intentionally left empty
  end;

  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    FileMnu: TMenuItem;
    OptionsMnu: TMenuItem;
    BrolMnu: TMenuItem;
    N1: TMenuItem;
    ExitMnu: TMenuItem;
    ServiceMnu: TMenuItem;
    TestMnu: TMenuItem;
    procedure ServiceMnuClick(Sender: TObject);
    procedure ServiceMnuDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect;
      Selected: Boolean);
  private
    FServiceMnuRect : TRect;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ServiceMnuClick(Sender: TObject);
begin
    Form2.Top  := FServiceMnuRect.Top;
    Form2.Left := FServiceMnuRect.Left;
    Form2.ShowModal;
end;

procedure TForm1.ServiceMnuDrawItem(
    Sender   : TObject;
    ACanvas  : TCanvas;
    ARect    : TRect;
    Selected : Boolean);
var
    State    : TOwnerDrawState;
    MenuItem : TMenuItem;
    Handler  : TMenuDrawItemEvent;
begin
    MenuItem := Sender as TMenuItem;
    GetWindowRect(WindowFromDc(ACanvas.Handle), FServiceMnuRect);
    FServiceMnuRect.Top := FServiceMnuRect.Top  + ARect.Top;

    Handler := MenuItem.OnDrawItem;
    try
        MenuItem.OnDrawItem := nil;
        if Selected then
            State := [odSelected]
        else
            State := [];
        THackMenuItem(MenuItem).AdvancedDrawItem(ACanvas, ARect, State, FALSE);
    finally
MenuItem.OnDrawItem := Handler; end; end; end.



Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

October 5, 2013

Updated: IPC using pipes

My blog post about using pipes for inter-process communication has a lot of interest. It has been updated by Arno Garrels from DuoData Software. Arno added 64-bit support and fixed code to compile with Delphi 7 to XE5 (earlier versions may compile however untested).

The original blog post is here. The link to the source code in the article point to the updated code.
Download updated source code from my website at
      http://www.overbyte.be/frame_index.html?redirTo=/blog_source_code.html

Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
This article is available from http://francois-piette.blogspot.be

All about Delphi XE5 and mobile development


CodeRage 8 is just around the corner on October 15th through 17th. This online event is completely free and full of technical sessions all about Delphi XE5 and mobile development.



Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

October 3, 2013

ICS version 8 released

ICS which exists since 1996, has been released in his version 8. V8 was at beta stage for about one year. Now it is released!

The new ICS-V8 is for Delphi 7 / C++ Builder 2006 to Delphi XE5 / C++ Builder XE3 with FireMonkey cross platform support for POSIX/MacOS, also IPv6 support (main development tree, 32 and 64-bit). Note that latest C++ Builder version supported is XE3 (lack of spare time, sorry).

The major changes in V8 include:
- Support for Delphi 7 to RAD Studio XE5
- Support for Firemonkey and Mac OS-X (still beta)
- New support for IPv6 in most components
- New SocketFamily property (sfAny, sfAnyIPv4, sfAnyIPv6, sfIPv4, sfIPv6)
- New MultiListenSockets property for most servers to add extra listening sockets, each with Addr/Port/SocketFamily/SslEnable
- New IPv4 and IPv6 string addresses helpers
- New SMTP mail server component
- New threaded ping component and trace route demo
- Revised directory structure with more, smaller sample directories

You can download ICS-V8 from http://www.overbyte.be as well as from the wiki download page at http://wiki.overbyte.be/wiki/index.php/ICS_Download

If you use the SVN repository, you will get the V8 automatically since it has been merged to the trunk.

Look at the readme: http://svn.overbyte.be:8443/svn/ics/trunk/ReadMe8.txt (User code and password are both "ics").

If you need help please don't ask thru this blog, but use the support mailing list. I will NOT answer to questions posted as comment to this blog post. If you don't know the mailing list, just go to the support page at http://www.overbyte.be.

By the way, there is a ICS-V9 in preparation. It will support Android. Stay tuned!

Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

October 1, 2013

Path issue when having installed a lot of software

Windows is having some trouble with very long path environment variable, even in Windows 7. There are several limits which prevent the path to be as long as one would like.

This present an issue when you have installed a bunch of software, each one adding is own items to the path variable. A some point, the path becomes too long and strange things happens such as control panel applet not working anymore.

This happened to me when installing Delphi XE5 on a system which already had a lot of software, including 6 other Delphi versions.

Actually, the solution is simple. Three steps:
  1. Remove the items installed by the application from the path
  2. Create a batch file adding the items back to the path and launch the application
  3. Change the shortcut (Desktop and Start Menu) to run your batch instead of the application.
I have done that for all the Delphi version I have currently installed on my system. Here is the batch for XE5:

@ECHO OFF
ECHO Starting Delphi XE5
SET RSBIN=D:\Program Files (x86)\Embarcadero\RAD Studio\12.0
SET RSBPL=C:\Users\Public\Documents\RAD Studio\12.0\Bpl
PATH | FIND /I "%RSBIN%"
IF ERRORLEVEL 1 PATH=%RSBIN%\bin;%RSBPL%;%RSBIN%\bin64;%RSBPL%\Win64;%PATH%
START /D "%RSBIN%\bin" bds.exe %1

Please note that the paths in this batch reflect my own installation: Delphi is on D: driver and common files on C: drive. You must adapt this to your own setup!

The batch is almost the same for X2, XE3, XE4 and XE5. Just change the version number form 12.0 for XE5 down to 9.0 for XE2.

Before XE2, the batch is a little bit different because there was no win64 compiler. The arts with win64 and bin64 must be removed. At some point in the past, Embarcadero directory was CodeGear and even before it was Borland. The executable file is bds.exe for most versions, but it was Delphi32.exe for Delphi 7 and earlier. All in all, just have a look at your path variable and reproduce what you see.


Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be