Почему VBA выходит из режима отладки, когда метод «.paste» используется для вставки данных из ячейки в MS Excel в ячейку в таблице MS Word?

#excel #vba #ms-word #copy-paste #onerror

#excel #vba #ms-word #копировать-вставить #onerror

Вопрос:

Приведенный ниже код предназначен для последовательного копирования строки из ячеек в столбце Excel (i = 3-61), поиска папки каталога, содержащей множество копий одного и того же файла .doc, и вставки каждой строки во вторую строку, первый столбец первой таблицы в каждом файле .doc.

Проблема: программа намеренно продолжает цикл и завершает выполнение остальной части кода после первого выполнения следующей строки:

 wddoc.Tables(1).Cell(2, 1).Range.Paste
  

Это происходит даже при том, что я перехожу в каждую строку кода, используя F8, чтобы добраться до этой строки кода. Код завершает выполнение, ничего не вставив в оставшиеся файлы в каталоге. (Строка в строке 3 документа Excel была успешно вставлена в plan template — Copy (10).docx, но остальные строки не были вставлены в остальные файлы)

Код:

 Option Explicit
Sub CopyExcelToWord(path As String)

'variables----------------------------------------------------------------

'Decare Object variables for the Word application and file or documentl
Dim wdapp As Object, wddoc As Object, i As Integer

'Declare a String variable for the directory root and current file in that directory
Dim currentPath As String


'main process----------------------------------------------------------

'error handling is extremely important in making Excel work with Word
On Error Resume Next
'GetObject needs two parameters. The first is optional
Set wdapp = GetObject(, "Word.Application")
If Err.Number = 429 Then
Err.Clear
'we create a new instance of MS Word
Set wdapp = CreateObject("Word.Application")
End If

'Our application is made visible

wdapp.Visible = True

currentPath = Dir(path, vbDirectory)

For i = 3 To 61
Do Until currentPath = vbNullString
Debug.Print currentPath



        If Left(currentPath, 1) <> "." And Left(currentPath, 1) <> "" Then
        Debug.Print path amp; currentPath
           
            Sheet1.Range(Cells(i, 2), Cells(i, 2)).Copy
            'we activate our MS Word instance
            wdapp.Activate
            Set wddoc = wdapp.Documents(path amp; currentPath)
            If wddoc Is Nothing Then Set wddoc = wdapp.Documents.Open(path amp; currentPath)
            wddoc.Activate
            wddoc.Tables(1).Cell(2, 1).Range.Paste
            'Free alocated memory and close
            wdapp.Quit
            Set wddoc = Nothing
            Set wdapp = Nothing
        'The following line of code removes the cell selection in Excel
        Application.CutCopyMode = False
        currentPath = Dir()
        Else
        
        currentPath = Dir()
        
        End If


           
Loop


Next
End Sub
  

Печать (я поместил ««, где я пропустил часть пути):

. . . . plan template — Copy (10).docx L C:**…** plan template — Copy (10).docx

Программа непреднамеренно выполняет остальную часть кода. Строка в строке 3 документа Excel была успешно вставлена в шаблон плана — Копировать (10).docx, а остальные строки не были вставлены в остальные файлы)

копия шаблона плана (11).docx L C: *…**шаблон плана — Копировать (11).docx Шаблон плана урока — Копировать (12).docx L C: * … шаблон плана -копировать (12).docx шаблон плана — копировать (13).docx LC: ** … plan template — L C: *…** plan template — Копировать (9).план урока docx template.docx L C:*…**план template.docx

Комментарии:

1. Согласно комментарию ниже, а также после первого завершения, если поместить строку в ошибку GoTo 0 , а затем заменить Лист1. Диапазон (ячейки (i, 2), ячейки (i, 2)). Скопируйте с помощью ws.Range(ws.Cells(i, 2), ws.Cells(i, 2)). Скопируйте и убедитесь, что вы поместили лист для копирования в переменную с именем ws в начале кода, т.е. Dim ws Как Worksheet: Set ws = ThisWorkbook . Рабочие листы («Лист1») (или соответствующее имя листа)

2. Я сталкивался с несколькими случаями, когда вызов приведет к продолжению кода вместо пошагового выполнения. Я бы назвал это ошибкой в отладчике VBA. О, ирония судьбы.

