【问题标题】:Possible to delete empty macros in Word - or prevent them?可以删除 Word 中的空宏 - 还是阻止它们?
【发布时间】:2023-03-05 10:30:02
【问题描述】:

我正在为执行相同功能(但在单个模板中产生不同结果)的不同用户创建一系列模板以及配套宏和样式(嵌入在模板中)。

例如,每个模板都有一系列宏来更改样式中的字体颜色,在默认的黑色和各种颜色之间随意切换,作为校对格式的视觉辅助。

在另一个示例中,每个模板都将具有类似的重叠样式、样式名称和宏。但是,例如,一个模板的“问题”样式可能在左边距有 Q,带有 0.5" 悬挂缩进,而另一个模板可能以 1" 处的第一行缩进开始 Q 并换行后续行到边缘。但是两种样式都将具有相同的名称。并且模板中的伴随宏也将具有相同的名称。

一些宏将被分配给键盘快捷键,但对于其中许多,用户要么按 Alt+F8 进入宏列表,要么使用文本扩展器命令功能来访问它们(例如,qcol 可能会运行宏COLORS_Question_Style_Red,将问题样式更改为红色字体。文本扩展器命令将执行与用户相同的操作:Alt+F8、COLORS_Question_Style_Red、[ENTER]。

所有这些都可以正常工作,除非用户忘记了她不在基于其中一个模板的文档中,并且她完成了调用宏的过程(例如,使用文本扩展器快捷方式或按 Alt +F8,键入部分宏名称,然后按 ENTER)。

当然,当 Word 没有找到这样的宏时,它假定您要创建一个,它会将您带到当前活动模板的宏编辑器窗口,它已在其中创建一个空的宏,您现在可以用代码填充它。有的用户看到空宏就会删除,有的用户会直接关闭窗口,空宏不受影响。

如果用户在关闭编辑器窗口时选择保持空宏不变,那么下次当她尝试触发当前不可访问的宏时,系统不会“打扰她” ” 通过将她拖到编辑器窗口中,因为它认为它已经找到了宏。但结果将是它完全没有任何作用,她最终会意识到自己的错误并根据正确的模板打开或创建一个文档。

这一切都很好,花花公子,除了当她真的想在 proper 模板中使用现在也以“空”形式存在于 Normal 模板中的宏时,她去运行宏,什么都没有发生,因为 Normal 模板中的空宏胜过当前模板中的真实宏。

此外,如果她在此状态下尝试为伪复制宏分配自定义键盘快捷键(在适当的模板中),该宏甚至不会出现在自定义键盘的宏/命令列表中对话窗口。

我在 Google 中进行了详尽的搜索,但找不到一个对自动删除空宏的宏代码的引用。我搜索的所有内容都会返回删除 Excel 或 Word 表格中的空单元格的结果。

有人知道可以编写任何代码来解决这个问题吗?我认为最好将其作为 Auto_New 和 Auto_Open 宏包含在内,并在编辑会话期间根据需要提供给用户以按需运行。

或者也许有更好的方法。我愿意接受建议。提前致谢!


18 年 1 月 23 日更新,包括宏窗口的屏幕截图:

【问题讨论】:

  • 为什么不保护您的代码以防止用户在您的模板中添加宏?
  • 嗨,丽莎 - 我实际上打算允许他们将宏添加到模板中,以实现与这些文档的生产相关的各种有益目的,尽管我预计不会经常需要这样做。但更大的问题是模板宏不是问题。事实上,如果您不在基于模板的文档中,宏会在当前处于活动状态的任何模板中创建(很可能是 Normal.dotm),并且 Normal.dotm 中的空宏优先。我不能取消他们在自己的 Normal 模板中创建宏的权限,我也不会。:)
  • 我认为您正在尝试实现我 3 年前所做的事情 - 创建启用宏的 Word 模板,共享核心宏,例如更改样式等。这个核心模板(作为插件)具有所有宏少了自定义样式,UI。子模板具有样式/UI 的所有方面,并且在打开时,如果尚未打开核心模板,也会打开。自定义 UI 是针对其他模板进行自定义的。这里解释太多了。
  • 这是哪个版本的 Word?在我的安装(2010)中,当我输入一个不存在的宏名称时,除了“取消”之外,没有任何按钮处于活动状态......
  • 嗨,@CindyMeister - 我正在使用 Office 365/Word 2016。我已经编辑了我的原始帖子以包含宏窗口的屏幕截图,其中创建作为默认和唯一选项(由 ENTER 调用key) 如果在宏名称窗口中没有匹配当前字符串的宏。

标签: vba ms-word


【解决方案1】:

我倾向于为用户提供一个界面,使他们不太可能遇到问题,例如一系列键盘快捷键或功能区控件。但我怀疑你已经考虑过了。

可以使用 VB 可扩展性库(Office 的一部分,但您必须在 VBA 编辑器的工具/参考中添加对 Microsoft Visual Basic for Applications Extensibility 5.3 的参考)来处理代码模块)。这个对象模型有点挑剔,没有很好的文档记录。我不记得曾经在论坛上看到过关于这个特定主题的讨论......

这还没有经过广泛的测试,但它似乎在我的测试环境中工作。它仅搜索 Normal.dotm 的 NewMacros 模块,因为这将是您描述的场景中最常见的问题。您需要对其进行调整以搜索不同的模板或文档(ActiveDocumentActiveDocument.AttachedTemplate),但我怀疑这对您来说不是问题。

'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

【讨论】:

  • 哇,@CindyMeister。我一定会试一试的!非常感谢您在此故障排除任务中与我一起坚持 - 非常感谢。我将有许多宏分配给键盘快捷键——我是它的忠实粉丝。但我也坚信应该明智地使用捷径,因为它们不是无限的。当它们被分配到特定的模板或文档以与他人共享时,需要仔细考虑。这些宏中的许多都属于可以从宏列表中简单地访问它们的类别。我会试试这个。非常感谢! :)
  • @HappyNanaM​​O 我完全理解。在自定义工具栏的旧时代,这将是理想的。您可以查看的一件事是将它们合并到自定义功能区 UI 中,作为右键单击菜单的一部分,可能会在菜单点下分成列表(对它们进行分类?)。
  • 哦,我多么想念工具栏!实际上,我的功能区上确实有几个自定义选项卡,以及 QAT 上的一些宏,并且会鼓励其他用户也添加它们,如果这样做是有意义的。不幸的是,功能区并不容易管理,因此它不能涵盖所有情况。我当然希望共享功能区更加用户友好,就像在 WordPerfect 世界中一样。但是,是的,我确实经常合并自定义功能区选项卡。但即使有所有的自定义选项,总会有一些孤立的宏只存在于宏列表中。 〜凯伦:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-08
相关资源
最近更新 更多