Page 1 of 1

Windows 7 (Vista?) Show a progress bar in taskbar button!

Posted: Sat Jan 23, 2010 7:38 pm
by Rescator
Kind of a pain to make so I posted this here to save others the headache.

This example just shows the taskbar button progressbar,
I leave it to others to toy around with taskbar thumbnail toolbar controls. (mini play/pause at bottom of thumb preview for example),
or changing the thumb tooltip and whatnot.
There is also a ITaskbarList4 but it only ads one method so it's not used in this code...

If anyone see any mistakes (I'm green when it comes to interfaces and COM!) by all means post corrections.

Introducing The Taskbar APIs
ITaskbarList3

Code: Select all

;Public Domain
EnableExplicit

#PB_Compiler_Exe=#True ;This does not exist (yet?)

;http://msdn.microsoft.com/en-us/library/dd464660%28VS.85%29.aspx
Global _ScaleDPI_X_.f=1.0,_ScaleDPI_Y_.f=1.0,_ScaleDPI_font_$
#DefaultDPIX=96.0 ;Different platforms might have different default DPI, Windows is 96 DPI.
#DefaultDPIY=96.0
Procedure InitScaleDPI() ;Windows 5.0 or higher needed for minimum functionality of this procedure.
 Protected dpiaware.l=#False,dc.i,lpx.i,lpy.i,dll.i,*SetProcessDPIAware,*IsProcessDPIAware,ncm.NONCLIENTMETRICS,font.i
 ;This part is Windows 6.x+ only (Vista etc.) and must be done before we use devcaps.
 ;http://msdn.microsoft.com/en-us/library/dd464660%28VS.85%29.aspx#declaring_dpi_awareness
 ;You really should use the DPI aware manifest instead of SetProcessDPIAware() when possible.
 ;On Windows 2000 and XP the manifest has no effect and set dpi aware is not available,
 ;however Devicecaps still returns usefull info that can be used.
 ;Note! If the dpi aware manifest is missing on Vista and Win7 then the OS will lie on devicecaps and will autoscale the entire app window.
 CompilerIf #PB_Compiler_Exe ;Only use this in exes, as dlls inherit DPI from the calling process.
  ;If the exe or the calling exe in case of this being a dll is allready dpi aware (like through a manifest),
  ;then we skip using the the set dpi aware function, a dll should never use the set function, but it should check if the process id dpi aware
  ;and apply the proper modifiers where appropriate obviously.
  dll=OpenLibrary(#PB_Any,"user32.dll")
  If dll
	  *IsProcessDPIAware=GetFunction(dll,"IsProcessDPIAware")
   If *IsProcessDPIAware
    dpiaware=CallFunctionFast(*IsProcessDPIAware)
   EndIf
   If Not dpiaware
	   *SetProcessDPIAware=GetFunction(dll,"SetProcessDPIAware")
	   If *SetProcessDPIAware
	    CallFunctionFast(*SetProcessDPIAware)
	   EndIf
   EndIf
  EndIf
 CompilerEndIf
 dc=GetDC_(#Null)
 If dc
	 lpx=GetDeviceCaps_(dc,#LOGPIXELSX)
	 lpy=GetDeviceCaps_(dc,#LOGPIXELSY)
	 If lpx>0
 	 _ScaleDPI_X_=lpx/#DefaultDPIX
 	EndIf
	 If lpy>0
 	 _ScaleDPI_Y_=lpy/#DefaultDPIY
 	EndIf
	 ReleaseDC_(#Null,dc)
 EndIf
 ;Get the system font for message boxes etc.
 ;We default to a size of 9, which is also the Vista and Win7 default size.
 ;The OS will automatically (Vista and Win7 at least) scale the font per the current user's DPI setting.
 ncm\cbSize=SizeOf(NONCLIENTMETRICS)
 If SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS,SizeOf(NONCLIENTMETRICS),ncm,#Null)
  _ScaleDPI_font_$=PeekS(@ncm\lfMessageFont\lfFaceName)
		font=LoadFont(#PB_Any,_ScaleDPI_font_$,9,#PB_Font_HighQuality)
		If font
		 SetGadgetFont(#PB_Default,FontID(font))
		EndIf
 EndIf
EndProcedure
InitScaleDPI()
Macro ScaleDPIx(x)
 (x)*_ScaleDPI_X_
EndMacro
Macro ScaleDPIy(y)
 (y)*_ScaleDPI_Y_
EndMacro
Macro ScaleDPIfont()
 _ScaleDPI_font_$
EndMacro

#CLSCTX_ALL = 0

#TBPF_NOPROGRESS    = $0
#TBPF_INDETERMINATE = $1
#TBPF_NORMAL        = $2
#TBPF_ERROR         = $4
#TBPF_PAUSED        = $8

#TBATF_USEMDITHUMBNAIL   = $1
#TBATF_USEMDILIVEPREVIEW = $2

#THB_BITMAP  = $1
#THB_ICON    = $2
#THB_TOOLTIP = $4
#THB_FLAGS   = $8

#STPF_NONE                      = $00000000
#STPF_USEAPPTHUMBNAILALWAYS     = $00000001
#STPF_USEAPPTHUMBNAILWHENACTIVE = $00000002
#STPF_USEAPPPEEKALWAYS          = $00000004
#STPF_USEAPPPEEKWHENACTIVE      = $00000008

Structure THUMBBUTTON
 dwMask.l
 iId.l
 iBitmap.l
 hIcon.i
 szTip.u[260]
 dwFlags.l
EndStructure

Interface ITaskbarList3 Extends ITaskbarList2
 SetProgressValue.i(hWnd.i,ullCompleted.q,ullTotal.q)
 SetProgressState.i(hWnd.i,tbpFlags.l)
 RegisterTab.i(hWndTab.i,hWndMDI.i)
 UnregisterTab.i(hWndTab.i)
 SetTabOrder.i(hWndTab.i,hWndInsertBefore.i)
 SetTabActive.i(hWndTab.i,hWndMDI.i,tbatFlags.l)
 ThumbBarAddButtons.i(hWnd.i,cButtons.l,*pButton)
 ThumbBarUpdateButtons.i(hWnd.i,cButtons.l,*pButton)
 ThumbBarSetImageList.i(hWnd.i,himl.i)
 SetOverlayIcon.i(hWnd.i,hIcon.i,pszDescription$)
 SetThumbnailTooltip.i(hWnd.i,pszTip$)
 SetThumbnailClip.i(hWnd.i,*prcClip)
EndInterface

Interface ITaskbarList4 Extends ITaskbarList3
 SetTabProperties.i(hwndTab.i,stpFlags.l)
EndInterface

DataSection
 CLSID_TaskBarList:
  Data.l $56FDF344
  Data.w $FD6D, $11D0
  Data.b $95, $8A, $00, $60, $97, $C9, $A0, $90
 IID_ITaskBarList3:
  Data.l $ea1afb91
  Data.w $9e28,$4b86
  Data.b $90,$E9,$9e,$9f,$8a,$5e,$ef,$af
EndDataSection

Enumeration 1
 #Window_Main
EndEnumeration

Enumeration 1
 #Gadget_Main_Text
 #Gadget_Main_Progress
EndEnumeration

Enumeration 1
 #Timer_Progress
EndEnumeration

InitScaleDPI()
Define ptl.ITaskbarList3,event,progress,progressmax,pp.ITaskbarList3

;Note1: It seems that CoInitialize, HrInit and CoUninitialize are not needed (does PB apply these?)
;Note2: Only tried this on Windows 7, does Vista display the taskbutton progress?
;Note3: You should wait to use the taskbar stuff until the taskbar button has been created.
; DWORD g_wmTBC = (DWORD)-1;
; 
; //In the window Procedure:
; switch (msg) {
; Case WM_CREATE:
;     g_wmTBC = RegisterWindowMessage(L"TaskbarButtonCreated");
;     //Proceed To create the window
; Case g_wmTBC:
;     //The taskbar button has been created
; }

If OpenWindow(#Window_Main,0,0,ScaleDPIx(320),ScaleDPIy(160),"ProgressBar",#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
 progress=0
 progressmax=100
 TextGadget(#Gadget_Main_Text,ScaleDPIx(10),ScaleDPIy(10),ScaleDPIx(250),ScaleDPIy(20),"Progress ("+Str(progress)+"/"+Str(progressmax)+")",#PB_Text_Center)
 ProgressBarGadget(#Gadget_Main_Progress,ScaleDPIx(10),ScaleDPIy(30),ScaleDPIx(250),ScaleDPIy(30),0,100)
 SetGadgetState(#Gadget_Main_Progress,progress)
 CoInitialize_(#Null)

 CoCreateInstance_(?CLSID_TaskBarList,#Null,1,?IID_ITaskBarList3,@ptl)

 If ptl
  ptl\HrInit()
  ptl\SetProgressValue(WindowID(#Window_Main),progressmax,progressmax)
  ptl\SetProgressState(WindowID(#Window_Main),#TBPF_INDETERMINATE)
 EndIf
 AddWindowTimer(#Window_Main,#Timer_Progress,1000)
 Repeat
  event=WaitWindowEvent()
  If event=#PB_Event_Timer
   progress+1;Random(progressmax)
   SetGadgetState(#Gadget_Main_Progress,progress)
   SetGadgetText(#Gadget_Main_Text,"Progress ("+Str(progress)+"/"+Str(progressmax)+")")
   If ptl
    If progress=5
     ptl\SetProgressState(WindowID(#Window_Main),#TBPF_NORMAL)
     RemoveWindowTimer(#Window_Main,#Timer_Progress)
     AddWindowTimer(#Window_Main,#Timer_Progress,100)
    ElseIf progress>5
     ptl\SetProgressValue(WindowID(#Window_Main),progress,progressmax)
    EndIf
   EndIf
   If progress=101
    RemoveWindowTimer(#Window_Main,#Timer_Progress)
    AddWindowTimer(#Window_Main,#Timer_Progress,1000)
    ptl\SetProgressState(WindowID(#Window_Main),#TBPF_PAUSED)
   ElseIf progress=102
    ptl\SetProgressState(WindowID(#Window_Main),#TBPF_NORMAL)
   ElseIf progress=103
    ptl\SetProgressState(WindowID(#Window_Main),#TBPF_ERROR)
   ElseIf progress=104
    ptl\SetProgressState(WindowID(#Window_Main),#TBPF_INDETERMINATE)
    progress=0
   EndIf
  EndIf
 Until event=#PB_Event_CloseWindow
EndIf
RemoveWindowTimer(#Window_Main,#Timer_Progress)
If ptl
 ptl\Release()
EndIf
CloseWindow(#Window_Main)
CoUninitialize_()

Re: Windows 7 (Vista?) Show a progress bar in taskbar button!

Posted: Sat Jan 23, 2010 8:09 pm
by Foz
Very cool. 8)

Unfortunately, it seems ridiculously complex for such a minor effect, you do have to ask the question... is it worth the effort? :?

Re: Windows 7 (Vista?) Show a progress bar in taskbar button!

Posted: Sat Jan 23, 2010 8:14 pm
by rsts
Damn slick effect.

Thanks for sharing this :)

cheers

Re: Windows 7 (Vista?) Show a progress bar in taskbar button!

Posted: Sat Jan 23, 2010 10:14 pm
by SFSxOI
Very Nice! Thank you very much! :)

Re: Windows 7 (Vista?) Show a progress bar in taskbar button!

Posted: Mon Jan 25, 2010 2:56 pm
by Shardik
Rescator,

thank you for your code example, especially your helpful comments and the MSDN links.

For those interested in yet another code example: DarkDragon already posted this example
in the German forum:
http://www.purebasic.fr/german/viewtopi ... 47&start=8

Re: Windows 7 (Vista?) Show a progress bar in taskbar button!

Posted: Mon Jan 25, 2010 9:50 pm
by SFSxOI
@Shardik

The code by Dragon doesn't take into account DPI Scaling.

Re: Windows 7 (Vista?) Show a progress bar in taskbar button!

Posted: Wed Jan 27, 2010 9:55 pm
by Rescator
DPI scaling, oh right, that was code in my test source, leftover from my ScaleDPI in another post here, but since it was fully functional I just left it in.
I've made it standard to always take DPI scaling into account on current and future software, and when PureBasic gets more support for DPI scaling it's very easy to just do a search and replace to remove the scale macros etc.

Didn't know of DarkDragon's code, then again I only frequent the international forum, so... Never hurts to have more examples though :)

Re: Windows 7 (Vista?) Show a progress bar in taskbar button

Posted: Tue Apr 20, 2010 1:44 pm
by rudz
Nice code!

Only adds ~1kb to my compiled+compressed .exe :)

Oh, and it works fine for me in win7/64