3. @QHarr — Спасибо за ваше предложение. Я следовал вашим инструкциям, но получаю ошибку времени выполнения 9 «ошибка нижнего индекса вне диапазона». Приведенный выше код взят из модуля с именем ‘Module1’. Я вызываю модуль из листа 1 в рабочей книге. Что я сделал не так?

4. @HackSlash. Хах, да, вполне возможно, что этот отладчик ржавый… В этом экземпляре.. Я искренне надеюсь, что нет.

5. @QHarr. Я решаю имя ошибки во время выполнения, изменив ‘sheet1’ на пользовательское имя листа. Однако теперь программа достигает только строки: Set wddoc = wdapp . Документы (path amp; currentPath), прежде чем я получу ошибку времени выполнения 4160 — «ошибка, определенная приложением или объектом»

Ответ №1:

Я не уверен, что исправление этого решит вашу проблему, но у вас есть

         wddoc.Tables(1).Cell(2, 1).Range.Paste
        'Free alocated memory and close
        wdapp.Quit
        Set wddoc = Nothing
        Set wdapp = Nothing
  

После того, как вы выполнили wdapp.Quit, у вас больше нет wdapp, поэтому на следующей итерации вашего цикла «For i» ничего не будет работать.

Но если вы хотите сохранить свой wddoc, вы не можете полагаться на Set wddoc = Nothing это. Вам нужно выполнить явное закрытие или сохранить и закрыть

Так, например

         wddoc.Tables(1).Cell(2, 1).Range.Paste
        wddoc.Close -1 ' SaveChanges:=Word.wdSaveOptions.wdSaveChanges
        ' Only do this outside your "For i =" loop
        'Free alocated memory and close
        'wdapp.Quit
        Set wddoc = Nothing
        ' Only do this outside your "For i =" loop
        ' Set wdapp = Nothing
  

Комментарии:

1. Спасибо за ваше предложение. У меня такая же проблема после установки ‘wddoc’ и ‘wdapp’ на ‘nothing’ вне ‘For-loop’. Я думаю, мне нужно, чтобы ‘wdoc.save’ и ‘wdoc.close’ находились в i-цикле, потому что я хочу сохранить и закрыть документ перед открытием нового документа. ‘wddoc’ также инициализируется в цикле for. У меня есть код для «При возобновлении следующей ошибки», поэтому может возникнуть ошибка, вызывающая это для следующих итераций. Однако, когда я удаляю эту обработку ошибок, я получаю сообщение об ошибке в строке ‘Set wdapp = GetObject(, «Word. «).

2. Обновление: я закомментировал строки: Set wdapp = GetObject(, «Word. «). и «При возобновлении следующей ошибки», а также для остальной обработки ошибок. Я остаюсь с той же проблемой.

3. Вы не должны были этого делать. Верните свой код обратно, но добавьте On Error GoTo 0 непосредственно перед wdapp.Visible = True этим, чтобы вернуть обработку ошибок в состояние по умолчанию.

4. @EmptyStack — я согласен с @slightlysnarky — вам нужно закрыть документ и сохранить изменения в цикле, но выйти из приложения вне цикла. Но вы должны выйти из Word только в случае Set wdapp = GetObject(, "Word.Application") сбоя. Я бы добавил логическое значение в этот момент и протестировал его позже, например If quitWord then wdapp.Quit

5. @EmptyStack «Я думаю, мне нужно, чтобы ‘wdoc.save’ и ‘wdoc.close’ находились в цикле ввода-вывода, потому что я хочу сохранить и закрыть документ, прежде чем открывать новый документ» Ну, это то, что сказал мой пост. Что произошло, когда вы это сделали?

Ответ №2:

Ваша «проблема» не имеет ничего общего с командой paste .

Ваш код устанавливает, что все ошибки должны игнорироваться, создает объект приложения Word, затем входит в цикл, где:

  1. значение ячейки копируется
  2. открывается документ Word
  3. содержимое буфера обмена вставляется в ячейку таблицы в документе Word
  4. Word закрывается, а объект приложения уничтожается

Первая итерация цикла будет выполнена успешно, но последующие итерации приведут к ошибке в каждой строке, которая включает Word, поскольку объект больше не существует. Эти ошибки игнорируются из-за On Error Resume Next .

