Click here to Skip to main content
15,900,816 members
Articles / Web Development / HTML

Walking the callstack

Rate me:
Please Sign up or sign in to vote.
4.95/5 (140 votes)
14 Nov 2005BSD12 min read 1.6M   19.1K   361   376
This article describes the (documented) way to walk a callstack for any thread (own, other and remote). It has an abstraction layer, so the calling app does not need to know the internals.

Sample Image - StackWalker.gif

This project is now maintained on GitHub / https://github.com/JochenKalmbach/StackWalker

...

Introduction

In some cases, you need to display the callstack of the current thread or you are just interested in the callstack of other threads / processes. Therefore, I wrote this project.

The goal for this project was the following:

  • Simple interface to generate a callstack
  • C++ based to allow overwrites of several methods
  • Hiding the implementation details (API) from the class' interface
  • Support of x86, x64 and IA64 architecture
  • Default output to debugger-output window (but can be customized)
  • Support of user-provided read-memory-function
  • Support of the widest range of development-IDEs (VC5-VC8)
  • Most portable solution to walk the callstack

Background

To walk the callstack, there is a documented interface: StackWalk64. Starting with Win9x/W2K, this interface is in the dbghelp.dll library (on NT, it is in imagehlp.dll). But the function name (StackWalk64) has changed starting with W2K (before it was called StackWalk (without the 64))! This project only supports the newer Xxx64-funtions. If you need to use it on older systems, you can download the redistributable for NT/W9x.

The latest dbghelp.dll can always be downloaded with the Debugging Tools for Windows. This also contains the symsrv.dll which enables the use of the public Microsoft symbols-server (can be used to retrieve debugging information for system-files; see below).

Using the Code

The usage of the class is very simple. For example, if you want to display the callstack of the current thread, just instantiate a StackWalk object and call the ShowCallstack member:

C++
#include <windows.h>
#include "StackWalker.h"

void Func5() { StackWalker sw; sw.ShowCallstack(); }
void Func4() { Func5(); }
void Func3() { Func4(); }
void Func2() { Func3(); }
void Func1() { Func2(); }

int main()
{
  Func1();
  return 0;
}

This produces the following output in the debugger-output window:

[...] (output stripped)
d:\privat\Articles\stackwalker\stackwalker.cpp (736): StackWalker::ShowCallstack
d:\privat\Articles\stackwalker\main.cpp (4): Func5
d:\privat\Articles\stackwalker\main.cpp (5): Func4
d:\privat\Articles\stackwalker\main.cpp (6): Func3
d:\privat\Articles\stackwalker\main.cpp (7): Func2
d:\privat\Articles\stackwalker\main.cpp (8): Func1
d:\privat\Articles\stackwalker\main.cpp (13): main
f:\vs70builds\3077\vc\crtbld\crt\src\crt0.c (259): mainCRTStartup
77E614C7 (kernel32): (filename not available): _BaseProcessStart@4

You can now double-click on a line and the IDE will automatically jump to the desired file/line.

Providing Own output-mechanism

If you want to direct the output to a file or want to use some other output-mechanism, you simply need to derive from the StackWalker class. You have two options to do this: only overwrite the OnOutput method or overwrite each OnXxx-function. The first solution (OnOutput) is very easy and uses the default-implementation of the other OnXxx-functions (which should be enough for most of the cases). To output also to the console, you need to do the following:

C++
class MyStackWalker : public StackWalker
{
public:
  MyStackWalker() : StackWalker() {}
protected:
  virtual void OnOutput(LPCSTR szText)
    { printf(szText); StackWalker::OnOutput(szText); }
};

Retrieving Detailed Callstack Information

If you want detailed information about the callstack (like loaded-modules, addresses, errors, ...) you can overwrite the corresponding methods. The following methods are provided:

C++
class StackWalker
{
protected:
  virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
  virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size,
    DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
  virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
  virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
};

These methods are called during the generation of the callstack.

Various Kinds of callstacks

In the constructor of the class, you need to specify if you want to generate callstacks for the current process or for another process. The following constructors are available:

C++
class StackWalker
{
public:
  StackWalker(
    int options = OptionsAll,
    LPCSTR szSymPath = NULL,
    DWORD dwProcessId = GetCurrentProcessId(),
    HANDLE hProcess = GetCurrentProcess()
    );
  // Just for other processes with
  // default-values for options and symPath
  StackWalker(
    DWORD dwProcessId,
    HANDLE hProcess
    );
};

To do the actual stack-walking, you need to call the following functions:

C++
class StackWalker
{
public:
  BOOL ShowCallstack(
    HANDLE hThread = GetCurrentThread(),
    CONTEXT *context = NULL,
    PReadProcessMemoryRoutine readMemoryFunction = NULL,
    LPVOID pUserData = NULL
    );
};

Displaying the callstack of an Exception

With this StackWalker, you can also display the callstack inside an exception handler. You only need to write a filter-function which does the stack-walking:

C++
// The exception filter function:
LONG WINAPI ExpFilter(EXCEPTION_POINTERS* pExp, DWORD dwExpCode)
{
  StackWalker sw;
  sw.ShowCallstack(GetCurrentThread(), pExp->ContextRecord);
  return EXCEPTION_EXECUTE_HANDLER;
}

// This is how to catch an exception:
__try
{
  // do some ugly stuff...
}
__except (ExpFilter(GetExceptionInformation(), GetExceptionCode()))
{
}

Points of Interest

Context and callstack

To walk the callstack of a given thread, you need at least two facts:

  • The context of the thread

    The context is used to retrieve the current Instruction Pointer and the values for the Stack Pointer (SP) and sometimes the Frame Pointer (FP). The difference between SP and FP is in short: SP points to the latest address on the stack. FP is used to reference the arguments for a function. See also Difference Between Stack Pointer and Frame Pointer. But only the SP is essential for the processor. The FP is only used by the compiler. You can also disable the usage of FP (see: /Oy (Frame-Pointer Omission)).

  • The callstack

    The callstack is a memory-region which contains all the data/addresses of the callers. This data must be used to retrieve the callstack (as the name says). The most important part is: this data-region must not change until the stack-walking is finished! This is also the reason why the thread must be in the Suspended state to retrieve a valid callstack. If you want to do a stack-walking for the current thread, then you must not change the callstack memory after the addresses which are declared in the context record.

Initializing the STACKFRAME64-structure

To successfully walk the callstack with StackWalk64, you need to initialize the STACKFRAME64-structure with meaningful values. In the documentation of StackWalk64, there is only a small note about this structure:

  • The first call to this function will fail if the AddrPC and AddrFrame members of the STACKFRAME64 structure passed in the StackFrame parameter are not initialized.

According to this documentation, most programs only initialize AddrPC and AddrFrame; and this had worked until the newest dbhhelp.dll (v5.6.3.7). Now, you also need to initialize AddrStack. After having some trouble with this (and other problems), I talked to the dbghelp-team and got the following answer (2005-08-02; my own comments are written in italics!):

  • AddrStack should always be set to the stack pointer value for all platforms. You can certainly publish that AddrStack should be set. You're also welcome to say that new releases of dbghelp are now requiring this.
  • Given a current dbghelp, your code should:
    1. Always use StackWalk64
    2. Always set AddrPC to the current instruction pointer (Eip on x86, Rip on x64 and StIIP on IA64)
    3. Always set AddrStack to the current stack pointer (Esp on x86, Rsp on x64 and IntSp on IA64)
    4. Set AddrFrame to the current frame pointer when meaningful. On x86 this is Ebp, on x64 you can use Rbp (but is not used by VC2005B2; instead it uses Rdi!) and on IA64 you can use RsBSP. StackWalk64 will ignore the value when it isn't needed for unwinding.
    5. Set AddrBStore to RsBSP for IA64

Walking the callstack of the Current Thread

On x86 systems (prior to XP), there is no direct supported function to retrieve the context of the current thread. The recommended way is to throw an exception and catch it. Now you will have a valid context-record. The default way of capturing the context of the current thread is by doing some inline-assembler to retrieve EIP, ESP and EBP. If you want to use the documented way, then you need to define CURRENT_THREAD_VIA_EXCEPTION for the project. But you should be aware of the fact, that GET_CURRENT_CONTEXT is a macro which internally uses __try __except. Your function must be able to contain these statements.

Starting with XP and on x64 and IA64 systems, there is a documented function to retrieve the context of the current thread: RtlCaptureContext.

To do a stack-walking of the current thread, you simply need to do:

C++
StackWalker sw;
sw.ShowCallstack();

Walking the callstack of Other Threads in the Same Process

To walk the callstack of another thread inside the same process, you need to suspend the target thread (so the callstack will not change during the stack-walking). But you should be aware that suspending a thread in the same process might lead to dead-locks! (See: Why you never should call Suspend/TerminateThread (Part I, Part II, Part III))

If you have the handle to the thread, you can do the following to retrieve the callstack:

C++
MyStackWalker sw;
sw.ShowCallstack(hThread);

For a complete sample to retrieve the callstack of another thread, you can take a look at the demo-project.

Walking the callstack of Other Threads in Other Processes

The approach is almost the same as for walking the callstack for the current process. You only need to provide the ProcessID and a handle to the process (hProcess). Then you also need to suspend the thread to do the stack-walking. A complete sample to retrieve the callstack of another process is in the demo-project.

Reusing the StackWalk Instance

It is no problem to reuse the StackWalk instance, as long as you want to do the stack-walking for the same process. If you want to do a lot of stack-walking, it is recommended to reuse the instance. The reason is simple: if you create a new instance, then the symbol-files must be re-loaded for each instance. And this is really time-consuming. Also, it is not allowed to access the StackWalk functions from different threads (the dbghelp.dll is not thread-safe!). Therefore, it makes no sense to create more than one instance...

Symbol-Search-Path

By default, a symbol-search path (SymBuildPath and SymUseSymSrv) is provided to the dbghelp.dll. This path contains the following directories:

  • The optional provided szSymPath. If this parameter is provided, the option SymBuildPath is automatically set. Each path must be separated with a ";"
  • The current directory
  • The directory of the EXE
  • The environment variable _NT_SYMBOL_PATH
  • The environment variable _NT_ALTERNATE_SYMBOL_PATH
  • The environment variable SYSTEMROOT
  • The environment variable SYSTEMROOT appended with "\system32"
  • The public Microsoft symbol-server: SRV*%SYSTEMDRIVE%\websymbols*http://msdl.microsoft.com/download/symbols

Symbol-Server

If you want to use the public symbols for the OS-files from the Microsoft-Symbol-Server, you either need the Debugging Tools for Windows (then symsrv.dll and the latest dbghelp.dll will be found automatically) or you need to redistribute "dbghelp.dll" and "smysrv.dll" from this package!

Loading the Modules and Symbols

To successfully walk the callstack of a thread, dbghelp.dll requires that the modules are known by the library. Therefore, you need to "register" each module of the process via SymLoadModule64. To accomplish this, you need to enumerate the modules of the given process.

Starting with Win9x and W2K, it is possible to use the ToolHelp32-API. You need to make a snapshot (CreateToolhelp32Snapshot) of the process and then you can enumerate the modules via Module32First and Module32Next. Normally, the ToolHelp functions are located in the kernel32.dll but on Win9x, it is located in a separate DLL: tlhelp32.dll. Therefore, we need to check the functions in both DLLs.

If you have NT4, then the ToolHelp32-API is not available. But in NT4, you can use the PSAPI. To enumerate all modules, you need to call EnumProcessModules, but you only get the handles to the modules. To feed SymLoadModule64, you also need to query the ModuleBaseAddr, SizeOfImage (via GetModuleInformation), ModuleBaseName (via GetModuleBaseName) and ModuleFileName(Path) (via GetModuleFileNameEx).

dbghelp.dll

There are a couple of issues with dbghelp.dll.

  • The first is, there are two "teams" at Microsoft which redistribute the dbghelp.dll. One team is the OS-team, the other is the Debugging-Tools-Team (I don't know the real names...). In general, you can say: The dbghelp.dll provided with the Debugging Tools for Windows is the most recent version. One problem of these two teams is the different versioning of the dbghelp.dll. For example, for XP-SP1 the version is 5.1.2600.1106 dated 2002-08-29. The version 6.0.0017.0 which was redistributed from the debug-team is dated 2002-04-31. So there is at least a conflict in the date (the newer version is older). And it is even harder to decide which version is "better" (or has more functionality).
  • Starting with Me/W2K, the dbghelp.dll-file in the system32 is protected by the System File Protection. So if you want to use a newer dbghelp.dll, you need to redistribute the version from the Debugging Tools for Windows (put it in the same directory as your EXE). This leads to a problem on W2K if you want to walk the callstack for an app which was built using VC7 or later. The VC7 compiler generates a new PDB-format (called DIA). This PDB-format cannot be read with the dbghelp.dll which is installed with the OS. Therefore, you will not get very useful callstacks (or at least with no debugging information like filename, line, function name, ...). To overcome this problem, you need to redistribute a newer dbghelp.dll.
  • The dbghelp.dll version 6.5.3.7 has a bug or at least a documentation change of the StackWalk64 function. In the documentation, you can read:

    The first call to this function will fail if the AddrPC and AddrFrame members of the STACKFRAME64 structure passed in the StackFrame parameter are not initialized.

    and

    [The ContextRecord] parameter is required only when the MachineType parameter is not IMAGE_FILE_MACHINE_I386.

    But this is not true anymore. Now the callstack on x86-systems cannot be retrieved if you pass NULL as ContextRecord. From my point of view, this is a major documentation change. Now you either need to initialize the AddrStack as well, or provide a valid ContextRecord which contains the EIP, EBP and ESP registers!

  • See also comments in the Initializing the STACKFRAME64-structure chapter...

Options

To do some kind of modification of the behaviour, you can optionally specify some options. Here is the list of the available options:

C++
typedef enum StackWalkOptions
{
  // No addition info will be retrieved
  // (only the address is available)
  RetrieveNone = 0,

  // Try to get the symbol-name
  RetrieveSymbol = 1,

  // Try to get the line for this symbol
  RetrieveLine = 2,

  // Try to retrieve the module-infos
  RetrieveModuleInfo = 4,

  // Also retrieve the version for the DLL/EXE
  RetrieveFileVersion = 8,

  // Contains all the above
  RetrieveVerbose = 0xF,

  // Generate a "good" symbol-search-path
  SymBuildPath = 0x10,

  // Also use the public Microsoft-Symbol-Server
  SymUseSymSrv = 0x20,

  // Contains all the above "Sym"-options
  SymAll = 0x30,

  // Contains all options (default)
  OptionsAll = 0x3F
} StackWalkOptions;

Known Issues

  • NT/Win9x: This project only support the StackWalk64 function. If you need to use it on NT4/Win9x, you need to redistribute the dbghelp.dll for this platform.
  • Currently only supports ANSI-names in callbacks (of course, the project can be compiled with UNICODE...).
  • To open a remote thread, I used "OpenThread" which is not available on NT4/W9x. To have an example of doing this in NT4/Win9x, please refer to Remote Library.
  • Walking mixed-mode callstacks (managed/unmanaged) does only return the unmanaged functions.

History

  • 2005-07-27
    • First public release
    • Supports x86, x64 and IA64
  • 2005-07-28
    • Changed the description, so it does not make the impression that this is the only documented way to walk the callstack...
    • ShowCallstack(hThread, ...) now accepts a NULL CONTEXT (this forces to capture the context of this thread); it also was simplified (now only one function for all situations)
    • Added chapters: Symbol-Search-Path, Loading the modules and symbols and dbghelp.dll
  • 2005-08-01
    • Added VC7.0 project/solution files
    • Added more comment to the GET_CURRENT_CONTEXT define regarding the use of RtlCaptureContext function
    • Problem with uninitialized cnt variable solved
    • Workaround for wrongly defined GetFileVersionInfoSize and GetFileVersionInfo (on older PSDK-Version (VC7 and before), the first parameter was declared as LPTSTR instead of LPCTSTR)
    • Changed the used ContextFlags parameter from CONTEX_ALL to CONTEXT_FULL (this also works correctly and is supported in older PSDK-versions)
    • Now compiles on VC5/6 without installing a newer Platform-SDK (all missing declarations are now embedded)
    • Added VC6 project/solution files
    • Added a pUserData member to the ShowCallstack function and the PReadProcessMemoryRoutine declaration (to pass some user-defined data, which can be used in the readMemoryFunction-callback)
  • 2005-08-02
    • OnSymInit now also outputs the OS-version by default
    • Added example for doing an exception-callstack-walking in main.cpp (thanks to owillebo)
    • Correction in article about RtlCaptureContext. This function is also available starting with XP (thanks to Dan Moulding)
    • Added chapters: Initializing the STACKFRAME64-structure, Displaying the callstack of an exception
  • 2005-08-05
    • Removed some Lint issues... thanks to Okko Willeboordse!
    • Removed all warnings with /analyze switch from VC8
  • 2005-09-06
    • Fixed a small bug regarding VC5/6 and old PSDK version (OSVERSIONINFOEX is not present)
  • 2005-11-07
    • Detects an "endless-callstack" and terminates the stackwalking
    • Usage of RtlCaptureContext only on x64 and IA64 platforms

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Software Developer (Senior)
Germany Germany
1982: My first computer (VC20)
1984: Finished to build my first own computer (Z80)
1993: Mission-Volunteer in Papua New Guinea
1998: Dipl. Inform. (FH)
... working, working, working....

Comments and Discussions

 
GeneralRe: demo code hangs with a small modification Pin
aceamit17-May-07 20:49
aceamit17-May-07 20:49 
GeneralRe: demo code hangs with a small modification Pin
Jochen Kalmbach [MVP VC++]17-May-07 22:27
Jochen Kalmbach [MVP VC++]17-May-07 22:27 
GeneralRetrieving the callstack for current thread [modified] Pin
Member 236229-May-07 21:21
Member 236229-May-07 21:21 
GeneralRe: Retrieving the callstack for current thread Pin
Jochen Kalmbach [MVP VC++]17-May-07 22:36
Jochen Kalmbach [MVP VC++]17-May-07 22:36 
GeneralGetting the call stack for all threads Pin
Penneys24-Apr-07 23:23
Penneys24-Apr-07 23:23 
GeneralRe: Getting the call stack for all threads Pin
Jochen Kalmbach [MVP VC++]17-May-07 22:30
Jochen Kalmbach [MVP VC++]17-May-07 22:30 
GeneralI added output stream redirection feature to your code. Pin
MG Man7-Dec-06 14:31
MG Man7-Dec-06 14:31 
GeneralRe: I added output stream redirection feature to your code. Pin
MG Man7-Dec-06 14:34
MG Man7-Dec-06 14:34 
StackWalker.h

<br />
/**********************************************************************<br />
 * <br />
 * StackWalker.h<br />
 *<br />
 *<br />
 * History:<br />
 *  2005-07-27   v1    - First public release on http://www.codeproject.com/<br />
 *  (for additional changes see History in 'StackWalker.cpp'!<br />
 *<br />
 **********************************************************************/<br />
// #pragma once is supported starting with _MCS_VER 1000, <br />
// so we need not to check the version (because we only support _MSC_VER >= 1100)!<br />
#pragma once<br />
<br />
#ifndef IMP_EXP_EH<br />
    #ifdef STACK_WALKER_DLL_ENABLE<br />
        #ifdef STACK_WALKER_EXPORT<br />
            #define IMP_EXP_EH _declspec(dllexport)<br />
        #else<br />
            #define IMP_EXP_EH _declspec(dllimport)<br />
        #endif<br />
    #else<br />
        #define IMP_EXP_EH<br />
    #endif<br />
#endif<br />
<br />
#include <windows.h><br />
#include <iostream><br />
#include <string><br />
<br />
typedef unsigned int u32;<br />
typedef std::ostream  ST_OSTREAMA;<br />
typedef std::wostream ST_OSTREAMW;<br />
<br />
// special defines for VC5/6 (if no actual PSDK is installed):<br />
#if _MSC_VER < 1300<br />
typedef unsigned __int64 DWORD64, *PDWORD64;<br />
#if defined(_WIN64)<br />
typedef unsigned __int64 SIZE_T, *PSIZE_T;<br />
#else<br />
typedef unsigned long SIZE_T, *PSIZE_T;<br />
#endif<br />
#endif  // _MSC_VER < 1300<br />
<br />
// forward declaration<br />
class StackWalkerInternal;<br />
<br />
class IMP_EXP_EH StackWalker<br />
{<br />
public:<br />
  typedef enum StackWalkOptions<br />
  {<br />
    // No addition info will be retrived <br />
    // (only the address is available)<br />
    RetrieveNone = 0,<br />
    <br />
    // Try to get the symbol-name<br />
    RetrieveSymbol = 1,<br />
    <br />
    // Try to get the line for this symbol<br />
    RetrieveLine = 2,<br />
    <br />
    // Try to retrieve the module-infos<br />
    RetrieveModuleInfo = 4,<br />
    <br />
    // Also retrieve the version for the DLL/EXE<br />
    RetrieveFileVersion = 8,<br />
    <br />
    // Contains all the above<br />
    RetrieveVerbose = 0xF,<br />
    <br />
    // Generate a "good" symbol-search-path<br />
    SymBuildPath = 0x10,<br />
    <br />
    // Also use the public Microsoft-Symbol-Server<br />
    SymUseSymSrv = 0x20,<br />
    <br />
    // Contains all the abouve "Sym"-options.<br />
    SymAll = 0x30,<br />
    <br />
    // Contains all options except Microsoft-Symbol-Server (default)<br />
    OptionsAll = RetrieveVerbose | SymBuildPath<br />
  } StackWalkOptions;<br />
<br />
  StackWalker(<br />
    int options = OptionsAll, // 'int' is by design, to combine the enum-flags<br />
    LPCSTR szSymPath = NULL, <br />
    DWORD dwProcessId = GetCurrentProcessId(), <br />
    HANDLE hProcess = GetCurrentProcess()<br />
    );<br />
  StackWalker(DWORD dwProcessId, HANDLE hProcess);<br />
  virtual ~StackWalker();<br />
<br />
  typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(<br />
    HANDLE      hProcess,<br />
    DWORD64     qwBaseAddress,<br />
    PVOID       lpBuffer,<br />
    DWORD       nSize,<br />
    LPDWORD     lpNumberOfBytesRead,<br />
    LPVOID      pUserData  // optional data, which was passed in "ShowCallstack"<br />
    );<br />
<br />
  BOOL LoadModules();<br />
<br />
  /** The following function will register output streams. <br />
   * Setting input argument null will reset all the output stream member variables in the instance.<br />
   * It will make the class instance to print out result to console. If you explicitly register<br />
   * output stream, the class instance will print out to the registered stream.<br />
   * If you register Unicode, ASCII output stream will disabled, and vice versa. */<br />
  void OstreamRegister( ST_OSTREAMA* pos );<br />
  void OstreamRegister( ST_OSTREAMW* pwos );<br />
<br />
  // If the output stream is registered. This will write to the stream.<br />
  BOOL ShowCallstack(<br />
    const CONTEXT *context = NULL, <br />
    HANDLE hThread = GetCurrentThread(), <br />
    PReadProcessMemoryRoutine readMemoryFunction = NULL,<br />
    LPVOID pUserData = NULL  // optional to identify some data in the 'readMemoryFunction'-callback<br />
    );<br />
  <br />
#if _MSC_VER >= 1300<br />
// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" <br />
// in older compilers in order to use it... starting with VC7 we can declare it as "protected"<br />
protected:<br />
#endif<br />
	enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols<br />
<br />
protected:<br />
  // Entry for each Callstack-Entry<br />
  typedef struct CallstackEntry<br />
  {<br />
    DWORD64 offset;  // if 0, we have no valid entry<br />
    CHAR name[STACKWALK_MAX_NAMELEN];<br />
    CHAR undName[STACKWALK_MAX_NAMELEN];<br />
    CHAR undFullName[STACKWALK_MAX_NAMELEN];<br />
    DWORD64 offsetFromSmybol;<br />
    DWORD offsetFromLine;<br />
    DWORD lineNumber;<br />
    CHAR lineFileName[STACKWALK_MAX_NAMELEN];<br />
    DWORD symType;<br />
    LPCSTR symTypeString;<br />
    CHAR moduleName[STACKWALK_MAX_NAMELEN];<br />
    DWORD64 baseOfImage;<br />
    CHAR loadedImageName[STACKWALK_MAX_NAMELEN];<br />
  } CallstackEntry;<br />
<br />
  typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};<br />
<br />
  virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);<br />
  virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);<br />
  virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);<br />
  virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);<br />
  virtual void OnOutput(LPCSTR szTextA);<br />
  virtual void OnOutput(LPCWSTR szTextW);<br />
<br />
  StackWalkerInternal *m_sw;<br />
  HANDLE m_hProcess;<br />
  DWORD m_dwProcessId;<br />
  BOOL m_modulesLoaded;<br />
  LPSTR m_szSymPath;<br />
  ST_OSTREAMA* m_pos;         // Ascii Output Stream.<br />
  ST_OSTREAMW* m_pwos;        // Unicode Output Stream.<br />
  BOOL m_useUnicode;<br />
<br />
  int m_options;<br />
<br />
  static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);<br />
<br />
  friend StackWalkerInternal;<br />
};<br />
<br />
<br />
// The "ugly" assembler-implementation is needed for systems before XP<br />
// If you have a new PSDK and you only compile for XP and later, then you can use <br />
// the "RtlCaptureContext"<br />
// Currently there is no define which determines the PSDK-Version... <br />
// So we just use the compiler-version (and assumes that the PSDK is <br />
// the one which was installed by the VS-IDE)<br />
<br />
// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...<br />
//       But I currently use it in x64/IA64 environments...<br />
//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)<br />
<br />
#if defined(_M_IX86)<br />
#ifdef CURRENT_THREAD_VIA_EXCEPTION<br />
// TODO: The following is not a "good" implementation, <br />
// because the callstack is only valid in the "__except" block...<br />
#define GET_CURRENT_CONTEXT(c, contextFlags) \<br />
  do { \<br />
    memset(&c, 0, sizeof(CONTEXT)); \<br />
    EXCEPTION_POINTERS *pExp = NULL; \<br />
    __try { \<br />
      throw 0; \<br />
    } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \<br />
    if (pExp != NULL) \<br />
      memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \<br />
      c.ContextFlags = contextFlags; \<br />
  } while(0);<br />
#else<br />
// The following should be enough for walking the callstack...<br />
#define GET_CURRENT_CONTEXT(c, contextFlags) \<br />
  do { \<br />
    memset(&c, 0, sizeof(CONTEXT)); \<br />
    c.ContextFlags = contextFlags; \<br />
    __asm    call x \<br />
    __asm x: pop eax \<br />
    __asm    mov c.Eip, eax \<br />
    __asm    mov c.Ebp, ebp \<br />
    __asm    mov c.Esp, esp \<br />
  } while(0);<br />
#endif<br />
<br />
#else<br />
<br />
// The following is defined for x86 (XP and higher), x64 and IA64:<br />
#define GET_CURRENT_CONTEXT(c, contextFlags) \<br />
  do { \<br />
    memset(&c, 0, sizeof(CONTEXT)); \<br />
    c.ContextFlags = contextFlags; \<br />
    RtlCaptureContext(&c); \<br />
} while(0);<br />
#endif<br />



StackWalker.cpp

<br />
/**********************************************************************<br />
 * <br />
 * StackWalker.cpp<br />
 *<br />
 *<br />
 * History:<br />
 *  2005-07-27   v1    - First public release on http://www.codeproject.com/<br />
 *                       http://www.codeproject.com/threads/StackWalker.asp<br />
 *  2005-07-28   v2    - Changed the params of the constructor and ShowCallstack<br />
 *                       (to simplify the usage)<br />
 *  2005-08-01   v3    - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL <br />
 *                       (should also be enough)<br />
 *                     - Changed to compile correctly with the PSDK of VC7.0<br />
 *                       (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined:<br />
 *                        it uses LPSTR instead of LPCSTR as first paremeter)<br />
 *                     - Added declarations to support VC5/6 without using 'dbghelp.h'<br />
 *                     - Added a 'pUserData' member to the ShowCallstack function and the <br />
 *                       PReadProcessMemoryRoutine declaration (to pass some user-defined data, <br />
 *                       which can be used in the readMemoryFunction-callback)<br />
 *  2005-08-02   v4    - OnSymInit now also outputs the OS-Version by default<br />
 *                     - Added example for doing an exception-callstack-walking in main.cpp<br />
 *                       (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268)<br />
 *  2005-08-05   v5    - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse!<br />
 *<br />
 **********************************************************************/<br />
#include <windows.h><br />
#include <tchar.h><br />
#include <stdio.h><br />
#pragma comment(lib, "version.lib")  // for "VerQueryValue"<br />
<br />
#define STACK_WALKER_EXPORT         // This makes header export biased<br />
#include "StackWalker.h"<br />
<br />
<br />
// If VC7 and later, then use the shipped 'dbghelp.h'-file<br />
#if _MSC_VER >= 1300<br />
#include <dbghelp.h><br />
#else<br />
// inline the important dbghelp.h-declarations...<br />
typedef enum {<br />
    SymNone = 0,<br />
    SymCoff,<br />
    SymCv,<br />
    SymPdb,<br />
    SymExport,<br />
    SymDeferred,<br />
    SymSym,<br />
    SymDia,<br />
    SymVirtual,<br />
    NumSymTypes<br />
} SYM_TYPE;<br />
typedef struct _IMAGEHLP_LINE64 {<br />
    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_LINE64)<br />
    PVOID                       Key;                    // internal<br />
    DWORD                       LineNumber;             // line number in file<br />
    PCHAR                       FileName;               // full filename<br />
    DWORD64                     Address;                // first instruction of line<br />
} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;<br />
typedef struct _IMAGEHLP_MODULE64 {<br />
    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)<br />
    DWORD64                     BaseOfImage;            // base load address of module<br />
    DWORD                       ImageSize;              // virtual size of the loaded module<br />
    DWORD                       TimeDateStamp;          // date/time stamp from pe header<br />
    DWORD                       CheckSum;               // checksum from the pe header<br />
    DWORD                       NumSyms;                // number of symbols in the symbol table<br />
    SYM_TYPE                    SymType;                // type of symbols loaded<br />
    CHAR                        ModuleName[32];         // module name<br />
    CHAR                        ImageName[256];         // image name<br />
    CHAR                        LoadedImageName[256];   // symbol file name<br />
} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;<br />
typedef struct _IMAGEHLP_SYMBOL64 {<br />
    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_SYMBOL64)<br />
    DWORD64                     Address;                // virtual address including dll base address<br />
    DWORD                       Size;                   // estimated size of symbol, can be zero<br />
    DWORD                       Flags;                  // info about the symbols, see the SYMF defines<br />
    DWORD                       MaxNameLength;          // maximum size of symbol name in 'Name'<br />
    CHAR                        Name[1];                // symbol name (null terminated string)<br />
} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;<br />
typedef enum {<br />
    AddrMode1616,<br />
    AddrMode1632,<br />
    AddrModeReal,<br />
    AddrModeFlat<br />
} ADDRESS_MODE;<br />
typedef struct _tagADDRESS64 {<br />
    DWORD64       Offset;<br />
    WORD          Segment;<br />
    ADDRESS_MODE  Mode;<br />
} ADDRESS64, *LPADDRESS64;<br />
typedef struct _KDHELP64 {<br />
    DWORD64   Thread;<br />
    DWORD   ThCallbackStack;<br />
    DWORD   ThCallbackBStore;<br />
    DWORD   NextCallback;<br />
    DWORD   FramePointer;<br />
    DWORD64   KiCallUserMode;<br />
    DWORD64   KeUserCallbackDispatcher;<br />
    DWORD64   SystemRangeStart;<br />
    DWORD64  Reserved[8];<br />
} KDHELP64, *PKDHELP64;<br />
typedef struct _tagSTACKFRAME64 {<br />
    ADDRESS64   AddrPC;               // program counter<br />
    ADDRESS64   AddrReturn;           // return address<br />
    ADDRESS64   AddrFrame;            // frame pointer<br />
    ADDRESS64   AddrStack;            // stack pointer<br />
    ADDRESS64   AddrBStore;           // backing store pointer<br />
    PVOID       FuncTableEntry;       // pointer to pdata/fpo or NULL<br />
    DWORD64     Params[4];            // possible arguments to the function<br />
    BOOL        Far;                  // WOW far call<br />
    BOOL        Virtual;              // is this a virtual frame?<br />
    DWORD64     Reserved[3];<br />
    KDHELP64    KdHelp;<br />
} STACKFRAME64, *LPSTACKFRAME64;<br />
typedef<br />
BOOL<br />
(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(<br />
    HANDLE      hProcess,<br />
    DWORD64     qwBaseAddress,<br />
    PVOID       lpBuffer,<br />
    DWORD       nSize,<br />
    LPDWORD     lpNumberOfBytesRead<br />
    );<br />
typedef<br />
PVOID<br />
(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(<br />
    HANDLE  hProcess,<br />
    DWORD64 AddrBase<br />
    );<br />
typedef<br />
DWORD64<br />
(__stdcall *PGET_MODULE_BASE_ROUTINE64)(<br />
    HANDLE  hProcess,<br />
    DWORD64 Address<br />
    );<br />
typedef<br />
DWORD64<br />
(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(<br />
    HANDLE    hProcess,<br />
    HANDLE    hThread,<br />
    LPADDRESS64 lpaddr<br />
    );<br />
#define SYMOPT_CASE_INSENSITIVE         0x00000001<br />
#define SYMOPT_UNDNAME                  0x00000002<br />
#define SYMOPT_DEFERRED_LOADS           0x00000004<br />
#define SYMOPT_NO_CPP                   0x00000008<br />
#define SYMOPT_LOAD_LINES               0x00000010<br />
#define SYMOPT_OMAP_FIND_NEAREST        0x00000020<br />
#define SYMOPT_LOAD_ANYTHING            0x00000040<br />
#define SYMOPT_IGNORE_CVREC             0x00000080<br />
#define SYMOPT_NO_UNQUALIFIED_LOADS     0x00000100<br />
#define SYMOPT_FAIL_CRITICAL_ERRORS     0x00000200<br />
#define SYMOPT_EXACT_SYMBOLS            0x00000400<br />
#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS   0x00000800<br />
#define SYMOPT_IGNORE_NT_SYMPATH        0x00001000<br />
#define SYMOPT_INCLUDE_32BIT_MODULES    0x00002000<br />
#define SYMOPT_PUBLICS_ONLY             0x00004000<br />
#define SYMOPT_NO_PUBLICS               0x00008000<br />
#define SYMOPT_AUTO_PUBLICS             0x00010000<br />
#define SYMOPT_NO_IMAGE_SEARCH          0x00020000<br />
#define SYMOPT_SECURE                   0x00040000<br />
#define SYMOPT_DEBUG                    0x80000000<br />
#define UNDNAME_COMPLETE                 (0x0000)  // Enable full undecoration<br />
#define UNDNAME_NAME_ONLY                (0x1000)  // Crack only the name for primary declaration;<br />
#endif  // _MSC_VER < 1300<br />
<br />
// Some missing defines (for VC5/6):<br />
#ifndef INVALID_FILE_ATTRIBUTES<br />
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)<br />
#endif  <br />
<br />
<br />
// secure-CRT_functions are only available starting with VC8<br />
#if _MSC_VER < 1400<br />
#define strcpy_s strcpy<br />
#define strcat_s(dst, len, src) strcat(dst, src)<br />
#define _snprintf_s _snprintf<br />
#define _tcscat_s _tcscat<br />
#endif<br />
<br />
// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')<br />
#define USED_CONTEXT_FLAGS CONTEXT_FULL<br />
<br />
<br />
class StackWalkerInternal<br />
{<br />
public:<br />
  StackWalkerInternal(StackWalker *parent, HANDLE hProcess)<br />
  {<br />
    m_parent = parent;<br />
    m_hDbhHelp = NULL;<br />
    pSC = NULL;<br />
    m_hProcess = hProcess;<br />
    m_szSymPath = NULL;<br />
    pSFTA = NULL;<br />
    pSGLFA = NULL;<br />
    pSGMB = NULL;<br />
    pSGMI = NULL;<br />
    pSGO = NULL;<br />
    pSGSFA = NULL;<br />
    pSI = NULL;<br />
    pSLM = NULL;<br />
    pSSO = NULL;<br />
    pSW = NULL;<br />
    pUDSN = NULL;<br />
    pSGSP = NULL;<br />
  }<br />
  ~StackWalkerInternal()<br />
  {<br />
    if (pSC != NULL)<br />
      pSC(m_hProcess);  // SymCleanup<br />
    if (m_hDbhHelp != NULL)<br />
      FreeLibrary(m_hDbhHelp);<br />
    m_hDbhHelp = NULL;<br />
    m_parent = NULL;<br />
    if(m_szSymPath != NULL)<br />
      free(m_szSymPath);<br />
    m_szSymPath = NULL;<br />
  }<br />
  BOOL Init(LPCSTR szSymPath)<br />
  {<br />
    if (m_parent == NULL)<br />
      return FALSE;<br />
    // Dynamically load the Entry-Points for dbghelp.dll:<br />
    // First try to load the newsest one from<br />
    TCHAR szTemp[4096];<br />
    // But before wqe do this, we first check if the ".local" file exists<br />
    if (GetModuleFileName(NULL, szTemp, 4096) > 0)<br />
    {<br />
      _tcscat_s(szTemp, _T(".local"));<br />
      if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)<br />
      {<br />
        // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"<br />
        if (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)<br />
        {<br />
          _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));<br />
          // now check if the file exists:<br />
          if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)<br />
          {<br />
            m_hDbhHelp = LoadLibrary(szTemp);<br />
          }<br />
        }<br />
          // Still not found? Then try to load the 64-Bit version:<br />
        if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )<br />
        {<br />
          _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));<br />
          if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)<br />
          {<br />
            m_hDbhHelp = LoadLibrary(szTemp);<br />
          }<br />
        }<br />
      }<br />
    }<br />
    if (m_hDbhHelp == NULL)  // if not already loaded, try to load a default-one<br />
      m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") );<br />
    if (m_hDbhHelp == NULL)<br />
      return FALSE;<br />
    pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" );<br />
    pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" );<br />
<br />
    pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" );<br />
    pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" );<br />
    pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" );<br />
<br />
    pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );<br />
    pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );<br />
    pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );<br />
    pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );<br />
    //pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );<br />
    pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );<br />
    pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );<br />
    pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" );<br />
    pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" );<br />
<br />
    if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||<br />
      pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||<br />
      pSW == NULL || pUDSN == NULL || pSLM == NULL )<br />
    {<br />
      FreeLibrary(m_hDbhHelp);<br />
      m_hDbhHelp = NULL;<br />
      pSC = NULL;<br />
      return FALSE;<br />
    }<br />
<br />
    // SymInitialize<br />
    if (szSymPath != NULL)<br />
      m_szSymPath = _strdup(szSymPath);<br />
    if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)<br />
      this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);<br />
      <br />
    DWORD symOptions = this->pSGO();  // SymGetOptions<br />
    //symOptions |= SYMOPT_CASE_INSENSITIVE | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_IGNORE_CVREC<br />
    //    | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_LOAD_ANYTHING | SYMOPT_LOAD_LINES;<br />
    symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_ANYTHING | SYMOPT_LOAD_LINES;<br />
    // SymSetOptions<br />
    symOptions = this->pSSO(symOptions);<br />
<br />
    char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};<br />
    if (this->pSGSP != NULL)<br />
    {<br />
      if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)<br />
        this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);<br />
    }<br />
    char szUserName[1024] = {0};<br />
    DWORD dwSize = 1024;<br />
    GetUserNameA(szUserName, &dwSize);<br />
    this->m_parent->OnSymInit(buf, symOptions, szUserName);<br />
<br />
    return TRUE;<br />
  }<br />
<br />
  StackWalker *m_parent;<br />
<br />
  HMODULE m_hDbhHelp;<br />
  HANDLE m_hProcess;<br />
  LPSTR m_szSymPath;<br />
<br />
/*typedef struct IMAGEHLP_MODULE64_V3 {<br />
    DWORD    SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)<br />
    DWORD64  BaseOfImage;            // base load address of module<br />
    DWORD    ImageSize;              // virtual size of the loaded module<br />
    DWORD    TimeDateStamp;          // date/time stamp from pe header<br />
    DWORD    CheckSum;               // checksum from the pe header<br />
    DWORD    NumSyms;                // number of symbols in the symbol table<br />
    SYM_TYPE SymType;                // type of symbols loaded<br />
    CHAR     ModuleName[32];         // module name<br />
    CHAR     ImageName[256];         // image name<br />
    // new elements: 07-Jun-2002<br />
    CHAR     LoadedImageName[256];   // symbol file name<br />
    CHAR     LoadedPdbName[256];     // pdb file name<br />
    DWORD    CVSig;                  // Signature of the CV record in the debug directories<br />
    CHAR         CVData[MAX_PATH * 3];   // Contents of the CV record<br />
    DWORD    PdbSig;                 // Signature of PDB<br />
    GUID     PdbSig70;               // Signature of PDB (VC 7 and up)<br />
    DWORD    PdbAge;                 // DBI age of pdb<br />
    BOOL     PdbUnmatched;           // loaded an unmatched pdb<br />
    BOOL     DbgUnmatched;           // loaded an unmatched dbg<br />
    BOOL     LineNumbers;            // we have line number information<br />
    BOOL     GlobalSymbols;          // we have internal symbol information<br />
    BOOL     TypeInfo;               // we have type information<br />
    // new elements: 17-Dec-2003<br />
    BOOL     SourceIndexed;          // pdb supports source server<br />
    BOOL     Publics;                // contains public symbols<br />
};<br />
*/<br />
typedef struct IMAGEHLP_MODULE64_V2 {<br />
    DWORD    SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)<br />
    DWORD64  BaseOfImage;            // base load address of module<br />
    DWORD    ImageSize;              // virtual size of the loaded module<br />
    DWORD    TimeDateStamp;          // date/time stamp from pe header<br />
    DWORD    CheckSum;               // checksum from the pe header<br />
    DWORD    NumSyms;                // number of symbols in the symbol table<br />
    SYM_TYPE SymType;                // type of symbols loaded<br />
    CHAR     ModuleName[32];         // module name<br />
    CHAR     ImageName[256];         // image name<br />
    CHAR     LoadedImageName[256];   // symbol file name<br />
};<br />
<br />
<br />
  // SymCleanup()<br />
  typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );<br />
  tSC pSC;<br />
<br />
  // SymFunctionTableAccess64()<br />
  typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );<br />
  tSFTA pSFTA;<br />
<br />
  // SymGetLineFromAddr64()<br />
  typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,<br />
    OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );<br />
  tSGLFA pSGLFA;<br />
<br />
  // SymGetModuleBase64()<br />
  typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );<br />
  tSGMB pSGMB;<br />
<br />
  // SymGetModuleInfo64()<br />
  typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo );<br />
  tSGMI pSGMI;<br />
<br />
//  // SymGetModuleInfo64()<br />
//  typedef BOOL (__stdcall *tSGMI_V3)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo );<br />
//  tSGMI_V3 pSGMI_V3;<br />
<br />
  // SymGetOptions()<br />
  typedef DWORD (__stdcall *tSGO)( VOID );<br />
  tSGO pSGO;<br />
<br />
  // SymGetSymFromAddr64()<br />
  typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,<br />
    OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );<br />
  tSGSFA pSGSFA;<br />
<br />
  // SymInitialize()<br />
  typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );<br />
  tSI pSI;<br />
<br />
  // SymLoadModule64()<br />
  typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,<br />
    IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );<br />
  tSLM pSLM;<br />
<br />
  // SymSetOptions()<br />
  typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );<br />
  tSSO pSSO;<br />
<br />
  // StackWalk64()<br />
  typedef BOOL (__stdcall *tSW)( <br />
    DWORD MachineType, <br />
    HANDLE hProcess,<br />
    HANDLE hThread, <br />
    LPSTACKFRAME64 StackFrame, <br />
    PVOID ContextRecord,<br />
    PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,<br />
    PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,<br />
    PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,<br />
    PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );<br />
  tSW pSW;<br />
<br />
  // UnDecorateSymbolName()<br />
  typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,<br />
    DWORD UndecoratedLength, DWORD Flags );<br />
  tUDSN pUDSN;<br />
<br />
  typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);<br />
  tSGSP pSGSP;<br />
<br />
<br />
private:<br />
  // **************************************** ToolHelp32 ************************<br />
  #define MAX_MODULE_NAME32 255<br />
  #define TH32CS_SNAPMODULE   0x00000008<br />
  #pragma pack( push, 8 )<br />
  typedef struct tagMODULEENTRY32<br />
  {<br />
      DWORD   dwSize;<br />
      DWORD   th32ModuleID;       // This module<br />
      DWORD   th32ProcessID;      // owning process<br />
      DWORD   GlblcntUsage;       // Global usage count on the module<br />
      DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context<br />
      BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context<br />
      DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr<br />
      HMODULE hModule;            // The hModule of this module in th32ProcessID's context<br />
      char    szModule[MAX_MODULE_NAME32 + 1];<br />
      char    szExePath[MAX_PATH];<br />
  } MODULEENTRY32;<br />
  typedef MODULEENTRY32 *  PMODULEENTRY32;<br />
  typedef MODULEENTRY32 *  LPMODULEENTRY32;<br />
  #pragma pack( pop )<br />
<br />
  BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid)<br />
  {<br />
    // CreateToolhelp32Snapshot()<br />
    typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);<br />
    // Module32First()<br />
    typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);<br />
    // Module32Next()<br />
    typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);<br />
<br />
    // try both dlls...<br />
    const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };<br />
    HINSTANCE hToolhelp = NULL;<br />
    tCT32S pCT32S = NULL;<br />
    tM32F pM32F = NULL;<br />
    tM32N pM32N = NULL;<br />
<br />
    HANDLE hSnap;<br />
    MODULEENTRY32 me;<br />
    me.dwSize = sizeof(me);<br />
    BOOL keepGoing;<br />
    size_t i;<br />
<br />
    for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )<br />
    {<br />
      hToolhelp = LoadLibrary( dllname[i] );<br />
      if (hToolhelp == NULL)<br />
        continue;<br />
      pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");<br />
      pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");<br />
      pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");<br />
      if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )<br />
        break; // found the functions!<br />
      FreeLibrary(hToolhelp);<br />
      hToolhelp = NULL;<br />
    }<br />
<br />
    if (hToolhelp == NULL)<br />
      return FALSE;<br />
<br />
    hSnap = pCT32S( TH32CS_SNAPMODULE, pid );<br />
    if (hSnap == (HANDLE) -1)<br />
      return FALSE;<br />
<br />
    keepGoing = !!pM32F( hSnap, &me );<br />
    int cnt = 0;<br />
    while (keepGoing)<br />
    {<br />
      this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);<br />
      cnt++;<br />
      keepGoing = !!pM32N( hSnap, &me );<br />
    }<br />
    CloseHandle(hSnap);<br />
    FreeLibrary(hToolhelp);<br />
    if (cnt <= 0)<br />
      return FALSE;<br />
    return TRUE;<br />
  }  // GetModuleListTH32<br />
<br />
  // **************************************** PSAPI ************************<br />
  typedef struct _MODULEINFO {<br />
      LPVOID lpBaseOfDll;<br />
      DWORD SizeOfImage;<br />
      LPVOID EntryPoint;<br />
  } MODULEINFO, *LPMODULEINFO;<br />
<br />
  BOOL GetModuleListPSAPI(HANDLE hProcess)<br />
  {<br />
    // EnumProcessModules()<br />
    typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );<br />
    // GetModuleFileNameEx()<br />
    typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );<br />
    // GetModuleBaseName()<br />
    typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );<br />
    // GetModuleInformation()<br />
    typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );<br />
<br />
    HINSTANCE hPsapi;<br />
    tEPM pEPM;<br />
    tGMFNE pGMFNE;<br />
    tGMBN pGMBN;<br />
    tGMI pGMI;<br />
<br />
    DWORD i;<br />
    //ModuleEntry e;<br />
    DWORD cbNeeded;<br />
    MODULEINFO mi;<br />
    HMODULE *hMods = 0;<br />
    char *tt = NULL;<br />
    char *tt2 = NULL;<br />
    const SIZE_T TTBUFLEN = 8096;<br />
    int cnt = 0;<br />
<br />
    hPsapi = LoadLibrary( _T("psapi.dll") );<br />
    if (hPsapi == NULL)<br />
      return FALSE;<br />
<br />
    pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );<br />
    pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );<br />
    pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );<br />
    pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );<br />
    if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) )<br />
    {<br />
      // we couldn큧 find all functions<br />
      FreeLibrary(hPsapi);<br />
      return FALSE;<br />
    }<br />
<br />
    hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));<br />
    tt = (char*) malloc(sizeof(char) * TTBUFLEN);<br />
    tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);<br />
    if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )<br />
      goto cleanup;<br />
<br />
    if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )<br />
    {<br />
      //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );<br />
      goto cleanup;<br />
    }<br />
<br />
    if ( cbNeeded > TTBUFLEN )<br />
    {<br />
      //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );<br />
      goto cleanup;<br />
    }<br />
<br />
    for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )<br />
    {<br />
      // base address, size<br />
      pGMI(hProcess, hMods[i], &mi, sizeof mi );<br />
      // image file name<br />
      tt[0] = 0;<br />
      pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );<br />
      // module name<br />
      tt2[0] = 0;<br />
      pGMBN(hProcess, hMods[i], tt2, TTBUFLEN );<br />
<br />
      DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);<br />
      if (dwRes != ERROR_SUCCESS)<br />
        this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);<br />
      cnt++;<br />
    }<br />
<br />
  cleanup:<br />
    if (hPsapi != NULL) FreeLibrary(hPsapi);<br />
    if (tt2 != NULL) free(tt2);<br />
    if (tt != NULL) free(tt);<br />
    if (hMods != NULL) free(hMods);<br />
<br />
    return cnt != 0;<br />
  }  // GetModuleListPSAPI<br />
<br />
  DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)<br />
  {<br />
    CHAR *szImg = _strdup(img);<br />
    CHAR *szMod = _strdup(mod);<br />
    DWORD result = ERROR_SUCCESS;<br />
    if ( (szImg == NULL) || (szMod == NULL) )<br />
      result = ERROR_NOT_ENOUGH_MEMORY;<br />
    else<br />
    {<br />
      if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)<br />
        result = GetLastError();<br />
    }<br />
    ULONGLONG fileVersion = 0;<br />
    if ( (m_parent != NULL) && (szImg != NULL) )<br />
    {<br />
      // try to retrive the file-version:<br />
      if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0)<br />
      {<br />
        VS_FIXEDFILEINFO *fInfo = NULL;<br />
        DWORD dwHandle;<br />
        DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);<br />
        if (dwSize > 0)<br />
        {<br />
          LPVOID vData = malloc(dwSize);<br />
          if (vData != NULL)<br />
          {<br />
            if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)<br />
            {<br />
              UINT len;<br />
              TCHAR szSubBlock[] = _T("\\");<br />
              if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)<br />
                fInfo = NULL;<br />
              else<br />
              {<br />
                fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);<br />
              }<br />
            }<br />
            free(vData);<br />
          }<br />
        }<br />
      }<br />
<br />
      // Retrive some additional-infos about the module<br />
      IMAGEHLP_MODULE64_V2 Module;<br />
      const char *szSymType = "-unknown-";<br />
      if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)<br />
      {<br />
        switch(Module.SymType)<br />
        {<br />
          case SymNone:<br />
            szSymType = "-nosymbols-";<br />
            break;<br />
          case SymCoff:<br />
            szSymType = "COFF";<br />
            break;<br />
          case SymCv:<br />
            szSymType = "CV";<br />
            break;<br />
          case SymPdb:<br />
            szSymType = "PDB";<br />
            break;<br />
          case SymExport:<br />
            szSymType = "-exported-";<br />
            break;<br />
          case SymDeferred:<br />
            szSymType = "-deferred-";<br />
            break;<br />
          case SymSym:<br />
            szSymType = "SYM";<br />
            break;<br />
          case 8: //SymVirtual:<br />
            szSymType = "Virtual";<br />
            break;<br />
          case 9: // SymDia:<br />
            szSymType = "DIA";<br />
            break;<br />
        }<br />
      }<br />
      this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);<br />
    }<br />
    if (szImg != NULL) free(szImg);<br />
    if (szMod != NULL) free(szMod);<br />
    return result;<br />
  }<br />
public:<br />
  BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId)<br />
  {<br />
    // first try toolhelp32<br />
    if (GetModuleListTH32(hProcess, dwProcessId))<br />
      return true;<br />
    // then try psapi<br />
    return GetModuleListPSAPI(hProcess);<br />
  }<br />
<br />
<br />
  BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo)<br />
  {<br />
    if(this->pSGMI == NULL)<br />
    {<br />
      SetLastError(ERROR_DLL_INIT_FAILED);<br />
      return FALSE;<br />
    }<br />
    // First try to use the larger ModuleInfo-Structure<br />
//    memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));<br />
//    pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);<br />
//    if (this->pSGMI_V3 != NULL)<br />
//    {<br />
//      if (this->pSGMI_V3(hProcess, baseAddr, pModuleInfo) != FALSE)<br />
//        return TRUE;<br />
//      // check if the parameter was wrong (size is bad...)<br />
//      if (GetLastError() != ERROR_INVALID_PARAMETER)<br />
//        return FALSE;<br />
//    }<br />
    // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)...<br />
    pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);<br />
    void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...<br />
    if (pData == NULL)<br />
    {<br />
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);<br />
      return FALSE;<br />
    }<br />
    memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));<br />
    if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*) pData)) {<br />
      // only copy as much memory as is reserved...<br />
      memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));<br />
      pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);<br />
      free(pData);<br />
      return TRUE;<br />
    }<br />
    free(pData);<br />
    SetLastError(ERROR_DLL_INIT_FAILED);<br />
    return FALSE;<br />
  }<br />
};<br />
<br />
<br />
// #############################################################<br />
<br />
StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)<br />
{<br />
  this->m_options = OptionsAll;<br />
  this->m_modulesLoaded = FALSE;<br />
  this->m_hProcess = hProcess;<br />
  this->m_sw = new StackWalkerInternal(this, this->m_hProcess);<br />
  this->m_dwProcessId = dwProcessId;<br />
  this->m_szSymPath = NULL;<br />
  this->m_pwos = NULL;<br />
}<br />
StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)<br />
{<br />
  this->m_options = options;<br />
  this->m_modulesLoaded = FALSE;<br />
  this->m_hProcess = hProcess;<br />
  this->m_sw = new StackWalkerInternal(this, this->m_hProcess);<br />
  this->m_dwProcessId = dwProcessId;<br />
  if (szSymPath)<br />
    this->m_szSymPath = _strdup(szSymPath);<br />
  else<br />
    this->m_szSymPath = NULL;<br />
  this->m_pwos = NULL;<br />
}<br />
<br />
StackWalker::~StackWalker()<br />
{<br />
  if (m_szSymPath)<br />
    free(m_szSymPath);<br />
  m_szSymPath = NULL;<br />
  if (this->m_sw)<br />
    delete this->m_sw;<br />
  this->m_sw = NULL;<br />
}<br />
<br />
BOOL StackWalker::LoadModules()<br />
{<br />
  if (this->m_sw == NULL)<br />
  {<br />
    SetLastError(ERROR_DLL_INIT_FAILED);<br />
    return FALSE;<br />
  }<br />
  if (m_modulesLoaded != FALSE)<br />
    return TRUE;<br />
<br />
  // Build the sym-path:<br />
  char *szSymPath = NULL;<br />
  if (this->m_options & SymBuildPath)<br />
  {<br />
    const size_t nSymPathLen = 4096;<br />
    szSymPath = (char*) malloc(nSymPathLen);<br />
    if (szSymPath == NULL)<br />
    {<br />
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);<br />
      return FALSE;<br />
    }<br />
    szSymPath[0] = 0;<br />
    // Now first add the (optional) provided sympath:<br />
    if (this->m_szSymPath)<br />
    {<br />
      strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);<br />
      strcat_s(szSymPath, nSymPathLen, ";");<br />
    }<br />
<br />
    strcat_s(szSymPath, nSymPathLen, ".;");<br />
<br />
    const size_t nTempLen = 1024;<br />
    char szTemp[nTempLen];<br />
    // Now add the current directory:<br />
    if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)<br />
    {<br />
      szTemp[nTempLen-1] = 0;<br />
      strcat_s(szSymPath, nSymPathLen, szTemp);<br />
      strcat_s(szSymPath, nSymPathLen, ";");<br />
    }<br />
<br />
    // Now add the path for the main-module:<br />
    if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)<br />
    {<br />
      szTemp[nTempLen-1] = 0;<br />
      for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)<br />
      {<br />
        // locate the rightmost path separator<br />
        if ( (*p == '\\') || (*p == '/') || (*p == ':') )<br />
        {<br />
          *p = 0;<br />
          break;<br />
        }<br />
      }  // for (search for path separator...)<br />
      if (strlen(szTemp) > 0)<br />
      {<br />
        strcat_s(szSymPath, nSymPathLen, szTemp);<br />
        strcat_s(szSymPath, nSymPathLen, ";");<br />
      }<br />
    }<br />
    if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)<br />
    {<br />
      szTemp[nTempLen-1] = 0;<br />
      strcat_s(szSymPath, nSymPathLen, szTemp);<br />
      strcat_s(szSymPath, nSymPathLen, ";");<br />
    }<br />
    if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)<br />
    {<br />
      szTemp[nTempLen-1] = 0;<br />
      strcat_s(szSymPath, nSymPathLen, szTemp);<br />
      strcat_s(szSymPath, nSymPathLen, ";");<br />
    }<br />
    if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)<br />
    {<br />
      szTemp[nTempLen-1] = 0;<br />
      strcat_s(szSymPath, nSymPathLen, szTemp);<br />
      strcat_s(szSymPath, nSymPathLen, ";");<br />
      // also add the "system32"-directory:<br />
      strcat_s(szTemp, nTempLen, "\\system32");<br />
      strcat_s(szSymPath, nSymPathLen, szTemp);<br />
      strcat_s(szSymPath, nSymPathLen, ";");<br />
    }<br />
<br />
    if ((this->m_options & SymBuildPath) && (this->m_options & SymUseSymSrv))<br />
    {<br />
      if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)<br />
      {<br />
        szTemp[nTempLen-1] = 0;<br />
        strcat_s(szSymPath, nSymPathLen, "SRV*");<br />
        strcat_s(szSymPath, nSymPathLen, szTemp);<br />
        strcat_s(szSymPath, nSymPathLen, "\\websymbols");<br />
        strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");<br />
      }<br />
      else<br />
        strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");<br />
    }<br />
  }<br />
<br />
  // First Init the whole stuff...<br />
  BOOL bRet = this->m_sw->Init(szSymPath);<br />
  if (szSymPath != NULL) free(szSymPath); szSymPath = NULL;<br />
  if (bRet == FALSE)<br />
  {<br />
    this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);<br />
    SetLastError(ERROR_DLL_INIT_FAILED);<br />
    return FALSE;<br />
  }<br />
<br />
  bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);<br />
  if (bRet != FALSE)<br />
    m_modulesLoaded = TRUE;<br />
  return bRet;<br />
}<br />
<br />
<br />
// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction<br />
// This has to be done due to a problem with the "hProcess"-parameter in x64...<br />
// Because this class is in no case multi-threading-enabled (because of the limitations <br />
// of dbghelp.dll) it is "safe" to use a static-variable<br />
static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;<br />
static LPVOID s_readMemoryFunction_UserData = NULL;<br />
<br />
void StackWalker::OstreamRegister( ST_OSTREAMA* pos )<br />
{<br />
    m_pos = pos;<br />
    m_pwos = NULL;<br />
}<br />
<br />
void StackWalker::OstreamRegister( ST_OSTREAMW* pwos )<br />
{<br />
    m_pos = NULL;<br />
    m_pwos = pwos;<br />
}<br />
        <br />
BOOL StackWalker::ShowCallstack(const CONTEXT *context, HANDLE hThread, <br />
        PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)<br />
{<br />
  CONTEXT c;<br />
  CallstackEntry csEntry;<br />
  IMAGEHLP_SYMBOL64 *pSym = NULL;<br />
  StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module;<br />
  IMAGEHLP_LINE64 Line;<br />
  int frameNum;<br />
<br />
  if (m_modulesLoaded == FALSE)<br />
    this->LoadModules();  // ignore the result...<br />
<br />
  if (this->m_sw->m_hDbhHelp == NULL)<br />
  {<br />
    SetLastError(ERROR_DLL_INIT_FAILED);<br />
    return FALSE;<br />
  }<br />
<br />
  s_readMemoryFunction = readMemoryFunction;<br />
  s_readMemoryFunction_UserData = pUserData;<br />
<br />
  if (context == NULL)<br />
  {<br />
    // If no context is provided, capture the context<br />
    if (hThread == GetCurrentThread())<br />
    {<br />
      GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS);<br />
    }<br />
    else<br />
    {<br />
      SuspendThread(hThread);<br />
      memset(&c, 0, sizeof(CONTEXT));<br />
      c.ContextFlags = USED_CONTEXT_FLAGS;<br />
      if (GetThreadContext(hThread, &c) == FALSE)<br />
      {<br />
        ResumeThread(hThread);<br />
        return FALSE;<br />
      }<br />
    }<br />
  }<br />
  else<br />
    c = *context;<br />
<br />
  // init STACKFRAME for first call<br />
  STACKFRAME64 s; // in/out stackframe<br />
  memset(&s, 0, sizeof(s));<br />
  DWORD imageType;<br />
#ifdef _M_IX86<br />
  // normally, call ImageNtHeader() and use machine info from PE header<br />
  imageType = IMAGE_FILE_MACHINE_I386;<br />
  s.AddrPC.Offset = c.Eip;<br />
  s.AddrPC.Mode = AddrModeFlat;<br />
  s.AddrFrame.Offset = c.Ebp;<br />
  s.AddrFrame.Mode = AddrModeFlat;<br />
  s.AddrStack.Offset = c.Esp;<br />
  s.AddrStack.Mode = AddrModeFlat;<br />
#elif _M_X64<br />
  imageType = IMAGE_FILE_MACHINE_AMD64;<br />
  s.AddrPC.Offset = c.Rip;<br />
  s.AddrPC.Mode = AddrModeFlat;<br />
  s.AddrFrame.Offset = c.Rsp;<br />
  s.AddrFrame.Mode = AddrModeFlat;<br />
  s.AddrStack.Offset = c.Rsp;<br />
  s.AddrStack.Mode = AddrModeFlat;<br />
#elif _M_IA64<br />
  imageType = IMAGE_FILE_MACHINE_IA64;<br />
  s.AddrPC.Offset = c.StIIP;<br />
  s.AddrPC.Mode = AddrModeFlat;<br />
  s.AddrFrame.Offset = c.IntSp;<br />
  s.AddrFrame.Mode = AddrModeFlat;<br />
  s.AddrBStore.Offset = c.RsBSP;<br />
  s.AddrBStore.Mode = AddrModeFlat;<br />
  s.AddrStack.Offset = c.IntSp;<br />
  s.AddrStack.Mode = AddrModeFlat;<br />
#else<br />
#error "Platform not supported!"<br />
#endif<br />
<br />
  pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);<br />
  if (!pSym) goto cleanup;  // not enough memory...<br />
  memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);<br />
  pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);<br />
  pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;<br />
<br />
  memset(&Line, 0, sizeof(Line));<br />
  Line.SizeOfStruct = sizeof(Line);<br />
<br />
  memset(&Module, 0, sizeof(Module));<br />
  Module.SizeOfStruct = sizeof(Module);<br />
<br />
  for (frameNum = 0; ; ++frameNum )<br />
  {<br />
    // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())<br />
    // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can<br />
    // assume that either you are done, or that the stack is so hosed that the next<br />
    // deeper frame could not be found.<br />
    // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!<br />
    if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) )<br />
    {<br />
      this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset);<br />
      break;<br />
    }<br />
<br />
    csEntry.offset = s.AddrPC.Offset;<br />
    csEntry.name[0] = 0;<br />
    csEntry.undName[0] = 0;<br />
    csEntry.undFullName[0] = 0;<br />
    csEntry.offsetFromSmybol = 0;<br />
    csEntry.offsetFromLine = 0;<br />
    csEntry.lineFileName[0] = 0;<br />
    csEntry.lineNumber = 0;<br />
    csEntry.loadedImageName[0] = 0;<br />
    csEntry.moduleName[0] = 0;<br />
    if (s.AddrPC.Offset == s.AddrReturn.Offset)<br />
    {<br />
      this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);<br />
      break;<br />
    }<br />
    if (s.AddrPC.Offset != 0)<br />
    {<br />
      // we seem to have a valid PC<br />
      // show procedure info (SymGetSymFromAddr64())<br />
      if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)<br />
      {<br />
        // TODO: Mache dies sicher...!<br />
        strcpy_s(csEntry.name, pSym->Name);<br />
        // UnDecorateSymbolName()<br />
        this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );<br />
        this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );<br />
      }<br />
      else<br />
      {<br />
        this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);<br />
      }<br />
<br />
      // show line number info, NT5.0-method (SymGetLineFromAddr64())<br />
      if (this->m_sw->pSGLFA != NULL )<br />
      { // yes, we have SymGetLineFromAddr64()<br />
        if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE)<br />
        {<br />
          csEntry.lineNumber = Line.LineNumber;<br />
          // TODO: Mache dies sicher...!<br />
          strcpy_s(csEntry.lineFileName, Line.FileName);<br />
        }<br />
        else<br />
        {<br />
          this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);<br />
        }<br />
      } // yes, we have SymGetLineFromAddr64()<br />
