Walk Native Callstack without Symbolic Info

As being taking charge of CER project, reviewing some basic knowledge of callstack should be useful. Here is a demo code used to walk callstack when an unhandled exception is raised. In fact, the demo code of StackWalk1 should be able to walk in a common case. It’s just difficult to obtain a thread-related CONTEXT. Win32 API GetThreadContext will only work as expected when the thread being inspected is suppended.
[principle]
I think the principle is supposed to be easy enough, just using ebp register, and traverse the list that ebp represents.
[Note]
The demo code is CPU specific. I am working on an X86 duel core CPU.
The demo code should be running with out FPO (Frame Pointer Omit) optimization.
[Shortcoming]
The callstack dumped out is shown as raw linear address.
 
#include "stdafx.h"
#include "windows.h"
LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
void StackWalk1(PCONTEXT pContext);
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);
int _tmain(int argc, _TCHAR* argv[])
{
 previousFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
 // cause a EXCEPTION_FLT_DIVIDE_BY_ZERO
 int a = 0;
 int b = 1 / a;
 return 0;
}
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
 _tprintf(_T("Exception code: [%08x]\n"),
  pExceptionRecord->ExceptionCode);
 StackWalk1(pExceptionInfo->ContextRecord);
 if (previousFilter)
  return previousFilter(pExceptionInfo); // bypass to the user-installed handler
 else
  return EXCEPTION_CONTINUE_SEARCH; // transfer the control to operating system
}
//
// stack walker version 1, without symbol information
// should not be FPO optimization
//
void StackWalk1(PCONTEXT pContext)
{
 _tprintf(_T("Callstack:\n"));
 _tprintf(_T("Address     Frame     Module\n"));
 // instruction pointer (EIP) at the time of the exception
 DWORD pc = pContext->Eip;
 PDWORD pFrame, pPrevFrame;
 pFrame = (PDWORD)pContext->Ebp;
 do
 {
  TCHAR szModule[MAX_PATH] = { 0 };
  MEMORY_BASIC_INFORMATION mbi;
  VirtualQuery((PVOID)pc, &mbi, sizeof(mbi));
  GetModuleFileName((HMODULE)mbi.AllocationBase, szModule, sizeof(szModule));
  _tprintf(_T("%08x    %08x    %s\n"), pc, pFrame, szModule);
  // push ebp
  // mov ebp, esp, looking at the following snapshot of a stack
  // [0x…]return address   pContext->Ebp[1]
  // [0x…]ebp                 pContext->Ebp[0]

  pc = pFrame[1];
  pPrevFrame = pFrame;
  pFrame = (PDWORD)pFrame[0];
  // check if the ebp is still valid, otherwise stop walking
  if ((DWORD)pFrame & 3)
   break;
  if (pFrame <= pPrevFrame)
   break;
  // check if the address pointed to by ebp is committed
  if (IsBadWritePtr(pFrame, sizeof(PVOID) * 2))
   break;
 } while (1);
 // when to stop the walking?
 // when it encounters a frame address that doesn’t look valid
}
[Result/Output]
Exception code: [c0000094]
Callstack:
Address     Frame     Module
00bb1414    0016f8a0    d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb25b8    0016f8f0    d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb23ff    0016f8f8    d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
76d84911    0016f904    C:\Windows\system32\kernel32.dll
76e5e4b6    0016f944    C:\Windows\system32\ntdll.dll
76e5e489    0016f95c    C:\Windows\system32\ntdll.dll