Delphi: Design Multi-Threaded SDI Application

SDI (Single Document Interface) has been pushed by Microsoft Office years ago.  Previously, it was MDI (Multiple Document Interface) in few older Office Suite.  Delphi Win32 VCL Form Application support MDI natively.  It requires some works to get SDI done in Delphi. A new document interface model: Tabbed Browsing has been pushed by Firefox that grab lot of attention too.  A more recent Google Chrome can even undock a tab to make it as a separate desktop window. It is no doubt that SDI has some advantages over MDI.  Research shows that using more than one desktop do improve productivity of user.  Dual-View environment will become popular in end user's day to day work.  MDI application can't utilize multi-desktop environment.  It may only show on one desktop only.  However, SDI doesn't has the restriction and may fully utilize multi-desktop environment.  Perhaps, the only drawback is the over created SDI window flow on desktop may irritate user's eye. Show form as a separate desktop button By default, all newly form create form may just show as is flowing on desktop.  There is only one desktop button for the VCL application you launch regardless of how many forms you created.  Running the following code for each form instance will create a desktop button: procedure TChildForm.CreateParams(var Params: TCreateParams); begin   inherited CreateParams(Params);   with Params do begin     ExStyle := ExStyle or WS_EX_APPWINDOW;   end; end; Now, all TChildForm instances will have separate desktop button.  That makes your application start looks like SDI. Make it more like SDI After play around with the above application for while, you start notice few things: If my child form overlap the main form, my main form always stay behind the child form Minimize main form will minimize all child forms and desktop button shrink to only one. Press Alt-Tab to bring the window selector doesn't show all correct number of main form and child form windows. This is because all child form instance's WndParent handle is set to Application.Handle or Application.MainformHandle.  To overcome above problems, we can code like this: procedure TChildForm.CreateParams(var Params: TCreateParams); begin   inherited CreateParams(Params);   with Params do begin     ExStyle := ExStyle or WS_EX_APPWINDOW;     WndParent := GetDesktopwindow;   end; end; Now all main form and child form behave similar and independent on your desktop.  Only different is closing the main form will close all child forms and end the process. Child Form invoke modal form freeze whole application So far, you should happy with the new SDI looks.  It works great.  But if your child form run the following codes: begin   ShowMessage('Modal Form Launched'); end; begin   raise Exception.Create('Exception raised'); end; You will notice that you can't activate other child form or main form anymore.  This reason behind this is simple.  TCustomForm.ShowModal is running in a repeat until loop waiting for the form to return a ModalResult: function TCustomForm.ShowModal: Integer; begin   ...   Application.ModalStarted;   try     ...     try       Show;       try         ModalResult := 0;         repeat           Application.HandleMessage;           if Application.Terminated then ModalResult := mrCancel else             if ModalResult <> 0 then CloseModal;         until ModalResult <> 0;         ...       finally         Hide;       end;     finally       ...     end;   finally     Application.ModalFinished;   end; end; For simple message dialog like ShowMessage or exception dialog, we may use Windows.MessageBox to prevent the freezing by introduce a parent form handle to the API function. Other modal dialog may need extra coding to prevent the freezing is mimic TForm.ShowModal by using TForm.Show method instead.  The following code reveals a possible solution: procedure ShowOwnModal(const aParentForm: TCustomForm; const aForm: TForm;     const Proc: TProc<TModalResult>); begin   aForm.PopupParent := aParentForm;   aForm.Show;   aParentForm.Enabled := False;   try     while aForm.Visible do       Application.HandleMessage;   finally     aParentForm.Enabled := True;   end;   Proc(aForm.ModalResult); end; procedure TChildForm.btnModalFormClick(Sender: TObject); var F: TForm; begin   F := TModalForm.Create(Self);   try     ShowOwnModal(Self, F,       procedure(M: TModalResult)       begin         if M = mrOK then           ShowMessage('OK pressed')         else           ShowMessage('Cancel pressed');       end     );   finally     F.Free;   end; end; We may reuse procedure ShowOwnModal else where in code that need to mimic ShowModal.  Furthermore, the ShowOwnModal strategy allow us to retain the classic coding style of using ShowModal in try...finally block: F := TModalForm.Create(Self); try   if F.ShowModal = mrOK then     ... finally   F.Free; end; Child form running lengthy task freeze whole application If the child form is running a task that takes a while to complete.  Whole application will freeze.  This doesn't seems to be a acceptable behavior as user may have a perception that each child window should work independently from others. A straight solution to the problem is using thread to run the task. Hybrid Mode: Working with SDI and MDI together There is an obvious disadvantage with SDI mode.  If too many SDI form has been open on desktop, user desktop getting messy and become harder for user to find and switch form.  It is also harder for user to identify a window was instantiated from which application.  In this scenarios, user may miss the classic MDI mode that collect all related forms under one roof. Perhaps a solution would be mixing both mode together.  If we make MDI as a primary mode, all new instantiated window will become MDI child form by default.  There is a gadget to switch the MDI child form to SDI form floating on desktop window and vice versa allowing user working on multi-desktop environment.  This design may be a favor solution for user who need SDI in ad-hoc manner. The following code reveal a possible design: type   TfmMDIChild = class(TForm)     Button1: TButton;     Button2: TButton;     procedure Button1Click(Sender: TObject);     procedure Button2Click(Sender: TObject);   protected     procedure CreateParams(var Params: TCreateParams); override;   end; procedure TfmMDIChild.Button1Click(Sender: TObject); begin   Self.FormStyle := fsNormal; end; procedure TfmMDIChild.Button2Click(Sender: TObject); begin   Self.Hide;   // Hide the form first else CreateParams will invoke twice   Self.FormStyle := fsMDIChild; end; procedure TfmMDIChild.CreateParams(var Params: TCreateParams); begin   inherited;   if FormStyle = fsNormal then begin     with Params do begin       ExStyle := Params.ExStyle or WS_EX_APPWINDOW;       WndParent := GetDesktopWindow;     end;   end else if FormStyle = fsMDIChild then begin     Params.Width := Self.Width;     Params.Height := Self.Height;   end; end; The form provide 2 buttons allowing user to switch from MDI to SDI and SDI to MDI.  CreateParams will be invoked if FormStyle has changed. Reference: Newsgroup: borland.public.delphi.objectpascal. Topic: SDI ShowModal howto? Author: Peter Below (TeamB) WebSite: About.Com: Delphi Programming. Topic: A more Powerful Delphi Form, TaskBar Button for every Delphi Form. Author: Zarko Gajic Newsgroup: borland.public.delphi.nativeapi.win32. Topic: How do I add a form to the taskbar? http://delphi.newswhat.com/geoxml/forumhistorythread?groupname=borland.public.delphi.language.delphi.general&messageid=40a1ffb8@newsgroups.borland.com Win32 user interface work is inherently single-threaded Newsgroup: Embarcadero Discussion Forums.Delphi.Language.Win32. Topic: Show a modal form freeze whole SDI application
Read More

