Playing with Windows 7

I was playing around with some (default Windows) programs these days and noticed that taskbar items for those programs show some cool effects whenever there is some progress going on So I decided to write a little unit to take advantage of these effects in one of my Delphi applications.

To take advantage of the new Windows 7 TaskBar API take a look at the ITaskBarList3 interface (an probably 4 for that matter). I have translated the interface to Delphi and added some initialization code and came up with a little unit that exposes a class helper for TCustomForm.

The final user-friendly code looks like this:

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetProgressState(tbpsError);
  SetProgressValue(50, 100);
end;

The SetProgressState and SetProgressValue methods are inserted by the class helper. The example makes the taskbar icon for the window look like a 50% complete red progress bar (indicating an error):

error_task_bar

or something like this for a “indefinite” state (of course the animation is missing so you can’t see the effects):

ind_task_item

If you want to play with the unit, take a look here: Windows 7 TaskBar helper for Delphi (2186)

23 Responses to 'Playing with Windows 7'

  1. magnoz says:

    very nice and simple to use

  2. Daniel says:

    Take a look at my whole set of components. An update will be coming soon ;)

    http://www.gumpi.com/Blog/2009/01/20/Alpha1OfWindows7ControlsForDelphi.aspx

    Greetings,
    Daniel Wischnewski

  3. alex says:

    @Daniel, nice, I was not aware someone has starting creating components already! I’ll have a look.

    Thanks

  4. Bas says:

    Hi Alex,

    Looks nice. Maybe you can check for the Windows version instead of catching the exception?

    Kind regards,
    Bas

    procedure InitializeAPI();
    var
    Unk: IInterface;
    begin
    GlobalTaskBarInterface := nil;

    if CheckWin32Version(6) then // Interface probably supported
    begin
    { Make sure that COM is initialized }
    CoInitializeEx(nil, 0);
    try
    { Obtain an IUnknown }
    Unk := CreateComObject(TASKBAR_CID);

    if Assigned(Unk) then
    begin
    { Cast to the required interface }
    GlobalTaskBarInterface := Unk as ITaskBarList3;

    { Initialize }
    GlobalTaskBarInterface.HrInit();
    end;
    except
    GlobalTaskBarInterface := nil;
    end;
    end;
    end;

  5. alex says:

    @Bas,

    yes, the unit needs more checks of course but I just put it up in a few minutes and did not bother with it’s quality too much. I was interested in the effects :)

    Alex.

  6. Markus says:

    Hello,

    could you please reveal your name or at least make YAPB appear as author on EMBTs blog overview site?

  7. alex says:

    @Markus, the blogs.embt.. site uses a custom feed generated by this blog. It seems to have some problems with that and is not showing up the name properly.

    BTW, my name is Ciobanu Alexandru

  8. Jolyon Smith says:

    Nice, but PLEASE will people STOP using class helpers for this kind of thing!

    You only have to google for “class helper for TCustomForm” to find that other people are using class helpers to “add” functionality to TCustomForm (and that of course isn’t going to find such helpers that people are using in their own projects but not publishing/blogging about) , and since only ONE class helper can be in scope at any time, and it is impossible to qualify multiple class helpers, you can only USE one class helper in any given “consumer” unit.

    There are other ways of doing this:

    1. Modify TCustomForm directly (difficult/tricky/not advisable),

    2. Use simple unit level procedures that take a TCustomForm parameter to operate on (put them in a class and implement as class procedures if you really have to and enjoy unnecessary typing).

    3. If you enjoy hack-ology you can even do it with a fake, member-data-less class type and hard casting.

    4. You can “spoof” a new TForm class (without having to modify VCL code) and introduce the functionality there (see my “Deltics Forms Magic” posts in my blog).

    #2 is far and away the cleanest and safest implementation, at the expense of a little extra verbosity in the API (one extra param – big deal!). #4 is a bit clever but also very safe and avoids that extra param.

    Borland/CodeGear have always said that class helpers were and are not intended for general use – they were created to solve a particular problem in the VCL.

    Maybe one day they will be usable for this sort of thing, but for that either all in-scope class helpers will have to be “merged” into a single resolvable scope, or qualification/helpcasting will need to be supported.

  9. [Off topic] @Jolyon : In the current state of Delphi, class helpers indeed have the shortcoming that only one is allowed – but what would happen if that irritation was alleviated? Then I envision class helpers would be a mighty powerful tool, giving us a taste of “Aspect Oriented” programming – except no data could yet be added…

    Regarding that last point (adding data) : Delphi 2009 and later have extended TObject with 4 extra bytes (TObject. InstanceSize returns 8 now), in which a so-called ‘Monitor’ can be stored (used for native object-level locking).
    Thorsten Engler propsed a (IMHO) small modification to this construct (he coined the word ‘ObjectExtension’), which would make it possible that any data could be added to TObject (even at runtime). This would open up the way to let ‘aspects’ add data to any class too (!)

    Sadly, I’ve heard nothing since (Allan Bauer did participate in that discussion, but never reacted to Thorsten’s proposal) – Let’s hope this improves in one of the next releases of Delphi…

    [On topic] @Alex : Nice work! I can already imaging someone will back-port this to WinXP/Vista in some form, so we can show an application-progress bar in a generic (OS-independent) fashion.

  10. alex says:

    @Patrick,
    It may be dangerous to tie over monitors. I for example use this locking in my code. But that is a good point to think about…

  11. alex says:

    @Jolyon:

    It was a simple example. It’s not like it should be used anywhere. A proper solution should be implemented, for example a normal component or related.

    See Daniel’s post about the components he’s been working on.

  12. Jolyon Smith says:

    Alex et al…. a “simple example” would have been a couple of simple unit level methods.

    Using a class helper:

    a) actually makes the example more complex (for people who haven’t come across class helpers or are using Delphi 7 or earlier)

    b) is likely to result in problems when people try to use the simple example but are already using someone-elses “simple example” using a class helper for TCustomForm and find that *this* simple example doesn’t work OR that using it breaks their previously working consumption of that other “simple example”.

    Using class helpers results in code that is less portable and intrinsically less re-usable.

    Don’t. Do . It.

    Keep “simple examples” SIMPLE!

    As for using class helpers for AO, I think AO is about more than just adding new methods and data to an existing class … it’s more about adding new behaviours to existing class behaviours.

    I don’t see that class helpers can “help” with that, no matter how they may be improved.

    To my mind class helpers are like partial classes…. something to be avoided at all costs if you wish to create a maintainable code base, rather than simply create a code base quickly.

    e.g. When curious about the behaviour of some method on a class (perhaps even just to find the in code documentation) you inspect that class’ source code only to find that the method isn’t introduced by that class or any ancestor/class part and then you have to go spelunking to find it.

    The apologists for class helpers and partial classes then wheel out the “IDE will find the declaration for you”, which is fine and dandy if you are IN the IDE at the time and the IDE is able to find it, which ime is not always the case even with less exotic code constructs/elements, especially if you are viewing the source – in the IDE – outside the context of any particular project context

    imho language features that lean on/require IDE support to be usable have to pass a higher standard of useFULness to justify their existence. Primarily – if there are less obfuscated means by which essentially the same thing may be achieved then their existence is NOT justified.

    Class helpers (outside of the their initial need, as identified by Borland at the time) do not meet that criteria – again imho.

    ditto Partial classes.

  13. alex says:

    @Jolyon

    Thanks, I agree that class helpers are a horrendous invention. Will try to refrain their use in the next examples :)

    P.S. This was the first time I have ever used class helpers.

  14. Jolyon Smith says:

    @alex – no worries. Just trying to be helpful. Class helpers are most disruptive when used in publicly shared code, is all.

    Thanks for the Windows 7 API stuff. I’m not sure I adequately conveyed how useful the actual subject of your post was, in amongst my ranting at the delivery vehicle.

    :)

  15. SagePtr says:

    Great thanks for this unit! Used in my program, and it works very fine!
    But some code I needed to rewrite for compability with WindowsMediaPlayer activeX and removed Form helper, changed Handle to Application.Handle (Form1.Handle didn’t worked for me)

  16. alex says:

    You should better try to use some components specific for Windows 7. Daniel posted a link to http://www.gumpi.com/Blog/2009/01/20/Alpha1OfWindows7ControlsForDelphi.aspx component pack.

  17. buletov says:

    Hi, nice example, it all works, but I can not use TOpenDialog in my project once I include W7TaskBar.pas unit? Any hints?

  18. Tim Ellis says:

    Hey I was just wondering if the link which is for Delphi 7, Delphi 2006, Delphi 2007, and Delphi 2009, could be converted so that I can use it on my Delphi 5 or possibly C Builder 5?

    Is this possible??
    Or is there another solution on how I can achieve it?

  19. alex says:

    The link points to the site of the person developing the components. It’s not my work. But, you should be aware that Delphi 5 is really old and very few people would bother supporting it. Delphi 7 is usually supported because it’s still being used a lot in our days (and considered an iconic release).

    Cheers.

  20. Tim Ellis says:

    @alex, ok Thanks for the advice.
    As I see the .pas is available for download but if could it be possible to upload the entire project to see how it all fits together?
    Possibly in a zip?

  21. Anonymous says:

    Thanks, this is very usefull!
    However, to avoid delphi 7 compiler errors, you should not use UInt64.
    When I cahenged the line below, it worked perfectly.

    procedure SetProgressValue(hwnd: THandle; ullCompleted: int64; ullTotal: int64); stdcall;

  22. alex says:

    Cool, thanks for the tip. I haven’t used D7 for 5-6 years now so I don’t really write D7 compatible code.

Trackbacks/Pingbacks

Leave a Reply

Your email address will not be published.