Intermittent 462 Error when Using Word - vba

I have the most baffling set of errors cropping up in my code. The goal is just to create a Word document from a template, and edit the document using find/replace to fill in some data from excel. Here are the symptoms:
When I run the code the first time, everything works perfectly
The next time I run the code, one of two thing happens depending on what I did before calling it:
If I closed the word document before running the code again, the second time I run it (and every even-numbered run after that) it fails. This happens even if I've closed the userform and reran the code from the VBA editor. I think this has something to do with binding the word objects, but I'm new to VBA and don't see what I've done wrong.
If I didn't close the word document and just press the button again, the code runs and it spawns a new document, but that must not be set to the active document because it just edits the first document I spawned again.
This is the offending code:
Private Sub Generate_Click()
Set wordApp = New Word.Application
wordApp.Visible = True
wordApp.Documents.Add Template:=ThisWorkbook.Path & "\Template.dotx"
FindReplace "[[[DATE_TAG]]]", DateBox.Value
FindReplace "[[[SHIPPING_TAG]]]", POBox.Value
' ... and more of that ...
Set wordApp = Nothing
End Sub
Sub FindReplace(find As String, replace As String)
With Word.ActiveDocument.Range.find ' <---- This line is where the debugger points
' on the 462 error
.Text = find
.Replacement.Text = replace
.Wrap = wdFindContinue
.MatchCase = True
.MatchWholeWord = True
.Forward = True
.Execute replace:=wdReplaceAll
End With
End Sub

In Generate_Click you create an instance of Word referenced by the variable wordApp, but that variable isn't included in the scope of the called Sub FindReplace.
To resolve this you have options:
Create a global variable to reference the Word instance (which would also be accessible to FindReplace) or
Pass an additional parameter to FindReplace via which it can use that Word instance without requiring a Global variable.
Try this instead:
Private Sub Generate_Click()
Dim wdDoc as Word.Document, wordApp As Word.Application
Set wordApp = New Word.Application
wordApp.Visible = True
Set wdDoc = wordApp.Documents.Add(Template:=ThisWorkbook.Path & "\Template.dotx")
FindReplace wdDoc, "[[[DATE_TAG]]]", DateBox.Value
FindReplace wdDoc, "[[[SHIPPING_TAG]]]", POBox.Value
' ... and more of that ...
Set wordApp = Nothing
End Sub
Sub FindReplace(wdDoc as Word.Document, find As String, replace As String)
With wdDoc.Range.find
.Text = find
.Replacement.Text = replace
.Wrap = wdFindContinue
.MatchCase = True
.MatchWholeWord = True
.Forward = True
.Execute replace:=wdReplaceAll
End With
End Sub

Related

Replacing text in Word doc with text from Excel

