{"id":222,"date":"2008-08-24T12:24:43","date_gmt":"2008-08-24T00:24:43","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=222"},"modified":"2009-08-07T14:57:10","modified_gmt":"2009-08-07T02:57:10","slug":"more-delticsforms-magic","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/222\/","title":{"rendered":"More Deltics.Forms Magic"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Last time I introduced a bit of skullduggery with my <strong>Deltics.Forms<\/strong> unit as a way to easily &#8220;inject&#8221; a new <strong>TForm<\/strong> class into my projects.\u00a0 We used this new class to add public property that we could use to add a size grip control to any form.<\/p>\n<p>This time we shall introduce some more code the <strong>Deltics.Forms<\/strong> unit in order to add some frequently useful capabilities to the <strong>Application<\/strong> object.<\/p>\n<p><!--more--><\/p>\n<h3>What Is Application?<\/h3>\n<p>As you are no doubt aware, the <strong>Application<\/strong> object is a single object declared in the VCL <strong>Forms<\/strong> unit (but instantiated in the <strong>Controls<\/strong> unit), and is the heart of most Delphi applications.\u00a0 It&#8217;s a well named object in that respect.<\/p>\n<p>Unfortunately, the <strong>Application<\/strong> object is of a fixed class &#8211; <strong>TApplication<\/strong>.\u00a0 This class cannot be extended or replaced with a class that might, for example, contain extensions that you might wish to have included in all of <em><span style=\"text-decoration: underline;\">your<\/span><\/em> applications.<\/p>\n<p>It was a surprise to me to find some years ago, for example, that the <strong>Application<\/strong> object in a <em>Windows Application<\/em> project has nothing in common with the <strong>Application<\/strong> object in a <em>Windows Service Application<\/em> project, for example &#8211; one is a <strong>TApplication<\/strong> the other a <strong>TServiceApplication<\/strong> and both derive directly from <strong>TComponent<\/strong>.<\/p>\n<p>It is also something of a surprise that the basic capabilities of the <strong>TApplication<\/strong> object have not evolved over the years to provide better access to version information or command line parameters, for example.\u00a0 But despite the rather closed nature of <strong>TApplication<\/strong>, the <strong>Deltics.Forms<\/strong> unit provides the platform for adding these things ourselves.<\/p>\n<h3>Helpers For The Rest Of Us<\/h3>\n<p><a href=\"http:\/\/melander.dk\/articles\/versioninfo\/\">A post on MelanderBlog<\/a> shows how to do this with a class helper, but these things are a) a bad idea (imho) and b) not available to anyone using Delphi 2006 (I think) or earlier.<\/p>\n<p>Class helpers are intrinsically a &#8220;bad idea&#8221; because only one can be in scope at a time, so if you had some other code that introduced another helper for the same class (perhaps without you even knowing it) you have a problem &#8211; one or other of those helpers will &#8220;disappear&#8221; and code that compiled perfectly well before suddenly stops compiling.<\/p>\n<p>But we can achieve the same result more safely without class helpers.\u00a0 They are merely syntactic sugar for a time-tested technique that is actually safer (and more opaque) than the formalised &#8220;helper&#8221; mechanism.\u00a0 &#8220;Safer&#8221; because code that uses the non-helper technique cannot cause problems with other code using the same technique in the way that formal class helpers suffer from.<\/p>\n<p>As we saw, the <strong>Deltics.Forms<\/strong> unit introduces a new <strong>TForm<\/strong> class that is compatible with the VCL <strong>TForm<\/strong> class.\u00a0 We can also introduce a new <strong>TApplication<\/strong> class that is similarly compatible with the VCL <strong>TApplication<\/strong>:<\/p>\n<pre class=\"delphi\">  type\r\n    TApplication = class(Forms.TApplication);<\/pre>\n<p>A problem here however is that we cannot alter the fact that the <strong>Controls<\/strong> unit instantiates a <strong><em>Forms<\/em>.TApplication<\/strong>, and knows nothing of this new <strong>TApplication<\/strong> class.\u00a0 Our new <strong>TApplication<\/strong> class must therefore observe the same limitations of a class helper &#8211; we cannot introduce new member data and we cannot override any virtual methods.<\/p>\n<p>Another problem is that the <strong>Application<\/strong> variable in <strong>Forms<\/strong> is also declared using the <strong><em>Forms<\/em>.TApplication<\/strong> class, so if we are to refer to <strong>Application<\/strong> as our new class we&#8217;ll need just a little more smoke and mirrors in <strong>Deltics.Forms<\/strong>:<\/p>\n<pre class=\"delphi\">interface\r\n\r\n  function Application: TApplication;\r\n\r\nimplementation\r\n\r\n  function Application: TApplication;\r\n  begin\r\n    result := TApplication(Forms.Application);\r\n  end;<\/pre>\n<p>We still need to do a little work in our actual projects to make use of this new <strong>Application<\/strong> object &#8211; the <strong>Forms<\/strong> unit reference in our project source needs to be replaced by a reference to <strong>Deltics.Forms<\/strong>.\u00a0 It is far less likely that we will need to have both <strong>Forms<\/strong> and <strong>Deltics.Forms<\/strong> referenced in the project source, but if that is necessary then instead of replacing the <strong>Forms<\/strong> reference, add the <strong>Deltics.Forms<\/strong> unit <em><span style=\"text-decoration: underline;\">after<\/span><\/em> it in the uses list.<\/p>\n<p>With these fundamentals in place, we can now &#8211; with a little care &#8211; add some useful new capabilities to the <strong>Application<\/strong> object.\u00a0 For starters let&#8217;s add a <strong>VersionInfo<\/strong> property.<\/p>\n<h3>Adding Properties Without Adding Member Data<\/h3>\n<p>The &#8220;care&#8221; that we need to exercise is as previously mentioned &#8211; we cannot add member data, and we cannot override any virtual methods.  So how can we add any new properties?<\/p>\n<p>In this case, things are made a great deal simpler by the fact that the <strong>Application<\/strong> object is a singleton &#8211; there is only ever one of them in any project.\u00a0 There is also only ever one <strong>Deltics.Forms<\/strong> unit in an application, so we can declare variables in the unit to hold the data we would otherwise introduce as member data in the <strong>Application<\/strong> object.<\/p>\n<p>These unit variables (sometimes mistakenly referred to as global variables &#8211; Delphi actually has no such thing) need only be declared in the implementation section as they are exposed only via our extensions to the <strong>TApplication<\/strong> class:<\/p>\n<pre class=\"delphi\">interface\r\n\r\n  uses\r\n    Forms,\r\n    Deltics.VersionInfo;\r\n\r\n  type\r\n    TApplication = class(Forms.TApplication)\r\n    private\r\n      function get_VersionInfo: TVersionInfo;\r\n    public\r\n      property VersionInfo: TVersionInfo read get_VersionInfo;\r\n    end;\r\n\r\nimplementation\r\n\r\n  var\r\n    _VersionInfo: TVersionInfo = NIL;\r\n\r\n  function TApplication.get_VersionInfo: TVersionInfo;\r\n  begin\r\n    if NOT Assigned(_VersionInfo) then\r\n      _VersionInfo := TVersionInfo.Create;\r\n\r\n    result := _VersionInfo;\r\n  end;\r\n\r\ninitialization\r\n  \/\/ NO-OP\r\n\r\nfinalization\r\n  FreeAndNIL(_VersionInfo);\r\n\r\nend.<\/pre>\n<p>Obviously the above code is not entirely complete, only showing the changes and additions needed to add the <strong>VersionInfo<\/strong> property.\u00a0\u00a0 The details of the <strong>TVersionInfo<\/strong> implementation aren&#8217;t particularly important &#8211; there are lots of implementations of such a class freely available (the one I use will be available shortly &#8211; more on that in my next post).<\/p>\n<p>So we can add some useful new properties, but I also mentioned previously that we could introduce some behavioural changes inspired by an article about <a href=\"http:\/\/www.installationexcellence.com\/articles\/VistaWithDelphi\/Original\/Index.html#Wheres_My_Induction\" target=\"_blank\">Creating Vista Ready Applications<\/a>.<\/p>\n<p>How do we do that without overriding any virtuals?<\/p>\n<h3>Changing Behaviour Without Overriding<\/h3>\n<p>The original article about <a href=\"http:\/\/www.installationexcellence.com\/articles\/VistaWithDelphi\/Original\/Index.html\" target=\"_blank\">creating Vista ready applications<\/a> mentioned adding some code to a <strong>FormCreate<\/strong> event (presumably the <strong>FormCreate<\/strong> of the main form) to modify the characteristics of the hidden application window (<strong>Application.Handle<\/strong>). Not only is this a little inelegant (using an event in one form to make changes to some other form), it is also going to be cumbersome having to do this in every application.<\/p>\n<p>Now that we have our own <strong>TApplication<\/strong> class we can introduce these changes once and have that change apply in our applications simply by using the <strong>Deltics.Forms<\/strong> unit.\u00a0 The key method in this case is the <strong>TApplication.CreateForm<\/strong> method.\u00a0 This is not virtual and typically isn&#8217;t even referenced outside of our project source file anyway, so replacing this method with our own implementation is quite safe:<\/p>\n<pre class=\"delphi\">interface\r\n\r\n  type\r\n    TApplication = class(Forms.TApplication)\r\n    public\r\n      procedure CreateForm(InstanceClass: TComponentClass; var Reference);\r\n    end;\r\n\r\nimplementation\r\n\r\n  procedure TApplication.CreateForm(InstanceClass: TComponentClass;\r\n                                    var Reference);\r\n  var\r\n    bMainForm: Boolean;\r\n  begin\r\n    bMainForm := (Forms.Application.MainForm = NIL);\r\n\r\n    inherited;\r\n\r\n    if NOT bMainForm\r\n    \u00a0or NOT MainFormOnTaskBar\r\n     or NOT (Application.MainForm.InheritsFrom(TForm)) then\r\n      EXIT;\r\n\r\n    ShowWindow(Application.Handle, SW_HIDE);\r\n    SetWindowLong(Application.Handle,\r\n                  GWL_EXSTYLE,\r\n                  GetWindowLong(Application.Handle, GWL_EXSTYLE)\r\n                   and NOT WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);\r\n    ShowWindow(Application.Handle, SW_SHOW);\r\n\r\n    TForm(Application.MainForm).RecreateWnd;\r\n  end;<\/pre>\n<p>Again the above code showns only the changes and additions and is not complete.<\/p>\n<p>The <strong>MainFormOnTaskBar<\/strong> property is another property that I introduce to <strong>TApplication<\/strong> in a similar way that <strong>VersionInfo<\/strong> is introduced.\u00a0 It is deliberately named for a property that is introduced in Delphi 2007, and is used to determine whether the form shown on the Task Bar will be the hidden application hidden window (normal Delphi behaviour) or the MainForm window (the new behaviour introduced here).<\/p>\n<p>Our new <strong>CreateForm<\/strong> takes care of modifying the characteristics of the hidden application window when creating the <strong>Application<\/strong> <strong>MainForm<\/strong> (by design in the VCL, the <strong>MainForm<\/strong> is the first form created via a call to the <strong>Application.CreateForm<\/strong> method).<\/p>\n<p>We also only apply these changes if the <strong>MainForm<\/strong> is derived from our <strong><em>Deltics.Forms<\/em>.TForm<\/strong> (otherwise it won&#8217;t be aware of our Vista readiness changes).<\/p>\n<p>Finally, if the changes are applied, the <strong>MainForm<\/strong> window itself needs to be re-created to ensure that the necessary window parameter changes are applied that will present that form on the task bar.\u00a0 These parameter changes are applied in an override of <strong>CreateParams<\/strong> in the <strong><em>Deltics.Forms<\/em>.TForm<\/strong> class:<\/p>\n<pre class=\"delphi\">  procedure TForm.CreateParams(var Params: TCreateParams);\r\n  begin\r\n    inherited;\r\n\r\n    Params.Style := Params.Style and (not WS_CHILD) or WS_GROUP or WS_TABSTOP;\r\n\r\n    if ShowOnTaskBar then\r\n      Params.ExStyle := Params.ExStyle and (NOT WS_EX_TOOLWINDOW) or WS_EX_APPWINDOW;\r\n  end;<\/pre>\n<p>Time for a quick confession &#8211; I originally did all this stuff quite a while ago now and rather foolishly neglected to document a lot of the finer details.  The changes to <strong>Params.Style<\/strong> being one such undocumented detail.<\/p>\n<p>It solves a puzzling problem &#8211; changing the caption of an <strong>AutoSize<\/strong> <strong>TLabel<\/strong> on a <strong>Deltics.Form.TForm<\/strong> results in that form changing size (a result of <strong>AlignControls<\/strong>).\u00a0 Setting these Style params fixes that behaviour.\u00a0 I must have reasoned this out myself at the time or come across some information that gave me the insight, but I&#8217;ve since lost that information.\u00a0 I hate code that &#8220;just works&#8221;, so if anyone can explain why this is necessary I would be very grateful.<\/p>\n<p>One last thing we can do is introduce the <strong>WM_SYSCOMMAND<\/strong> message handling described in the Vista readiness article:<\/p>\n<pre class=\"delphi\">  procedure TForm.WMSysCommand(var aMessage: TWMSysCommand);\r\n  begin\r\n    if Application.MainFormOnTaskBar then\r\n    begin\r\n      case (aMessage.CmdType and $FFF0) of\r\n        SC_MINIMIZE : begin\r\n                        ShowWindow(Handle, SW_MINIMIZE);\r\n                        aMessage.Result := 0;\r\n                      end;\r\n\r\n        SC_RESTORE  : begin\r\n                        ShowWindow(Handle, SW_RESTORE);\r\n                        aMessage.Result := 0;\r\n                      end;\r\n      else\r\n        inherited;\r\n      end;\r\n    end\r\n    else\r\n      inherited;\r\n  end;<\/pre>\n<h3>Code In Preparation<\/h3>\n<p>There is no code to download this time &#8211; for the time being &#8211; other than that provided in the article above.<\/p>\n<p>I am currently in the process of setting up a <a href=\"http:\/\/www.codeplex.com\" target=\"_blank\">CodePlex<\/a> hosted project where I shall release all code that I cover in my blog &#8211; and more.  I have decided to release under the X11 (a.k.a &#8220;MIT&#8221;) License.  I find the Team Foundation version control model as unpalatable as SVN (as I understand it CodePlex projects can be accessed using Team Explorer or SVN) &#8211; the tight coupling between repository and local workspace is particularly uncomfortable to me.<\/p>\n<p>But this, and other aspects of the CodePlex experience, is something I shall blog about separately.<\/p>\n<p>Once the project is available I shall post the details in my blog.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> Last time I introduced a bit of skullduggery with my Deltics.Forms unit as a way to easily &#8220;inject&#8221; a new TForm class into my projects.\u00a0 We used this new class to add public property that we could use to add a size grip control to any form. This time we shall introduce some more code [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4],"tags":[45,292,44,46,41,43],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-3A","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":208,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/208\/","url_meta":{"origin":222,"position":0},"title":"When Type(name)s Collide","date":"19 Aug 2008","format":false,"excerpt":"It's funny how this happens.\u00a0 I was already intending to write about the way I have exploited like-type names to my advantage, and then noticed that the same subject cropped up recently in the re-invented Delphi forums.\u00a0 In that instance, the issue though was a problem, and one that I'm\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":295,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/295\/","url_meta":{"origin":222,"position":1},"title":"Making A Splash","date":"04 Sep 2008","format":false,"excerpt":"OK, so who hasn't done this a million times - adding a splash screen to a Delphi application.\u00a0 I've seen any number of \"easy\" ways to do this but during a session at Tech Ed '08 this week I saw the latest in splash screen technology from Microsoft:\u00a0 A SplashScreen\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1833,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1833\/","url_meta":{"origin":222,"position":2},"title":"Importing an Android Class For Use in Delphi","date":"03 Oct 2013","format":false,"excerpt":"In a previous post I noted the absence of the BatteryManager class in the AndroidAPI.JNI units. This class contains some constants useful when reading battery information. I showed how to use a suitably massaged literal in place of these missing constants, but in response to observations from Paul and Brian\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1503,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1503\/","url_meta":{"origin":222,"position":3},"title":"Sharing Code Across Platforms in Oxygene","date":"22 Aug 2013","format":false,"excerpt":"There seems to be a perception among some people that Delphi is in the unique position of allowing developers to share and re-use code across the various platforms that it's compiler can now (and will soon) target. But this is not the case. Oxygene has had this capability right from\u2026","rel":"","context":"In &quot;Cooper&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1938,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1938\/","url_meta":{"origin":222,"position":4},"title":"Menu Items, APIs, Crackers and Hackers","date":"16 Oct 2013","format":false,"excerpt":"Francois Piette recently posted a solution to obtaining the screen position of a menu item involving using a \"hacker\" class. There is however a safer, more direct mechanism which I hope Francois won't mind me sharing and a far less safe related hacking technique that his post brought to mind.\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":160,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/160\/","url_meta":{"origin":222,"position":5},"title":"MultiCast Events &#8211; Conclusion","date":"11 Aug 2008","format":false,"excerpt":"Drawing this subject to a close (finally!), here's the concluding post I promised, including the fully documented and finished implementation that has been serving me well for almost 2 years.\u00a0 The finished implementation incorporates a number of refinements to the core framework, and those are what we shall briefly look\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/222"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=222"}],"version-history":[{"count":14,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/222\/revisions"}],"predecessor-version":[{"id":238,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/222\/revisions\/238"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=222"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=222"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}