I'm very picky about my desktop environment. I like windows, toolbars, icons, etc. to be arranged in a certain way. Typically, I turn off annoying prompts and nag screens in Windows, and unhide "scary" advanced options in OS X. One hard-to-fix pet peeve is when applications put an icon in the notification area (near the clock) AND leave a tab in the taskbar. This wastes valuable taskbar real estate.
I use Spark on my workstation to connect to the company's internal IM server. The application works alright, but the contacts window always appears in the taskbar. So I started to think about ways I could programmatically solve my problem.
I could set the WS_EX_TOOLWINDOW style on the window, but that would alter the window's appearance. What I really wanted was a way to tell Windows to remove the tab. A quick search on Google turned up the answer: use COM to create an instance of ITaskbarList. The interface has the function ITaskbarList::DeleteTab() which takes a window handle. Perfect!
Now I just needed to get the window's handle. FindWindow() would have worked, but that meant hard-coding my username into the program. I felt a more elegant solution was to enumerate all of the windows, and look for the one with the right class and title prefix.
Since the tab would reappear every time I brought the contact list window to the foreground, I ended up wrapping my fix with a loop and a timer. Here's the finished product:
/* NoSpark.cpp - Hides the Spark contacts window tab in the taskbar */ #include <windows.h> #include <Shobjidl.h> BOOL CALLBACK EnumWindowsProc(HWND, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HANDLE htmr = CreateWaitableTimer(NULL, TRUE, L"CheckSpark"); LARGE_INTEGER lidt; ITaskbarList* ptl; __int64 qwdt = -60 * 10000000; /* 1 minute */ lidt.LowPart = (DWORD)(qwdt & 0xFFFFFFFF); lidt.HighPart = (LONG)(qwdt >> 32); while (TRUE) { SetWaitableTimer(htmr, &lidt, 0, NULL, NULL, FALSE); WaitForSingleObject(htmr, INFINITE); HWND hsparkwnd = 0; EnumWindows(&EnumWindowsProc, (LPARAM)&hsparkwnd); if (hsparkwnd == 0) continue; CoInitialize(NULL); HRESULT ret = CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_SERVER, IID_ITaskbarList, (LPVOID*) &ptl ); if (ret == S_OK) ptl->DeleteTab(hsparkwnd); ptl->Release(); CoUninitialize(); } return 0; } BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { int tmpsz = 16; LPWSTR lptmp = (LPWSTR)malloc(sizeof(WCHAR) * tmpsz); RtlZeroMemory(lptmp, tmpsz); GetClassName(hwnd, lptmp, tmpsz); if (wcscmp(lptmp, L"SunAwtFrame") != 0) { free(lptmp); return TRUE; } RtlZeroMemory(lptmp, tmpsz); GetWindowText(hwnd, lptmp, tmpsz); if (wcscmp(lptmp, L"Spark -") > 0) { *((HWND*) lParam) = hwnd; free(lptmp); return FALSE; } free(lptmp); return TRUE; }
To compile this program, create a new, empty Visual C++ project. Create a new cpp file and drop the code above inside. If you get compile errors about converting wchar_t* to const char* then change your character set from Multibyte to Unicode in the project properties.