I am looking to create a via script in excel that will replace a text holder in a word doc with some text from excel.
I can get the via script to open the word doc, and then save the doc under a new name. however it will not execute the replace text part :(
Private Sub CommandButton1_Click()
Dim wdApp As Object
Dim wdDoc As Object
Set wdApp = CreateObject("Word.Application")
wdApp.Visible = False
Set wdDoc = wdApp.Documents.Open("temp.docx")
With wdDoc.Content.Find
.ClearFormatting
.Text = "<<name>>"
With .Replacement
.ClearFormatting
.Font.Bold = True
.Text = "John Smith"
End With
.Execute Replace:=wdReplaceAll
End With
wdDoc.SaveAs2 Filename:=("temp2.docx")
Set wdApp = Nothing
Set wdDoc = Nothing
End Sub
I have tried doing a search in here but can't see where I am going wrong :(
currently it opens the word doc and saves it under a new name but does not replace the find and replace the text. Can anyone see where I have gone wrong and show me how to get it right?
When I set up a test for your problem description in Word, by typing <<name>> I see that Word replaces the two angled brackets with special symbols. And it offers the possibility to undo the AutoCorrect causing this.
Querying ASC(Selection.Text) on them gives Chr(171) and Chr(187), which are also double-angled bracket symbols, but using them in Find does not work. Querying AscW() reveals the two symbols are Unicode 8810 and 8811, so they need to be searched differently.
Assuming that's the issue in your case, the following works:
With wdDoc.content.Find
.ClearFormatting
.Text = ChrW(8810) & "name" & ChrW(8811) '"<<name>>"
With .Replacement
.ClearFormatting
.Font.Bold = True
.Text = "John Smith"
End With
.Execute Replace:=wdReplaceAll
End With
Further to your code - it has other, potentially grave problems (memory leak):
If you do this: wdApp.Visible = False then you need to be absolutely certain to remove Word from memory:
wdDoc.Close
wdApp.Quit
Set wdDoc = Nothing
Set wdApp = Nothing
Unlike Excel, Word won't quit automatically when its object goes out of scope (macro ends). It will stay open, which you can see in the Task Manager.
In addition, you need to release the objects in the reverse order in which they were instantiated - wdDoc before wdApp.
Setup some DocVariables in your Word doc and run the code below, from within Excel.
Sub PushToWord()
Dim objWord As New Word.Application
Dim doc As Word.Document
Dim bkmk As Word.Bookmark
sWdFileName = Application.GetOpenFilename(, , , , False)
Set doc = objWord.Documents.Open(sWdFileName)
'On Error Resume Next
objWord.ActiveDocument.variables("BrokerFirstName").Value = Range("BrokerFirstName").Value
objWord.ActiveDocument.variables("BrokerLastName").Value = Range("BrokerLastName").Value
objWord.ActiveDocument.variables("Ryan").Value = Range("Ryan").Value
objWord.ActiveDocument.Fields.Update
'On Error Resume Next
objWord.Visible = True
End Sub
You can use essentially the same process by setting up Bookmarks in Word, and pushing data from fields in Excel to fields (Bookmarks) in Word.
Sub PushToWord()
Dim objWord As New Word.Application
Dim doc As Word.Document
Dim bkmk As Word.Bookmark
sWdFileName = Application.GetOpenFilename(, , , , False)
Set doc = objWord.Documents.Open(sWdFileName)
On Error Resume Next
ActiveDocument.Variables("BrokerFirstName").Value = Range("B1").Value
ActiveDocument.Variables("BrokerLastName").Value = Range("B2").Value
ActiveDocument.Fields.Update
On Error Resume Next
objWord.Visible = True
End Sub
The famouse hope - to have one button for all live cases with caption: "Make it OK!"
Do divide the task on parts:
- Get "... text from excel ..."
- "replace text in word doc ..." with text getted from Excel on previouse step
Do it by two separate procedures for each of tasks,
called from the third procedure united them.
.

VB.NET replacing text in a word document, including a TextBox

I'm trying to replace some placeholder text in a word document using my VB.NET code. Here's the code I'm using:
Dim oWord As Word.Application
Dim aDoc As Word.Document
'Start Word and open the document template.
oWord = CreateObject("Word.Application")
oWord.Visible = True
'Load Invoice Template From Resource File
Dim myTempFile As String = Application.UserAppDataPath & "\mytemp.docx"
My.Computer.FileSystem.WriteAllBytes(myTempFile, My.Resources.InvoiceTemp, False)
aDoc = oWord.Documents.Add(myTempFile, , , True)
oWord.Selection.Find.ClearFormatting()
oWord.Selection.Find.Replacement.ClearFormatting()
With oWord.Selection.Find
.Text = "[iOrder]"
.Replacement.Text = bwOrderID
.Forward = True
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
oWord.Selection.Find.Execute(Replace:=Word.WdReplace.wdReplaceAll)
For Each oCtl As Word.Shape In aDoc.Shapes
If oCtl.Type = Microsoft.Office.Core.MsoShapeType.msoTextBox Then
oCtl.TextFrame.TextRange.Text.Replace("[iOrder]", bwOrderID)
End If
Next
This code executes correctly, with no errors, but it doesn't find "[iOrder]" in the textbox. However, if I then go to the word file and press ctrl + F for find and replace, the search criteria specified in the code is there, if I then click replace, it correctly replaces "[iOrder]" with the bwOrderID string.
I must be missing something here?
Update 1
I've updated my code to:
For Each oCtl As Word.Shape In aDoc.Shapes
If oCtl.Type = Microsoft.Office.Core.MsoShapeType.msoTextBox Then
oCtl.TextFrame.TextRange.Text = "Invoice: " & oCtl.TextFrame.TextRange.Text.Replace("[iOrder]", bwOrderID)
End If
Next
This is working correctly, however I am losing my formatting. Is there a way to keep the formatting? The word "Invoice:" is a different colour to the "[iOrder]" and I'd very much like to keep it like that if possible.
Update 2
I have got the "Find/Replace" working, but I have a formatting issue as a result. Before the Find/Replace, the formatting of the textbox is like this:
Code:
For Each oCtl As Word.Shape In aDoc.Shapes
If oCtl.Type = Microsoft.Office.Core.MsoShapeType.msoTextBox Then
oCtl.TextFrame.TextRange.FormattedText.Text = oCtl.TextFrame.TextRange.FormattedText.Text.Replace("[iOrder]", bwOrderID)
End If
Next
This is what the result is after the above code runs:
I have lost the alignment of the whole string and the color of the [iOrder] section that has been replaced. Is there a way of preserving this formatting?

Find specific string in MS Word document and set a bookmark at that location

H ey folks,
I need to find a specific string (formatted as heading 1) in a MS Word document via Excel VBA and then set a bookmark at that location. The latter part shouldn't be a problem as soon as I've got the range of the searched string.
However, I can't seem to figure out how to search for a string in Word using Excel VBA.
I tried the following (shortened):
Option Explicit
Sub exportWord(button As IRibbonControl)
Application.ScreenUpdating = False
Dim wrdDoc As Word.document
Dim wrdApp As Word.Application
Dim wrdLoc As String
wrdLoc = ThisWorkbook.Worksheets("Konfiguration").Range("changelogPath")
Set wrdApp = New Word.Application
wrdApp.Visible = True
Set wrdDoc = wrdApp.Documents.Open(wrdLoc)
Dim wrdRange As Word.Range
Dim searchString As String
Set wrdRange = wrdDoc.Range
searchString = "Test"
With wrdRange.Find
.Text = searchString
.Replacement.Text = "Replacement Test"
.wrap = wdFindContinue
.ClearFormatting
.Replacement.ClearFormatting
.Replacement.Highlight = False
.Execute Replace:=wdReplaceAll
End With
End Sub
This wouldn't actually do anything, but I just wanted to check of the finding works. It does not though and Excel just crashes without any VBA error or anything. Just says something amont of the lines of "Program not responding, the application has encountered a problem and will be closed down"
Does anyone have an idea why Excel would just crash without any proper error message? Or how to implemented a search in a Word document properly?
best regards,
daZza
Tried something different and solved it with:
For Each rngStory In wrdDoc.StoryRanges
With rngStory.Find
.Replacement.ClearFormatting
.Text = "Ă„nderungen in Test12345"
.Replacement.Text = "test"
.wrap = wdFindContinue
.ExecuteReplace:=wdReplaceAll
End With
Next

How to replace a word in MS Word to a table by VBA

I have opened the MS word using create object then ,I have to search for a word and replace it with a table.I was able to build a table using VBA.
But, I need to replace that word (Matched) with a Table and later fill the table as per cell.
This is my code:-
Dim MyApp As New Word.Application
Dim MyDoc As Word.Document
Set MyDoc = MyApp.Documents.Add
MyApp.Visible = True
MyDoc.Activate
With ActiveDocument.Content.Find
.Text = "blue"
.Forward = True
.Execute
If .Found = True Then .Parent.Bold = True
End With
MyApp.ActiveDocument.Tables.Add Range:=MyApp.Selection.Range, numrows:=5, numcolumns:=5
MyApp.ActiveDocument.Save
MyApp.Quit
It will help if you do more object-oriented programming, instead of relying on "ActiveDocument", etc.
Start by defining a Range object that represents the Document.Content. The Find.Execute method, if returns True, will redefine this Range object to the found word. So you can use this as the Range argument in the Tables.Add method.
Update from comments I realize that you are instantiating Word, and then adding a new (blank) document. As expected, the Find.Execute will not find anything in this document. Instead, I revised to specifically open a document from a known file path.
'Create a new instance of MS Word
Dim MyApp As New Word.Application
Dim MyDoc As Word.Document
Dim wdRange As Word.Range
'Open an existing Word file:
Set MyDoc = MyApp.Documents.Open("c:\myfile.docx") '# REVISE AS NEEDED
'Make MS Word visible:
MyApp.Visible = True
'Assign the wdRange variable to the document's Content
Set wdRange = MyDoc.Content
'Use the Find method within wdRange:
With wdRange.Find
.Text = "blue"
.Forward = True
.Execute
If .Found = True Then
.Parent.Bold = True
' Since Find.Execute resizes the wdRange to the "found" word, we can
' use this wdRange as the Range argument for Tables.Add:
MyDoc.Tables.Add Range:=wdRange, numrows:=5, numcolumns:=5
End If
End With

VBA loop won't stop/doesn't find the "\EndofDoc" marker

I am writing a vba macro to search a word document line by line and trying to find certain names in the document. The looping works fine except for when it gets to the end of the document, it just continues from the top and starts over. Here is the code:
Application.ScreenUpdating = False
Dim i As Integer, Rng As Range
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "?"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.found
i = i + 1
Set Rng = .Duplicate
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\line")
MsgBox "Line " & i & vbTab & Rng.Text
If Rng.Bookmarks.Exists("\EndOfDoc") Then Exit Do
.start = Rng.End
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Set Rng = Nothing
Application.ScreenUpdating = True
I have also tried this piece of code:
Dim appWD As Word.Application
Dim docWD As Word.Document
Dim rngWD As Word.Range
Dim strDoc As String
Dim intVal As Integer
Dim strLine As String
Dim bolEOF As Boolean
bolEOF = False
' Set strDoc here to include the full
' file path and file name
On Error Resume Next
Set appWD = GetObject(, "Word.Application")
If Err.Number <> 0 Then
Set appWD = CreateObject("Word.Application")
End If
Err.Clear
On Error GoTo 0
strDoc = "c:\KenGraves\Project2\output\master.doc"
Set docWD = appWD.Documents.Open(strDoc)
appWD.Visible = True
docWD.Characters(1).Select
Do
appWD.Selection.MoveEnd Unit:=wdLine, Count:=1
strLine = appWD.Selection.Text
Debug.Print strLine
intVal = LineContainsDescendant(strLine)
If intVal = 1 Then
MsgBox strLine
End If
appWD.Selection.Collapse wdCollapseEnd
If appWD.Selection.Bookmarks.Exists("\EndOfDoc") Then bolEOF = True
Loop Until bolEOF = True
Neither seem to recognize the bookmark ("\EndOfDoc"). It doesn't matter which one gets working. Is it possible that my document does not contain this bookmark?
Not terribly elegant, but this change to one line of your first procedure seems to stop it at the appropriate time. I believe you actually have to insert bookmarks into your document if you want to reference them. They aren't automatically generated.
If i >= ActiveDocument.BuiltInProperties("NUMBER OF LINES") Then Exit Do
Cheers, LC
Unless you have a corrupted document, all Word documents should have the \EndOfDoc bookmark. You can check using simply ActiveDocument.Range.Bookmarks("\EndOfDoc").Exists. If it doesn't then you'll need to supply more details on the version of Word and if possible supply a sample document via Dropbox or the like.
I'm not sure why you're looping to the start of the Word document, when I run the code it works fine. However, if I put a footnote at the end of the document it runs into an endless loop, depending on your documents you may run into additional situations like this where your code fails to handle the document setup.
I would suggest modifying slightly how you check for the end of the document to make your code a bit more robust. I'd still use the bookmark "\EndOfDoc", however I'd check the limits of the range against your current search range.
So at the top of your code declare a range variable and set it to range of the end of the document eg:
Dim rEnd As Range
Set rEnd = ActiveDocument.Bookmarks("\EndOfDoc").Range
and then in your loop, instead of this line:
If Rng.Bookmarks.Exists("\EndOfDoc") Then Exit Do
use this line:
If Rng.End >= rEnd.End Then Exit Do