Click here to Skip to main content
Licence CPOL
First Posted 22 Feb 2006
Views 69,317
Downloads 621
Bookmarked 21 times

Implementing the __FUNCTION__ Macro in VC++ 6.0

By | 27 Mar 2006 | Article
Tired of searching for the __FUNCTION__ macro to debug your apps in VC++ 6.0? Here's a little overkill to replicate it
Sample Image - xtrace.jpg

Introduction

Ok, so you're stuck with VC++ 6.0 (can't upgrade to .NET) but you need that nifty __FUNCTION__ macro for your debug sessions or, as in my case for debug session logging, and you Googled for it all over the world (including China) to get such ambiguous answers as "I'm a 100% sure that VC++ 6.0 implements it" and "get .NET you @$$#ole!". So you're outdated, sour and two steps shy of self-suicide (what an oxymoron).

Enter The Code Project (where you should have looked in the first place) to find that there's a whole world of portable executable image help APIs and symbols and processes and threads you really never cared about before, and... oh well.

There's also a whole debug section on CodeProject I didn't know about. Now get to Zoltan Csizmadia's article in which he implements the Extended Trace functions. My version is a downsized one from that of Csizmadia's, in which I only retrieve function names (to the point of omitting class names and arguments) to produce the kind of output shown in the figure. Granted, the code looks like overkill to achieve what .NET (VC++ 7.0 to my understanding) does intrinsically, but it works for me, and I bet you something not too worthy that intrinsically means going through a similar image-symbol-scanning process. In any case, I learned a lot about PEs, the Image Help API, debugging and a lot of stuff I wasn't really looking for, and ultimately, I got away with what I wanted to do.

To learn about all that stuff, get to the debug section of The Code Project, for I will not go into it in this article. The code is almost self-explanatory and fairly commented; just add the two files (header and CPP) to your project, add the #include directives at the proper places, and call InitSymInfo() upon start, and KillSymInfo() before exiting; use the __FUNCTION__ macro anywhere in your code; it returns a (temp) LPCTSTR you can use as you like.

Zoltan's version does retrieve the full function prototype, its arguments and their values; if you need all that or just want to check it out, click here.

Thank Zoltan and enjoy!

License

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

About the Author

hector.j.rivas

Software Developer (Senior)

United States United States

Member

Hector J. Rivas has 25+ years of experience managing hardware and software development under many different operating systems, platforms and languages. He has developed microcontroller interfaces and PC games; authored computer based training lessons and delivered fully functional financial and administrative data-intensive applications, as well as image processing and other calculation-intensive applications. Mr. Rivas has also managed Y2K remediation, large scale platform migration and Web site projects, from R&D to actual deployment.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionDoes not work for me. StackWalk() fails with error code: error_partial_copy. Pinmembersondhi_chakraborty18:48 11 Feb '12  
GeneralRe: callStack.AddrFrame.Offset = 0 PinmemberMYeager569:34 30 Sep '10  
QuestionSymGetSymFromAddr in GetFuncInfo fails due to GetCurrentProcess returning 0xffffffff PinmemberX-treem4:51 18 Aug '06  
AnswerRe: SymGetSymFromAddr in GetFuncInfo fails due to GetCurrentProcess returning 0xffffffff PinmemberTor2k6:14 22 Aug '06  
GeneralRe: SymGetSymFromAddr in GetFuncInfo fails due to GetCurrentProcess returning 0xffffffff Pinmembercavalierfire22:53 22 May '08  
Generalmy modification Pinmembercomealong20:07 25 Jul '06  
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* xtrace.cpp by Hector J. Rivas, torjo2k@hotmail.com from "ExtendedTrace"
* by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
*
* A Win32 VC++ 6.0 implementation of the __FUNCTION__ macro that works for me.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
 
#if defined(_DEBUG) && defined(WIN32)
 
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <ImageHlp.h>
#include "xtrace.h"
 
// Unicode safe char* -> TCHAR* conversion
void PCSTR2LPTSTR(PCSTR lpszIn, LPTSTR lpszOut)
{
#if defined(UNICODE) || defined(_UNICODE)
    
     ULONG index = 0;
     PCSTR lpAct = lpszIn;
    
     for(;; lpAct++)
     {
          lpszOut[index++] = (TCHAR)(*lpAct);
         
          if (*lpAct == 0) break;
     }
#else
     strcpy(lpszOut, lpszIn);
#endif
}
 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* InitSymPath(): figure   out   the   path for the symbol files; the search
path is:
*
*           . +
*          __FILE__ (path) + Debug +
*           %_NT_SYMBOL_PATH% +
*           %_NT_ALTERNATE_SYMBOL_PATH% +
*           %SYSTEMROOT% +
*           %SYSTEMROOT%\System32 +
*           lpszIniPath
*
* NOTES: There is no size check for lpszSymbolPath. If you want to limit the
macro to
* symbols in your debug executable, you can omit the environment variables (
default).
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
 
