Visual Basic

Calling on All Your Problem-Solving Skills

Constantly examine your approach to solving problems, and always encourage input and criticism from all quarters on the same. Think problems through. And always profile your code!

A truly useful code profiler would include some way to time Visual Basic's routines. For example, how fast is Val when compared with its near functional equivalent CInt? You can do some of this profiling using the subclassing technique discussed in Chapter 1 (replacing some VBA routine with one of your own-see Tip 11), but here's a small example anyway:

Declarations Section

  Option Explicit
  Declare Function WinQueryPerformanceCounter Lib "kernel32"  _
  Alias "QueryPerformanceCounter" (lpPerformanceCount As LARGE_INTEGER) _
  As Long
  Declare Function WinQueryPerformanceFrequency Lib "kernel32" _
  Alias "QueryPerformanceFrequency" (lpFrequency As LARGE_INTEGER) _
  As Long
  Type LARGE_INTEGER
      LowPart     As Long
      HighPart    As Long
  End Type

In a Module

  Function TimeGetTime() As Single
      Static Frequency     As Long
      Dim CurrentTime      As LARGE_INTEGER
      If 0 = Frequency Then
          Call WinQueryPerformanceFrequency(CurrentTime)
          Frequency = CurrentTime.LowPart / 1000
          TimeGetTime = 0
      Else
          Call WinQueryPerformanceCounter(CurrentTime)
          TimeGetTime = CurrentTime.LowPart / Frequency
      End If
  End Function

Replacement for Val

  Public Function Val(ByVal exp As Variant) As Long
      Dim l1 As Single, l2 As Single
      l1 = TimeGetTime()
      Val = VBA.Conversion.Val(exp)
      l2 = TimeGetTime()
      Debug.Print "Val - " & l2 - l1
  End Function

The TimeGetTime routine uses the high-resolution timer in the operating system to determine how many ticks it (the operating system's precision timing mechanism) is capable of per second (WinQueryPerformanceFrequency). TimeGetTime then divides this figure by 1000 to determine the number of ticks per millisecond. It stores this value in a static variable so that the value is calculated only once.

On subsequent calls, the routine simply returns a number of milliseconds; that is, it queries the system time, converts that to milliseconds, and returns this value. For the calling program to determine a quantity of time passing, it must call the routine twice and compare the results of two calls. Subtract the result of the second call from the first, and you'll get the number of milliseconds that have elapsed between the calls. This process is shown in the "Replacement for Val" code.

With this example, one can imagine being able to profile the whole of VBA. Unfortunately, that isn't possible. If you attempt to replace certain routines, you'll find that you can't. For example, the CInt routine cannot be replaced using this technique. (Your replacement CInt is reported as being an illegal name.) According to Microsoft, for speed, some routines were not implemented externally in the VBA ActiveX server but were kept internal-CInt is one of those routines.