Figure 1   clsLIMODS
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsLIMODS"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'    John Robbins - June '99 Microsoft Systems Journal Bugslayer Column
' The LIMODS helper class for Visual Basic development.
' 0. Include this class file in your Visual Basic project.  Create a global
'    instance of this class in your project.  (I would call it Trace)
' 1. Just call Trace.Trace to redirect everything through here.
' 2. LIMODS is only active using compiled Visual Basic.  If you run this class
'    under the Visual Basic IDE debugger, your trace statements will be sent
'    through the Debug.Print regular trace statement.
Option Explicit
Private Declare Function LoadLibrary Lib "kernel32" _
    Alias "Loadlibrarya" (ByVal lpLibFileName As String) As Long
Private Declare Sub OutputDebugString Lib "kernel32" _
    Alias "OutputDebugStringA" (ByVal lpOutputString As String)
Private Declare Function GetModuleFileName Lib "kernel32" _
    Alias "GetModuleFileNameA" (ByVal hModule As Long, _
    ByVal lpFileName As String, ByVal nSize As Long) As Long
Private Declare Function GetModuleHandle Lib "kernel32" _
    Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private m_IsInIDE As Boolean

#If LIMODS Then
Private Sub Class_Initialize()
    ' I need to check the module this class is running under.  If it is
    ' running under VB6.EXE, then I just redirect everything to Debug.Trace.
    Dim Str As String
    Dim lRet As Long

    Str = String$(255, vbNullChar)
    lRet = GetModuleHandle(vbNullString)
    lRet = GetModuleFileName(lRet, Str, 255)
    lRet = InStr(1, UCase(Str), "VB6.EXE", vbTextCompare)
    ' Check VB5 too.
    If (0 = lRet) Then
        lRet = InStr(1, UCase(Str), "VB5.EXE", vbTextCompare)
    End If
    ' If sRet is 0 then the main module is not the Visual Basic IDE so
    ' I can load LIMODSDLL.DLL.
    If (0 = lRet) Then
        LoadLibrary "LIMODSDLL.DLL"
        m_IsInIDE = False
    Else
        m_IsInIDE = True
    End If
End Sub
#End If

#If LIMODS Then
Public Sub Trace(sOut As Variant)
    If (True = m_IsInIDE) Then
        Debug.Print sOut
    Else
        Dim s As String
        s = sOut
        OutputDebugString s
    End If
End Sub
#Else   ' LIMODS is *not* conditionally defined.
Public Sub Trace(sOut As Variant)
End Sub
#End If


Figure 3  
GenLIMODS.EXE Sample Address Ranges

Start
End
Source File
0x00401910
0x00401A9A
CommandLine.cpp
0x00401D10
0x00402FB8
GenLIMODS.cpp
0x00403510
0x00403841
..\include\SymbolEngine.h
0x00403870
0x00403B94
ResString.h
0x00403BA0
0x00403BBD
GenLIMODS.h
0x00403BD0
0x00403CF9
..\include\SymbolEngine.h
0x004047B0
0x004047CC
GenLIMODS.cpp
0x004071A0
0x00408598
LOMFile.cpp
0x00409D30
0x0040A5A2
ReadIgnoreFiles.cpp
0x0040C8E0
0x0040C98C
Verbose.cpp


Figure 4   A Sample .LOM File

[Module Info]
DateTimeStamp=36e1cad9
BaseAddress=400000
ModuleName=LIMODS.EXE
[Ranges]
RangeCount=16
Range0=004017C0,00401C7E,0,D:\Dev\Column\June99\SourceCode\LIMODS\About.cpp
Range1=00401EE0,00402303,0,D:\Dev\Column\June99\SourceCode\LIMODS\BigIcon.CPP
Range2=00402420,00402A36,0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODS.cpp
Range3=00402D30,004036F7,0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSDoc.cpp
Range4=00404480,004047DD,0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSOptions.cpp
Range5=00404920,004057F3,0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSView.cpp
Range6=00405D40,00405D80,0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSDoc.h
Range7=00406120,0040731C,0,D:\Dev\Column\June99\SourceCode\LIMODS\LOMFile.cpp
Range8=00408A50,00408E4F,0,D:\Dev\Column\June99\SourceCode\LIMODS\MainFrm.cpp
Range9=00408FC0,00409266,0,D:\Dev\Column\June99\SourceCode\LIMODS\OptionsDialog.cpp
Range10=00409A80,00409AD6,0,atonexit.c
Range11=00409B00,00409CE8,0,crtexe.c
Range12=00409D20,00409D35,0,intel\fp8.c
Range13=00409D40,00409D45,0,merr.c
Range14=00409D50,00409D53,0,dllargv.c
Range15=00409DF0,00409E90,0,appmodul.cpp
[Sources]
Source0=0,D:\Dev\Column\June99\SourceCode\LIMODS\About.cpp
Source1=0,D:\Dev\Column\June99\SourceCode\LIMODS\BigIcon.CPP
Source2=0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODS.cpp
Source3=0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSDoc.cpp
Source4=0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSOptions.cpp
Source5=0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSView.cpp
Source6=0,D:\Dev\Column\June99\SourceCode\LIMODS\LIMODSDoc.h
Source7=0,D:\Dev\Column\June99\SourceCode\LIMODS\LOMFile.cpp
Source8=0,D:\Dev\Column\June99\SourceCode\LIMODS\MainFrm.cpp
Source9=0,D:\Dev\Column\June99\SourceCode\LIMODS\OptionsDialog.cpp
Source10=0,appmodul.cpp
Source11=0,atonexit.c
Source12=0,crtexe.c
Source13=0,intel\fp8.c
Source14=0,merr.c
Source15=0,dllargv.c
SourceCount=16


Figure 5   An Expanded __cdecl Hooking Function

VOID NAKEDDEF LIMODS_DiagOutputA( void ){
    DWORD_PTR dwRet; //Holds the return address in order to find the caller.
    // The saved ESI so VC6 debug builds work.
    DWORD_PTR dwESI;
    __asm PUSH  EBP                /* Set up the standard frame.*/
    __asm MOV   EBP, ESP
    __asm SUB   ESP, __LOCAL_SIZE  /* Save room for the locals  */
    __asm MOV   EAX, EBP           /* EBP has the stack coming  */
                                   /* into the fn. in it.       */
    __asm ADD   EAX, 4             /* Account for PUSH EBP      */
    __asm MOV   EAX, [EAX]         /* Get return address.       */
    __asm MOV   [dwRet], EAX       /* Save return address.      */
    __asm MOV   [dwESI], ESI       /* Save ESI so chkesp in dbg */
                                   /* builds works.             */
    // Call the function that determines if this address is one to show.
    // The return value is in EAX after this call and is checked below.
    // A return of TRUE means call the trace function.  A return of
    // FALSE means skip the trace function.
    CheckIfAddressIsOn( dwRet );
    __asm MOV   ESI, [dwESI]       /* Restore ESI.              */
    __asm ADD   ESP, __LOCAL_SIZE  /* Take away local var space */
    __asm MOV   ESP, EBP           /* Restore standard frame.   */
    __asm POP   EBP
    // Here's where the fun begins!  The above 4 lines of asm code
    // restored the stack to exactly what it looked like coming into
    // this function so I am now prepared to call the real function.
    // pReadDiagOutputA holds the real function address that I got
    // during initialization.
    __asm TEST  EAX , EAX          /* Test EAX for zero.        */
    __asm JZ    lblDiagOutputA     /* If zero, just return.     */
    __asm JMP   pReadDiagOutputA   /* Do it!  THE JUMP WILL     */
                                   /* RETURN TO THE CALLER NOT  */
                                   /* TO THIS FUNCTION.         */
    lblDiagOutputA:                \
                                   /* Skipped the TRACE!, just  */
    __asm RET                      /* return to the caller.     */
}


Figure 6   HookOrdinalExport

