Archive for the ‘Visual Basic’ Category

Visual Basic 6 – quickest way to find first/last character in string

Friday, October 29th, 2010

When you are parsing large amounts of data, the way you code string matching can make a huge difference. In one case I needed to find if a string was contained within quotes, here are the test results from quickest to slowest.

The test situation was to find if the last character in the string ‘abcdefghijklmnopqrstuvwxyz’ is ‘z’ and iterated 100000000 times.

First, the most intuitive which most would use. TickCount of 49546.

Right("abcdefghijklmnopqrstuvwxyz", 1) = "z"

Function Right takes in a Variant by default, by succeeding it with a dollar sign it accepts Strings by default. TickCount of 22828, a significant saving.

Right$("abcdefghijklmnopqrstuvwxyz", 1) = "z"

Using the equals sign would be seem the norm, but what if the String Compare function was used. TickCount of 18047.

StrComp(Right$("abcdefghijklmnopqrstuvwxyz", 1), "z", vbBinaryCompare) = 0

What if Mid was used to extract the last character instead of Right. TickCount of 28391.

StrComp(Mid$("abcdefghijklmnopqrstuvwxyz", Len("abcdefghijklmnopqrstuvwxyz"), 1), "z") = 0

Now what if we use the In String function. String length binary is faster than standard String length. TickCount of 14516.

InStrRev("abcdefghijklmnopqrstuvwxyz", "z", -1, vbBinaryCompare) = LenB("abcdefghijklmnopqrstuvwxyz") / 2

Finally, with out String length binary. TickCount of 11312.

InStrRev("abcdefghijklmnopqrstuvwxyz", "z", -1, vbBinaryCompare) = Len("abcdefghijklmnopqrstuvwxyz")

So using In String Reverse is 77% faster.

UPDATE next day: Three that I totally forgot about

In String starting at last character, TickCount 11266.

InStr(Len("abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz", "z", vbBinaryCompare) = Len("abcdefghijklmnopqrstuvwxyz")

And In String Binary which is naturally fast. TickCount 6672.

InStrB(LenB("abcdefghijklmnopqrstuvwxyz") - 1, "abcdefghijklmnopqrstuvwxyz", "z", vbBinaryCompare) = LenB("abcdefghijklmnopqrstuvwxyz") - 1

So using In String Binary is 70% faster.

Related posts:

  1. Base64/sexatrigesimal encoding/decoding in VBA/VB6/Visual Basic
  2. Optimizing/faster String Concatenation in VBA
  3. Longest Common Subsequence implemented in VBA (Visual Basic for Applications)
  4. VBA automatically saves Excel 2003 Workbook in compatibility mode as Excel 2007 Workbook
  5. Get the parameters/arguments being called to an executable

Base64/sexatrigesimal encoding/decoding in VBA/VB6/Visual Basic

Sunday, October 10th, 2010

Here is an implementation of Base 36 enconding/decoding functions is VB6.

'Convert positive integer to a base36 string.
Function base36encode(ByRef number As Long) As String

    Dim alphabet As String
    alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    If number = 0 Then
        base36encode = "0"
        Exit Function
    End If
    base36encode = vbNullString
    Do While number <> 0
        base36encode = Mid(alphabet, number Mod 36 + 1, 1) & base36encode
        number = number \ 36
    Loop

End Function

'Convert base36 string to positive integer.
Function base36decode(ByRef base36 As String) As Long

    Dim alphabet As String
    alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    base36decode = InStr(1, alphabet, Right(base36, 1), vbTextCompare) - 1 'get the last character
    For i = Len(base36) - 1 To 1 Step -1
        base36decode = base36decode + 36 ^ (Len(base36) - i) * (InStr(1, alphabet, Mid(base36, i, 1), vbTextCompare) - 1)
    Next i

End Function

Related posts:

  1. Longest Common Subsequence implemented in VBA (Visual Basic for Applications)
  2. Optimizing/faster String Concatenation in VBA
  3. Generic file selection window function in VBA

eventquery.vbs – ERROR: Unable to execute the query for the…

Friday, August 6th, 2010

If you received this error when trying to execute eventquery.vbs the cause is an overflow of events past 32,767 (the maximum capacity of a VB6 Integer). There is a very simple fix for this which is to change the data type from an Integer to a Long (which has a maximum capacity of 2,147,483,647).

In eventquery.vbs, scroll down to line 1700 and 1703 and change both CInt to CLng.
Before;

        If CInt(objLogs.Item(arrKeyName(intLoopCount))) > 0 Then
            strFilterLog = arrKeyName(intLoopCount)
            intRecordRangeFrom = 0
            intRecordRangeTo = CInt(objLogs.Item(arrKeyName(intLoopCount)))

After;

        If CLng(objLogs.Item(arrKeyName(intLoopCount))) > 0 Then
            strFilterLog = arrKeyName(intLoopCount)
            intRecordRangeFrom = 0
            intRecordRangeTo = CLng(objLogs.Item(arrKeyName(intLoopCount)))

Or download the already updated eventquery.vbs.

Related posts:

  1. Rigid file manipulation functions for VBA/VBS

Longest Common Subsequence implemented in VBA (Visual Basic for Applications)

Friday, April 23rd, 2010

From Wikipedia, The longest common subsequence (LCS) problem is to find the longest subsequence common to all sequences in a set of sequences (often just two).

The following is a VBA implementation of this problem. The following functions are included;

String functions;

  • longestCommonSubsequence - calculate an LCS array.
  • backTraceUp and backTraceLeft - trace back either defaulting up or left respectively, and find the LCS.
  • getDiff - returns the difference of the two strings. The succeeding character of =, – or + indicated if the character was equal, removed or added, respectively.
  • passGetDiffOutput - passes the output of getDiff so that =, – or + are now values in a 2 x n array, with indix 0 being equal, indix 1 being removed and indix 2 being added.

Array functions;

  • longestCommonSubsequenceArr - calculate an LCS array.
  • backTraceUpArr - trace back defaulting up and find the LCS.
  • getDiffArr - returns the difference of the two arrays as a 2 x n array, with indix 0 being equal, indix 1 being removed and indix 2 being added.

Common functions;

  • max - standard maximum function.
  • stringToArray - convert a string to an array for array functions.

Examples;

  • exampleString
  • exampleArr

Download the Basic (bas) File

Unfortunately, the limitations of VBA makes a dog’s dinner out of what would be some very concise code or, perhaps that’s just my implementation…

Option Explicit

Public Function longestCommonSubsequence(ByRef string1 As String, ByRef string2 As String) As Long()
    If string1 = vbNullString Or string2 = vbNullString Then
        Exit Function
    End If

    Dim num() As Long

    'define the array, note rows of zeros get added to front automatically
    ReDim num(Len(string1), Len(string2))

    Dim i As Long, j As Long

    For i = 1 To Len(string1)
        For j = 1 To Len(string2)
            If Mid$(string1, i, 1) = Mid$(string2, j, 1) Then
                num(i, j) = num(i - 1, j - 1) + 1
            Else
                num(i, j) = max(num(i - 1, j), num(i, j - 1))
            End If
        Next j
    Next i

    longestCommonSubsequence = num
End Function

Sub exampleString()

    Dim arr() As Long

    Dim string1 As String
    Dim string2 As String

    string1 = "this is a find the haystack string"
    string2 = "this is a replace the needle string"

    arr = longestCommonSubsequence(string1, string2)

    Dim s As String, t As String
    s = backTraceUp(arr, string1, string2, Len(string1), Len(string2))
    t = backTraceLeft(arr, string1, string2, Len(string1), Len(string2))
    Dim a As String, b As String
    a = getDiff(arr, string1, string2, Len(string1), Len(string2))

    Dim brr() As Long

    brr = passGetDiffOutput(a)
End Sub

Public Function max(ByRef a As Long, ByRef b As Long) As Long
    If a >= b Then
        max = a
    Else
        max = b
    End If
End Function

'back traces c, defaulting in the up direction
Public Function backTraceUp(ByRef c() As Long, ByRef string1 As String, ByRef string2 As String, ByRef i As Long, ByRef j As Long) As String
    If i < 1 Or j < 1 Then
        backTraceUp = vbNullString
    ElseIf Mid$(string1, i, 1) = Mid$(string2, j, 1) Then
        'equal characters, save it and then go up and left
        backTraceUp = backTraceUp(c, string1, string2, i - 1, j - 1) & Mid$(string1, i, 1)
    Else
        'go in the direction of the highest number, defaulting to up
        If (c(i, j - 1) > c(i - 1, j)) Then
            backTraceUp = backTraceUp(c, string1, string2, i, j - 1)
        Else
            backTraceUp = backTraceUp(c, string1, string2, i - 1, j)
        End If
    End If
End Function

'back traces c, defaulting in the left direction
Public Function backTraceLeft(ByRef c() As Long, ByRef string1 As String, ByRef string2 As String, ByRef i As Long, ByRef j As Long) As String
    If i < 1 Or j < 1 Then
        backTraceLeft = vbNullString
    ElseIf Mid$(string1, i, 1) = Mid$(string2, j, 1) Then
        'equal characters, save it and then go up and left
        backTraceLeft = backTraceLeft(c, string1, string2, i - 1, j - 1) & Mid$(string1, i, 1)
    Else
        'go in the direction of the highest number, defaulting to left
        If (c(i, j - 1) >= c(i - 1, j)) Then
            backTraceLeft = backTraceLeft(c, string1, string2, i, j - 1)
        Else
            backTraceLeft = backTraceLeft(c, string1, string2, i - 1, j)
        End If
    End If
End Function

'the following function returns a string with indication to what was deleted or added
'proceding character can be;
' = no change
' - deletion
' + addition
Public Function getDiff(ByRef c() As Long, ByRef stringOld As String, ByRef stringNew As String, ByRef i As Long, ByRef j As Long) As String
    If i > 0 Then
        If j > 0 Then 'both are greater than zero
            'can only do the following comparison when i and j are greater than zero
            If Mid$(stringOld, i, 1) = Mid$(stringNew, j, 1) Then
                getDiff = getDiff(c, stringOld, stringNew, i - 1, j - 1) & Mid$(stringOld, i, 1) & "="
            Else
                If i = 0 Then
                    getDiff = getDiff(c, stringOld, stringNew, i, j - 1) & Mid$(stringNew, j, 1) & "+"
                ElseIf c(i, j - 1) >= c(i - 1, j) Then
                    getDiff = getDiff(c, stringOld, stringNew, i, j - 1) & Mid$(stringNew, j, 1) & "+"
                ElseIf j = 0 Then
                    getDiff = getDiff(c, stringOld, stringNew, i - 1, j) & Mid$(stringOld, i, 1) & "-"
                ElseIf c(i, j - 1) < c(i - 1, j) Then
                    getDiff = getDiff(c, stringOld, stringNew, i - 1, j) & Mid$(stringOld, i, 1) & "-"
                Else
                    getDiff = vbNullString
                End If
            End If
        Else 'i is is greater than zero
                If j = 0 Then
                    getDiff = getDiff(c, stringOld, stringNew, i - 1, j) & Mid$(stringOld, i, 1) & "-"
                ElseIf c(i, j - 1) < c(i - 1, j) Then
                    getDiff = getDiff(c, stringOld, stringNew, i - 1, j) & Mid$(stringOld, i, 1) & "-"
                Else
                    getDiff = vbNullString
                End If
        End If
    Else
        If j > 0 Then 'j is  greater than zero
                If i = 0 Then
                    getDiff = getDiff(c, stringOld, stringNew, i, j - 1) & Mid$(stringNew, j, 1) & "+"
                ElseIf c(i, j - 1) >= c(i - 1, j) Then
                    getDiff = getDiff(c, stringOld, stringNew, i, j - 1) & Mid$(stringNew, j, 1) & "+"
                Else
                    getDiff = vbNullString
                End If
        Else 'none are greater than zero
                getDiff = vbNullString
        End If
    End If
End Function

'this function returns the location of the string difference
Public Function passGetDiffOutput(ByRef outputStr As String) As Long()
    Dim i As Long
    i = 1

    Dim typeChr As String

    Dim oldi As Long
    Dim newi As Long
    oldi = 0
    newi = 0

    Dim toFrom() As Long
    Dim toFromCount As Long
    toFromCount = -1

    Dim typeChrPrev As String
    typeChrPrev = vbNullString

    Do While i < Len(outputStr)
        typeChr = Mid$(outputStr, i + 1, 1)
        Select Case typeChr
            Case "="
                If typeChr <> typeChrPrev Then

                    'check if it is comming from a deletion
                    If typeChrPrev = "-" Then
                        toFrom(2, toFromCount) = oldi
                    End If

                    'check if it is comming from a addition
                    If typeChrPrev = "+" Then
                        toFrom(2, toFromCount) = newi
                    End If
                End If

                oldi = oldi + 1 'update old index
                newi = newi + 1 'update new index
            Case "-"
                'check if it is comming from a addition
                If typeChrPrev = "+" Then
                    toFrom(2, toFromCount) = newi
                End If

                oldi = oldi + 1 'update old index
                If typeChr <> typeChrPrev Then
                    toFromCount = toFromCount + 1
                    ReDim Preserve toFrom(2, toFromCount)
                    'let old be -1
                    toFrom(0, toFromCount) = -1
                    toFrom(1, toFromCount) = oldi
                End If
            Case "+"
                'check if it is comming from a deletion
                If typeChrPrev = "-" Then
                    toFrom(2, toFromCount) = oldi
                End If

                newi = newi + 1 'update new index
                If typeChr <> typeChrPrev Then
                    toFromCount = toFromCount + 1
                    ReDim Preserve toFrom(2, toFromCount)
                    'let new be 1
                    toFrom(0, toFromCount) = 1
                    toFrom(1, toFromCount) = newi
                End If
        End Select

        i = i + 2
        typeChrPrev = typeChr
    Loop

    'check if it ended on a deletion or adition
    If typeChrPrev = "-" Then
        toFrom(2, toFromCount) = oldi
    End If

    If typeChrPrev = "+" Then
        toFrom(2, toFromCount) = newi
    End If

    passGetDiffOutput = toFrom
End Function

'note, arrays must be single dimension
Public Function longestCommonSubsequenceArr(ByRef array1() As String, ByRef array2() As String) As Long()
    On Error Resume Next
    If UBound(array1, 2) > 0 Or UBound(array2, 2) > 0 Then 'multidimensional arrays
        If Error = vbNullString Then
            Exit Function
        End If
    End If

    If UBound(array1) < 0 Or UBound(array2) < 0 Then 'check if arrays are bounded
        If Error <> vbNullString Then
            Exit Function
        End If
    End If

    Dim num() As Long

    'define the array, note rows of zeros get added to front automatically
    ReDim num(UBound(array1) + 1, UBound(array2) + 1)

    Dim i As Long, j As Long

    'note, arrays must always start at indice zero.
    For i = 0 To UBound(array1)
        For j = 0 To UBound(array2)
            If array1(i) = array2(j) Then
                num(i + 1, j + 1) = num(i, j) + 1
            Else
                num(i + 1, j + 1) = max(num(i, j + 1), num(i + 1, j))
            End If
        Next j
    Next i

    longestCommonSubsequenceArr = num
End Function

Public Function stringToArray(ByRef str As String) As String()
    Dim i As Long
    Dim arr() As String
    ReDim arr(Len(str) - 1)
    For i = 1 To Len(str)
        arr(i - 1) = Mid$(str, i, 1)
    Next i
    stringToArray = arr
End Function

Sub exampleArr()

    Dim string1 As String
    Dim string2 As String

    string1 = "this is a find the haystack string"
    string2 = "this is a replace the needle string"

    Dim a1() As String
    Dim a2() As String

    a1 = stringToArray(string1)
    a2 = stringToArray(string2)

    Dim c() As Long

    c = longestCommonSubsequenceArr(a1, a2)

    Dim str() As String

    str = backTraceUpArr(c, a1, a2, UBound(a1), UBound(a2))

    Dim dif() As String
    dif = getDiffArr(c, a1, a2, UBound(a1), UBound(a2))

End Sub

'back traces c, defaulting in the up direction
Public Function backTraceUpArr(ByRef c() As Long, ByRef array1() As String, ByRef array2() As String, ByRef i As Long, ByRef j As Long) As String()
    Dim arr() As String
    If i < 0 Or j < 0 Then
        backTraceUpArr = arr
    ElseIf array1(i) = array2(j) Then
        'equal characters, save it and then go up and left
        arr = backTraceUpArr(c, array1, array2, i - 1, j - 1)
        'check the bounding of arr
        Dim bound As Long
        On Error Resume Next
        bound = UBound(arr)
        If Error <> vbNullString Then
            ReDim arr(0)
            arr(0) = array1(i)
        Else 'no error
            ReDim Preserve arr(bound + 1)
            arr(bound + 1) = array1(i)
        End If
        backTraceUpArr = arr
    Else
        'go in the direction of the highest number, defaulting to up
        If (c(i + 1, j) > c(i, j + 1)) Then
            backTraceUpArr = backTraceUpArr(c, array1, array2, i, j - 1)
        Else
            backTraceUpArr = backTraceUpArr(c, array1, array2, i - 1, j)
        End If
    End If
End Function

'returns a 2xn array, where
'indice 0 are equal
'indice 1 are deletions
'indice 2 are additions
Public Function getDiffArr(ByRef c() As Long, ByRef arrayOld() As String, ByRef arrayNew() As String, ByRef i As Long, ByRef j As Long) As String()
    Dim arr() As String
    Dim bound As Long
    On Error Resume Next
    If i >= 0 Then
        If j >= 0 Then 'both are greater or equal to zero
            'can only do the following comparison when i and j are greater or equal than zero
            If arrayOld(i) = arrayNew(j) Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i - 1, j - 1)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(0, 0) = arrayOld(i)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(0, bound + 1) = arrayOld(i)
                    End If
                    getDiffArr = arr
            Else
                If i = 0 Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i, j - 1)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(2, 0) = arrayNew(j)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(2, bound + 1) = arrayNew(j)
                    End If
                    getDiffArr = arr
                ElseIf c(i + 1, j - 1 + 1) >= c(i - 1 + 1, j + 1) Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i, j - 1)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(2, 0) = arrayNew(j)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(2, bound + 1) = arrayNew(j)
                    End If
                    getDiffArr = arr
                ElseIf j = 0 Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i - 1, j)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(1, 0) = arrayOld(i)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(1, bound + 1) = arrayOld(i)
                    End If
                    getDiffArr = arr
                ElseIf c(i + 1, j - 1 + 1) < c(i - 1 + 1, j + 1) Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i - 1, j)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(1, 0) = arrayOld(i)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(1, bound + 1) = arrayOld(i)
                    End If
                    getDiffArr = arr
                Else
                    getDiffArr = arr
                End If
            End If
        Else 'i is is greater or equal to zero
                If j = 0 Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i - 1, j)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(1, 0) = arrayOld(i)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(1, bound + 1) = arrayOld(i)
                    End If
                    getDiffArr = arr
                ElseIf c(i + 1, j - 1 + 1) < c(i - 1 + 1, j + 1) Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i - 1, j)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(1, 0) = arrayOld(i)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(1, bound + 1) = arrayOld(i)
                    End If
                    getDiffArr = arr
                Else
                    getDiffArr = arr
                End If
        End If
    Else
        If j >= 0 Then 'j is  greater than zero
                If i = 0 Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i - 1, j)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(2, 0) = arrayNew(j)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(2, bound + 1) = arrayNew(j)
                    End If
                    getDiffArr = arr
                ElseIf c(i + 1, j - 1 + 1) >= c(i - 1 + 1, j + 1) Then
                    arr = getDiffArr(c, arrayOld, arrayNew, i, j - 1)
                    bound = UBound(arr, 2) 'check the bounding of arr
                    If Error <> vbNullString Then
                        Err.Clear
                        ReDim arr(2, 0)
                        arr(2, 0) = arrayNew(j)
                    Else 'no error
                        ReDim Preserve arr(2, bound + 1)
                        arr(2, bound + 1) = arrayNew(j)
                    End If
                    getDiffArr = arr
                Else
                    getDiffArr = arr
                End If
        Else 'none are greater than zero
                getDiffArr = arr
        End If
    End If
End Function

Related posts:

  1. Optimizing/faster String Concatenation in VBA
  2. Generic file selection window function in VBA
  3. Rigid file manipulation functions for VBA/VBS
  4. VBA automatically saves Excel 2003 Workbook in compatibility mode as Excel 2007 Workbook
  5. Get the parameters/arguments being called to an executable

Get the parameters/arguments being called to an executable

Friday, April 16th, 2010

Lets say you have some program ‘A’ that has no documentation and no help files but is being executed by some program ‘B’. You want to run program ‘A’ is individually, but you need to know what parameters/arguments are being passed from program ‘B’.

The following executable will help. Replace program ‘A’ (temporarily) with the following executable. Once program ‘B’ executes this new program, the parameters will be displayed in a message box.

Download getparams.exe

Source code below;

    Public Sub main()
        Dim msg As String = vbNullString

        Dim separators As String = " "
        Dim commands As String = Microsoft.VisualBasic.Command()
        Dim str() As String = commands.Split(separators.ToCharArray)

        Dim i As Long

        For i = 0 To UBound(str)
            msg = msg & str(i).ToString & vbCr
        Next

        MsgBox(msg)
    End Sub

Related posts:

  1. Optimizing/faster String Concatenation in VBA
  2. Generic file selection window function in VBA