Delphi 2009 Essentials in Stockholm, Sweden

From Monday, Feb 23rd until Wednesday Feb 25th, 2008, I'll be in Stockholm, Sweden, for another 3-day RAD Studio 2009 Essentials seminar (spoken in English) covering Delphi topics like the IDE and Language enhancements (Generics, Anonymous Methods), UNICODE, VCL Database Development (DBX4 and DataSnap), Web Development (IntraWeb, ASP.NET 2.0 and AJAX), and XML, SOAP and Web Services. The .NET topics will be covered using Delphi Prism.
Read More

Delphi 2009 Development Essentials PDF (updated)

I'm pleased to announce my Delphi 2009 Development Essentials PDF manual, covering the new features in Delphi 2009 including sections on the Delphi 2009 IDE, Language Enhancements (including the Generics.Defaults and Generics.Collections units), RTL/VCL Enhancements (including coverage of the Ribbon controls), COM Enhancements, DBX4 and DataSnap Enhancements and detailed coverage of Unicode.
Read More

EKON 12 – with a compiler engineer from CodeGear!

Today I visited the page of EKON12, the german conference for Delphi and all the other stuff from CodeGear. And, wooow, there are in the Delphi - area two very, very interesting sessions with Barry Kelly, who is one of the software engineers working on the Delphi compiler at Embarcadero's CodeGear.Here are the details from the EKON-page:New Delphi for Win32 language features Barry Kelly - CodeGear This session provides an overview of the new Delphi language features for RAD Studio 2009 - Unicode, generics and anonymous methods, and some intermediate and more advanced usage scenarios, including generic collections and concurrency idioms enabled by anonymous methods. Advanced anonymous method and generics usage Barry Kelly - CodeGear This session covers more advanced usage scenarios for anonymous methods and generics. For anonymous methods, it will show a survey of closure-passing idioms as well as uses in threading: marshalling, futures, parallel-loop constructs. For generics it will be about escaping from constraint limitations via helper interfaces.
Read More

