Developer FAQs - Macros

Welcome to Baarns Publishing's Word Developers FAQ section. If you develop in Microsoft Word using Word Basic, these tips are for you. Feel free to copy any of the code you find here and use in your development projects.


How do I make a variable stay persistent for another macro?

When my macro runs, it sometimes gets "out of memory" messages, why?

 

How do I make a variable stay persistent for another macro?
Keywords: WordBasic Variable
Re-Posted July 21, 1997

WordBasic only has a module and procedure level variable scoping. To save variable across instances of a macro the variable must be saved to another location such as the registry, ini file, text file or document variable. When the macro is executed it should read the values stored into memory.

Storing values in the registry or INI file is a good solutions for information that needs to be maintained across instances of Word too.

Note: This is only true for WordBasic. In WordVBA all module and global variables are persistent while a template is loaded. For more details read the white paper on Migrating to VBA from WordBasic. This method is still useful for storing values across different sessions or instances of Word.

WordBasic Command

Sub MAIN
   'retrieve counter setting in the registry.
   HKEY_COUNTER$ = "HKEY_CURRENT_USER\software\Microsoft\Word\7.0\Options"
   'Convert return value into a numeric value.
   iCount = Val(GetPrivateProfileString$(HKEY_COUNTER$, "MyCounter", "")) + 1
   MsgBox "This macro has been run" + Str$(iCount) + " number of times."
   '
   'Store update value in registry.
   SetPrivateProfileString HKEY_COUNTER$, "MyCounter", Str$(iCount), ""
End Sub

Word VBA Command

Sub PrivateProfileStringExample()
   'retrieve counter setting in the registry.
   Const HKEY_COUNTER As String = "HKEY_CURRENT_USER\software\Microsoft\Office\8.0\Word\Options"
   Dim iCount As Long
   
   'Convert return value into a numeric value.
   iCount = Val(Application.System.PrivateProfileString("", HKEY_COUNTER, "MyCounter")) + 1
   
   
   MsgBox "This macro has been run " & iCount & " times.", vbOKOnly + vbInformation
   '
   'Store update value in registry.
   Application.System.PrivateProfileString("", HKEY_COUNTER, "MyCounter") = iCount
End Sub
			

For solutions that are more document centric, for example calculating how many times a user has opened a document. If the following example were added to as an AutoOpen macro it would track how many times a user has opened the document. Of course this would require read/write access to the document.

WordBasic Command

Sub MAIN
   On Error Goto Exit_Main
   'Get User information from logon screen.
   HKEY_LOGON$ = "HKEY_LOCAL_MACHINE\Network\Logon"
   szUser$ = GetPrivateProfileString$(HKEY_LOGON$, "UserName", "")
   'Get number of time user has opened the document.
   iCount = Val(GetDocumentVar$(szUser$)) + 1
   SetDocumentVar szUser$, Str$(iCount)
   'Display the message.
   MsgBox szUser$ + " has opened this document" + Str$(iCount) + " times."
Exit_Main:
   If Err Then MsgBox "Cannot run inside of Macro window."
End Sub

Word VBA Command

Sub AutoOpen()
   On Error GoTo ErrorHandler
   'Get User information from logon screen.
   '''This does not work on NT4. Need to test on Win95.
   Const HKEY_LOGON As String = "HKEY_LOCAL_MACHINE\Network\Logon"
   Dim szUser As String
   Dim iCount As Long
   Dim wrdVariable As Word.Variable
   
   szUser = Application.System.PrivateProfileString("", HKEY_LOGON, "UserName")
   If szUser = "" Then
    szUser = "unknown"
   End If
   'Get number of time user has opened the document.
   Set wrdVariable = ActiveDocument.Variables(szUser)
   
   iCount = Val(wrdVariable.Value) + 1
   wrdVariable.Value = iCount
   
   'Display the message.
   MsgBox szUser$ + " has opened this document" + Str$(iCount) + " times."
   Exit Sub
   
ErrorHandler:
    Select Case Err.Number
        Case 5825
            '''Document variable doesn't exist, so create it.
            Err.Clear
            iCount = 1
            Set wrdVariable = ActiveDocument.Variables.Add(szUser, (iCount))
            
            Resume Next
        Case Else
            MsgBox Err.Number & ", " & Err.Description
            Err.Clear
   End Select
End Sub

The administrator would also want to review who has opened the document and how many times.

