【问题标题】:Excel macro inconsistently causes Excel to "stop working"Excel 宏不一致导致 Excel“停止工作”
【发布时间】:2014-12-18 21:51:38
【问题描述】:

我正在运行 Excel 2013,并且我最近开发的一个宏不一致(万岁!)导致 Excel 冻结,给我“Excel 已停止工作”错误。简而言之,宏会提示用户提供一些关于他们想要做什么的基本信息,然后创建许多新工作表(基于模板工作表)并用摘要选项卡中的信息行填充每个工作表(用户填写在开始宏之前)。当我运行宏时,它会在我完成提示后立即冻结。我可以立即重新启动工作簿,使用相同的输入再次运行宏,它就会工作。因此,问题难以重现,因此难以诊断和修复。

我已经尝试了很多方法来解决这个问题(更改 Shift:=xl--- 调用 xlShift---;将选项卡从分页预览更改为普通视图;将宏按钮移动到另一个工作表),但它仍然冻结。最近的尝试(似乎工作了一段时间)是将用于启动宏的表单控件按钮更改为 ActiveX 控件按钮(位于“摘要”选项卡上)。我尝试设置一个基本的错误处理程序,但是当我让它再次冻结时没有找到任何东西。我试过单步执行代码,当它冻结时,是在第一次执行 selection.insert 调用之后。

我在下面包含了完整的宏,但(潜在的)问题行是:

Sheets(yr_temp).Rows(3 + task_counter).Insert (xlShiftDown)