Что вам нужно сделать:

  1. Сброс обработки ошибок после получения объекта Word
  2. Добавьте флаг, если Word не был открыт, чтобы его можно было закрыть по завершении операций
  3. Закройте документ и сохраните изменения после его завершения внутри цикла
  4. Перемещение wdapp.quit за пределы цикла

Поскольку Word сохраняет историю буфера обмена, и вы копируете только значение одной ячейки, я бы не стал использовать copy paste для этого. Вместо этого запишите значение непосредственно в ячейку таблицы.

Вот как я бы написал ваш код:

 Option Explicit

Sub CopyExcelToWord(path As String)

   'variables----------------------------------------------------------------

   'Decare Object variables for the Word application and file or document
   Dim wdapp As Object, wddoc As Object, i As Integer

   'Declare a String variable for the directory root and current file in that directory
   Dim currentPath As String

   'declare flag to show if Word needs to be quit
   Dim quitWord As Boolean
   
   'main process----------------------------------------------------------

   'error handling is extremely important in making Excel work with Word
   On Error Resume Next
   'GetObject needs two parameters. The first is optional
   Set wdapp = GetObject(, "Word.Application")
   If Err.Number = 429 Then
      Err.Clear
      'we create a new instance of MS Word
      Set wdapp = CreateObject("Word.Application")
      'as Word wasn't already open make application visible
      wdapp.Visible = True
      'set flag to show Word needs to be shut down
      quitWord = True
   End If

   'reset error handling so that any subsequent errors aren't ignored
   On Error GoTo 0
   
   currentPath = Dir(path, vbDirectory)

   For i = 3 To 61
      Do Until currentPath = vbNullString
         Debug.Print currentPath

         If Left(currentPath, 1) <> "." And Left(currentPath, 1) <> "" Then
            Debug.Print path amp; currentPath
           
            Set wddoc = wdapp.Documents.Open(path amp; currentPath)
            wddoc.Tables(1).Cell(2, 1).Range.Text = Sheet1.Range(Cells(i, 2), Cells(i, 2)).Value
            'document no longer required so close and save changes
            wddoc.Close -1 ' SaveChanges:=Word.wdSaveOptions.wdSaveChanges
            Set wddoc = Nothing
            
            currentPath = Dir()
         Else
        
            currentPath = Dir()
        
         End If
      Loop
   Next
   
   'Now that operations involving Word are complete quit Word if necessary and destroy objects
   If quitWord Then wdapp.Quit
   Set wdapp = Nothing
End Sub
  

Комментарии:

1. Спасибо. Я сделал так, как посоветовали вы и немного придирчивый. Строка кода: wddoc. Таблицы (1).Ячейка (2, 1).Диапазон. Текст = Лист1. Диапазон (ячейки (i, 2), ячейки (i, 2)). Значение и добавление флага quitWord, похоже, остановили нежелательное продолжение кода в режиме отладки. Однако теперь я получаю ошибку времени выполнения 5 (недопустимый вызов процедуры или аргумент) в строке: ‘currentPath = Dir ()’ (над оператором ‘else’).

2. @EmptyStack — вы уверены, что у вас 59 файлов в этой папке?

3. Короткий ответ Нет. вопрос: требуется ли это для работы кода? У меня 59 ячеек, в которых 59 уникальных строк. Предполагается, что каждый документ будет иметь одну из 59 уникальных строк. Если строка currentPath= Null выполняется до того, как я скопировал последнюю строку, цикл прервется, что просто означает, что мне нужно добавить больше шаблонов документов в файл каталога. Пожалуйста, поправьте меня, если я ошибаюсь. Еще раз спасибо

4. @EmptyStack — если вы намерены добавить каждую из строк в документ, то да, вам нужно 59 документов. Это слабое место вашего решения. Если вы удалите внешний цикл и у вас будет более 59 документов, ваш код сломается. Если вы оставите внешний цикл включенным, но у вас будет меньше 59 документов, ваш код сломается. Для этого есть решение, но вам нужно задать отдельный вопрос.

5. Я удалил строку кода, которая изменяла имя документа после того, как я вставил в него строку: If Dir(currentPath) <> «» Затем назовите currentPath как newFileName, потому что его строка currentPath = Dir() выдавала ошибку времени выполнения. Код выполняется по назначению. Спасибо