View Full Version : [Windows7] ProgressBar in TaskBar in Delphi 7
Atak_Snajpera
7th May 2010, 21:40
http://img232.imageshack.us/img232/9272/new1m.png
What components do you use to achieve above result.
LoRd_MuldeR
7th May 2010, 21:45
http://img232.imageshack.us/img232/9272/new1m.png
What components do you use to achieve above result.
None. I coded it myself, directly from the Win32 API documentation :)
Unit_Win7Taskbar.pas (http://code.google.com/p/mulder/source/browse/trunk/LameXP/src/Unit_Win7Taskbar.pas)
BTW: You should call InitializeTaskbarAPI() as soon as your main window received the RegisterWindowMessage('TaskbarButtonCreated') message.
Atak_Snajpera
7th May 2010, 22:05
Do you also have a demo? I learn faster with sample code :)
LoRd_MuldeR
7th May 2010, 22:08
Here's the "Demo" application :D
http://code.google.com/p/mulder/source/browse/trunk/LameXP/src/
But it's really not hard to use:
* Once Windows has indicated that the Win7 taskbar icon is ready (by sending a 'TaskbarButtonCreated' message) you call InitializeTaskbarAPI() once.
* Then you enable the progress indicator via SetTaskbarProgressState(TBPF_NORMAL) and update the update the progress value by calling SetTaskbarProgressValue().
* You can then "hide" the progress indicator via SetTaskbarProgressState(TBPF_NOPROGRESS). Optionally overlay icons can be added via SetTaskbarOverlayIcon().
Atak_Snajpera
7th May 2010, 22:28
* Once Windows has indicated that the Win7 taskbar icon is ready (by sending a 'TaskbarButtonCreated' message) you call InitializeTaskbarAPI() once.
I added InitializeTaskbarAPI() to form1.oncreate . is this a right place?
LoRd_MuldeR
7th May 2010, 22:41
I added InitializeTaskbarAPI() to form1.oncreate . is this a right place?
The proper method is to add an ApplicationsEvents component and call it from the OnMessage() callback.
Of course you only call it if Msg.message == RegisterWindowMessage('TaskbarButtonCreated'), which avoids calling it too early or calling it on unsupported OS.
However at some point I encountered the problem that my application didn't receive a "TaskbarButtonCreated" from some reason I couldn't figure out :rolleyes:
Now my ugly workaround is:
procedure TForm_Main.FormCreate(Sender: TObject);
begin
[...]
{Workaround for Windows 7 taskbar}
if (ComputerInfo.OS.VersionMajor = 6) and (ComputerInfo.OS.VersionMinor > 0) then
begin
InitializeTaskbarAPI;
end;
[...]
end;
You should only do that, if you can't get it to work with the "proper" method...
Atak_Snajpera
7th May 2010, 22:46
I've noticed that taskbar sometimes flashes (it disappears for ~100ms). Did you have the same problem?
LoRd_MuldeR
7th May 2010, 22:50
I've noticed that taskbar sometimes flashes (it disappears for ~100ms). Did you have the same problem?
Uhm, nope.
You aren't calling SetTaskbarProgressState() and/or SetTaskbarProgressValue() like a hundred times per second? ^^
Atak_Snajpera
7th May 2010, 23:14
this is what i see
http://www.mediafire.com/file/xijzmztqd2t/flashingtaskbar.mp4
You aren't calling SetTaskbarProgressState() and/or SetTaskbarProgressValue() like a hundred times per second? ^^
How do you update progress? by using Timer?
LoRd_MuldeR
8th May 2010, 00:03
How do you update progress? by using Timer?
Nope. In LamXP I update the (global) progress every time a file is completed, because I do batch processing of multiple files (in parallel).
And in my x264 GUI the progress is updated every time the percentage reported by x264 increases by at least one.
TFM_TheMask
11th May 2010, 15:57
You can also use the following components:
http://www.inniosoft.co.cc/devstation/topic/windows-7-support-for-delphi
It has also a delphi 7 package.
LoRd_MuldeR
11th May 2010, 20:39
this is what i see
http://www.mediafire.com/file/xijzmztqd2t/flashingtaskbar.mp4
Looks strange.
You call SetTaskbarProgressState() for one single time and then you call only SetTaskbarProgressValue() now and then to update the progress. Don't you?
LoRd_MuldeR
11th May 2010, 21:00
Here's a simplistic "Demo" application:
http://dl.dropbox.com/u/3191920/Beta/Misc/TaskbarDemo.2010-05-11.zip
Atak_Snajpera
11th May 2010, 23:07
I did as you advised...
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;var Handled: Boolean);
begin
if Msg.message = RegisterWindowMessage('TaskbarButtonCreated') then InitializeTaskbarAPI;
end;
...and it does not work. Only your 'ugly' method works.
LoRd_MuldeR
11th May 2010, 23:13
You should probably better do it like in the example app:
type
TForm1 = class(TForm)
private
Msg_TaskbarButtonCreated: Cardinal;
public
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Msg_TaskbarButtonCreated := RegisterWindowMessage('TaskbarButtonCreated');
end;
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
if Msg.message = Msg_TaskbarButtonCreated then
begin
if Unit_Win7Taskbar.InitializeTaskbarAPI then
begin
Edit1.Text := 'Win7 Taskbar API initialized successfully :-)';
end else begin
Edit1.Text := 'Win7 Taskbar API failed to initialize :-(';
end;
Handled := True;
end;
end;
This avoids the overhead for re-calling RegisterWindowMessage() for every single message that is processed by your application.
And that method works for me, in the example application I posted. It didn't work in one of my more complex apps for a reason I couldn't figure out.
Unfortunately the Windows API documentation clearly says you should wait for the "TaskbarButtonCreated" message :rolleyes:
When an application displays a window, its taskbar button is created by the system. When the button is in place, the taskbar sends a TaskbarButtonCreated message to the window. Your application should call RegisterWindowMessage(L"TaskbarButtonCreated") and handle that message in its wndproc. That message must be received by your application before it calls any ITaskbarList3 method.
Atak_Snajpera
11th May 2010, 23:28
Ok. I will do as you say. Thanks!
Atak_Snajpera
12th May 2010, 15:34
Ok i did everything exactly like you said
private
Msg_TaskbarButtonCreated: Cardinal;
procedure TForm1.FormCreate(Sender: TObject);
begin
Msg_TaskbarButtonCreated := RegisterWindowMessage('TaskbarButtonCreated');
end;
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if Msg.message = Msg_TaskbarButtonCreated then
begin
if w7taskbar.InitializeTaskbarAPI then form1.caption := 'Win7 Taskbar API initialized successfully :-)'
else form1.caption := 'Win7 Taskbar API failed to initialize :-(';
Handled := True;
end
else form1.Caption:='dupa';
end;
and i see this
http://img200.imageshack.us/img200/5396/new1lt.png
Value of Msg_TaskbarButtonCreated is 49299 so this basically means that Msg.message is never equal to 49299.
LoRd_MuldeR
13th May 2010, 13:41
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if Msg.message = Msg_TaskbarButtonCreated then
begin
if w7taskbar.InitializeTaskbarAPI then form1.caption := 'Win7 Taskbar API initialized successfully :-)'
else form1.caption := 'Win7 Taskbar API failed to initialize :-(';
Handled := True;
end
else form1.Caption:='dupa';
end;
That's wrong :eek:
If your window receives the Msg_TaskbarButtonCreated message, then it tries to initilaize the Win7 Taskbar API. That's still okay.
But: For any other message (received either before or after Msg_TaskbarButtonCreated) the caption of your Form is reset to "dupa" immediately!
So even if your app received Msg_TaskbarButtonCreated and successfully processed it, the caption is back to "dupa" on the next message received!
Conclusion: With that code the caption of the Form doesn't tell you anything about the receiving of Msg_TaskbarButtonCreated ;)
Try this:
procedure TForm1.FormCreate(Sender: TObject);
begin
Msg_TaskbarButtonCreated := RegisterWindowMessage('TaskbarButtonCreated');
form1.Caption:='dupa';
end;
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if Msg.message = Msg_TaskbarButtonCreated then
begin
if w7taskbar.InitializeTaskbarAPI then form1.caption := 'Win7 Taskbar API initialized successfully :-)'
else form1.caption := 'Win7 Taskbar API failed to initialize :-(';
Handled := True;
end
end;
Value of Msg_TaskbarButtonCreated is 49299 so this basically means that Msg.message is never equal to 49299.
The ''TaskbarButtonCreated" message doesn't have a fixed value. Therefore it cannot be hardcoded to a specific value.
Instead the message must be registered at runtime and the resulting value may be different each time...
Atak_Snajpera
13th May 2010, 15:33
Yes! you are right obviously! However I think I may have the same problem like you metioned before. It works with simple apps but with more complicated nothing is hapening. On my simpler project Form1.Caption changes to "..initialized successfully" and I see progress in task bar. However the same code in Ripbot264 does not work. form1.caption does not change to anything :(. It simply remains unchanged.
LoRd_MuldeR
13th May 2010, 16:27
Yes! you are right obviously! However I think I may have the same problem like you metioned before. It works with simple apps but with more complicated nothing is hapening. On my simpler project Form1.Caption changes to "..initialized successfully" and I see progress in task bar. However the same code in Ripbot264 does not work. form1.caption does not change to anything :(. It simply remains unchanged.
My assumption would be that in "more complex" applications the message simply is received before the VCL has set up the "OnMessage" callback method :rolleyes:
So while not being entirely "correct", calling InitializeTaskbarAPI() in the "OnShow" callback method on all supported OS (Windows NT >= 6.1) seems to be the most reliably option.
Even if that fails for some reason, nothing evil will happen. In the worst case InitializeTaskbarAPI() fails and returns false.
In that case all the other Taskbar functions , like SetTaskbarProgressState() and SetTaskbarProgressValue(), will simply be NOP's (i.e. they do nothing at all).
I have implemented those functions in a way to check for the availability of the Taskbar API first. They return immediately, if the Taskbar API wasn't successfully initialized yet.
So if something goes wrong with initialization, the user will have to live without the Taskbar progress. But that's it ;)
mrdutchie
17th April 2011, 02:33
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if Msg.message = Msg_TaskbarButtonCreated then
begin
if w7taskbar.InitializeTaskbarAPI then form1.caption := 'Win7 Taskbar API initialized successfully :-)'
else form1.caption := 'Win7 Taskbar API failed to initialize :-(';
Handled := True;
end
else form1.Caption:='dupa';
end;
That's wrong :eek:
If your window receives the Msg_TaskbarButtonCreated message, then it tries to initilaize the Win7 Taskbar API. That's still okay.
But: For any other message (received either before or after Msg_TaskbarButtonCreated) the caption of your Form is reset to "dupa" immediately!
So even if your app received Msg_TaskbarButtonCreated and successfully processed it, the caption is back to "dupa" on the next message received!
Conclusion: With that code the caption of the Form doesn't tell you anything about the receiving of Msg_TaskbarButtonCreated ;)
Try this:
procedure TForm1.FormCreate(Sender: TObject);
begin
Msg_TaskbarButtonCreated := RegisterWindowMessage('TaskbarButtonCreated');
form1.Caption:='dupa';
end;
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if Msg.message = Msg_TaskbarButtonCreated then
begin
if w7taskbar.InitializeTaskbarAPI then form1.caption := 'Win7 Taskbar API initialized successfully :-)'
else form1.caption := 'Win7 Taskbar API failed to initialize :-(';
Handled := True;
end
end;
The ''TaskbarButtonCreated" message doesn't have a fixed value. Therefore it cannot be hardcoded to a specific value.
Instead the message must be registered at runtime and the resulting value may be different each time...
I'm playing with this, but doesn't seem to work
I tried
WM_TASKBARBUTTONCREATED := RegisterWindowMessage('TaskbarButtonCreated');
mainform.Caption:='test form';
and
mainform.Caption:='test form';
WM_TASKBARBUTTONCREATED := RegisterWindowMessage('TaskbarButtonCreated');
but it never does this
if Message.Msg = WM_TASKBARBUTTONCREATED then
begin
// they are never equal to execute this
CoCreateInstance(CLSID_TaskbarList, nil, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, FTaskBar);
LoRd_MuldeR
17th April 2011, 12:26
It's important that you do the "WM_TASKBARBUTTONCREATED := RegisterWindowMessage('TaskbarButtonCreated')" early in your initialization and that WM_TASKBARBUTTONCREATED is a member variable (or a global one), so that WM_TASKBARBUTTONCREATED is already assigned to the proper value when the first messages are processed. If nothing else helps, try calling InitializeTaskbarAPI() simply from your Main form's OnShow() callback - without waiting for the 'TaskbarButtonCreated' message. In that case you may want to check the Windows version first, i.e. only do the initialization on Windows 7 (and later).
See also:
http://forum.doom9.org/showpost.php?p=1398170&postcount=6
mrdutchie
17th April 2011, 14:59
Have no clue why it ain't working.
I used the unit part this time, and then it works just fine..
but straight the code in my mainform, does not work.
I am running Delphi XE btw.
Very strange.
LoRd_MuldeR
17th April 2011, 22:08
Have no clue why it ain't working.
I used the unit part this time, and then it works just fine..
but straight the code in my mainform, does not work.
I am running Delphi XE btw.
Very strange.
Hard to say without seeing your code. Also I am still on good old Delphi 7, but I guess may code should still work under newer Delphi.
(After all it's just a very simple wrapper around the Win32 API)
mrdutchie
21st May 2011, 13:51
Well, I got everything to work.
Although I have 1 issue and can't figure out, why it's doing that.
Normally the Statusbar is clear, and as soon as you use it
the progressbar works... but how do you clear it when it's done??
I can clear the bar to go back to position 0 but I always
see a little bit of green on the left side of it.
if I use -1 then the bar stays full green.
I am using
Settaskbarprogressvalue (0,0)
Settaskbarprogressvalue (0,100)
Settaskbarprogressvalue (0,1)
Settaskbarprogressvalue (-1,0)
etc. but it won't clear it completely.
What am I missing herE?
LoRd_MuldeR
21st May 2011, 13:58
Before you use SetTaskbarProgressValue(), you first have to call SetTaskbarProgressState(tbpsNormal) in order to show the progress indicator.
Then you can update the progress with calls to SetTaskbarProgressValue(). Finally you call SetTaskbarProgressState(tbpsNone) to remove the progress indicator.
mrdutchie
21st May 2011, 14:13
Thank you for your help.
That worked just fine.
Had to changeit to tbpsNormal instead of the one you gave me.
MilesAhead
5th August 2011, 21:48
None. I coded it myself, directly from the Win32 API documentation :)
Unit_Win7Taskbar.pas (http://code.google.com/p/mulder/source/browse/trunk/LameXP/src/Unit_Win7Taskbar.pas)
BTW: You should call InitializeTaskbarAPI() as soon as your main window received the RegisterWindowMessage('TaskbarButtonCreated') message.
I've been looking for ITaskbarList3 code I could call from various languages. I have code that works in AutoIt3, AHK, C# but they are all different. It would be great if you could wrap the unit as a std call DLL that could be called from any language that can load and run a DLL. AutoIt3 and AHK both have DllCall() and even FreeBASIC could use it. I don't have Delphi installed to write the wrapper myself so I'm hoping I could persuade you to release a 32 bit DLL for it?
http://smileys.smilchat.net/emoticon/expressions/heureux/3d-yep.gif
LoRd_MuldeR
5th August 2011, 21:55
Well, the "native" COM interface is documented in MSDN, so creating a DLL with a simple C interface with Visual C++ should be straight forward.
Also with Delphi you can create DLL's and export functions in a C-style way as well...
MilesAhead
6th August 2011, 21:05
I understand. I was asking because I don't have either C++ or Delphi installed. I'm stuck way back with Delphi 5 Pro and don't have the money to upgrade. So now I'm using free tools. Guess I'll put D5 on eventually to get the DLL since it's a more generally useful package. I used to do VC++ shell extensions using ATL3 but I've had my head out of all those calling conventions and who cleans up the stack for a long time. Been playing with hotkey stuff where I can code something in an hour. :)
IIRC all I'd have to do is make a library of stdcall functions that use your unit and called them, put 'em in export list. I was hoping to avoid install of D5 for the hundreth time with Jedi library addons. Man that code is old! :)
edit: btw thanks for publishing the unit. I browsed though it and it looks like it should work. The only DLL I found was one where the only progressbar functions that work correctly are the green normal color. Plus it's implemented with C calling conventions instead of stdcall. Aighh!!
LoRd_MuldeR
6th August 2011, 21:09
You can get Visual Studio Express for free. You will also need the latest Platform SDK for ITaskBarList3.
As long as you export the functions from the DLL with extern "C", they should be callable from most programming languages. ATL is not needed.
The calling convention can be changed to "stdcall" by using the __stdcall keyword, if desired...
LoRd_MuldeR
6th August 2011, 23:29
Attached is a DLL which exports the Windows 7 Taskbar API functions as a simple C interface (using "stdcall" calling convention).
There also is a simple Delphi sample application included, which shows how to use the DLL.
MilesAhead
7th August 2011, 00:29
Attached is a DLL which exports the Windows 7 Taskbar API functions as a simple C interface (using "stdcall" calling convention).
There also is a simple Delphi sample application included, which shows how to use the DLL.
Thank you. I'll try it out with AutoIt3 today and report results. I appreciate the code. :)
MilesAhead
7th August 2011, 01:22
Works very easily with DllCall(). Thanks again.
http://smileys.smilchat.net/emoticon/expressions/heureux/thank-you.gif
MilesAhead
1st September 2011, 19:46
Attached is a DLL which exports the Windows 7 Taskbar API functions as a simple C interface (using "stdcall" calling convention).
There also is a simple Delphi sample application included, which shows how to use the DLL.
One thing I found kind of interesting. All the docs say to use RegisterWindowMessage to be notified when the Taskbar Button is there. Especially with scripts using DllCall() I found it more reliable just to create a window and wait 1 second.
Seems like if you don't get a button in that time your system is dead or some setting is disabled for showing buttons. The other way with catching the message and setting a flag sure makes for messy code.
Maybe it's not how it's "supposed" to be done but eliminating all that message crap seems to work in AHK and FreeBASIC
Here's an AHK showing the stuff I could cut out
DoTaskbar:
Gui,Show,,Taskbar Test
Gui +LastFound
;~ MsgNum := DllCall( "RegisterWindowMessage", Str,"TaskbarButtonCreated" )
;~ if MsgNum =
;~ {
;~ MsgBox, 4112, Test, RegisterWindowMessage() Failed!
;~ ExitApp
;~ }
;~ if OnMessage(MsgNum,"_SetFlag") =
;~ {
;~ MsgBox, 4112, Test, Could Not Set Message Handler
;~ ExitApp
;~ }
;~ While !TaskInit
;~ {
;~ Sleep,250
;~ }
hModule := DllCall("LoadLibrary", Str,"Win7TaskbarLib.dll")
if hModule = 0
{
MsgBox, 4112, Test, LoadLibrary Failed!
ExitApp
}
Sleep,1000
if !DllCall("Win7TaskbarLib\win7taskbar_init")
;
; stuff from here down to set state and bump taskbar in a loop
; works fine
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.