Optimizing/faster String Concatenation in VBA

Tuesday, June 9th, 2009

There are numerous links about Visual Basic string concatenation, one particular is Microsoft’s How To Improve String Concatenation Performance. But the article is overwritten for the point it is trying to make, so I will share a simplified example.

Lets say we want to perform the following concatenation:

Dim i As Long
Dim s As String
For i = 1 To 100000000
	s = "A" & i & "B"
Next i

This takes approximately 6000 ticks. The faster approach, yet more complex functionality would be as follows:

Dim sourceLength As Long
sourceLength = 1
Dim source As String
s = "A B"

Dim i As Long
Dim s As String
For i = 1 To 100000000
	source = CStr(i)
	If Len(source) > sourceLength Then
		sourceLength = Len(source)
		s = "A" & Space$(sourceLength) & "B"
	End If
	Mid$(s, 2, sourceLength) = source
Next i

Which takes approximately 3700 ticks, a saving of nearly 40%.

Related posts:

  1. VBA automatically saves Excel 2003 Workbook in compatibility mode as Excel 2007 Workbook
  2. Generic file selection window function in VBA
  3. Rigid file manipulation functions for VBA/VBS

Word Minimize/Maximize event capture VBA

Friday, January 30th, 2009

Here is a quick little post outlining how to create a minimize/maximize event routine.

Add the following code to a class named EventClassModule

Public WithEvents App As Word.Application

Private Sub App_WindowDeactivate(ByVal Doc As Document, ByVal Wn As Window)
    If Doc.ActiveWindow.WindowState = wdWindowStateMinimize Then
        'you code in here
    End If
End Sub

and then add the following code under ThisDocument

Dim X As New EventClassModule

Private Sub Document_Open()
    'Call Register_Event_Handler
    Set X.App = Word.Application
End Sub

Related posts:

  1. Generic file selection window function in VBA
  2. VBA automatically saves Excel 2003 Workbook in compatibility mode as Excel 2007 Workbook
  3. Rigid file manipulation functions for VBA/VBS

VBA automatically saves Excel 2003 Workbook in compatibility mode as Excel 2007 Workbook

Wednesday, July 2nd, 2008

Lets say you have a neat little Excel 2003 macro, when you run your macro in Excel 2007, Excel runs it in Compatibility Mode, and any benefits (such as the 16384 columns) you were hoping to use are still unavailable. So how can we enable these benefits depending on the Excel version? Simply by including the following code in the Auto_Open subroutine.

Public Sub auto_open()
'add some smarts if opened in Excel 2007 or later
Dim oldFileName As String
oldFileName = ThisWorkbook.Name
Dim tempi As Integer 'used to store position of '.' before file extension in workbook file name
tempi = InStrRev(oldFileName, ".xls", -1, vbTextCompare)
If Application.version > 11 And Len(oldFileName) - tempi = 3 Then 'assume running in compatability mode
Application.DisplayAlerts = False
Dim newFileName As String
newFileName = Mid$(oldFileName, 1, tempi) & "xlsm"
If fileExist(newFileName) Then 'if the new workbook version already exists, then open it and close this one
'open the new workbook by emulating double clicking the file, as this is the only way to run the auto_open
Shell "Excel """ & ThisWorkbook.Path & "\" & newFileName & """", 3 '3 = vbMaximizedFocus
'close this Excel application
Application.Quit
Else 'if the new workbook version doesn't exist, then save it as new workbook version
'save as macro enables office 2007 workbook
ActiveWorkbook.SaveAs fileName:=newFileName, FileFormat:=52, CreateBackup:=False '52 = xlOpenXMLWorkbookMacroEnabled
'create a timer to call the same new workbook as it will be now opened in non compatibility mode
Application.OnTime Now + TimeValue("00:00:01"), "auto_open"
Workbooks(newFileName).Close
End If
Application.DisplayAlerts = True
End If

UserForm1.Show
End Sub

Some brief info on how this works, so far the only way I have found to run in Non-Compatibility Mode is to save the Workbook as an Excel Macro Enabled Workbook and reopen the file, or if the file already exists then open that file and close the Excel 2003 Workbook. The main problem is the showing of a UserForm on the reopen or existing open. The bove code is the only way I have found so far, and it involves some trickery. It can be simplified if portions are separated into the Workbook_Activate subroutine, but I wanted to provide a copy and paste solution with minimal fiddling around. The only change in the above code is the UserForm1 name.

One of the problems was the Workbook_Activate or Auto_open not running when called via the normal Open method. I actually had to ask ExpertsExchange, the expert was very helpful.

Any comments or suggestions welcome as always.

Related posts:

  1. Rigid file manipulation functions for VBA/VBS
  2. Generic file selection window function in VBA