Not able to call second macro after first macro is generated - excel

I have a few subs that works perfectly fine individually. However when I put few subs together to be called in one sub, it won't run properly. After first sub is called and generated, the second sub won't run and came out an error. I just don't get it how it won't run, please anyone tell me where went wrong. I tried adjusting the codes but it won't work.
Mail merge won't work for my project as I have thousands of data to be generated. Thank you in advance.
This sub below is to call sub test1 and test2 and came out an error
Sub callsubs()
test1
test2
End Sub
Here is my sub test1 and test2 is the same as test1 so I won't be putting it again.
Option Explicit
Const FilePath As String = "Z:\PJ General\Staff\Admin Dept\Ching Lu\AP LOAN v3 Test\Correspondences\"
Dim wd As New Word.Application
Sub Copy2word(bookmarkName As String, Text2Type As String)
'copy each cell to relevant Word bookmark
wd.Selection.GoTo What:=wdGoToBookmark, Name:=bookmarkName
wd.Selection.TypeText Text2Type
End Sub
Sub test1()
Dim doc As Object
Set doc = CreateObject("Word.Application")
Dim strName As String
strName = Sheet15.Range("C67")
wd.Visible = True
Dim dev_attn As String
Set doc = wd.Documents.Open(FilePath & "Step 2a LBS-LPPSA.doc", ReadOnly:=True)
doc.Unprotect Password:="stwppj312"
With wd.ActiveDocument
dev_attn = ThisWorkbook.Sheets("Data Entry B").Range("C809").Value
Copy2word "dev_attn", dev_attn
Call devref
Call stwpref
doc.SaveAs Filename:="C:\Users\" & Environ$("username") & "\Downloads\" & "Step2a " & strName & ".doc"
End With
wd.Quit
Set doc = Nothing
End Sub

In the code, you create the Word application object several times - once via Dim wd As New Word.Application (early binding; the instance is assigned to the global variable wd), and every time you run test1() and test2() via Set doc = CreateObject("Word.Application") (late binding; the instance is assigned to the local variable doc). At the end of each test1() and test2() procedure, you destroy the global wd instance via wd.Quit. So by running test1() and test2() procedures sequentially, after the first one finishes, the wd object (which you access, for example in wd.Visible = True) will no longer be bound to the Word.Application instance, hence the error.
Creating multiple instances of "Word.Application" is time-consuming and resource-intensive, so I recommend creating such an object once and using it in all procedures, and removing it when done. Accordingly, don't create a local instance of "Word.Application" inside each of test1() and test2() procedures, but use only the global one.
Dim wd As New Word.Application
...
Sub callsubs()
test1 ' without wd.Quit, without Set doc = CreateObject("Word.Application")
test2 ' without wd.Quit, without Set doc = CreateObject("Word.Application")
wd.Quit
End Sub
In addition, the command Set doc = CreateObject("Word.Application") in your procedures creates a local instance of "Word.Application" that is not used at all, but wastes time and resources. This happens as follows:
Set doc = CreateObject("Word.Application") ' a new instance of the "Word.Application" is created and bound with doc variable
...
Set doc = wd.Documents.Open(FilePath & "Step 2a LBS-LPPSA.doc", ReadOnly:=True) ' doc variable re-binding with opened Document
...
'The "Word.Application" instance hasn't been destroyed, it still lives in memory (see Task Manager, section Background Processes)
I recommend you remove the line Set doc = CreateObject("Word.Application")

It looks to me like the problem is that you've created your wd object as a global variable that you then quit in Test1. This will cause Test2 to have no wd to reference.
I would bring the wd declaration into the Test1 and Test2 and then pass it to Copy2word as a parameter instead. Something like this:
Sub Copy2word(bookmarkName As String, Text2Type As String, wd As Word.Application)
[Same code]
End Sub
Sub test1()
Dim wd As New Word.Application
...
Copy2word "dev_attn", dev_attn, wd
...
End Sub

Related

Writing from Excel, to Word Activex Text box

I have an Excel application which collects information via a form based interface. This is used to;
Fill values in the workbook
A procedure opens a Word document (template essentially) and names the file according to rules, based
on some of the input data. (Okay to this point)
The idea then, is to transfer collected information (from the Excel app driving this process) to
the same Word document opened and named.
Specifically, I intend to populate a number of uniquely named ActiveX textboxes with document.
*** This is where I fail miserably.
I have enabled the "Microsoft Word 16.0 Object Library" under references in the MSExcel VBA environment.
Given that I know the name/title of the content control (ActiveX textbox is a 'content control' isn't it?). The code below is a simplified example, if it works for the example I should be able to sort out the broader document:
Sub trial()
Dim Word As Word.Application
Dim wdDoc As Word.Document
On error resume next
Set Word = New Word.Application
Set wdDoc = Word.Documents.Open("G:\CAPS Management Tool\Customer.docm")
Word.Application.Visible = True
Dim cc As Object
Set cc = ActiveDocument.SelectContentControlsByTitle(txt_PersonName) 'txt_PersonName is the control name
cc.Range.Text = "SUCCESS" 'Run-time error 438
'Object does not support property or method
Set cc = ActiveDocument.SelectContentControlsByTitle(txt_Address) 'txt_Address is the control name
cc.Range.Text = "SUCCESS" 'Run-time error 438
'Object does not support property or method
End Sub
Anybody able to assist? There are a lot of text boxes in the Word document I wish to plug in to.
Thanks in advance.
OK, so I kept digging (I don't like accepting defeat) and found that my entire premise was wrong! ActiveX controls in Word are considered "InlineShapes" not "ContentControls". But the results I was reading in the internet searches had me confused (didn't claim to be the sharpest tool in the shed).
Once I realised this, some more digging provided the answer (see below).
So, first to list the 3 controls in my document (and their index) with the following Sub
Sub ListActiveXControls()
Dim i As Integer
i = 1
Do Until i > ActiveDocument.InlineShapes.Count
Debug.Print ActiveDocument.InlineShapes(i).OLEFormat.Object.Name & " Control Index = " & i
i = i + 1
Loop
End Sub
Now moving to EXCEL, I used the following:
Sub trial()
Dim Word As Word.Application
Dim wdDoc As Word.Document
Set Word = New Word.Application
Set wdDoc = Word.Documents.Open("G:\CAPS Management Tool\Customer.docm")
Word.Application.Visible = True
debug.print "ActiveDocument Name is : " & ActiveDocument.Name
' Result = Nothing
' Allowing the code to continue without the pause caused the operation to fail
Application.Wait (Now + TimeValue("0:00:10")) ' See text below, would not work without pause
wdDoc.Activate
' Begin set ActiveX control values. In this instance,
' The first line corresponds to 'Textbox1'
ActiveDocument.InlineShapes(1).OLEFormat.Object.Text = "Success"
' The second line corresponds to 'Textbox2'
ActiveDocument.InlineShapes(2).OLEFormat.Object.Text = "Success"
' The third line corresponds to 'ChkBox1'
ActiveDocument.InlineShapes(3).OLEFormat.Object.Value = True
End Sub
For some reason, without the 'Wait' command, the operation fails. Stepping through, if there is no pause, the ActiveDocument seems to be null, no idea why. It occurs with a document with 2 ActiveX controls or 165 ActiveX controls and the wait required seems to
be 10 secs on my PC. Incidentally, setting almost 150 control values was only seconds, once the wait period was completed.
If anyone knows why the 'Wait' is seemingly required, I'd be interested to know!
Here are a few tips to help you work this out.
Don't use On Error Resume Next unless you really need it. And then when you do use it, re-enable error catching with On Error Goto 0 as quickly as possible. Otherwise you'll miss many errors in your code and it will be hard to tell what's happening.
Don't use the name Word as a variable name. It is reserved for the Word.Application and will only confuse the compiler (and you).
The control titles are text strings, so you must enclose them in double-quotes.
I've thrown in a bonus Sub that gives you a quick method to either open a new Word application instance or attach to an existing application instance. You'll find that (especially during debugging) there will be dozens of Word exe's opened and running.
The example code below also breaks the assignment of your "SUCCESS" value to the control into a separate Sub. It's in this short Sub that using On Error Resume Next is appropriate -- and isolated from the rest of the logic -- limiting its scope.
Option Explicit
Sub trial()
Dim wdApp As Word.Application
Dim wdDoc As Word.Document
Set wdApp = AttachToMSWordApplication
Set wdDoc = wdApp.Documents.Open("C:\Temp\Customer.docm")
wdApp.Application.Visible = True
TextToControl wdDoc, "txt_PersonName", "SUCCESS"
TextToControl wdDoc, "txt_Address", "SUCCESS"
End Sub
Private Sub TextToControl(ByRef doc As Word.Document, _
ByVal title As String, _
ByVal value As String)
Dim cc As ContentControl
On Error Resume Next
Set cc = doc.SelectContentControlsByTitle(title).Item(1)
If Not cc Is Nothing Then
cc.Range.Text = value
Else
Debug.Print "ERROR: could not find control titled '" & title & "'"
'--- you could also raise an error here to be handled by the caller
End If
End Sub
Public Function AttachToMSWordApplication() As Word.Application
'--- finds an existing and running instance of MS Word, or starts
' the application if one is not already running
Dim msApp As Word.Application
On Error Resume Next
Set msApp = GetObject(, "Word.Application")
If Err > 0 Then
'--- we have to start one
' an exception will be raised if the application is not installed
Set msApp = CreateObject("Word.Application")
End If
Set AttachToMSWordApplication = msApp
End Function

Setting range in Word with VBA in Excel

How do I set a range in Word while opening that file with VBA in Excel?
Dim wordApp As Word.Application
Dim wordObject As Word.Document
Dim wordRange As Word.Range
Dim filePath As String
Dim fileName As String
filePath = "C:\Users\"
fileName = "somename.docx"
Set wordApp = CreateObject("Word.Application")
With wordApp
.Visible = True
.Activate
.WindowState = wdWindowStateNormal
End With
Set wordObject = wordApp.Documents.Open(filePath & fileName)
Set wordRange = Documents(fileName).Sections(1).Range
With wordRange
'code
End With
The line causing trouble:
Set wordRange = Documents(fileName).Sections(1).Range
Regardless of the string I put in this returns
4160 runtime error "Bad File Name"
If I use ActiveDocument instead of Documents(), I get
4248 runtime error: "This command is not available because no document is open".
The error persists even after opening multiple unsaved and saved Word docs whilst running the code, only to have the same error message show up.
Set wordRange = Documents(fileName).Sections(1).Range errors because Excel doesn't know what Documents is (or it resolves it to something other than Word.Documents)
To fix that, you'd use (just as you did in the previous line)
Set wordRange = wordApp.Documents(fileName).Sections(1).Range
That said, you've already Set the Document(filepath & filename) to wordObject, so use it:
Set wordRange = wordObject.Sections(1).Range
Also, Excel doesn't know wdWindowStateNormal, so a new Variant variable is created (unless you have Option Explicit, which you should, always) and assigned the default value 0. Which just happens to be the value of Word.wdWindowStateNormal so no harm done, but the code is misleading.
To fix, use
.WindowState = 0 'wdWindowStateNormal
I'm curious about the way you've created the object. Using early binding but instead of creating New Word.Application you use CreateObject
Was this an intentional decision?
What is the benefit?

Write to Word template from Excel crushes on the second run

When I open Workbook and run this code everything is fine. Also if I close Workbook and open it again and run this code everything if functioning. However if I open Workbook and try to run this code for the second time then all Word operations are crushing. Word template is opened by code and even is saved to needed destination but it is not able to close Word document and gives an error:
Is there some variable or something in Windows memory still present after code has been executed because after closing and reopening Workbook everything works fine. Any ideas how to fix this?
Sub opentemplateWordOL()
Dim sh As Shape
Dim objWord As Object, objNewDoc As Object ''Word.Document
Dim objOL As OLEObject
Dim wSystem As Worksheet
'Application.ScreenUpdating = False
Set wSystem = ThisWorkbook.Sheets("Templates")
''The shape holding the object from 'Create from file'
''Object 2 is the name of the shape
Set sh = wSystem.Shapes("OfferLetterTemplate")
''The OLE Object contained
Set objOL = sh.OLEFormat.Object
'Instead of activating in-place, open in Word
objOL.Verb xlOpen
'Set objWord = objOL.Object 'The Word document
Set objNewDoc = objOL.Object
Set objWord = objNewDoc.Application
Dim objUndo As Object 'Word.UndoRecord
'Be able to undo all editing performed by the macro in one step
Set objUndo = objWord.UndoRecord
objUndo.StartCustomRecord "Edit In Word"
With objNewDoc
'Cover page
.Bookmarks("CoverPageTitle").Range.Text = ThisWorkbook.Sheets("Sheet1").Range("B2").Value
objNewDoc.SaveAs2 Environ$("Temp") & "\" & _
"MyFile" & ".docx"
objUndo.EndCustomRecord
objNewDoc.Undo
.Application.Quit False
End With
Set objWord = Nothing
Set objUndo = Nothing
Set sh = Nothing
Set wSystem = Nothing
Set objNewDoc = Nothing
'Application.ScreenUpdating = True
End Sub
You can check how this behaves on my computer here: https://streamable.com/2xd8k
You can see (by time of creation of file) that it is getting overwritten every time even when there is an error.

'Compile Error' - 'Object required' VBA Code

I am trying to print a document however I am receiving the error
'Compile Error' - 'Object required'
and then it highlights the line
Set previousPrinter = objWord.ActivePrinter
I am using the following code:
Private Sub CommandButton1_Click()
Dim previousPrinter
Dim objWord As New Word.Application
Set previousPrinter = objWord.ActivePrinter
objWord.ActivePrinter = "Followprint"
On Error GoTo CLOSE_WORD_APP 'error handling to ensure there will not be any orphaned and invisible Word application left
Dim doc As Word.Document
Set doc = objWord.Documents.Open("test.docx")
doc.Variables("refBook").Value = Me.txtRef.Text
doc.Fields.Update
doc.Save
objDoc.PrintOut
' Restore the original printer
objWord.ActivePrinter = previousPrinter
doc.Close
CLOSE_WORD_APP:
objWord.Quit SaveChanges:=False
End Sub
ActivePrinter returns a string with the name of the printer, not an object. Therefore, declare previousPrinter as string and simply assign the result of ActivePrinter to it.
Dim previousPrinter as String
Dim objWord As New Word.Application
previousPrinter = objWord.ActivePrinter
objWord.ActivePrinter = "Followprint"
(...)
In VBA, the keyword Set is only used to assign an object to a variable (eg the result of the Documents.Open-function). If you use it when trying to assign anything that is not an object, the compiler (or the runtime) will throw the Object required error message.

Excel process stuck in Task Manager

In Access I open an Excel file, read from it and close it. The Excel process does not go away from Task Manager.
I found the same problem here but no solution that worked.
If I click the Reset button in the VB Editor, it goes away (or if I change anything in the code, which also causes the project to reset and gets rid of the unwanted Excel process).
I have the following class, called clsTest:
Option Compare Database
Option Explicit
Private xlFolder As String
Private xlFile As String
Private xlSheet As String
Private colShortURL As String
Private oXL As Excel.Application
Private oWB As Excel.Workbook
Private oWS As Excel.Worksheet
Private iLastRow As Long
Private Sub Class_Initialize()
Debug.Print "From class: Going through initialization inside class - constructor"
xlFolder = "E:\COMH\Excel"
xlFile = "Records v8z.xlsm"
xlSheet = "comh"
Set oXL = New Excel.Application
Set oWB = oXL.Workbooks.Open(Filename:=(xlFolder & "\" & xlFile), ReadOnly:=True)
Set oWS = oWB.Sheets(xlSheet)
iLastRow = oWS.Range("A" & Rows.Count).End(xlUp).row
End Sub
Public Property Get ShortURL() As String
ShortURL = "Hello World " & iLastRow
End Property
Private Sub Class_Terminate()
Debug.Print "From class: Going through the clean-up inside class - destructor"
oWB.Close SaveChanges:=False
Set oWS = Nothing
Set oWB = Nothing
oXL.Quit
Set oXL = Nothing
End Sub
I have the following Module to use above class:
Option Compare Database
Option Explicit
Private Sub TestClass()
Dim newExcel As clsTest
Try:
On Error GoTo Catch
Set newExcel = New clsTest
Debug.Print "Class instantiated, all good"
Debug.Print "ShortURL=" & newExcel.ShortURL
GoTo Finally
Catch:
Debug.Print "dealing with the error"
Debug.Print Err.Description & " - " & Err.Number
Finally:
Debug.Print "doing the finally stuff"
Set newExcel = Nothing
End Sub
I get the result I want:
From class: Going through initialization inside class - constructor
Class instantiated, all good
ShortURL=Hello World 2603
doing the finally stuff
From class: Going through the clean-up inside class - destructor
There are no errors but a process for Excel persists in Task Manager Processes tab.
nice troubleshooting!
try changing the problematic line from
iLastRow = oWS.Range("A" & Rows.Count).End(xlUp).row
to this (you may need to go further and reference the range at the Application level, I can't remember this and can't test now)
iLastRow = oWS.Range("A" & oWS.Rows.Count).End(xlUp).row
The likely issue is that you do not have a fully qualified reference, see here
In my scenario, excel and other office apps didnt close completely from TaskManager because i was running Fiddler tool(for tracing the http requests since i mostly work on web applications).
If i close or stop capturing on fiddler tool then it exits from TaskManager properly.

Resources