Thursday, February 04, 2010

Building and Debugging Tips in Visual C++

Building and Debugging Tips in Visual C++

(Jan 2010-Present)
This document compile my ongoing experience with Visual C++.  There are many more tips I know, but they are related to generic C++.  I'm thinking about creating a separate document for them or just put them here.

Can we debug a DLL project?  Why can we set a DLL project as a startup project, even though it is not executable per se?

Yes, we can debug a DLL project.  Plus, it does make sense to make it a startup project.  In fact, setting it as a startup project is a way to debug the DLL project, provided that we have any executable file to call the DLL.

Complete info is a available in this link.  Here, I quoted the essential information for Visual C++.


To specify the calling application in a C++ project

  1. In Solution Explorer, select the DLL project.

  2. On the View menu, choose Property Pages.

  3. In the Project Property Pages window, in the Configuration drop-down list, choose Debug.

  4. Open the Configuration Properties folder, and select the Debugging category.

  5. In the Debugger to launch list, choose Local Windows Debugger or Remote Windows Debugger.

  6. In the Command or Remote Command box, click the drop-down arrow, and select Browse from the list to locate the application. Alternatively, type the path and name of the application.

  7. Type any necessary program arguments in the Command Arguments box.


What if a break point cannot be hit because 'no symbol has been loaded for this document'.

In Visual C++, this seems to be a common problem.  First of all, we have to check if we have debugging symbols loaded or not.  Go to the 'Output' window (this is typically one of the tabs at the bottom of your VC++ window).   Then, look for a line telling about loading DLL.  For example,  there should be a line like this

'TesterConsole.exe': Loaded 'F:\Work\A.dll', [IMPORTANT MESSAGE HERE]

Next, check what your IMPORTANT MESSAGE HERE is.   If it is 'Binary was not built with debug information', we have to check a few points in our project properties.  Go to the project properties.  In the tab 'Configuration Properties -> C/C++ -> General', it is best that 'Debug Information Format' is a good one, which is 'Program Database (/Zi)' and 'Program Database for Edit & Continue (/ZI)'.  Then, go to a neigboring tab 'Optimization', it is highly recommended to set 'Optimization' to 'Disabled (/Od)'.

The final step is check your linker option.  Go to the tab 'Configuration Properties -> Linker -> Debugging'.  Next, set 'Generate Debug Info' to 'Yes (/DEBUG)'.
(Some additional information is in this link.)

If your IMPORTANT MESSAGE HERE is NOT about 'Binary was not built with debug information', chances are your debugging information is not synchronized with your DLL or your PDB (program data base) file is corrupt.  Try cleaning and rebuilding the affected DLL project.

Stand Between Debug and Release Modes

In some cases, it is too time consuming to debug a program in the Debug mode for a computationally intensive application, especially if we only want to see a crash site and call stack.  In such cases, we can choose to create another configuration.  Let's call the configuration 'Release w Assertion Enabled'.  This is actually a release mode without a preprocessor symbol NDEBUG.  Without NDEBUG, all assertion mechanisms are performed like in a debug mode, but the code is optimized.  If your program crashes, you can see the crash site and call stack, even though you cannot see accurate values of variables.  This is significantly useful when we need to get some idea about the problem. 

At this point, you might ask, 'Why do we need another configuration?' and 'Why don't we just remove NDEBUG during debugging process and insert it back when debugging is done?'  The answer is if we change any preprocessor symbol, the compiler must re-compile everything.  This is very time consuming if your project is large.  Moreover, in most cases, the debugging process never ends.  We have to go back and forth between release and debug builds all the time.  Creating a separate build, therefore, is a better way for a large and computationally intensive project in the long run.

Playing around with Visual C++, I found that Visual C++ might behave strangely if I modify a debug mode by just removing the preprocessor symbol _DEBUG and letting the executable call another library built in a release mode.  In the scenario, the run-time check might find that the heap or stack around a variable from the library is corrupted, but there is actually nothing wrong.

String Conversion

String conversion is usually necessary when we work with XML data, as XML uses Unicode character set by default.  Most parts of our work, however, may mainly use the ANSI character set.  This typically requires us to employ CA2W to convert an ANSI text to a Unicode text.  The usage of CA2W, however, may be quite unnatural to most of us.

For example, the use LPCWSTR szr = CA2W(szReplaceFile); is actually invalid because LPCTSTR becomes a pointer to a temporary string created during a conversion.  Therefore, it will be scrapped immediately after CA2W returns.  The correct use is CA2W szr(szReplaceFile);.  Look strange, right?  No doubt, a macro is usually strange.

Assertion Failed at the line ASSERT(AfxGetThread() == NULL)