<br />
      // show module info (SymGetModuleInfo64())<br />
      if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE)<br />
      { // got module info OK<br />
        switch ( Module.SymType )<br />
        {<br />
        case SymNone:<br />
          csEntry.symTypeString = "-nosymbols-";<br />
          break;<br />
        case SymCoff:<br />
          csEntry.symTypeString = "COFF";<br />
          break;<br />
        case SymCv:<br />
          csEntry.symTypeString = "CV";<br />
          break;<br />
        case SymPdb:<br />
          csEntry.symTypeString = "PDB";<br />
          break;<br />
        case SymExport:<br />
          csEntry.symTypeString = "-exported-";<br />
          break;<br />
        case SymDeferred:<br />
          csEntry.symTypeString = "-deferred-";<br />
          break;<br />
        case SymSym:<br />
          csEntry.symTypeString = "SYM";<br />
          break;<br />
#if API_VERSION_NUMBER >= 9<br />
        case SymDia:<br />
          csEntry.symTypeString = "DIA";<br />
          break;<br />
#endif<br />
        case 8: //SymVirtual:<br />
          csEntry.symTypeString = "Virtual";<br />
          break;<br />
        default:<br />
          //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );<br />
          csEntry.symTypeString = NULL;<br />
          break;<br />
        }<br />
<br />
        // TODO: Mache dies sicher...!<br />
        strcpy_s(csEntry.moduleName, Module.ModuleName);<br />
        csEntry.baseOfImage = Module.BaseOfImage;<br />
        strcpy_s(csEntry.loadedImageName, Module.LoadedImageName);<br />
      } // got module info OK<br />
      else<br />
      {<br />
        this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);<br />
      }<br />
    } // we seem to have a valid PC<br />
<br />
    CallstackEntryType et = nextEntry;<br />
    if (frameNum == 0)<br />
      et = firstEntry;<br />
    this->OnCallstackEntry(et, csEntry);<br />
    <br />
    if (s.AddrReturn.Offset == 0)<br />
    {<br />
      this->OnCallstackEntry(lastEntry, csEntry);<br />
      SetLastError(ERROR_SUCCESS);<br />
      break;<br />
    }<br />
  } // for ( frameNum )<br />
<br />
  cleanup:<br />
    if (pSym) free( pSym );<br />
<br />
  if (context == NULL)<br />
    ResumeThread(hThread);<br />
<br />
  return TRUE;<br />
}<br />
<br />
BOOL __stdcall StackWalker::myReadProcMem(<br />
    HANDLE      hProcess,<br />
    DWORD64     qwBaseAddress,<br />
    PVOID       lpBuffer,<br />
    DWORD       nSize,<br />
    LPDWORD     lpNumberOfBytesRead<br />
    )<br />
{<br />
  if (s_readMemoryFunction == NULL)<br />
  {<br />
    SIZE_T st;<br />
    BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);<br />
    *lpNumberOfBytesRead = (DWORD) st;<br />
    //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);<br />
    return bRet;<br />
  }<br />
  else<br />
  {<br />
    return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);<br />
  }<br />
}<br />
<br />
void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)<br />
{<br />
  CHAR buffer[STACKWALK_MAX_NAMELEN];<br />
  if (fileVersion == 0)<br />
    _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);<br />
  else<br />
  {<br />
    DWORD v4 = (DWORD) fileVersion & 0xFFFF;<br />
    DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF;<br />
    DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF;<br />
    DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF;<br />
    _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);<br />
  }<br />
  OnOutput(buffer);<br />
}<br />
<br />
void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)<br />
{<br />
  CHAR buffer[STACKWALK_MAX_NAMELEN];<br />
  if ( (eType != lastEntry) && (entry.offset != 0) )<br />
  {<br />
    if (entry.name[0] == 0)<br />
      strcpy_s(entry.name, "(function-name not available)");<br />
    if (entry.undName[0] != 0)<br />
      strcpy_s(entry.name, entry.undName);<br />
    if (entry.undFullName[0] != 0)<br />
      strcpy_s(entry.name, entry.undFullName);<br />
    if (entry.lineFileName[0] == 0)<br />
    {<br />
      strcpy_s(entry.lineFileName, "(filename not available)");<br />
      if (entry.moduleName[0] == 0)<br />
        strcpy_s(entry.moduleName, "(module-name not available)");<br />
      _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);<br />
    }<br />
    else<br />
      _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name);<br />
    OnOutput(buffer);<br />
  }<br />
}<br />
<br />
void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)<br />
{<br />
  CHAR buffer[STACKWALK_MAX_NAMELEN];<br />
  _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);<br />
  OnOutput(buffer);<br />
}<br />
<br />
void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)<br />
{<br />
  CHAR buffer[STACKWALK_MAX_NAMELEN];<br />
  _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);<br />
  OnOutput(buffer);<br />
  // Also display the OS-version<br />
#if _MSC_VER <= 1200<br />
  OSVERSIONINFOA ver;<br />
  ZeroMemory(&ver, sizeof(OSVERSIONINFOA));<br />
  ver.dwOSVersionInfoSize = sizeof(ver);<br />
  if (GetVersionExA(&ver) != FALSE)<br />
  {<br />
    _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n", <br />
      ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,<br />
      ver.szCSDVersion);<br />
    OnOutput(buffer);<br />
  }<br />
#else<br />
  OSVERSIONINFOEXA ver;<br />
  ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));<br />
  ver.dwOSVersionInfoSize = sizeof(ver);<br />
  if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE)<br />
  {<br />
    _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", <br />
      ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,<br />
      ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);<br />
    OnOutput(buffer);<br />
  }<br />
#endif<br />
}<br />
<br />
void StackWalker::OnOutput(LPCSTR buffer)<br />
{<br />
    if (m_pos) {<br />
        // Write to output stream<br />
        *m_pos << buffer;<br />
    } else if (m_pwos) {<br />
        // Unicode output stream is ready. Use the output stream.<br />
        // Convert ASCII stream to UNICODE stream.<br />
        register u32 iLen = strlen(buffer) + 1;		  // Including NULL termination<br />
        register wchar_t *pBufferW = (wchar_t*)malloc(sizeof(wchar_t)*iLen);<br />
        for (register u32 i = 0; i < iLen; i++) {<br />
            pBufferW[i] = (wchar_t)buffer[i];<br />
        }<br />
        // Write to output stream<br />
        *m_pwos << pBufferW;<br />
        // Delete pBufferW<br />
        free(pBufferW);<br />
    } else {<br />
        // Write to console.<br />
        OutputDebugStringA(buffer);<br />
    }<br />
}<br />
<br />
void StackWalker::OnOutput(LPCWSTR buffer)<br />
{<br />
    if (m_pos) {<br />
        // ASCII output stream is ready. Use the output stream.<br />
        // Convert UNICODE stream to ASCII stream.<br />
        register u32 iLen = lstrlen(buffer) + 1;		  // Including NULL termination<br />
        register char *pBufferA = (char*)malloc(sizeof(char)*iLen);<br />
        for (register u32 i = 0; i < iLen; i++) {<br />
            pBufferA[i] = (char)buffer[i];<br />
        }<br />
        // Write to output stream<br />
        *m_pos << pBufferA;<br />
        // Delete pBufferA<br />
        free(pBufferA);<br />
    } else if (m_pwos) {<br />
        // Write to output stream<br />
        *m_pwos << buffer;<br />
    } else {<br />
        // Write to console.<br />
        OutputDebugStringW(buffer);<br />
    }<br />
}<br />

Generalcallstack from memory allocation hook Pin
~MyXa~30-Oct-06 4:44
~MyXa~30-Oct-06 4:44 
GeneralRe: callstack from memory allocation hook Pin
Jochen Kalmbach [MVP VC++]18-Nov-06 5:26
Jochen Kalmbach [MVP VC++]18-Nov-06 5:26 
GeneralRe: callstack from memory allocation hook Pin
~MyXa~18-Nov-06 6:26
~MyXa~18-Nov-06 6:26 
QuestionUsing from DLL Pin
z8z815-Oct-06 22:49
z8z815-Oct-06 22:49 
AnswerRe: Using from DLL Pin
Jochen Kalmbach [MVP VC++]18-Nov-06 5:26
Jochen Kalmbach [MVP VC++]18-Nov-06 5:26 
GeneralRe: Using from DLL Pin
z8z821-Nov-06 0:51
z8z821-Nov-06 0:51 
QuestionLicense? Pin
cipactli81025-Aug-06 6:07
cipactli81025-Aug-06 6:07 
AnswerRe: License? Pin
Jochen Kalmbach [MVP VC++]25-Aug-06 6:48
Jochen Kalmbach [MVP VC++]25-Aug-06 6:48 
Generaldll(created by vc6) called from vb6 doesn't work&#65311; Pin
boredjin10-Aug-06 21:40
boredjin10-Aug-06 21:40 
GeneralRelease vs. Debug issue Pin
zpTal4-Aug-06 4:37
zpTal4-Aug-06 4:37 
GeneralRe: Release vs. Debug issue [modified] Pin
Jochen Kalmbach [MVP VC++]5-Aug-06 6:49
Jochen Kalmbach [MVP VC++]5-Aug-06 6:49 
GeneralThis is now a feature of Visual Studio 2005 Pin
Andrew Krause21-Jun-06 10:31
Andrew Krause21-Jun-06 10:31 
GeneralRe: This is now a feature of Visual Studio 2005 [modified] Pin
Jochen Kalmbach [MVP VC++]21-Jun-06 19:25
Jochen Kalmbach [MVP VC++]21-Jun-06 19:25 
Generalretrieves stack info when the code breaks [modified] Pin
f214-Jun-06 3:32
f214-Jun-06 3:32 
GeneralRe: retrieves stack info when the code breaks Pin
Jochen Kalmbach [MVP VC++]14-Jun-06 3:54
Jochen Kalmbach [MVP VC++]14-Jun-06 3:54 
QuestionTraceback not proper with DLL Pin
aceamit6-Jun-06 4:50
aceamit6-Jun-06 4:50 
AnswerRe: Traceback not proper with DLL Pin
Jochen Kalmbach [MVP VC++]6-Jun-06 5:52
Jochen Kalmbach [MVP VC++]6-Jun-06 5:52 

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.