× Aktuálně z oboru

SHIELD Experience Upgrade 7 – méně hledání a více zábavy [ clanek/2018052902-shield-experience-upgrade-7-mene-hledani-a-vice-zabavy/ ]
Celá zprávička [ clanek/2018052902-shield-experience-upgrade-7-mene-hledani-a-vice-zabavy/ ]

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

[ http://programujte.com/profil/17127-libor-benes/ ]Google [ ?rel=author ]       [ http://programujte.com/profil/14523-martin-simecek/ ]Google [ ?rel=author ]       28. 3. 2012       14 635×

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í [ http://msdn.microsoft.com/en-us/library/windows/desktop/dd391692(v=vs.85).aspx ]). 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 [ http://programujte.com/storage/redakce/2012/17_taskbarprogress.zip ]. Přeložená aplikace na vyzkoušení je ke stažení zde [ http://programujte.com/storage/redakce/2012/16_taskbarprogress.exe ].


Článek stažen z webu Programujte.com [ http://programujte.com/clanek/2012030200-zobrazeni-prubehu-na-hlavnim-panelu-nebo-v-tooltipu-za-pomoci-winapi/ ].