WordBasic Command

Sub MAIN
   On Error Goto Exit_Main
   fReset = 0 'Set this to 1 to set all counters back to 0.
   uBound = CountDocumentVars()
   'Because list might be cleared work from the bottom to the top.
   'Word compresses the number of document variable entries every time
   'an entry is removed.
   For iLoop = uBound To 1 Step - 1
      szUser$ = GetDocumentVarName$(iLoop)
      msg$ = msg$ + szUser$ + ":=" + GetDocumentVar$(szUser$) + Chr$(13) + Chr$(10)
      If fReSet Then
         SetDocumentVar szUser$, ""
      End If
   Next   
   If msg$ <> "" Then
      MsgBox msg$
   Else
      MsgBox "List is empty."
   End If
Exit_Main:
   If Err Then MsgBox "Cannot run inside of Macro window."
End Sub	

Word VBA Command

Sub GetUsageTally()
    Dim wrdVariable As Word.Variable
    Dim wrdDocSource As Word.Document
    Dim wrdDocTemp As Word.Document
    
    On Error GoTo ErrorHandler
    
    '''Store a pointer to the current active document, later the
    '''temporary document will become the active document.
    Set wrdDocSource = ActiveDocument
    
    '''If no variables exist raise an error.
    If wrdDocSource.Variables.Count = 0 Then
        Err.Raise vbObjectError + 1, "GetUsageTally", "List is empty."
    End If
    
    '''Walk the variables collection.
    For Each wrdVariable In wrdDocSource.Variables
        '''Store results in a new document. Test to see if the temporary
        '''document exist. If not create it, i.e. the first time through
        '''the loop.
        If wrdDocTemp Is Nothing Then Set wrdDocTemp = Word.Documents.Add()
        With wrdDocTemp.Content
            .InsertAfter wrdVariable.Name & " := " & wrdVariable.Value & vbCr
        End With
    Next
   
    '''Now Delete all of the entries.
    For Each wrdVariable In wrdDocSource.Variables
        wrdVariable.Delete
    Next
   
   Exit Sub
ErrorHandler:
    Select Case Err.Number
        Case Else
            MsgBox Err.Number & ", " & Err.Description
            Err.Clear
   End Select
End Sub

When my macro runs, it sometimes gets "out of memory" messages, why?
Keywords: Memory Message
Re-Posted July 21, 1997

Frequently this message occurs with macros that do a lot of text manipulate: copy and paste, inserting of AutoText, searching and replacing of text. The common cause here is the Undo buffer gets filled up. This has nothing to do with the amount of memory the system has. Therefore the simplest solution is to clear the undo buffer. There are 2 methods to clearing the undo buffer. None of this is straight forward because we are using side-effects of other features of Word. When ever you go into Tools Revisions you clear the undo buffer. Also when ever you Protect then un-protect a document you clear the undo buffer. Many solution providers use one of these 2 methods to clear the undo buffer.

Function ClearUndo1
    ToolsProtectDocument .DocumentPassword = "", .NoReset = 0, .Type = 0
    ToolsUnprotectDocument
End Function
Function ClearUndo2
    Dim dlgTR as ToolsRevisions
    Getcurvalues dlgTR
    ToolsRevisions .MarkRevisions = ABS(dlgTR.MarkRevisions - 1)
    ToolsRevisions .MarkRevisions = dlgTR.MarkRevisions
End Function

ClearUndo2 is the recommended as a utility function. ClearUndo1 will cause form fields to clear, where as toggling revisions doesn't effect revision marks.

Clearing the undo buffer frequently resolves many issues. However if you are making a tremendous amount of changes to a document, save frequently. Many times the performance of the macro will actually increase if the active document is saved from time to time. This is because Word handles all of the changes that are made to a document in a temporary file(s). Saving clears up these files reducing the size of them, therefore increasing access speed.

Word VBA Command

Word 97 still exhibits this same behavior and the solution is still similar. Word 97 provides a method off of the document object to clear the Undo Buffer. Microsoft help suggest to include the UndoClear method at the end of a macro to keep Visual Basic actions from appearing in the Undo box.

ActiveDocument.UndoClear

Back to Top


Back Back to the FAQ Table of Contents


  Random Thoughts...
It's starting to smoke...Should I turn it off?


Copyright© 1996-1999, Baarns Consulting Group, Inc. - All rights reserved.