Introduction
When I cleaned up my harddisk lately, I found some ancient text files I created a long time ago. One of them contained "coding tips" and code fragments concerning problems I had to solve during the development of some of my applications.
Whenever I found a solution for a specific problem, I pasted the corresponding code into the text file, and now, dozens of self-made applications later, I know what stuff you will run into over and over again:
- How do I prevent an application from showing up in the taskbar?
- How do I start an application minimized without "flickering"?
- How do I prevent my application from being started multiple times?
- How do I get rid of this annoying "Gridlines-Scrolling" bug in my custom CListctrl?
I present the answers here and hope they come along handy for you and your application; maybe this article even helps saving some of your valuable time.
So here we go…
1. Preventing an application from showing up in the taskbar
Years ago, a friend of mine came up with the idea for a small digital clock hovering above all other windows with adjustable transparency. I started coding and quickly ran into two major problems: first, the clock application (based on CDialog
) showed up in the taskbar and second, its window was also accessible via ALT-TAB.
I tried several approaches to get around these problems, and finally found the solution presented below. The trick for "hiding" your application is quite simple: the main dialog window is attached to an "invisible" parent window. That's all, and it's perfectly preventing my clock application from showing up in the taskbar or being accessible via ALT-TAB ever since.
Here is the default code generated by MSVC for any new CDialog
based application:
CTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
So, to make your dialog "taskbar-resistent", just use the init method shown below. If you also want to prevent the user from accessing your window using the ALT-TAB taskswitch mechanism, just make your dialog a TOOL
window using the MSVC property editor.
CWnd *pWnd = NULL;
if(!::IsWindow(m_Invisible_pWnd.m_hWnd))
{
LPCTSTR pstrOwnerClass = AfxRegisterWndClass(0);
if(m_Invisible_pWnd.CreateEx(0, pstrOwnerClass, _T(""), WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, 0))
{
pWnd = &m_Invisible_pWnd;
}
}
CTestDlg dlg(pWnd);
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
Don't forget to add the appropriate definition for m_Invisible_pWnd
in your corresponding .h file:
class CTestApp : public CWinApp
{
public:
CTestApp();
protected:
CWnd m_Invisible_pWnd;
...
2. Starting an application minimized without flickering
To start an application minimized (e.g. appearing directly in the tray area), the common way to do this is calling
ShowWindow(SW_HIDE)
as soon as possible, preferably directly after the call to
CDialog::OnInitDialog()
.
This surely does the trick, but every now and then, you will see your main dialog window flickering for a fraction of a second before it is finally hidden or sent to the tray area.
This is because the basic window has already been created when CDialog::OnInitDialog()
returns, and its default behaviour is to appear on screen (in fact, this is what windows were made for).
A few years from now, when computers operate faster than light, you'll get rid of this annoying flickering automatically, because your Operating System will then hide your window before you even start the corresponding application.
But for now, just add this OnWindowPosChanging()
handler to your application:
void CTestDlg::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
if(m_bDlgVisible == false)
lpwndpos->flags &= ~SWP_SHOWWINDOW;
CDialog::OnWindowPosChanging(lpwndpos);
}
You can also declare an optional boolean member variable (m_bDlgVisible
in this example), which is initialized to true in the constructor and gets toggled in the corresponding SendToTray()
and RestoreFromTray()
functions. This way, I can track the current window state (IsWindowVisible()
would also work) for different purposes.
Note: Adding this message handler to your code will not make your computer operate faster than light.
3. Avoiding multiple starts of your application
So you finally coded it, this ultimate application, only to find your beta testers launching it a hundred times, rendering your database useless because your function calls are not MT safe? Well, how about this little code snippet that allows only one instance of your application running at a time.
There is more than one approach to check if your application's already running or not. You could, for example, read in the list of running processes on startup and see if you can find your application more than once. But this is kind of overkill and, even more important, it's not cool.
It's more efficient (and cool) to create a system-wide unique object (called a "mutex") and check if it already exists when your program starts. If it does exist, all that's left to do then is to exit the recently started instance…et voilà!
As an option, you can also broadcast a custom message and grab the HWND of the responding application to perform other magic tricks.
Let's do it
The code shown below tries to grab the HWND
of the running instance to bring its parent window to front. This is optional, but probably exactly what you're looking for, so I included the code lines required to to this.
If you don't need this kind of hocus pocus, you can quietly exit the recently started instance and be at peace.
But we want it nice, so first of all, create your own custom message; you do this in the main instance .h file:
const UINT WM_ANYBODY_OUT_THERE<BR> = RegisterWindowMessage(_T("Is there anybody out there?"));
We use RegisterWindowMessage()
here to obtain a unique message number we can send. (A duplicate message number would only lead to results which are probably not desired.)
And by the way…you can of course fill in any other message text that fits your needs!
And since you're editing the .h file right now, add the following to it:
struct SingleInstance
{
SingleInstance()
{
HANDLE hMutex = CreateMutex(0, FALSE, _T("My very own mutex"));
if (GetLastError() == ERROR_ALREADY_EXISTS || <BR> GetLastError() == ERROR_ACCESS_DENIED)
{
HWND hThisApp = NULL;
EnumWindows(ThisAppSearcher, (LPARAM)&hThisApp);
if(hThisApp)
SetForegroundWindow(hThisApp);
CloseHandle(hMutex);
exit(0);
}
}
static BOOL CALLBACK ThisAppSearcher(HWND hWnd, LPARAM lParam)
{
DWORD dwAnswer;
if(!SendMessageTimeout(hWnd, WM_ANYBODY_OUT_THERE, 0, 0, <BR> SMTO_BLOCK|SMTO_ABORTIFHUNG, 500, &dwAnswer) ||
dwAnswer != WM_ANYBODY_OUT_THERE)
{
return TRUE;
}
*((HWND *)lParam) = hWnd;
return FALSE;
}
};
Looks impressive, doesn't it! And best of all, the majority of this code is optional…
But wait! There's one last step to take: add SingleInstance TheOneAndOnly;
before the call to InitInstance()
:
SingleInstance TheOneAndOnly;
BOOL CMyApp::InitInstance()
{
...
And there it is, your ultimate application, blocking any of your beta tester's attempts to start it more than once!
4. Solving the XP gridlines problem
You've probably seen it all before: using a custom CListCtrl
in report mode with gridlines enabled causes (under XP) the control to mess up its content as soon as you start scrolling up or down. This is a bug confirmed bei M$, but it looks like it won't be fixed…so here's the workaround.
The gridlines scrolling bug only applies to systems that have the Smooth Scrolling feature enabled, but you can't go wrong adding (or modifying) the OnVScroll()
handler of the CListCtrl
(s) in question:
void CMyListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
Invalidate();
UpdateWindow();
}
That was easy! We're just forcing an additional redraw to get things right. If you think it's a waste of valuable CPU time to redraw the control even with Smooth Scrolling disabled, then this might come in handy:
SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &bIsEnabled, 0);
This will set bIsEnabled
to true when Smooth Scrolling is enabled on a particular XP system. You can then pass this value on to the OnVScroll()
handler to decide if the control should be redrawed or not.
Points of Interest
And that was my box of tricks…I'm not quite sure if any of the topics have already been discussed on this site. Anyway, now they're all together here, and it would be cool if you find them useful.
History
This is the very first version of this article, and probably the last one, too, and there's no version number. In fact, there never was one.