Click here to Skip to main content
15,886,578 members
Articles / Operating Systems / Windows
Article

A box of four fancy C++ Windows tricks

Rate me:
Please Sign up or sign in to vote.
4.44/5 (35 votes)
28 Jul 2006CPOL6 min read 56.2K   55   7
This article presents four useful code snippets, solving four common coding problems

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:

//
// Default MSVC init: window will show up in taskbar and is accessible
// via ALT+TAB
//
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.

//
// Custom init: Prevent application from showing up in the taskbar
//
CWnd *pWnd = NULL;

if(!::IsWindow(m_Invisible_pWnd.m_hWnd))
{
    LPCTSTR pstrOwnerClass = AfxRegisterWndClass(0);

    // Create an invisible parent window
    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);    // "Attach" dialog to the "invisible" parent window
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;    // "Invisible" parent window
    
    ...

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:

//
// Avoid flickering of main window when starting minimized
//
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()
    {
        // Create a global (and unique) mutex object
        HANDLE hMutex = CreateMutex(0, FALSE, _T("My very own mutex"));

        // Failed to create the mutex object: there's an instance of this <BR>        // application running!
        if (GetLastError() == ERROR_ALREADY_EXISTS || <BR>           GetLastError() == ERROR_ACCESS_DENIED)
        {
            //************************************************************
            // THE FOLLOWING STUFF IS OPTIONAL. YOU ONLY NEED IT TO GRAB <BR>            // THE "HWND" OF THE INSTANCE!
            //************************************************************
            HWND hThisApp = NULL;

            // Call out for each and every window
            EnumWindows(ThisAppSearcher, (LPARAM)&hThisApp);

            // Bring window to front
            if(hThisApp)
                SetForegroundWindow(hThisApp);
                
                // It's also possible to send a message to the application:
                // SendMessage(hThisApp, WM_USER+4711, (WPARAM)1, <BR>                //           (LPARAM)WM_LBUTTONDBLCLK);
            
            //***********************
            // END OF OPTIONAL STUFF
            //***********************
            CloseHandle(hMutex);

            // Exit this (recently started) instance
            exit(0);
        }
    }

    //**************************************************************
    // Broadcast custom message and check if someone responds to it
    // This is also optional and only needed if you want to get the
    // instance's HWND!
    //**************************************************************
    static BOOL CALLBACK ThisAppSearcher(HWND hWnd, LPARAM lParam)
    {
        DWORD dwAnswer;

        // Message sent, but no one replied: this is probably the first <BR>        // instance
        if(!SendMessageTimeout(hWnd, WM_ANYBODY_OUT_THERE, 0, 0, <BR>           SMTO_BLOCK|SMTO_ABORTIFHUNG, 500, &dwAnswer) ||
           dwAnswer != WM_ANYBODY_OUT_THERE)
        {
            return TRUE;
        }
        
        // Application responded to our message: grab the HWND for further <BR>        // processing
        *((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:

// Put this somewhere in your app's init routine
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Germany Germany
The first thing I did when I was born in 1966 was to start crying. For years, this was my favorite hobby, until I got my first computer in 1981. I then continued crying until I finally mastered 65xx assembler. I learned to smile then, and that's what I've been doing ever since.

Comments and Discussions

 
GeneralSINGLE INSTANCE - Windows trick. Pin
ch_swisstec14-May-09 17:54
ch_swisstec14-May-09 17:54 
GeneralRe: SINGLE INSTANCE - Windows trick. Pin
Jörg Anslik8-Feb-10 5:06
Jörg Anslik8-Feb-10 5:06 
Generalthanks for your beautiful codes Pin
hongbo zou9-Oct-06 2:48
hongbo zou9-Oct-06 2:48 
QuestionAvoiding multiple starts of your application Pin
paratracker11-Aug-06 19:38
paratracker11-Aug-06 19:38 
Generalnice Pin
flippydeflippydebop4-Aug-06 11:10
flippydeflippydebop4-Aug-06 11:10 
GeneralNice job Pin
Canopener40001-Aug-06 21:56
Canopener40001-Aug-06 21:56 
GeneralNice and succinct Pin
David Crow1-Aug-06 3:04
David Crow1-Aug-06 3:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.