关于可能发生的事情以及我可以尝试解决此问题的任何其他想法?我唯一没有完全测试的是在 .Insert 行之后插入 Application.Wait Now + (TimeValue("00:00:01") ;另一个用户说这对他们有用(在moment UPDATE #4:找到了!这是link。),虽然情况略有不同。此外,该行可以在宏的一次执行中运行数百次,所以请等待每次一秒钟是相当繁重的(我没有尝试过使用几分之一秒,我也不确定多短的等待仍然会产生预期的效果)。

更新 #1:我忘记了这个花絮。当它冻结时,它似乎总是在我打开工作簿后第一次运行宏时。我不相信它曾经在第二次或第三次或...运行宏时冻结。

更新 #2:在为 yr_temp 和 task_counter 添加 debug.print 调用(感谢 Alex Bell 的建议)并多次运行宏后,我终于让它冻结了我当我观看即时窗口时。同样,在第一次调用奇怪的麻烦的 sheet.insert 行之后,它似乎已经崩溃了。更重要的是,yr_temp 和 task_counter 的值与之前每次顺利运行的尝试中的有效数字相同。那么,任何其他想法可能导致这个问题?这个周末我将尝试在另一台计算机上运行它,看看它是否可能与这个系统有关,而不是宏本身。

更新 #3:我尝试在我的另一台计算机上使用工作簿(运行 Office 2010)。到目前为止,我还没有让它冻结,但我现在断言它不会在那个系统上冻结还为时过早。

Sub Generate_MM_Sheets()

'' Turn off screen updating for duration of macro (to improve macro speed...and prevent seizures)
Application.ScreenUpdating = False

'' Declare all variables
    Dim valid_resp As Boolean, user_resp As String    '' For user prompts
    Dim yr_start As Integer, yr_num As Integer, yr_counter As Integer, yr_temp As String    '' For year values
    Dim task_num As Integer, task_counter As Integer, task_temp As Integer    '' For task numbers
    Dim sheets_num As Integer, sheet_counter As Integer    '' For sheet numbers
    Dim proj_name As String    '' For project name
    Dim wb_current As String, wb_new As String    '' For workbook names

'' Prompt user to define starting year for the Maintenance Manual sheets
    valid_resp = False
    Do
        user_resp = InputBox("Enter the desired starting year for the Maintenance Manual sheets", , Year(Now()))
        If user_resp = "" Then     '' If the user hits Cancel or returns an empty response, restore screen updating and end the macro
            Application.ScreenUpdating = True
            Exit Sub
        ElseIf Not IsNumeric(user_resp) Then     '' If the response is not a number, warn the user and retry
            MsgBox ("Doh! Invalid entry. The value you entered was not a number.")
        ElseIf Not user_resp = Int(user_resp) Or user_resp <= 0 Then     '' If the response is not a positive integer, warn the user and retry
            MsgBox ("Aw snap! Invalid entry. The value you entered was not a positive integer.")
        Else     '' Otherwise the response is deemed valid. Set the response validity to true and define the macro variable as the user response
            valid_resp = True
            yr_start = user_resp
        End If
    Loop Until valid_resp = True     '' Loop until a valid response is entered

'' Same logic as above, but this time to define the number of years for the Maintenance Manual sheets
    valid_resp = False
    Do
        user_resp = InputBox("Enter the desired total number of years for the Maintenance Manual sheets", , 30)
        If user_resp = "" Then
            Application.ScreenUpdating = True
            Exit Sub
        ElseIf Not IsNumeric(user_resp) Then
            MsgBox ("Come on! Invalid entry. The value you entered was not a number.")
        ElseIf Not user_resp = Int(user_resp) Or user_resp <= 0 Then
            MsgBox ("Bummer, dude! Invalid entry. The value you entered was not a positive integer.")
        Else
            valid_resp = True
            yr_num = user_resp
        End If
    Loop Until valid_resp = True

'' Prompt user to define project name for use in the Maintenance Manual sheet headers
    proj_name = InputBox("Enter the name of the project for the Maintenance Manual sheets")

'' Use the above responses, the data in the Summary tab and the template in the Template tab to create and populate the Maintenance Manual sheets
    task_num = Range(Sheets("Summary").Range("A4"), Sheets("Summary").Range("A4").End(xlDown)).Rows.Count     '' Count the number of Tasks in the Summary tab
    sheets_num = ActiveWorkbook.Sheets.Count     '' Count the current number of sheets in the macro workbook (this value is used when moving the generated sheets to new workbook)
    Sheets("Template").PageSetup.CenterHeader = proj_name & Chr(10) & "Maintenance Items"     '' Update the header of the Template tab to be used on the generated sheets
    For yr_counter = 1 To yr_num     '' Loop through each year
        yr_temp = yr_start + yr_counter - 1     '' Determine the year value for this loop
        Sheets("Template").Copy After:=Sheets(Sheets.Count)     '' Copy the Template tab to the end of the workbook
        Sheets("Template (2)").Name = yr_temp     '' Rename the new tab based on the year value for this loop
        Sheets(yr_temp).Range("A1").Value = Sheets(yr_temp).Range("A1").Value & yr_temp     '' Revise the new tab's description based on the year value for this loop

        task_counter = 0
        For task_temp = 1 To task_num     '' Loop through each task
            task_rem = (yr_counter + Sheets("Summary").Range("E4").Offset(task_temp - 1, 0).Value) Mod _
                    Sheets("Summary").Range("D4").Offset(task_temp - 1, 0).Value     '' Check if the task is due this year (i.e., the year count (plus task age) is a factor of the task frequency)
            If task_rem = 0 Then     '' Then, for each task that is due this year...
                task_counter = task_counter + 1     '' Increment the counter for the number of tasks that have been identified as due this year
                Sheets("Summary").Rows(3 + task_temp).Copy     '' Copy the task from the Summary sheet and insert it at the bottom of the new tab
                Sheets(yr_temp).Rows(3 + task_counter).Insert (xlShiftDown)
            End If
        Next task_temp

        Sheets(yr_temp).Columns("D:E").Delete (xlShiftToLeft)     '' Delete the frequency and current age columns from the new tab
        Sheets(yr_temp).Rows(4 + task_counter).Delete (xlShiftUp)     '' Delete the blank placeholder row (used to preserve formatting and print area) from the new tab
    Next yr_counter

'' Move all of the newly generated Maintenance Manual sheets to a new workbook
    wb_current = ActiveWorkbook.Name     '' Note: This is used in the following code block, but needs to be performed here for simplicity
    Sheets(sheets_num + 1).Select     '' Select the first annual sheet
    For sheet_counter = 2 To yr_num     '' Add each of the remaining annual sheets to the selection
        Sheets(sheets_num + sheet_counter).Select False
    Next sheet_counter
    ActiveWindow.SelectedSheets.Move     '' Move the selected sheets (hopefully all of the newly generated annual sheets) to a new workbook

'' Restore the macro workbook tabs to their original state (for aesthetic/convenience reasons) and then focus back to the newly created workbook
    wb_new = ActiveWorkbook.Name
    Workbooks(wb_current).Sheets("Instructions").Activate
    Workbooks(wb_current).Sheets("Summary").Activate
    Workbooks(wb_current).Sheets("Template").PageSetup.CenterHeader = Chr(10) & "Maintenance Items"     '' Remove project name from Template tab header
    Workbooks(wb_new).Activate

'' Restore screen updating
Application.ScreenUpdating = True

End Sub

【问题讨论】:

  • 代码的哪个特定部分导致了问题?
  • 当我单步执行代码时,我让它冻结了两次。两次它都冻结了,这是它第一次尝试执行以下插入行时:Sheets(yr_temp).Rows(3 + task_counter).Insert (xlShiftDown)
  • 然后你必须检查各个变量的状态:task_temp、yr_temp、task_counter。为此目的使用 Debug.Print 或添加 Watches。

标签: excel freeze vba


【解决方案1】:

我通过操作中心提取了冻结的错误详细报告,看起来“故障模块”是 mso.dll(至少在我检查的几个错误报告中)。我发现最有希望的建议修复是运行 Office 修复(通过控制面板中的程序和功能)。到目前为止,这一直有效,但我不会称这个问题已解决,直到我再使用几天而没有崩溃。

更新:我已经测试了几天,仍然没有崩溃。我将此标记为可接受的解决方案,但如果崩溃再次出现,我会更新。对于任何感兴趣的人,这里是错误报告之一:

**Source**
Microsoft Excel

**Summary**
Stopped working

**Date**
‎12/‎11/‎2014 4:55 PM

**Status**
Report sent

**Description**
Faulting Application Path:  C:\Program Files (x86)\Microsoft Office\Office15\EXCEL.EXE

**Problem signature**
Problem Event Name: APPCRASH
Application Name:   EXCEL.EXE
Application Version:    15.0.4569.1504
Application Timestamp:  52c5e9e1
Fault Module Name:  mso.dll
Fault Module Version:   15.0.4569.1506
Fault Module Timestamp: 52e0c1d0
Exception Code: c0000602
Exception Offset:   0116b30f
OS Version: 6.1.7601.2.1.0.256.4
Locale ID:  1033
Additional Information 1:   6e8a
Additional Information 2:   6e8ae308d57954ab0513d50f2363e5fc
Additional Information 3:   8248
Additional Information 4:   8248af4ab5d8a2564e54f9d6dd7f5d2b

**Extra information about the problem**
Bucket ID:  94371593

【讨论】:

    【解决方案2】:

    与您的情况相关,如果没有实际数据,很难执行远程调试。作为一般建议,将调试技术应用于代码中的有问题的语句,例如插入:

    ' debugging: output to be sent to Immediate Window
    Debug.Print task_temp
    Debug.Print yr_temp
    Debug.Print task_counter
    
    ' rest of your code, starting with problematic part
    Sheets("Summary").Rows(3 + task_temp).Copy
    Sheets(yr_temp).Rows(3 + task_counter).Insert (xlShiftDown)
    

    在执行有问题的行之前分析值的有效性,特别是:行号是否在有效范围内,以及 Sheets(yr_temp) 是否存在。 希望这将有助于追查错误的来源。

    【讨论】:

    • 谢谢,亚历克斯。我以前用 msgbox 调试过,但我认为 debug.print 是一个更好的做法。那么我应该在即时窗口中寻找什么?会导致代码错误(特别是插入行)的无效条目?当我在代码中设置错误处理程序时,当我冻结它时它没有弹出任何错误。如果这些值是问题所在,为什么每次我给它相同的输入时它都不会冻结?感谢您的帮助!
    • 我在答案中添加了几个建议。检查行范围的有效性,它可能在某些迭代中超出限制,等等。亲切的问候,
    • 感谢亚历克斯的跟进。我已经按照建议插入了调试,现在是尝试让它再次冻结的困难部分。
    • 嗯...调试不存在的问题有点困难:)。但是,现在你有了一个工具。很可能(根据您的描述)某些索引超出了范围,例如行索引或工作表索引。如果您不介意,请将答案标记为已接受。祝你的项目好运。亲切的问候,
    • 感谢您对调试的建议,但我仍然不相信行或工作表索引是问题的根源。首先,如果我每次都为这些值提供相同的输入(相同的用户输入,“摘要”选项卡上的相同数据),那么 Excel 应该每次冻结或平稳运行每次 ,而不是冻结一些时间,对吗?此外,如果这些值之一是问题,它不应该导致运行时错误而不是导致“Excel 已停止工作”错误吗?我测试的简单错误处理程序不应该捕获错误吗?只是试图理解。问候,
    最近更新 更多