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.
Related
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
I am getting inconsistent results when I try to refer to an active workbook. About half the time I get the "Method of 'Sheets' of Object '_Global' not failed" error and other times the code works fine. I don't see a pattern.
The VBA code is part of a Word document that allows the user to open a template Excel file and select/copy text from the Word doc into rows on the Excel file.
In a previous sub I successfully open an Excel template file (I call it a RTM template). In the code below I want to activate the "RTM" worksheet, select the first cell where the template could already have data in it from a previous execution and if there is, then count how many rows of data exist. In this way the new data will be posted in the first row which does not have any data. I am using named ranges in my Workbook to refer to the starting cell ("First_Cell_For_Data").
When I run my code sometimes it runs without error and other times it stops on the "Sheets("RTM").Activate" and gives me the "Method...." error. The same result occurs when I change the variable definition of wb_open to Object. I have also tried using "wb_open.Sheets("RTM").Activate" with the same results.
As suggested in the comments below I added "If wb_open is nothing ...." to debug the issue. I also added the sub List_Open_Workbooks which enumerates the open workbooks (of which there is only 1) and activates the one that matches the name of the one with the correct filename. This is successful. But upon returning to Check_Excel_RTM_Template I still get the Method error on the "Sheets("RTM").Activate" line.
Second Update: after more time diagnosing the problem (which still occurs intermittently) I have added some code that may help getting to the root of the problem. In the "List_Open_Workbooks" sub I test for xlApp.Workbooks.Count = 0. So all references to an open Excel workbook will fail. At this point my template workbook is open in Windows. Am I drawing the correct conclusion?
Third Update: I tried Set wb_open = GetObject(str_filename) where str_filename contains the name of the Excel template file I just opened.
I get the following error message.
Also, I noticed that if I start with a fresh launch of Word and Excel it seems to run just fine.
Sub Check_Excel_RTM_Template(b_Excel_File_Has_Data As Boolean, i_rows_of_data As Integer)
Dim i_starting_row_for_data As Integer
Dim wb_open As Object
Set wb_open = ActiveWorkbook
i_rows_of_data = 0
If wb_open Is Nothing Then
MsgBox "RTM Workbook not open in Check_Excel_RTM_Template"
Call List_Open_Workbooks(b_Excel_File_Has_Data, i_rows_of_data)
Else
' On Error GoTo Err1:
' Sheets("RTM").Activate
' range("First_Cell_For_Data").Select
Workbooks(wb_open.Name).Worksheets("RTM").range("First_Cell_For_Data").Select
If Trim(ActiveCell.Value) <> "" Then
b_Excel_File_Has_Data = True
Do Until Trim(ActiveCell.Value) = ""
ActiveCell.Offset(1, 0).Select
i_rows_of_data = i_rows_of_data + 1
Loop
Else
b_Excel_File_Has_Data = False
End If
End If
Exit Sub
Err1:
MsgBox getName(str_Excel_Filename) & " is not a RTM template file."
b_abort = True
End Sub
Sub to enumerate all open workbooks
Sub List_Open_Workbooks(b_Excel_File_Has_Data As Boolean, i_rows_of_data As Integer)
Dim xlApp As Excel.Application
Set xlApp = GetObject(, "Excel.Application")
Dim str_filename As String
Dim xlWB As Excel.Workbook
If xlApp.Workbooks.Count = 0 Then
MsgBox "Error: Windows thinks there are no workbooks open in List_Open_Workbooks"
b_abort = True
Exit Sub
End If
For Each xlWB In xlApp.Workbooks
Debug.Print xlWB.Name
str_filename = getName(str_Excel_Filename)
If Trim(xlWB.Name) = Trim(str_filename) Then
xlWB.Activate
If xlWB Is Nothing Then
MsgBox "Workbook still not active in List_Open_Workbooks"
b_abort = True
Exit Sub
Else
' Sheets("RTM").Activate
Workbooks(xlWB.Name).Worksheets("RTM").range("First_Cell_For_Data").Select
range("First_Cell_For_Data").Select
If Trim(ActiveCell.Value) <> "" Then
b_Excel_File_Has_Data = True
Do Until Trim(ActiveCell.Value) = ""
ActiveCell.Offset(1, 0).Select
i_rows_of_data = i_rows_of_data + 1
Loop
Else
b_Excel_File_Has_Data = False
End If
End If
End If
Next xlWB
Set xlApp = Nothing
Set xlWB = Nothing
End Sub
Function to extract filename from path/filename
Function getName(pf)
getName = Split(Mid(pf, InStrRev(pf, "\") + 1), ".")(0) & ".xlsx"
End Function
I am hoping I found the source of my problem and solved it.
I believe that referring to an open workbook in sub using Dim wb_open As Object & Set wb_open = ActiveWorkbook in the Check_Excel_RTM_Template sub is causing my inconsistent problems....perhaps this is an anomoly (bug) in the VBA implementation in Word.
In the revised code I posted below I am passing the o_Excel object from the calling routine and using oExcel.Activesheet.xxx to reference ranges and values.
Now I next problem is that I am having errors on the form control button code which also uses the Dim wb_open As Object & Set wb_open = ActiveWorkbook approach to referring to the open workbook. But I'll post that as a new question.
Thanks to all who commented and provided suggestions.
Sub Check_Excel_RTM_Template(oExcel As Object)
Dim i_starting_row_for_data As Integer
Dim str_filename As String
i_rows_of_data = 0
On Error GoTo Err1:
oExcel.ActiveSheet.range("First_Cell_For_Data").Select
If Trim(oExcel.ActiveCell.Value) <> "" Then
b_Excel_File_Has_Data = True
Do Until Trim(oExcel.ActiveCell.Value) = ""
oExcel.ActiveCell.Offset(1, 0).Select
i_rows_of_data = i_rows_of_data + 1
Loop
Else
b_Excel_File_Has_Data = False
End If
Exit Sub
Err1:
Documents(str_doc_index).Activate
MsgBox getName(str_Excel_Filename) & " is not a RTM template file."
b_abort = True
End Sub
I made a wizard button on a Access form where you can transfer the data of a Query to a new excel file and then the new file will automatically open.
Is there a way to make a reference to this new file or Sheet, because i want to put something in this new file.
This will get an open instance of Excel if it exists or create one if there isn't one.
Public Function GetExcelApp() As Excel.Application
' Returns open excel instance.
' If it doesn't exist, creates one to return
On Error GoTo ErrHandler
Const PROC_NAME As String = "GetExcelApp"
Const ERR_APP_NOTRUNNING As Long = 429
Set GetExcelApp = GetObject(, "Excel.Application")
CleanExit:
Exit Function
ErrHandler:
If Err.Number = ERR_APP_NOTRUNNING Then
Set GetExcelApp = CreateObject("Excel.Application")
Else
Err.Raise Err.Number, GetErrorSource(PROC_NAME), Err.Description & vbNewLine & "Unable to get instance of Excel.", Err.HelpFile, Err.HelpContext
End If
End Function
I keep this code in an XLHelper class and use it like this.
Dim helper As New XLHelper
Dim xl As Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
Set xl = helper.GetExcelApp
Set wb = xl.Workbooks.Add
Set ws = wb.Worksheets.Add
To get the reference of the new sheet/workbook, you just set it equal to the results of the collection's Add method.
This works because Workbook.Add and Worksheets.Add return the object they create.
I have vba code which opens a word document based on a template and when finished, runs the code below:
Public Sub Destroy(doc As Word.Document, app As Word.Application)
If Not (doc Is Nothing) Then doc.Close SaveChanges:=wdDoNotSaveChanges
If app.Documents.Count = 0 Then app.Quit wdDoNotSaveChanges
Set app = Nothing
End Sub
(this means that the app only closes if there are no other documents open and doesnt leave a blank application loaded when finished either)
I would like to extend this to excel and possibly other applications in the future; but rather than write a separate function for every different application, I wondered if I could have one function do-it-all.
The problem I'm finding is the declaration of arguments "doc As Word.Document" and "app As Word.Application".... is there a way of declaring what "doc" and "app" are in the calling program, and then getting the type definition of them inside my function to decide what to do depending on what type of application I choose to destroy()?
Edit:
I'm happy with the code, but on running a quick test in the code below, I founf that byref and byval both didnt effect the value of myval:
Private Sub Command12_Click()
Dim myval As Integer
myval = 1
MsgBox "the value of myval is " & myval
doByVal (myval)
MsgBox "the value of myval is " & myval
doByRef (myval)
MsgBox "the value of myval is " & myval
End Sub
Private Sub doByVal(ByVal a As Integer)
a = a + 1
MsgBox "byVal gives " & a
End Sub
Private Sub doByRef(ByRef a As Integer)
a = a + 1
MsgBox "byRef gives " & a
End Sub
Sure. You may declare generic Objects as args of the function, and verify their actual type in function. Basically, your code frame will look like:
Public Sub Destroy(ByVal doc As Object, ByVal app As Object)
If (TypeOf doc Is Word.Document) And (TypeOf app Is Word.Application) Then
' Word related stuff
ElseIf (TypeOf doc Is Excel.Workbook) And (TypeOf app Is Excel.Application) Then
' Excel related stuff
' ...
Else
' Do something about mixed cases, unhandled types etc.
End If
End Sub
And here's passing args to Destroy function, as (a bit exaggerated) example:
Dim my_doc As Excel.Workbooks
Dim my_app As Excel.Application
Set my_app = Excel.Application
Set my_doc = my_app.Workbooks("IWantYouClosed.xlsx")
Call Destroy(my_doc, my_app)
You are trying to use Early binding, and that requires creating a reference to the appropriate library (in Tools, References).
Alternatively, you can use Late binding, declaring doc As Object.
The web has lots of pages on "early binding vs late binding". One sample here.
Using the help given, this is the code I am now using, hope this helps someone.
Public Sub Document(ByVal doc As Object, ByVal app As Object)
Select Case True
Case (TypeOf doc Is Word.Document And TypeOf app Is Word.Application)
If Not (doc Is Nothing) Then doc.Close SaveChanges:=wdDoNotSaveChanges
If app.Documents.Count = 0 Then app.Quit wdDoNotSaveChanges
Set app = Nothing
Case (TypeOf doc Is Workbook And TypeOf app Is Excel.Application)
'code for excel workbook
Case Else
MsgBox "Cannot recognise the document/application, or there may be a mismatch"
End Select
End Sub
I have to know how can i link the excel database of Instrument loop Diagram in AutoCad format. I have AutoCad Template for a loop typical and Excel Database in which i have 100 Loops information for particular typical.I have AutoCad 2006,2007 and 2011 with me. please suggest idea for linking and generating he AutoCAD Drawings automatically.
The easiest way would be to learn a bit of AutoLisp, which is really worth learning if you're into generating drawings or automating your processes within AutoCAD.
Here's a great website for learning AutoLisp:
http://www.afralisp.net/index.php
AutoDesk's Lisp forum is also a great source of help.
As for extracting the data from Excel, here is a library which really facilitates access from AutoLisp:
http://download.cnet.com/KozMos-VLXLS/3000-2077_4-94214.html
'General rule: excel and acad have to be same like both 64bit or both 32 bit !!!
' You will need to add a reference to the AutoCAD
' Type Library to run this example book. Use the "Tools -
' References" menu. If you prefere you can switch to late
' binding by changeing the AutoCAD types to generic objects
Public Sub Excel_drives_acadPolyline_import_POINTs()
Dim objApp As AcadApplication
Dim objDoc As AcadDocument
Dim objEnt As AcadEntity
Dim varPnt As Variant
Dim strPrmpt As String
Dim intVCnt As Integer
Dim varCords As Variant
Dim varVert As Variant
Dim varCord As Variant
Dim varNext As Variant
Dim intCrdCnt As Integer
On Error GoTo Err_Control
Set objApp = AINTERFACE.Iapp
Set objDoc = objApp.activedocument
AppActivate objApp.CAPTION
objDoc.Utility.GetEntity objEnt, varPnt
If TypeOf objEnt Is AcadLWPolyline Then
AppActivate ThisDrawing.applicaTION.CAPTION
varCords = objEnt.COORDINATES
For Each varVert In varCords
intVCnt = intVCnt + 1
Next
For intCrdCnt = 0 To intVCnt / 2 - 1
varCord = objEnt.COORDINATE(intCrdCnt)
Excel.applicaTION.Cells(intCrdCnt + 1, 1).value = varCord(0)
Excel.applicaTION.Cells(intCrdCnt + 1, 2).value = varCord(1)
Next intCrdCnt
Else
MsgBox "Selected entity was not a LWPolyline"
End If
Exit_Here:
If Not objApp Is Nothing Then
Set objApp = Nothing
Set objDoc = Nothing
End If
Exit Sub
Err_Control:
'debug.print err.DESCRIPTION
Resume Exit_Here
End Sub
'----------------------------------------------------------------
' You will need to add a reference to the Excel
' Type Library to run this.In case of excel excel.exe is the library !
Sub acad-drives_excel()
Dim xAP As Excel.applicaTION
Dim xWB As Excel.Workbook
Dim xWS As Excel.WorkSheet
Set xAP = Excel.applicaTION
Set xWB = xAP.Workbooks.Open(SLOPEDIR.PROJECT & "\A2K2_VBA\IUnknown.xls")
Set xWS = xWB.Worksheets("Sheet1")
MsgBox "Excel says: """ & Cells(1, 1) & """"
Dim A2K As AcadApplication
Dim A2Kdwg As AcadDocument
Set A2K = AINTERFACE.Iapp
Set A2Kdwg = A2K.applicaTION.documents.Add
MsgBox A2K.NAME & " version " & A2K.version & _
" is running."
Dim HEIGHT As Double
Dim p(0 To 2) As Double
Dim TxtObj As ACADTEXT
Dim TxtStr As String
HEIGHT = 1
p(0) = 1: p(1) = 1: p(2) = 0
TxtStr = Cells(1, 1)
Set TxtObj = A2Kdwg.modelspace.AddText(TxtStr, _
p, HEIGHT)
A2Kdwg.SaveAs SLOPEDIR.PROJECT & "\A2K2_VBA\IUnknown.dwg"
A2K.documents.Close
A2K.Quit
Set A2K = Nothing
xAP.Workbooks.Close
xAP.Quit
Set xAP = Nothing
End Sub
Whatever way you choose now you can draw into the autocad drawing by using VBA.
There is another way for non programmers.
AUTOCAD SCRIPT
in fact you can create a excel table which creates this things and then you can export them to a text file. For simple task a solution but crap if you hase more complex things to do.
And last but not least you can create dynamic blocks and use vba to insert them and set the values of their parameters according to your excel sheet. But this would explode this tiny post