This assertion is at the line 380 in appcore.cpp of Visual C++ 2008--version 9.0.  (Note that other versions may have this assertion at different lines.)  This issue arises if there are multiple MFC-application threads running at the same time (to be more specific, they are generated by CWinApp objects).  It is usually caused by calling MFC DLLs that comes with their own 'theApp', an object derived from CWinApp.  When these DLLs are initialized, they may start their own MFC-application threads, while we expect to have only one MFC-application thread--the one from our executable project. 

There is a discussion about this issue in this link.  Unfortunately, it does not tell us much about any practical approach for a solution. Thus, we have to figure this out ourselves.  If your application involves many DLLs, you probably have no idea what DLLs are culprits.  After trying for hours, I finally come up with a systematic method to identify the problematic DLLs and, of course, I have a solution for this problem.

To begin, set a break point at the assertion line ASSERT(AfxGetThread() == NULL).  Then, start the debugger.  At the break point, check 'Call Stack'.  (Those who are not familiar with debugging and the call stack might want to see this reference.)  If the call stack involves a DLL that is not mfcXXd.dll, such a DLL is likely to be a culprit, especially if it uses MFC in a shared library.  

Next, check the source code of the library and look for the file with 'theApp' in the suspect library.  Then, check its InitInstance() function.  If you find no AFX_MANAGE_STATE(AfxGetStaticModuleState( )); at the beginning of InitInstance(), insert it at the very beginning of the function.  Then, continue this process until every involved CWinApp DLL is properly equipped with AFX_MANAGE_STATE(AfxGetStaticModuleState( )).  

If the AFX_MANAGE_STATE causes linker errors, you might have an invalid preprocessor symbol.  The CWinApp DLL is usually configured with _USRDLL, not _LIB, because it is a dynamically linked library.  You may find that changing _LIB to _USRDLL helps solve your linker errors.

Halt at First-Chance Exceptions

First-chance exceptions in Visual C++ are a kind of access violation with code c0000005.  Many of them are not critical and we can possibly ignore them.  However, it is better, if we can get rid all of them, as your customers may demand that no compiler/linker warnings and no such exceptions are present when you ship your product.  

Visual C++, nonetheless, may not stop at the problematic point and just continue.  This makes it hard to tackle the problem.  So, we might want to force break for all exception.  The exception sheet can be accessed from menu Debug -> Exceptions (also by Ctrl-Alt-E, see Figure FCE1) in Visual Studio 2008.

If all exceptions are to be tackled, check all of the check boxes there so that all exception will be thrown (and we are forced to handle or get rid of them from the first place).  See Figure FCE2.  If only first chance exceptions, which are in fact a kind of access violtion, are to be handled, go to Win32 Exceptions and check Access Violation (Figure FCE3). 

Note that there is a button on the right that can reset exception settings to default and can even add a new one to the list (Figure FCE 4).  Adding a new one can possibly add our custom exceptions to the list, I belive (not try it myself yet).

Figure FCE1.  Exeption setting menu

Figure FCE2.  Set to throw all exceptions known by Visual C++.

Figure FCE3. Focus on first-chance exception.  However, it is recommended to (finally) handle all kinds of exceptions that come into your way.

Figure FCE4.  Remember that we can reset the settings or add a new exception to the list.

Acknowledgement: Peter Kellner

Program Exits with Code 0, but Unexpectedly

Typically, a program exiting with code 0 means there is nothing wrong.  However, sometimes a program exits in this way unexpectedly.  Also, nothing shows up at all.  I encountered this problem when I tried to build a program that uses a statically linked library.  That library, nonetheless, was created as a dynamically linked library before.  Therefore, there is a variable theApp inside its main file.

In general, this does not cause any problem and we can switch between dynamic-lib and static-lib configurations any time we want.  Nevertheless, there is one thing we need to be careful.  In a static-lib configuration, we are not supposed to let the program execution see theApp.  Why?  Because theApp will be initialized and start an instance of an MFC application thread.  Subsequently, theApp just exits with code 0.  This problem arises if we add something to the library main file and that file is in a part of compilation/execution.

To check if that is the actual cause of the problem, set a break point at the beginning of InitInstance() in a suspect library.  If the function is called, theApp is initialized in a wrong way.  Therefore, we need to find a way to prevent theApp to get involved.  This can be done by removing things that you add to the library main file.  If that means a lot of work, try using the macro to exclude theApp. 

For example:
#ifdef _NO_THEAPP
CLibApp theApp;

The above lines of code should effectively deal with the problem, provided that _NO_THEAPP is defined properly (as a preprocessor, for example).

Pinyo Taeprasartsit

(This document is viewable at and


Anonymous said...

I think the suggestion you put to resolve the problem of
"Assertion Failed at the line ASSERT(AfxGetThread() == NULL)" is quite effective.

Thank you very much!

Anonymous said...

Many Thanks, 'String Conversion' section very helpful

Anonymous said...

Thank you so much..!!

Anonymous said...

Thank you so much..!!

Rachid1245 said...

Thank you very much. You suggestion solve the problem.