Zobrazení průběhu na hlavním panelu nebo v tooltipu za pomoci WinAPI
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Zobrazení průběhu na hlavním panelu nebo v tooltipu za pomoci WinAPIZobrazení průběhu na hlavním panelu nebo v tooltipu za pomoci WinAPI

 

Zobrazení průběhu na hlavním panelu nebo v tooltipu za pomoci WinAPI

Google       Google       28. 3. 2012       14 646×

Po přechodu na Windows 7 mě zaujala možnost zobrazování průběhu v titulku okna na hlavním panelu (Taskbaru). V tomto článku vám ukážu, jak ji dostat do aplikace i se starší verzí SDK. A pro majitele
starších Windows, aby také něco viděli, uděláme zobrazení průběhu (procent) pomocí tooltipu.

K zobrazování průběhu se používá rozhraní ITaskbarList3 (popis rozhraní). Toto rozhraní je definováno ve Windows SDK od verze 7.0, ale protože všechny moje dosavadní aplikace využívají starší verzi 6.1 (a Visual Studio 2008) a zatím je potřeba, aby běhaly i na Windows 2000 s posledním SP, tak jsem hledal způsob, jak mít v programu podporu pro zobrazování průběhu i se starší verzí SDK.

Zobrazení průběhu na hlavním panelu

První, co mě napadlo, je nahrazení pouze hlavičkového souboru z nového SDK ve starém SDK, ale to se nesetkalo s úspěchem. Takže jsem na to šel jinak. Každé rozhraní je definováno v souboru .idl (IDL je zkratka pro Interface Definition Language - jazyk pro definici rozhraní komponent) a stejně tak i rozhraní ITaskbarList3. Jeho definice je uložena v souboru ShObjIdl.idl. Odtud jsem ji zkopíroval do vlastního souboru TaskbarList3.idl. Následně jsem provedl kompilaci MIDL kompilátorem, který mi vytvořil zdrojové a hlavičkové soubory potřebné pro začlenění rozhraní do programu.

Poznámka: Výsledný hlavičkový soubor jsem ještě mírně upravil, a to definice výčtových typů, aby vše fungovalo i na (mém) VS 2008.

Nyní můžeme vytvořit zkušební aplikace (základem bude vygenerovaná Win32 okenní aplikace). Do této aplikace přidáme například do menu položku (Soubor -> Start), kterou budeme spouštět zkušební vlákno. Reakcí na tuto volbu bude zavolání naší funkce OnStart, kde si jako parametr předáme handle hlavního okna. Úkolem této funkce bude spuštění testovacího vlákna (a zakázání položky Start, aby vlákno nešlo spustit vícekrát).

void OnStart(HWND hWnd)
{
	DWORD	dwThreadID;
	HANDLE	hThread;
	
	EnableMenuItem(GetMenu(hWnd), ID_FILE_START,
                       MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	
	hThread = CreateThread(NULL, 0, ThreadProc, hWnd, 0, &dwThreadID);

	assert(hThread != NULL);
}

Nyní se pustíme do samotného vlákna. V něm chceme pouze demonstrovat funkci zobrazení, takže provedeme jednoduchý průběh 0 až 99 v cyklu, který zpomalíme funkcí Sleep. Nejprve se pokusíme získat pointer na rozhraní ITaskbarList3. Pokud budeme aplikaci spouštět na Windows 7, tak pointer dostaneme. Na starším máme smůlu, protože takové rozhraní v systému neexistuje. Nemusíme to tedy nijak zvlášť rozlišovat, takže nám bude stačit rozhraní máme/nemáme. Před samotnou prací s COM samozřejmě musíme provést inicializaci.

ITaskbarList3*	pTaskbar	= NULL;	
HRESULT		hResult;

hResult = CoInitialize(NULL);
assert(hResult == S_OK);
hResult = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL,
                           IID_ITaskbarList3, (void**)&pTaskbar);

Poznámka: Do zdrojového souboru jsme samozřejmě přidali #include "TaskbarList3.h" a přidali nový soubor TaskbarList3_i.c, tj. použili jsme soubory, které jsou výsledkem snažení MIDL kompilátoru.

Nejprve je nutné nastavit, že budeme průběh zobrazovat:

if (pTaskbar != NULL) pTaskbar->SetProgressState(hWnd, TBPF_NORMAL);

A pak se do toho můžeme pustit:

for (int i = 0;i < 100;i++) {
	if (pTaskbar != NULL) pTaskbar->SetProgressValue(hWnd, i, 100);
	Sleep(100);
}

Po skončení zobrazování průběhu opět vše vrátíme do základního stavu, rozhraní uvolníme a nezapomene zavolat CoUninitialize pro deinicializaci COM:

if (pTaskbar != NULL) {
	pTaskbar->SetProgressState(hWnd, TBPF_NOPROGRESS);
	pTaskbar->Release();
	pTaskbar = NULL;
}

CoUninitialize();

Tím jsme do naší aplikace zabudovali zobrazení průběhu, které ovšem uvidí pouze majitelé Windows 7 (a novějších). Pro ostatní bychom měli průběh také nějak zobrazovat. Výhodou zobrazení na hlavním panelu je to, že je vidět i při minimalizovaném stavu aplikace. My proto zobrazíme průběh (počet procent) v tooltip okně v pravém dolním rohu plochy, a to v pouze v případě, že průběh nezobrazujeme do Taskbaru (nemáme pointer na rozhraní ITaskbarList3).

Zobrazení průběhu v tooltipu.

Poznámka: Pro použití tooltipu v tomto stavu je potřeba používat novější verzi knihovny komponent Common Controls. To zařídíme přidáním odpovídajícího manifestu (v našem případě TaskbarProgress.exe.manifest) do projektu.

Nejprve si vytvoříme okno tooltipu a nastavíme mu základní vlastnosti, zejména specifikujeme, aby okno bylo vždy nahoře (TOPMOST) a vlastnost TTF_TRACK, tj. zobrazení v naší režii:

HWND CreateTooltip(HWND hWnd)
{
	TOOLINFO	sToolInfo;
	// vytvoreni okna tooltipu
	HWND		hTooltip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL,
                              WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
                              CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                              CW_USEDEFAULT, hWnd, NULL, hInst, NULL);	

	assert(hTooltip != NULL);
	// nastaveni okna jako TOPMOST
	::SetWindowPos(hTooltip, HWND_TOPMOST,0, 0, 0, 0,
                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
	
	// vyplneni struktury TOOLINFO (TTF_TRACK) a zatim zadny text
	sToolInfo.cbSize		= sizeof(TOOLINFO);
	sToolInfo.uFlags		= TTF_TRACK;
	sToolInfo.hwnd			= NULL;
	sToolInfo.hinst			= NULL;
	sToolInfo.uId			= 0;
	sToolInfo.lpszText		= L"";
	// inicializace velikosti okna tooltipu
	sToolInfo.rect.left		= 0;
	sToolInfo.rect.top		= 0;
	sToolInfo.rect.right	= 0;
	sToolInfo.rect.bottom	= 0;
	// nastaveni maximalni delky textu, jinak se provede zalomeni
	::SendMessage(hTooltip, TTM_SETMAXTIPWIDTH, 0, (LPARAM)650);
	// nastaveni titulku v tootltipu
	::SendMessage(hTooltip, TTM_SETTITLE, (WPARAM)TTI_INFO,
                   (LPARAM)L"programujte.com");

	// registrace tipu 
	::SendMessage(hTooltip, TTM_ADDTOOL, 0, 
                   (LPARAM)(LPTOOLINFO)&sToolInfo);			

	return(hTooltip);
}

Vytvořenému tooltipu nyní stačí nastavovat pozici a text a bude zobrazovat průběh:

void TooltipProgress(HWND hTooltip, UINT nPos, UINT nMax)
{
	wchar_t		wzTemp[65];
	RECT		rtRect;
	RECT		rtScreen;
	TOOLINFO	sToolInfo;
	int		nLeft;
	int		nTop;
	
	assert(nMax != 0);
	// zobrazovany text - prubeh
	swprintf_s(wzTemp, 64, L"%u%%", (nPos * 100) / nMax);
	// vyplneni noveho obsahu TOOLINFO (s novym textem)
	sToolInfo.cbSize		= sizeof(TOOLINFO);
	sToolInfo.uFlags		= TTF_TRACK;
	sToolInfo.hwnd			= NULL;
	sToolInfo.hinst			= NULL;
	sToolInfo.uId			= 0;
	sToolInfo.lpszText		= wzTemp;
	// velikost oblasti se prizpusobi sama
	sToolInfo.rect.left		= 0;
	sToolInfo.rect.top		= 0;
	sToolInfo.rect.right	        = 0;
	sToolInfo.rect.bottom	        = 0;
	// zjisteni aktualni velikosti okna tooltipu
	GetWindowRect(hTooltip, &rtRect);
	// zjisteni okraju pracovni plochy
	GetDesktopRect(&rtScreen);
	// zjisteni leveho horniho bodu pro zobrazni okna tooltipu
	nLeft	= rtScreen.right - (rtRect.right - rtRect.left);
	nTop	= rtScreen.bottom - (rtRect.bottom - rtRect.top);		
	// aktualizace textu tooltipu
	::SendMessage(hTooltip, TTM_UPDATETIPTEXT, 0,
                     (LPARAM)(LPTOOLINFO)&sToolInfo);

	// nastaveni pozice tooltipu
	::SendMessage(hTooltip, TTM_TRACKPOSITION, 0,
                     (LPARAM)MAKELONG(nLeft, nTop));

	// aktivace posunuti tooltipu
	::SendMessage(hTooltip, TTM_TRACKACTIVATE, true,
                     (LPARAM)(LPTOOLINFO)&sToolInfo);
}

Pro získání oblasti plochy máme svoji funkci, protože chceme tooltip zobrazit mimo případný taskbar:

void GetDesktopRect(RECT* pRect)
{
	APPBARDATA	sData;
	HWND		hDesktop;

	assert(pRect != NULL);
	// handle plochy
	hDesktop = GetDesktopWindow();
	assert(hDesktop != NULL);
	// klientska oblast plochy
	GetClientRect(hDesktop, pRect);
	// zjisteni pozice a velikosti Taskbaru
	ZeroMemory((LPBYTE)&sData, sizeof(sData));
	sData.cbSize = sizeof(sData);
	SHAppBarMessage(ABM_GETTASKBARPOS, &sData);
	SHAppBarMessage(ABM_GETAUTOHIDEBAR, &sData);
	// podle jeho umisteni uprav i klientskou oblast plochy
	switch(sData.uEdge) {
		case(ABE_BOTTOM):
			pRect->bottom = sData.rc.top;
			break;
		case(ABE_LEFT):
			pRect->left = sData.rc.right;
			break;
		case(ABE_RIGHT):
			pRect->right = sData.rc.left;
			break;
		case(ABE_TOP):
			pRect->top = sData.rc.bottom;
			break;
	};	
}

Výsledný kód vlákna vypadá následovně:

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	HWND		hWnd     = (HWND)lpParameter;	
	HWND		hTooltip = NULL;
	ITaskbarList3*	pTaskbar = NULL;	
	HRESULT		hResult;

	// inicializace COM
	hResult = CoInitialize(NULL);
	assert(hResult == S_OK);
	// ziskani pointer na rozhrani ITaskbarList3
	hResult = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL,
                                   IID_ITaskbarList3, (void**)&pTaskbar);

	// pokud mame pointer na rozhrani, tak nastav stav zobrazovani
	if (pTaskbar != NULL) pTaskbar->SetProgressState(hWnd, TBPF_NORMAL);
	// jinak vytvor tootlip
	else hTooltip = CreateTooltip(hWnd);

	// zobrazovani prubehu 0 az 99
	for (int i = 0;i < 100;i++) {
	   // pokud mame pointer na rozhrani, tak zobraz novou hodnotu
	   if (pTaskbar != NULL) pTaskbar->SetProgressValue(hWnd, i, 100);
	   // jinak zobrazuj prubeh v tooltipu
	   else TooltipProgress(hTooltip, i, 100);
	   // pockej 100ms
	   Sleep(100);
	}

	// pokud jsme pracovali s rozhranim
	if (pTaskbar != NULL) {
	   // tak nastav vychozi stav zobrazovani 
	   pTaskbar->SetProgressState(hWnd, TBPF_NOPROGRESS);
	   // a rozhrani uvolni
	   pTaskbar->Release();
	   pTaskbar = NULL;
	}
	// jinak zrus okno tooltipu
	else DestroyWindow(hTooltip);

	// povol polozku Start v menu
	EnableMenuItem(GetMenu(hWnd), ID_FILE_START,
                       MF_BYCOMMAND | MF_ENABLED);

	// deinicializace COM
	CoUninitialize();

	return(0);
}

Zdrojový kód celé aplikace je ke stažení zde. Přeložená aplikace na vyzkoušení je ke stažení zde.

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

4 názory  —  4 nové  
Hlasování bylo ukončeno    
8 hlasů
Google
Autor se věnuje programování za peníze :)

Nové články

Obrázek ke článku Hybridní inteligentní systémy 2

Hybridní inteligentní systémy 2

V technické praxi využíváme často kombinaci různých disciplín umělé inteligence a klasických výpočtů. Takovým systémům říkáme hybridní systémy. V tomto článku se zmíním o určitém typu hybridního systému, který je užitečný ve velmi složitých výrobních procesech.

Obrázek ke článku Jak vést kvalitně tým v IT oboru: Naprogramujte si ty správné manažerské kvality

Jak vést kvalitně tým v IT oboru: Naprogramujte si ty správné manažerské kvality

Vedení týmu v oboru informačních technologií se nijak zvlášť neliší od jiných oborů. Přesto však IT manažeři čelí výzvě v podobě velmi rychlého rozvoje a tím i rostoucími nároky na své lidi. Udržet pozornost, motivaci a efektivitu týmu vyžaduje opravdu pevné manažerské základy a zároveň otevřenost a flexibilitu pro stále nové výzvy.

Obrázek ke článku Síla týmů se na home office může vytrácet. Odborníci radí, jak z pracovních omezení vytěžit maximum

Síla týmů se na home office může vytrácet. Odborníci radí, jak z pracovních omezení vytěžit maximum

Za poslední rok se podoba práce zaměstnanců změnila k nepoznání. Především plošné zavedení home office, které mělo být zpočátku jen dočasným opatřením, je pro mnohé už více než rok každodenní realitou. Co ale dělat, když se při práci z domova ztrácí motivace, zaměstnanci přestávají komunikovat a dříve fungující tým se rozpadá na skupinu solitérů? Odborníci na personalistiku dali dohromady několik rad, jak udržet tým v chodu, i když pracovní podmínky nejsou ideální.

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý