Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Debug With Apps Displayed on Secondary Monitor

4.54/5 (20 votes)
29 Nov 20062 min read 1   734  
Here's some code that will automatically start your app on a secondary monitor if it's compiled with debug info.

Introduction

I'm working on a project that needs to be debugged on a dual monitor system. Fortunately, I have such a system at work. While dragging the application window to the secondary monitor for the umpteenth billion time, I thought to myself that it would be really cool if I could get the application to display itself on the secondary monitor automatically while running in debug mode.

The Code

All of the work we need to do is in the application's .CPP and .H files. To start things off, add the following code to your application's .H file:

class CSecondaryDebugApp : public CWinApp
{
private:
    CRect m_secondaryRect;
    void UseSecondaryMonitor();
    ...
};

That code simply defines a member variable that receives the secondary monitor's rectangle, and the function prototype for the code that actually does the work of finding your secondary monitor.

Next, we have to add some code to OnInitInstance():

BOOL CSecondaryDebugApp::InitInstance()
{
#ifdef _DEBUG
    UseSecondaryMonitor();

    // if we don't have a secondary monitor, go ahead and maximize the window
    if (m_secondaryRect.Width() + m_secondaryRect.Height() == 0)
    {
        m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
    }
    else
    // otherwise, use the rectange we got from UseSecondaryMonitor()
    {
        m_pMainWnd->MoveWindow(m_secondaryRect, TRUE);
    }
#else
    // The one and only window has been initialized, so show and update it.
    m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
#endif

    m_pMainWnd->UpdateWindow();
    return TRUE;
}

Here, we call our monitor finding method, and then react based on what the method found.

Finally, we add the meat of the code to the end of our .CPP file:

// a global CRect variable to hold the rectangle found by the callback function
CRect secondaryRect(0,0,0,0);

//---------------------------------------------------------------------------
// This callback function is called by EnumDisplayMonitors, and this is where 
// we snatch our secondary monitor rectangle.
//---------------------------------------------------------------------------
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor,  // handle to display monitor
                              HDC hdc1,           // handle to monitor DC
                              LPRECT lprcMonitor, // monitor intersection 
                                                  // rectangle
                              LPARAM data)        // data
{
    RECT rc = *lprcMonitor;

    MONITORINFOEX mInfo;
    mInfo.cbSize = sizeof(mInfo);
    ::GetMonitorInfo(hMonitor, &mInfo);

    if (mInfo.dwFlags != MONITORINFOF_PRIMARY)
    {
        // for purposes of example we always take the first secondary monitor 
        // we find.  I leave it as an exercise for the programmer to change 
        // the code to support multiple secondary displays (and therefore 
        // pick the desired monitor
        secondaryRect = mInfo.rcWork;
        // return 0 to stop the enumeration
        return 0;
    }
    // if we get here, the current monitor isn't a secondary monitor
    return 1;
}

//---------------------------------------------------------------------------
// This is the function we call to enumerate through all
// monitors attached to the system.
//---------------------------------------------------------------------------
void CSecondaryDebugApp::UseSecondaryMonitor() 
{
    // init the memebr rectangle
    m_secondaryRect.SetRect(0,0,0,0);
    // enumerate
    ::EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, 0);
    // store the secondary monitor's rectangle in our member rectangle
    m_secondaryRect = secondaryRect;
    // at this point, we are ready to display the application's window
}

The UseSecondaryMonitor() function simply initializes the member rectangle, enumerates the available monitors, and sets the member rectangle variable. Of course, you could choose to put these three lines of code into the OnInitInstance() function, but that's not how *I* chose do it.

The callback function checks the dwFlags field of the MONITORINFOEX structure to see if this is not the primary monitor, and if it isn't, it grabs the work rectangle (the rectangle available to the application) and returns 0 to stop the enumeration (remember, this example is only looking for the first secondary monitor on your system).

End Result

When you compile the program with debug info, running the program will cause the program to display itself on the secondary monitor. You could easily expand this concept to release versions that will re-display themselves on whatever monitor they were last displayed on.

Have fun.

Disclaimers

This article is meant to illustrate how to find a secondary monitor on your system using the EnumerateDisplayMonitors() and GetMonitorInfo() functions. As usual, the bulk of the info I found came from MSDN. Most of my articles are ripped directly out of a project I'm working on, so along with the "how" aspects, you usually get a real life example of why I developed the code.

I don't expect many here would have the same requirements that I do, and I can't be expected to divine everyone's needs in advance. Since you're all programmers (or at least claim to be), I leave it to you to adapt this code to your own project.

Finally, if this article inspires you to more fully investigate multiple monitor systems (one user here has six monitors on three different video cards in one box), by all means, write an article about it!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here