void InitSymPath(PSTR lpszSymbolPath, PCSTR lpszIniPath, BOOL bSysPath)
{
     CHAR lpszPath[BUFFERSIZE] = "";
     CHAR lpszTemp[BUFFERSIZE] = "";
    
     // create the default path
     strcpy(lpszSymbolPath, ".;");
 
     // get the current path
     sprintf(lpszTemp, __FILE__);
 
     strcpy(lpszPath, strrev(strchr(strrev(lpszTemp), '\\')));
 
     strcat(lpszPath, "Debug");
 
     strcat(lpszSymbolPath, lpszPath);
 
     if (bSysPath)
     {
          // environment variable _NT_SYMBOL_PATH
          if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE))
          {
               strcat(lpszSymbolPath, ";");
               strcat(lpszSymbolPath, lpszPath);
          }
         
          // environment variable _NT_ALTERNATE_SYMBOL_PATH
          if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", lpszPath,
BUFFERSIZE))
          {
               strcat(lpszSymbolPath, ";");
               strcat(lpszSymbolPath, lpszPath);
          }
         
          // environment variable SYSTEMROOT
          if (GetEnvironmentVariableA("SYSTEMROOT", lpszPath, BUFFERSIZE))
          {
               strcat(lpszSymbolPath, ";");
               strcat(lpszSymbolPath, lpszPath);
              
               // SYSTEMROOT\System32
               strcat(lpszSymbolPath, ";");
               strcat(lpszSymbolPath, lpszPath);
               strcat(lpszSymbolPath, "\\System32");
          }
     }
    
     // Add any user defined path
     if (lpszIniPath != NULL)
     {
          if (lpszIniPath[0] != '\0')
          {
               strcat(lpszSymbolPath, ";");
               strcat(lpszSymbolPath, lpszIniPath);
          }
     }
}
 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* InitSymInfo(): initializes the symbol files
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
 
BOOL InitSymInfo(PCSTR lpszInitialSymbolPath, BOOL bSysPath)
{
     CHAR     lpszSymbolPath[BUFFERSIZE];
    
     DWORD     symOptions = SymGetOptions();
    
     // set current image help API options; according to the SDK docs, with
     // SYMOPT_DEFERRED_LOADS: "Symbols are not loaded until a reference is made
     // requiring the symbols be loaded. This is the fastest, most efficient way
to use
     // the symbol handler.". SYMOPT_UNDNAME is excluded to do the undecoration
     // ourselves.
     symOptions |= SYMOPT_DEFERRED_LOADS;
     symOptions &= ~SYMOPT_UNDNAME;
    
     SymSetOptions(symOptions);
    
     // get the search path for the symbol files
     InitSymPath(lpszSymbolPath, lpszInitialSymbolPath, bSysPath);
    
     return SymInitialize(GetCurrentProcess(), lpszSymbolPath, TRUE);
}
 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* GetFuncInfo(): Get function prototype from address and stack address
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
 
BOOL GetFuncInfo(ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol)
{
     BOOL ret = FALSE;
    
     DWORD dwDisp = 0, dwSymSize = 10000;
    
     TCHAR lpszUDSymbol[BUFFERSIZE] = _T("?");
    
     CHAR lpszANSIUDSymbol[BUFFERSIZE] = "?";
         
     PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, dwSymSize);
 
     ZeroMemory(pSym, dwSymSize);
    
     pSym->SizeOfStruct   = dwSymSize;
     pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL);
 
     // Set the default to unknown
     _tcscpy(lpszSymbol, _T("?"));
 
     // Get symbol info
     if (SymGetSymFromAddr(GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym))
     {
          // Make the symbol readable for humans
          UnDecorateSymbolName(pSym->Name,
                                   lpszANSIUDSymbol,
                                   BUFFERSIZE,
                                   UNDNAME_COMPLETE                  |
                                   UNDNAME_NO_THISTYPE             |
                                   UNDNAME_NO_SPECIAL_SYMS        |
                                   UNDNAME_NO_MEMBER_TYPE             |
                                   UNDNAME_NO_MS_KEYWORDS             |
                                   UNDNAME_NO_ACCESS_SPECIFIERS |
                                   UNDNAME_NO_ARGUMENTS);
 
          // Symbol information is ANSI string
          PCSTR2LPTSTR(lpszANSIUDSymbol, lpszUDSymbol);
 
          lpszSymbol[0] = _T('\0');
 
          _tcscat(lpszSymbol, lpszUDSymbol);
  
          ret = TRUE;
     }
 
     GlobalFree(pSym);
 
     return ret;
}
 
#ifndef _MANUAL_INIT
 
class InitXTrace {
public:
     InitXTrace()
     {
          InitSymInfo(NULL);
     }
} initXTrace;
 