Repositorios lazarus testing

Thinking about what it is sometimes difficult for beginner Lazarus install a Linux system, I've created some repositories to facilitate their work.Here instructions. Repository Lazarus / testing FPC versions The version of Lazarus is the 0.9.25 revision 15753 compiled with GTK2 in ide and packages, which works perfectly in our labs (delphi 7 is a steroid): PThe version 2.3.1 is freepascal super testing, just to risky (though not confess has given us a single complaint, on the contrary)Following the instructions.Repositories Lazarus and freepascal testing.There's no guarantee at all! Use it at your own risk.Installation instructions:Add the key GPGgpg --keyserver hkp://pgp.mit.edu:11371 --recv-keys F93F2601Add the key to APT system: gpg --export F93F2601 | sudo apt-key add -Check whether the key was built:sudo apt-key list Add the repository to its list of repositoriesYou can use synaptic or edit / etc / apt / sources.list directly. Add the line:deb http://www.petrobox.net/lazarus/ lazarus-testing universeInstall Lazarussudo apt-get updatesudo apt-get install lazarusEnjoy!Namaste.
Read More

Detecting a virtualized environment

CubicDesign on delphi-talk.elists.org recently asked the question: "How do I know/detect if my software is running under Windows [or a virtual environment]?" Well, it turns out that it's a lot harder to tell than you would think. Apparently, the VM (VMware, Xen, Wine, etc.) doesn't really want you to be able to do this. At least not easily.For VMware, there is a decent little C routine called jerry.c that does the trick. Jerry actually uses a simple communication channel that VMware left open. It's not 100% foolproof since the admin can change the channel, but that's not likely going to happen unless the system is specifically designed to be a honeypot. If you're running on a honeypot and still have a legitimate reason for detection, you could look at the much more complex scoopy implementation which inspects how the system is actually virtualized using the SIDT CPU instruction instead of a communication channel. Another reference (red pill) is here.For Wine, there is a special dll entry called wine_nt_to_unix_file_name that they added to NTDLL.DLL. If you use LoadLibrary and GetProcAddress, you can determine if you are under Wine simply by the entry's presence or absence. Of course, if you specifically want to know what version of Wine you're on, you have to use NTDLL_wine_get_version but that function is only available in the bleeding edge version of wine. As an interesting side note, the Jerry test above actually returns true for VMware workstation under Wine. If you really have to know Wine vs VMware, make sure you test Wine first, otherwise you'll get a false positive.For Xen, the most reliable method appears to be to use the WMI interface and get the Bios manufacturer's information. If it's Xen, then you are under some version of Xen. Since I don't have a Xen environment setup currently, I couldn't test this information. I am relying on information published here to make this assertion. At some point, I'll setup a Xen server and play with it.For true VM's like Xen and Vmware, you can also test against the mac address. VMware uses 00-05-69 as the lead and Xen uses 00-16-3E. To see a list of all registered Mac addresses, go here. Keep in mind, that the VMM supports changing the MAC address so this is also easily defeated.At any rate, here's some sample code that uses the described methods to detect VMware and Wine. Enjoy!VMware://credit to chitchat@lycos.jp who discovered this method and//checkvm + IDA Pro ;)//converted from C source posted on www.trapkit.deprocedure TForm1.btnJerryClick(Sender: TObject);var a, b:cardinal;begina:=0;try asm push eax push ebx push ecx push edx mov eax, 'VMXh' mov ecx, 0Ah mov dx, 'VX' in eax, dx mov a, ebx mov b, ecx pop edx pop ecx pop ebx pop eax end;except on E:Exception do ShowMessage(E.Message);end;if a=$564D5868 then begin ShowMessage('In VMware'); case b of 1 : ShowMessage('Express'); 2 : ShowMessage('ESX'); 3 : ShowMessage('GSX'); 4 : ShowMessage('Workstation'); else ShowMessage('Unknown version') end; endelse ShowMessage('Native system');end;Wine:function TForm1.IsRunningWine: boolean;var hnd:THandle; wine_get_version: function : pchar; {$IFDEF Win32} stdcall; {$ENDIF} wine_unix2fn: procedure (p1:pointer; p2:pointer); {$IFDEF Win32} stdcall; {$ENDIF}beginresult:=false;hnd:=LoadLibrary('ntdll.dll');if hnd>32 then begin wine_get_version:= GetProcAddress(hnd, 'wine_get_version'); wine_unix2fn:= GetProcAddress(hnd, 'wine_nt_to_unix_file_name'); if assigned(wine_get_version) or assigned(wine_unix2fn) then result:=true; FreeLibrary(hnd); end;end;
Read More

Delphi 2007 Essentials in Göteborg, Sweden

From Monday, May 19th until Wednesday May 21st, 2008, I'll be in Göteborg, Sweden, for a 3-day Delphi 2007 Essentials seminar (spoken in English) covering topics like the IDE and Language enhancements, VCL Database Development (BDE/dbGo for ADO/TClientDataSet/DBX4 and DataSnap), Web Development (WebBroker/WebSnap but especially IntraWeb, ASP.NET 2.0, and AJAX), and XML, SOAP and Web Services. See the complete agenda online.
Read More

Upgrading OS X to Leopard

Having just returned from two holidays: Colorado with a motorbike (lovely people, stunning scenery) and Firenze with my lovely girlfriend (lovely people, stunning architecture and art) I decided to update my OS X partition to the new shiny leopard. Here are a couple of notes of how I got on, which is hopefully useful to anyone else doing the same. Read More ...
Read More

Professional Development on a MacBook Pro

Like a number of people in the Delphi Community ( Steve Trefethen, Dan Miser ), and the development community as a whole, I now do all my development on a MacBook Pro. I changed to an Apple machine six months ago, for a number of reasons: I needed a new laptop, and the MacBook Pro hardware is so goddamn thin, light, and good looking. I am becoming increasingly disillusioned by Windows, especially the new DRM stuff. I fancied trying out OSX, a change is as good as a rest and all that. Bootcamp and virtualisation allow me to use Windows XP on an Apple laptop. The web is freeing us from being locked into particular Operating Systems. As a professional developer, most of my clients run Windows, and most of my native development is done using Visual Studio 2005 or Delphi 2006, connecting to SQL Server or Oracle backends. I boot my laptop into Windows to do this work, or use Parallels to run it with Mac OSX (when I got my MacBook Pro I got it with 3G of RAM so Windows runs pretty fast inside OSX). Increasingly though I am doing web development, which means I can do much more work inside just OSX. The more I have used OSX, the more I prefer it to Windows, so I have put together this list of tools and hints for doing professional development on OSX: Read More ...
Read More

Rules of Thumb to Make a Project Work

I am currently coming to the end of a large project for one of our clients, where I have been acting in the Business Analyst and Technical Lead roles. It has been a fun and successful project, and I wanted to pass along some of the lessons I’ve learnt (again) about how to make Technical Analysis (and the project as a whole) successful (and also make a checklist for next time around ;–). It must be remembered that IT solutions (and really any business change) are all about two things: Incentives and Trade-offs. Read More ...
Read More