C++ Q&A

Performance Monitoring, Managed Extensions, and Lock Toolbars

Paul DiLascia

Code download available at:CQA0409.exe(248 KB)

In the June 2004 issue of MSDN®Magazine, I described a class called ShowTime that you can use to do simple performance monitoring for your app. ShowTime uses its constructor/destructor to record the start/stop times of its existence so you can instantiate it in a block of code like so:

{ ShowTime st(_T("Total time is:")); // some lengthy operation }

You'll get a TRACE message or log file with an entry that says "Total time is: nnn msec" where nnn is however many milliseconds from the time the instance is created on the stack to the time its destructor is called.

ShowTime uses the CRT ::clock function to get the number of milliseconds since the process started. As I pointed out, ::clock is not the most accurate function for precise timing. I never suggested what else you might use because ::clock was adequate for the purposes of the question I was answering. But reader Robert Bocquier pointed out there's a better function: timeGetTime.

Figure 1 Time Output

Figure 1** Time Output **

Robert reports from his experience writing music applications that the granularity of ::clock can be as high as 10 msec depending on which platform/version of Windows® you're running; whereas timeGetTime is more accurate. PerfTest.cpp is a simple program available in the MSDN Magazine code download which illustrates this (thanks to Robert for providing the idea for this test). PerfTest displays a message whenever the time changes, first using ::clock and then using timeGetTime. As you can see from the sample output in Figure 1, on my machine running Windows XP, ::clock has a granularity of 10 msec; whereas timeGetTime has a granularity of 1 msec. The only catch is that you must first call timeBeginPeriod to set the granularity you want, then timeEndPeriod when you're finished:

timeBeginPeriod(1); // ... run performance test timeEndPeriod(1);

Each call to timeBeginPeriod must be matched with a call to timeEndPeriod with the same value. On my machine, the granularity is 10 msec by default (same as ::clock), so don't forget to call timeBeginPeriod if you want something better. The timeXxx functions are part of the Windows multimedia support so you have to #include mmsystem.h and link with winmm.lib. There's a trick you can use to tell the linker to include a particular library—from your source module instead of adding the library to your project settings. It's #pragma comment and it goes like this:

// tell linker to use winmm.lib #pragma comment( lib, "winmm" )

#pragma comment has various other useful options you should check out on your own; the lib option stuffs a library-search record in your module's object file. I wrote a new version of ShowTime that uses timeGetTime and the pragma to link with winmm.lib. The ShowTime constructor now takes a granularity argument that ShowTime passes to timeBeginPeriod (default value=1). (PerfTest uses ShowTime to measure the time it takes to allocate a bunch of strings.) Aside from these simple modifications, ShowTime is pretty much the same as in the June 2004 issue, so I haven't bothered to show it here. But feel free to download the full source from the link at the top of this article.

Q I have a library written in C++ and I'm trying to expose some of my classes to the Microsoft® .NET Framework using the Managed Extensions. I was getting Compiler Error C3828, "placement arguments not allowed while creating instances of managed classes" and I was able to figure out that I need to insert the following code in my library:

#pragma push_macro("new") #undef new // managed stuff here #pragma pop_macro("new")

Q I have a library written in C++ and I'm trying to expose some of my classes to the Microsoft® .NET Framework using the Managed Extensions. I was getting Compiler Error C3828, "placement arguments not allowed while creating instances of managed classes" and I was able to figure out that I need to insert the following code in my library:

#pragma push_macro("new") #undef new // managed stuff here #pragma pop_macro("new")

This fixes my problem, but I don't really understand why because I have other modules that don't require this. Also, it's rather inconvenient to type the pragmas. Is there a better way to do this so I don't have to keep typing push_macro and pop_macro?

Jordie Marslan

A The problem isn't with C++, it's with MFC. One of the relatively more obscure features of C++ is that you can overload operator new, and you can even give it extra arguments. Normally, operator new receives only the size of the object to allocate:

void* operator new(size_t nAlloc) { return malloc(nAlloc); }

A The problem isn't with C++, it's with MFC. One of the relatively more obscure features of C++ is that you can overload operator new, and you can even give it extra arguments. Normally, operator new receives only the size of the object to allocate:

void* operator new(size_t nAlloc) { return malloc(nAlloc); }

But you can overload operator new with whatever additional parameters you like, as long as you supply them when you call new. This is what MFC does in its various App Wizards. A typical MFC program (.cpp) file has the following lines near the top, usually generated by the App Wizard:

#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif

MFC redefines new to DEBUG_NEW. And what is DEBUG_NEW? afx.h tells the story:

// (simplified) #ifdef _DEBUG # define DEBUG_NEW new(THIS_FILE, __LINE__) #else # define DEBUG_NEW new #endif

In debug builds, MFC overloads operator new to take a couple of extra arguments, like so:

void* operator new(size_t nSize, LPCSTR lpszFileName, int nLine);

The overloaded version has the same size argument as ordinary new, but adds two more args: the source file name and line number. So whenever you write

pfoo = new CFoo(..);

the preprocessor converts it to:

pfoo = new (sizeof(CFoo), THIS_FILE, __LINE__) CFoo(...);

__FILE__ (used to initialize THIS_FILE) and __LINE__ are special preprocessor symbols that hold the file name and line number of the current module being compiled. The whole point is that when your app leaks, MFC can display a message like:

Shame on you! You didn't free the CFoo object in foo.cpp, line 127!

This is a great boon for debugging, but it messes everything up when you use managed C++ because the new operator the common language runtime (CLR) uses for managed objects doesn't understand placement arguments. But MFC has redefined new using #define which causes the preprocessor to make a straight lexical substitution. When the Managed Extensions see the extra arguments, they have a conniption. That's why you need the #pragmas push_macro/pop_macro to temporarily undefine what MFC has defined, then redefine it again to restore MFC's universe.

The only way I can think of to work around this problem is to delete MFC's redefinition of new and replace it with something called mfcnew, as shown in Figure 2. The only drawback is that you have to remember to type mfcnew when you want to allocate normal C++ objects, and ordinary new when you allocate managed objects, as shown here:

pfoo = mfcnew CFoo(...); CManagedClass *pmc = new CManagedClass();

Figure 2 MFCNew

//////////////////////////////////////////////////////////////// // MSDN Magazine — September 2004 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual Studio .NET 2003 on Windows XP. Tab size=3. // // This program shows how to avoid "placement new" errors when using // managed extensions with MFC's DEBUG_NEW. This requires defining a new // symbol, mfcnew, which you must remember to use to allocate MFC // objects. // #include "stdafx.h" #include "resource.h" #using <mscorlib.dll> // Define mfcnew to DEBUG_NEW or ordinary new. #ifdef _DEBUG #define mfcnew DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #else #define mfcnew new #endif using namespace System; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { return -1; } // Allocate MFC CString with mfcnew // Since this string is never freed, MFC reports the memory leak if // you run in the debuggger. CString *s = mfcnew CString(_T("This is an MFC CString.\n")); _tprintf((LPCTSTR)*s); // Allocate managed String with new String* s2 = new String(S"This is a managed string literal."); Console::WriteLine(s2); return 0; }

If you forget to use mfcnew, you won't get the automatic memory-leak reporting. If you think this is pretty grody (I do), you'll be happy to know that all of this will be fixed in Visual C++® 2005, which does just the opposite: it has a gcnew operator that you must use to allocate managed objects. There's no chance of conflicting with ordinary operator new. In my opinion, this is a better solution because it's good to force programmers to know when they're allocating objects on the managed versus the native heap. Also, it leaves open the future possibility of allocating native objects on the managed heap and vice versa, through the automatic creation of suitable proxy classes that use GCHandle or whatever else is required.

In case you're wondering why the extra arguments are called "placement arguments," it's because originally the purpose was to allow you to allocate an object at a particular place in memory. For example, you could create an operator new(size_t size, void* p) that simply returns the pointer passed:

void* operator new(size_t size, void* p) { return p; }

Now you can call

new(p) CFoo ;

with a pointer p if you want to create the CFoo object at location p instead of letting malloc allocate the memory for you.

In any case, you're right, it's a pain to use push/pop_macro, and it makes your code look ugly to boot. For now, there's nothing much you can do—though another even simpler solution is to segregate all your managed and native code into separate source files and delete the DEBUG_NEW section from the managed module. This doesn't work if your code is tightly mixed—in other words, if you create managed and unmanaged objects from the same function or code block.

Q I've found a lot of articles about how to dock a toolbar, persist its state, and so on, and my application is doing all of that perfectly. Now I'm trying to implement the same "lock toolbars" feature found in Microsoft Internet Explorer and many other apps. It seems that once you call the EnableDocking member of CToolBar and the EnableDocking member of CMainFrame there is no way back to remove the "dockability" of a toolbar. Do you have any suggestions on how I can lock my MFC toolbars?

Q I've found a lot of articles about how to dock a toolbar, persist its state, and so on, and my application is doing all of that perfectly. Now I'm trying to implement the same "lock toolbars" feature found in Microsoft Internet Explorer and many other apps. It seems that once you call the EnableDocking member of CToolBar and the EnableDocking member of CMainFrame there is no way back to remove the "dockability" of a toolbar. Do you have any suggestions on how I can lock my MFC toolbars?

Sandro Franchi

A You're right, MFC doesn't have any code to let you lock/unlock toolbars, and there's no function to disable docking once you've enabled it. Never fear, in Windows there's always a way! I wrote a little class, CLockBar, that lets you lock your toolbars and a test program, LBTest, that shows how to use it.

A You're right, MFC doesn't have any code to let you lock/unlock toolbars, and there's no function to disable docking once you've enabled it. Never fear, in Windows there's always a way! I wrote a little class, CLockBar, that lets you lock your toolbars and a test program, LBTest, that shows how to use it.

Using CLockBar is easy. All you have to do is instantiate a CLockBar in your main frame class, one for each toolbar you want to lock, as shown in the following code:

class CMainFrame : public CFrameWnd { ••• protected: CToolBar m_wndToolBar; CLockBar m_lockToolbar; // toolbar lock };

Next, you have to install the CLockBar. The best place is in your main frame's OnCreate function:

int CMainFrame::OnCreate(...) { // create toolbar as normal m_lockToolbar.Install(&m_wndToolBar); return 0; }

With the lock installed, you can now call CLockBar::SetLocked with TRUE or FALSE any time to lock/unlock the toolbar. LBTest has a View | Lock Toolbar command that toggles the lock. Figure 3 shows the source for the main frame class in LBTest, which is where LBTest uses CLockBar.

Figure 3 MainFrm

MainFrm.h

/ //////////////////////////////////////////////////////////////// // MSDN Magazine — August 2004 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual Studio .NET 2003 on Windows XP. Tab size=3. // #include "LockBar.h" ////////////////// // Typical main frame. Uses CLockBar to lock the toolbar. // class CMainFrame : public CFrameWnd { public: CMainFrame(); virtual ~CMainFrame(); protected: DECLARE_DYNAMIC(CMainFrame) CStatusBar m_wndStatusBar; CToolBar m_wndToolBar; CLockBar m_lockToolbar; // toolbar lock virtual BOOL PreCreateWindow(CREATESTRUCT& cs); afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnLockBars(); afx_msg void OnUpdateLockBars(CCmdUI* pCmdUI); DECLARE_MESSAGE_MAP() };

MainFrm.cpp

//////////////////////////////////////////////////////////////// // MSDN Magazine — August 2004 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual Studio .NET 2003 on Windows XP. Tab size=3. // #include "StdAfx.h" #include "MainFrm.h" #include "resource.h" IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() ON_COMMAND(ID_VIEW_LOCKBARS, OnLockBars) ON_UPDATE_COMMAND_UI(ID_VIEW_LOCKBARS, OnUpdateLockBars) END_MESSAGE_MAP() // typical stuff omitted int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ••• VERIFY(m_wndToolBar.CreateEx(this)); VERIFY(m_wndToolBar.LoadToolBar(IDR_MAINFRAME)); m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC | CBRS_GRIPPER); // Enable toolbar docking m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); // Install toolbar lock m_lockToolbar.Install(&m_wndToolBar); return 0; } ////////////////// // Command: toggle toolbar locked state // void CMainFrame::OnLockBars() { m_lockToolbar.SetLocked(!m_lockToolbar.GetLocked()); // toggle lock } ////////////////// // Command update: display checkmark for toolbar locked state // void CMainFrame::OnUpdateLockBars(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_lockToolbar.GetLocked()); }

I've shown you how to use CLockBar, but how does it work? When I first set out to implement CLockBar, I made various attempts to get inside MFC to disable docking—that is, to undo what EnableDocking does. This turned out to be rather difficult. The MFC code for docking toolbars is fairly inscrutable, even with source. I'm sure it's possible eventually to succeed along this road—but why bother when there's a much simpler way?

The only thing you have to do in order to prevent the user from dragging the toolbar is block any mouse message that would trigger dragging. This is what the CLockBar class does: if the toolbar is locked, it blocks mouse messages that would drag the toolbar. You don't want to block the mouse messages if the user is pressing one of the buttons, only when the user tries to drag the toolbar by its gripper or non-client area.

It turns out MFC already has a function to determine when the mouse is in the "draggable" area of the toolbar: OnToolHitTest. If you look inside MFC at the source code for CControlBar::OnLButtonDown (the base class for CToolBar and all control bars), you'll see where MFC initiates dragging:

void CControlBar::OnLButtonDown(...) { // only start dragging if clicked in "void" space if (m_pDockBar != NULL && OnToolHitTest(pt, NULL) == -1) { // start the drag } else { // ignore—pass to base class CWnd CWnd::OnLButtonDown(...); } }

OnToolHitTest is the function MFC uses to determine whether to display a tooltip and when. For toolbars, this is precisely when the mouse is over a button. If the mouse is not over a button, it's in "dead" space such as the gripper or edge where the user can drag. So all you have to do to prevent dragging is eat the mouse message when OnToolHitTest returns -1. This is precisely what CLockBar does, as shown in the following code:

// in CLockBar::WindowProc if ((msg==WM_LBUTTONDOWN || msg==WM_LBUTTONDBLCLK) && m_bLocked) { CPoint pt(lp); if (m_pBar->OnToolHitTest(pt, NULL) == -1) return 0; // eat it }

CLockBar derives from CSubclassWnd, a class I frequently use in my columns to intercept window messages sent to another window. When you install the lock, CLockBar subclasses your toolbar in the old-fashioned Windows sense of installing its own window proc. CLockBar then gets first crack at all messages sent to the toolbar. CSubclassWnd manages the subclassing, and CLockBar::WindowProc is the CSubclassWnd-override that handles specific messages—in this case, WM_LBUTTONDOWN and WM_LBUTTONDBLCLK. (Did you know that with MFC floating toolbars, double-clicking the toolbar toggles it from docked to undocked state?) The upshot is that when the lock is set, CLockBar prevents the toolbar from ever seeing a mouse message that would cause MFC to enter drag mode. Figure 4 shows the full code for CLockBar. You should note that SetLock(TRUE) calls DockControlBar to dock the toolbar just in case it's not already docked. Happy programming!

Figure 4 LockBar

LockBar.h

//////////////////////////////////////////////////////////////// // MSDN Magazine — August 2004 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual Studio .NET 2003 on Windows XP. Tab size=3. // #include "Subclass.h" ////////////////// // Plug-in class to support locking toolbars. To use: instantiate one of // these for each toolbar you want to be able to lock. From your // InitInstance call: // // m_lock.Install(&m_wndToolbar); // // Then call m_lock.SetLocked(TRUE/FALSE) to lock/unlock the toolbar. // class CLockBar : public CSubclassWnd { protected: CToolBar* m_pBar; // toolbar I am locking BOOL m_bLocked; // whether locked or not public: CLockBar(); ~CLockBar(); // Call this to install the lock BOOL Install(CToolBar* pBar) { ASSERT(pBar && m_pBar==NULL); m_pBar = pBar; return HookWindow(pBar); } // Get/set locked state BOOL GetLocked() { return m_bLocked; } void SetLocked(BOOL bLocked); // message trap for msgs sent to toolbar virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp); };

LockBar.cpp

//////////////////////////////////////////////////////////////// // MSDN Magazine — August 2004 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual Studio .NET 2003 on Windows XP. Tab size=3. // #include "StdAfx.h" #include "LockBar.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CLockBar::CLockBar() : m_pBar(NULL), m_bLocked(0) { } CLockBar::~CLockBar() { } void CLockBar::SetLocked(BOOL bLocked) { CFrameWnd* pFrame = m_pBar->GetParentFrame(); ASSERT(pFrame); m_bLocked = bLocked; DWORD dwStyle = m_pBar->GetBarStyle(); if (bLocked) { pFrame->DockControlBar(m_pBar); // dock if not already dwStyle &= ~CBRS_GRIPPER; // turn off gripper } else { dwStyle |= CBRS_GRIPPER; } m_pBar->SetBarStyle(dwStyle); pFrame->RecalcLayout(); // make frame recalc toolbar sizes } ////////////////// // Trap messages sent to control bar. // LRESULT CLockBar::WindowProc(UINT msg, WPARAM wp, LPARAM lp) { if ((msg==WM_LBUTTONDOWN || msg==WM_LBUTTONDBLCLK) && m_bLocked) { // Got click or double-click and toolbar is locked: if mouse in // "dead zone" then ignore the message—don't pass to control bar // CPoint pt(lp); if (m_pBar->OnToolHitTest(pt, NULL) == -1) return 0; // return without handling: bypass control bar // dragging! } // pass unhandled messages subclassed window—this is important! return CSubclassWnd::WindowProc(msg, wp, lp); }

Send your questions and comments for Paul to  cppqa@microsoft.com.

Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at https://www.dilascia.com.