BOOL BUGSUTIL_DLLINTERFACE __stdcall HookOrdinalExport(HMODULE hModule,
    LPCTSTR szImportMod, DWORD dwOrdinal, PROC pHookFunc, PROC *ppOrigAddr){
    ASSERT (NULL != hModule);
    ASSERT (FALSE == IsBadStringPtr(szImportMod, MAX_PATH));
    ASSERT (0 != dwOrdinal);
    ASSERT (FALSE == IsBadCodePtr(pHookFunc));
    if((NULL == hModule)||(TRUE == IsBadStringPtr(szImportMod, MAX_PATH))||
        (0 == dwOrdinal)||(TRUE == IsBadCodePtr(pHookFunc))){
        SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);
        return(FALSE);
    }
    if(NULL != ppOrigAddr){
        ASSERT (FALSE == IsBadWritePtr(ppOrigAddr, sizeof(PROC)));
        if(TRUE == IsBadWritePtr(ppOrigAddr, sizeof(PROC))){
            SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);
            return(FALSE);
        }
    }
    // Find the import descriptor first.
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
                    GetNamedImportDescriptor(hModule, szImportMod);
    if(NULL == pImportDesc){
        // The requested module was not imported.  This is not an error.
        return(TRUE);
    }
    // Get the original thunk information for this DLL.  I cannot use
    //  the thunk information stored in the pImportDesc->FirstThunk
    //  because the that is the array that the loader has already
    //  bashed to fix up all the imports.  This pointer gives us access
    //  to the function names.
    PIMAGE_THUNK_DATA pOrigThunk = MakePtr(PIMAGE_THUNK_DATA, hModule,
                                       pImportDesc->OriginalFirstThunk);
    // Get the array pointed to by the pImportDesc->FirstThunk.  This is
    //  where I will do the actual bash.
    PIMAGE_THUNK_DATA pRealThunk = MakePtr(PIMAGE_THUNK_DATA, hModule,
                                       pImportDesc->FirstThunk);
    // The flag is to be set from the thunk so make it easy to look up.
    DWORD dwCompareOrdinal = IMAGE_ORDINAL_FLAG|dwOrdinal;
    // Loop through and look for the one that matches the ordinal.
    while ( NULL != pOrigThunk->u1.Function ){
        // Only look at those that are imported by ordinal.
        if(IMAGE_ORDINAL_FLAG==(pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)){
            // Is this the one?
            if ( dwCompareOrdinal == pOrigThunk->u1.Ordinal){
                // I found it.  Now I need to change the protection to
                //  writeable before I do the blast.  Note that I am now
                //  blasting into the real thunk area!
                MEMORY_BASIC_INFORMATION mbi_thunk;
                VirtualQuery(pRealThunk, &mbi_thunk,
                             sizeof(MEMORY_BASIC_INFORMATION));
                VERIFY (VirtualProtect(mbi_thunk.BaseAddress,
                            mbi_thunk.RegionSize, PAGE_READWRITE,
                            &mbi_thunk.Protect));
                // Save the original address if requested.
                if(NULL != ppOrigAddr){
                    *ppOrigAddr = (PROC)pRealThunk->u1.Function;
                }
                // Do the actual hook.
                pRealThunk->u1.Function = (PDWORD)pHookFunc;
                DWORD dwOldProtect;
                // Change the protection back to what it was before.
                VERIFY (VirtualProtect(mbi_thunk.BaseAddress,
                            mbi_thunk.RegionSize, mbi_thunk.Protect,
                            &dwOldProtect));
                SetLastError(ERROR_SUCCESS); // Life is good.
                return(TRUE);
            }
        }
        // Increment both tables.
        pOrigThunk++ ;
        pRealThunk++ ;
    }
    // Nothing was hooked.  This is technically not an error.  It just
    // means that the module is imported but the one function is not.
    SetLastError ( ERROR_SUCCESS ) ;
    return ( FALSE ) ;
}


Figure 7   VectorWalk.cpp

#include<stdlib.h>
#include<time.h>
#include<iostream.h>
#include<vector>
using namespace std;
int vectorwalk(vector<int> &v){
     int vsize = v.size();
     for(int i=0; i<vsize; i++){
         cout << "v[" << i << "] = " << v[i] << endl;
     }
     cout << endl;
     return vsize; 
}
main(){
     vector<int> a;
     int size;
     srand( (unsigned)time( NULL ) );
     do{
         size = rand()/100;
     }while((size != 0)&&(size > 50));
     for(int i = 0; i<size; i++){
         a.push_back(rand());
     }
     return 0;
}