#endif // #ifndef _MANUAL_INIT
 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* GetFuncName(): return the undecorated function name
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
 
LPCTSTR GetFuncName()
{
     BOOL               bResult = FALSE;
 
     STACKFRAME      callStack;
 
     TCHAR               lpszFnInfo[BUFFERSIZE];
    
     HANDLE            hProcess = GetCurrentProcess();
     HANDLE            hThread   = GetCurrentThread();
 
     // initialize a stack frame struct in preparation to walk the stack
     ZeroMemory(&callStack, sizeof(callStack));
 

     __asm {
          call     getEIP;
getEIP:
          pop          callStack.AddrPC.Offset;
          mov          callStack.AddrStack.Offset, Esp;
          mov          callStack.AddrFrame.Offset, Ebp;
     }
    
     callStack.AddrPC.Mode         = AddrModeFlat;
     callStack.AddrStack.Mode   = AddrModeFlat;
     callStack.AddrFrame.Mode   = AddrModeFlat;
 
     // obtain a stack trace of the calling function (i.e., omit this one)
     for (ULONG n = 0; n < 2; n++)
     {
          bResult = StackWalk(IMAGE_FILE_MACHINE_I386,
                                   hProcess,
                                   hThread,
                                   &callStack,
                                   NULL,
                                   NULL,
                                   SymFunctionTableAccess,
                                   SymGetModuleBase,
                                   NULL);
     }
 
     if (bResult && callStack.AddrFrame.Offset != 0)
     {
          GetFuncInfo(callStack.AddrPC.Offset, callStack.AddrFrame.Offset, lpszFnInfo);
 
          // from now on its all personal display preferences with string manipulation
 
          // tokenize the undecorated returned symbol to omit the class name
          CHAR* lpszToken = strtok(lpszFnInfo, "::");
          CHAR   lpszLast[BUFFERSIZE] = "";
 
          while (lpszToken != NULL)
          {
               strcpy(lpszLast, lpszToken);
               lpszToken = strtok(NULL, "::");
          }
 
          // append a delimiter, so that our display in printf instructions is
          // 'functionname: message' for debug builds and 'message' for release builds,
          // using the format string "%smessage" and __FUNCTION__ as an argument
          strcat(lpszLast, ": ");
 
          return lpszLast;
     }
 
     return "";
}
 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* KillSymInfo(): uninitialize the loaded symbol files
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
BOOL KillSymInfo() { return SymCleanup(GetCurrentProcess()); }
 
#endif //_DEBUG && WIN32
 
Sleepy | :zzz:
QuestionRe: my modification Pinmemberchris1752:19 26 Jul '06  
AnswerRe: my modification Pinmembercomealong20:21 26 Jul '06  
GeneralRe: my modification PinmemberTor2k8:24 29 Jul '06  
Question__FUNCTION__ not working in console debug mode Pinmemberchris1754:13 6 Jul '06  
AnswerRe: __FUNCTION__ not working in console debug mode Pinmemberjuls335:25 7 Jul '06  
GeneralRe: __FUNCTION__ not working in console debug mode Pinmemberdasdasa20:05 27 Aug '06  
GeneralRe: __FUNCTION__ not working in console debug mode Pinmemberconrad Braam5:17 14 Jan '08  
Question__FUNCTION__ working in Release Mode? Pinmemberchris1752:37 3 Jul '06  
AnswerRe: __FUNCTION__ working in Release Mode? PinmemberGraham Reeds3:52 7 Jul '06  
QuestionRe: __FUNCTION__ working in Release Mode? Pinmemberchris1753:56 7 Jul '06  
AnswerRe: __FUNCTION__ working in Release Mode? [modified] PinmemberTor2k14:44 12 Jul '06  
AnswerRe: __FUNCTION__ working in Release Mode? Pinmemberdasdasa1:55 25 Aug '06  
GeneralRe: __FUNCTION__ working in Release Mode? Pinmemberdasdasa2:05 25 Aug '06  
QuestionQuestion about UnDecorateSymbolName Flags Pinmemberchris1751:32 30 Jun '06  
AnswerRe: Question about UnDecorateSymbolName Flags PinmemberTor2k15:19 12 Jul '06  
GeneralcallStack.AddrFrame.Offset = 0 PinmemberRick Crone5:28 2 Mar '06  
GeneralRe: callStack.AddrFrame.Offset = 0 PinmemberTor2k15:10 10 Mar '06  
GeneralRe: callStack.AddrFrame.Offset = 0 PinmemberGraham Reeds4:11 7 Jul '06  
GeneralRe: callStack.AddrFrame.Offset = 0 PinmemberTor2k15:33 12 Jul '06  

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

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120529.1 | Last Updated 27 Mar 2006
Article Copyright 2006 by hector.j.rivas
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid