HappyNanaMO
HappyNanaMO

Reputation: 353

Possible to delete empty macros in Word - or prevent them?

I am in the process of creating a series of templates and companion macros and styles (embedded in the template) for different users that perform the same functions (but different results within an individual template).

As an example, every template will have a series of macros that change the font colors in the styles, flip-flopping at will between the default black and various colors, as a visual aid in proofing the formatting.

In another example, every template will have similarly overlapping styles and style names and macros. But the "Question" style for one template, for example, might have the Q at the left margin, with a .5" hanging indent, while another template might start the Q with a first-line indent at 1" and wrap subsequent lines to the margin. But both styles will have the same name. And the companion macros in the templates will also have identical names.

Some macros will be assigned to keyboard shortcuts, but for many of them, the user will either press Alt+F8 to get to the macro list or use a text expander command function to access them (e.g., qcol might run the macro COLORS_Question_Style_Red, which changes the Question style to the red font. The text expander command would do the same thing the user would do: Alt+F8, COLORS_Question_Style_Red, [ENTER].

All that works just fine, except when the user forgets that she is not in a document based on one of them templates, and she goes through the process to invoke the macro (e.g., using the text expander shortcut, or pressing Alt+F8, typing part of the macro name, and pressing ENTER).

Of course, when Word doesn't find such a macro, it assumes you want to create one, and it brings you to the macro editor window of the current active template, where it has created an empty macro, which you can now fill in with code. Some users will delete the empty macro when they see it, and others will just close the window, leaving the empty macro intact.

If the user opts to leave the empty macro intact when closing the editor window, the next time she goes through those same erroneous steps of attempting to trigger a macro that isn't currently accessible, the system won't "bother her" by dragging her into the editor window because it believes it has found the macro. But the result will be that it does absolutely nothing, and she'll eventually realize her error and open or create a document based on the proper template(s).

That's all fine and dandy, except for the fact that when she really does want to use a macro in the proper template that now also exists in "empty" form in the Normal template, and she goes to run the macro, nothing happens because the empty macro in the Normal template trumps the real macro in the current template.

Additionally, if she attempts to assign a customized keyboard shortcut (in the proper template) to the pseudo-duplicated macro while things are in this state, the macro will not even show up in the Macros/Commands list in the Customize Keyboard dialog window.

I have searched exhaustively in Google and cannot find a single reference to macro code that automatically deletes empty macros. Everything I search for returns hits for deleting empty cells in Excel or a Word table.

Is anyone aware of any code that can be written to address this? I'm thinking it would be good to include it as an Auto_New and Auto_Open macro, as well as provide it for the user to run on demand, as needed during an editing session.

Or maybe there's a better way. I'm open to suggestions. Thanks in advance!


UPDATED 1/23/18 to include screen shot of macro window:

enter image description here

Upvotes: 1

Views: 329

Answers (1)

Cindy Meister
Cindy Meister

Reputation: 25673

My inclination would be to offer the users an interface which makes them less likely to run into the problem, such as a series of keyboard shortcuts or Ribbon controls. But I suspect you've already considered that.

It's possible to work with code modules by using the VB Extensibility library (part of Office, but you have to add a reference to Microsoft Visual Basic for Applications Extensibility 5.3 in Tools/References the VBA Editor). This object model is a bit finicky and not well documented. I can't recall having ever seen a discussion about this particular topic in a forum...

This hasn't been tested extensively, but it seems to work in my test environment. It only searches the NewMacros module of Normal.dotm as that would be the most common problem in the scenario you describe. You'd need to tweak it to search a different template or document (ActiveDocument, ActiveDocument.AttachedTemplate), but I suspect that won't be a problem for you.

'Delete procedures that containt only empty lines,
'Sub, Dim, End Sub and comments    
Sub RemoveEmptyMacros()
    Dim VBProj As VBIDE.VBProject
    Dim VBComp As VBIDE.vbComponent
    Dim CodeMod As VBIDE.CodeModule
    Dim ProcKind As VBIDE.vbext_ProcKind

    Dim LineNum As Long
    Dim ProcName As String
    Dim iLineCounter As Long
    Dim iProcStart As Long, iProcNrLines As Long
    Dim sLineContent As String
    Dim isEmpty As Boolean

    'Needed only if you want to log deletions
    Dim Doc As word.Document
    Dim Rng As Range

    'Change this to search something other than Normal.dotm
    Set VBProj = NormalTemplate.VBProject
    Set VBComp = VBProj.vbComponents("NewMacros")
    Set CodeMod = VBComp.CodeModule
    iLineCounter = 0

    'Needed only if you want to log deletions
    Set Doc = ActiveDocument
    Set Rng = Doc.content

    With CodeMod
        'Start after the declaration section
        LineNum = .CountOfDeclarationLines + 1

        'Loop all the procedures by going line-by-line
        Do Until LineNum >= .CountOfLines
            'Assume a procedure is empty, if it's not
            'this will be set to false and nothing happens
            isEmpty = True
            ProcName = .ProcOfLine(LineNum, ProcKind)
            iProcStart = .ProcStartLine(ProcName, ProcKind)
            iProcNrLines = .ProcCountLines(ProcName, ProcKind)

            'Check all lines whether empty, sub, dim, end, comment
            'OR have content
            For iLineCounter = iProcStart To iProcStart + iProcNrLines
                sLineContent = .Lines(iLineCounter, 1)
                If Len(sLineContent) > 0 Then
                    'if there's content, procedure is not empty
                    'leave the FOR loop without deleting
                    If Left(Trim(sLineContent), 1) <> "'" And _
                        Left(Trim(sLineContent), 3) <> "Sub" And _
                        Left(Trim(sLineContent), 3) <> "End" Then
                            isEmpty = False
                            Exit For
                    End If
                End If
            Next

            'Increment line number to start of next procedure
            'for next DO loop
            LineNum = iProcStart + iProcNrLines + 1
            'If you want a list of all procedures in the Immediate Window
            'Debug.Print ProcName

            If isEmpty Then
                'If you want to log a list of the subs that were deleted
                'Rng.Text = ProcName & vbCr
                'Rng.Collapse wdCollapseEnd
                .DeleteLines iProcStart, iProcNrLines
            End If
        Loop
    End With
End Sub

Function ProcKindString(ProcKind As VBIDE.vbext_ProcKind) As String
    Select Case ProcKind
        Case vbext_pk_Get
            ProcKindString = "Property Get"
        Case vbext_pk_Let
            ProcKindString = "Property Let"
        Case vbext_pk_Set
            ProcKindString = "Property Set"
        Case vbext_pk_Proc
            ProcKindString = "Sub Or Function"
        Case Else
            ProcKindString = "Unknown Type: " & CStr(ProcKind)
    End Select
End Function

Upvotes: 1

Related Questions