I have a some command buttons that send access tables through to an excel spreadsheet and undergo some formatting and entering some formulas in them. The other command buttons work, but this one falls over at the LastRowInventory line.
I am sure its something to do with oBook but I can't quite figure out how to fix it. I think its because it is attempting to get an object it has already got. It runs smoothly every second time, but does not close the excel process. My attempts at resolving this over the last couple hours have not worked.
The error I have been getting is as follows:
Run-time error '462': The remote server machine does not exist or is unavailable
Any help is appreciated. I believe it is a simple fix but just can't quite get it, I'm pretty new to programming. The code is below.
Private Sub INVENTORYLIST_Click()
DTable = InputBox("Input Table Name")
'****************************TRANSFER TO EXCEL********************************
Dim strWorksheetPathTable As String
strWorksheetPathTable = "O:\GData\Downstream_LNG\Data Mgmt\CEDA\Reports\" & DTable & "\" & DTable & ".xls"
DoCmd.TransferSpreadsheet transfertype:=acExport, _
spreadsheettype:=acSpreadsheetTypeExcel12, _
TableName:=("" & DTable & "_INVENTORY LIST"), FileName:=strWorksheetPathTable, _
hasfieldnames:=True, _
Range:="InventoryList"
'****************************FORMAT INVENTORY SHEET***********************************
Dim xlApp As Object
Dim xlWB As Object
Set xlApp = CreateObject("Excel.Application")
Dim oBook As Excel.Workbook
Dim InventoryListSheet As Excel.Worksheet
Dim SummarySheet As Excel.Worksheet
Set xlWB = xlApp.Workbooks.Open("" & strWorksheetPathTable & "")
Set oBook = GetObject("" & strWorksheetPathTable & "")
Set InventoryListSheet = oBook.Sheets("InventoryList")
Set SummarySheet = oBook.Sheets("Summary")
With xlWB
With InventoryListSheet
'Some Spreadesheet Formatting in here
End With
End With
'****************************CREATE OE STATUS BREAKDOWN CALCULATIONS ON SUMMARY SHEET**********************
Dim LastRowInventory As Long
LastRowInventory = oBook.Sheets("InventoryList").Range("A" & Rows.Count & "").End(xlUp).Row
With xlWB
With SummarySheet
'Some Spreadsheet Formulas here
End With
End With
'*********************************ORDER WORKSHEETS*************************************
With xlWB
.Sheets("InventoryList").Select
.Sheets("InventoryList").Move Before:=oBook.Sheets(1)
.Sheets("Summary").Select
.Sheets("Summary").Move Before:=oBook.Sheets(1)
End With
If Not SummarySheet Is Nothing Then
Set SummarySheet = Nothing
End If
If Not InventoryListSheet Is Nothing Then
Set InventoryListSheet = Nothing
End If
If Not oBook Is Nothing Then
Set oBook = Nothing
End If
If Not xlWB Is Nothing Then
xlWB.Save
xlWB.Close
Set xlWB = Nothing
End If
If Not xlApp Is Nothing Then
xlApp.Quit
Set xlApp = Nothing
End If
DoCmd.SetWarnings True
MsgBox ("INVENTORY SHEET HAS BEEN CREATED.")
End Sub
Try this:
LastRowInventory = ActiveSheet.Range("B" & Rows.Count).End(xlUp).Row
Or if that doesn't work try:
LastRowInventory = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row
Does this help you?
EDIT:
LastRowInventory = InventoryListSheet.Range("A" & InventoryListSheet.Rows.Count & "").End(xlUp).Row
By specifying the sheet that the rows are to be counted on the issue is fixed.
Related
I have ContentControl drop down box in Word. Once I select an item from a Drop Down list I want to search for that in an Excel document and set the row number equal to a variable.
The code below is what I tried but the Columns("G:G").Find part says its not defined.
Sub findsomething(curRow)
Dim rng As Range
Dim rownumber As Long
curPath = ActiveDocument.path & "\"
Call Set_Variable(curPath)
StrWkShtNm = "Chapters"
If Dir(StrWkBkNm) = "" Then
MsgBox "Cannot find the designated workbook: " & StrWkBkNm, vbExclamation
Exit Sub
End If
Set rng = Columns("G:G").Find(what:=curRow)
rownumber = rng.Row
MsgBox rownumber
' Release Excel object memory
Set xlWkBk = Nothing
Set xlApp = Nothing
Application.ScreenUpdating = True
End Sub
While using more than one MS Office application it is a good idea to specify which application you are targeting:
Excel.Application.ThisWorkbook.Sheets(1).Range("A1").Select
this is what ended up working. you set me on the right track with referencing Excel.
Sub findsomething(curRow)
Dim rng As Long
Dim rownumber As Long
curPath = ActiveDocument.path & "\"
Call Set_Variable(curPath)
StrWkShtNm = "Chapters"
MsgBox "curRow = " & curRow
If Dir(StrWkBkNm) = "" Then
MsgBox "Cannot find the designated workbook: " & StrWkBkNm, vbExclamation
Exit Sub
End If
With xlApp
.Visible = False
Set xlWkBk = .Workbooks.Open(FileName:=StrWkBkNm, ReadOnly:=True, AddToMRU:=False)
With xlWkBk
With .Worksheets(StrWkShtNm)
rng = .Range("G:G").Find(what:=curRow)
MsgBox rng
End With
.Close False
End With
.Quit
End With
' Release Excel object memory
Set xlWkBk = Nothing: Set xlApp = Nothing
Application.ScreenUpdating = True
End Sub
I am having trouble ending the Excel process that I call open with Outlook VBA.
I have looked into a few solutions like setting variables to Nothing at the end and using With statements after all variables.
The orphaned process seems to be causing problems when I call Excel over and over again.
The code is suppose to download the attachment, copy some cell values into a workbook, save and close the documents.
Private WithEvents myOlItems As Outlook.Items
Private Sub Application_Startup()
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set myOlItems = objNS.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub myOlItems_ItemAdd(ByVal item As Object)
Dim Msg As Outlook.MailItem
Dim msgattach As Object
Dim wb As Workbook
Dim myXLApp As Excel.Application
Dim filepath As String
Dim filepathone As String
Dim filepathtwo As String
Dim wbhome As Worksheet
Dim comp As String
Dim wbtemp As Workbook
Dim testcode As Workbook
Dim matrix As Worksheet
Dim testflr As Worksheet
If TypeName(item) = "MailItem" Then
Set Msg = item
If Left(Msg.Subject, 14) = "SES Gas Matrix" Then
Set myXLApp = CreateObject("Excel.Application")
myXLApp.DisplayAlerts = False
If Msg.Attachments.Count <> 0 Then
For Each msgattach In Msg.Attachments
If Right(msgattach.FileName, 5) = ".xlsx" Then
filepath = "G:\Betts\Floor Matricies\FIFOs\" & Format(Now(), "YYYYMMDD") & " - " & "Gas Rates" & Right(msgattach.FileName, 5)
msgattach.SaveAsFile filepath
End If
Next
End If
Set msgattach = Nothing
Set wbtemp = Workbooks.Open(filepath, UpdateLinks:=3)
Set matrix = wbtemp.Sheets("Sheet1")
wbtemp.Activate
filepathtwo = Left(filepath, Len(filepath) - 5)
matrix.ExportAsFixedFormat Type:=xlTypePDF, FileName:= _
filepathtwo & ".pdf" _
, Quality:=xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas _
:=False, OpenAfterPublish:=False
filepathone = "http://intranet/Pricing%20and%20Rates/Floor%20Matrices/FIFOs/" & Format(Now(), "YYYYMMDD") & "%20-%20Gas%20Rates.pdf"
matrix.ExportAsFixedFormat Type:=xlTypePDF, FileName:= _
filepathone _
, Quality:=xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas _
:=False, OpenAfterPublish:=False
Dim rangeb5l9 As Range
Set rangeb5l9 = matrix.Range("B5:L9")
rangeb5l9.Copy
Set rangeb5l9 = Nothing
On Error GoTo ErrorHandler
Set testcode = Workbooks.Open(FileName:="G:\Betts\ReturnOnInvestment_Master_Backup Testcode.xlsm", UpdateLinks:=3)
Set testflr = testcode.Sheets("Floor Pricing")
Dim rangea44 As Range
Dim rangeb93 As Range
Dim rangeb94 As Range
Set rangea44 = testflr.Range("A44")
rangea44.PasteSpecial xlPasteValues
myXLApp.CutCopyMode = False
Set rangea44 = Nothing
Set rangeb93 = testflr.Range("B93")
rangeb93 = "Yes"
wbtemp.Close
Set wbtemp = Nothing
Kill (filepath)
Set rangeb94 = testflr.Range("B94")
If rangeb93 = "Yes" And rangeb94 = "Yes" Then
testcode.Application.Run ("Module34.OFVT")
rangeb93 = "No"
rangeb94 = "No"
End If
Set rangeb94 = Nothing
Set rangeb93 = Nothing
Set testflr = Nothing
testcode.Close savechanges:=True
Set testcode = Nothing
Set matrix = Nothing
myXLApp.DisplayAlerts = True
myXLApp.Quit
Set myXLApp = Nothing
Msg.UnRead = False
End If
Set Msg = Nothing
End If
'test area
Set item = Nothing
Exit Sub
ErrorHandler:
If (Err.Number = 50290) Then Resume
Stop
Resume
End Sub
There are a few recommended rules that you could apply in this kind of applications.
1- Before opening Excel, check if Excel is already open and get the running instance. You can create a custom routine to do that:
Function getExcelApp() As Excel.Application
On Error Resume Next
Set getExcelApp = GetObject(, "Excel.Application")
If Err.Number <> 0 Then Set getExcelApp = CreateObject("Excel.Application")
End Function
2- Make the application visible, at least in the phase where you're still writing and debugging your code.
Set myXLApp = getExcelApp ' <-- get it or create it
myXLApp .Visible = true ' <-- useful at least in the development phase
3- You can eventually shortcut the two-phases (create app, open doc) with just one step
Dim wb as Excel.Workbook
Set wb= GetObject(filepath)
This will either get an already open document instance or open it if not. You can later get the Application Object as wb.Application.
4- Make sure you correctly handle the error situations to that all paths will close the Excel application, including those resulting from an error.
5- Since the application you're using is temporary, keep it with DisplayAlerts = False state. As I see you reset it to DisplayAlerts = true before quitting. This is source of headache. Imagine the "non-visible" application blocked with some alert messagebox? I suggest you drop that line (keep false).
6- Qualify your ranges and object variables
Set wbtemp = myXlApp.Workbooks.Open(filepath, 3, True) '<-- better than using the unqualified Workbooks
I'm exporting an MS Access query to a template, doing some formatting, and then saving the template as a new name. When this is all done, I have an orphan MS Excel process that is interfering when the function is called again. I'm thinking this is either a problem with how I'm using ranges or a problem with my cleanup at the end.
Also I'm a novice coder so if anyone has any tips and tricks that I can take advantage of to make this better I'm always receptive.
Updated code after Andre's comments
Updated code after Rory's comments
Public Function OpenOrders(strSupplier As String)
'Excel file variables
Dim xlapp As Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
Dim xlsLRow As Long
Dim xlsLCol As Long
'Access variables
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
'Set up access objects
strSQL = "SELECT * FROM qryOpenOrderReport WHERE [Supplier Cd] = '" & strSupplier & "';"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSQL)
'Set up excel connection
Set xlapp = CreateObject("Excel.Application")
Set wb = xlapp.Workbooks.Open(Application.CurrentProject.Path & "\Open Order Template.xlsx")
Set ws = wb.Worksheets(1)
xlapp.Visible = True
'Make sure the form is clear
xlsLRow = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).row
xlsLCol = ws.UsedRange.Columns(ws.UsedRange.Columns.Count).Column
ws.Range("A2", ws.Cells(xlsLRow, xlsLCol)).ClearContents
'Copy recordset to worksheet
ws.Cells(2, 1).CopyFromRecordset rs
rs.Close
'Copy formats down and autofit
xlsLRow = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).row
xlsLCol = ws.UsedRange.Columns(ws.UsedRange.Columns.Count).Column
xlapp.CutCopyMode = False
ws.Range(ws.Cells(2, 1), ws.Cells(2, xlsLCol)).Copy
ws.Range(ws.Cells(3, 1), ws.Cells(xlsLRow, xlsLCol)).PasteSpecial (xlPasteFormats)
ws.UsedRange.Columns.AutoFit
'Clean up
xlapp.DisplayAlerts = False
Set ws = Nothing
wb.SaveAs Application.CurrentProject.Path & "\Open Orders\" & strSupplier & ".xlsx"
wb.Close True
Set wb = Nothing
xlapp.Quit
Set xlapp = Nothing
End Function
All of your Cells calls need to be changed to ws.Cells. That is what is causing your orphaned process.
My guess is that you still have an active reference to ws when doing the clean up, which prevents Excel from quitting.
I suggest doing it in this order:
'Clean up
xlapp.DisplayAlerts = False
Set ws = Nothing
wb.Close True, strSupplier
Set wb = Nothing
xlapp.Quit
Set xlapp = Nothing
ws.Range("A2", "XFD1048576").ClearContents seems a little radical :) - you can use .UsedRange for that.
An additional note:
After opening a recordset, you can never be in a situation where rs.EOF is False, but rs.BOF is True. So it is not necessary to test for rs.BOF.
With changing the loop to Do While, the If Not (rs.EOF And rs.BOF) Then becomes superfluous:
Set rs = db.OpenRecordset(strSQL)
Do While Not rs.EOF
' ...
rs.MoveNext
Loop
You have to be extremely specific with objects of Excel, opening them and closing in reverse order. Here's a skeleton that works:
Dim xls As Excel.Application
Dim wkb As Excel.Workbook
Dim wks As Excel.Worksheet
Dim rng As Excel.Range
Set xls = New Excel.Application
Set wkb = xls.Workbooks.Open("c:\test\workbook1.xlsx")
Set wks = wkb.Worksheets(1)
Set rng = wks.<somerange> ' Cells or whatever.
' Do stuff.
' Clean up.
Set rng = Nothing
wks.Name = "My New Name"
wkb.Close True
Set wks = Nothing
Set wkb = Nothing
xls.Quit
Set xls = Nothing
Don't ever use wkb.Sheets for a WorkSheet.
I am very much a novice when it comes to vba programming, and as such have not been able to find/figure out a solution to my problem.
I have a vba script that runs through a button in an access form (excerpt below) that does a calculation in excel, for approximately 30,000 records (Inventory List worksheet). Further to this, the "Data1" worksheet has 1,000,000 records and the "Data2" worksheet has approximately 100,000 records. The calculation checks for how many pieces of information a "tag" requires and how many pieces of information the "tag" currently has in our database, and calculates a decimal value for completeness.
This all results in 100% CPU usage when computing. I was wondering if anyone would have some pointers on how to improve the calculation so as to improve its running speed?
Thanks in advance.
Private Sub BUILD_INVENTORY_LIST_Click()
DoCmd.SetWarnings False
'Note: DTable is a variable entered earlier in the coding
Dim strWorksheetPathTable As String
strWorksheetPathTable = "O:\GData\POC\DataMgmt\Reports\" & DTable & "\" & DTable & ".xls"
Dim xlApp As Object
Dim xlWB As Object
Dim oBook As Excel.Workbook
Dim InventoryListSheet As Excel.Worksheet
Set xlApp = CreateObject("Excel.Application")
Set xlWB = xlApp.Workbooks.Open("" & strWorksheetPathTable & "")
Set oBook = GetObject("" & strWorksheetPathTable & "")
Set InventoryListSheet = oBook.Sheets("InventoryList")
Dim NumberofTags As String
NumberofTags = ActiveWorkbook.ActiveSheet.UsedRange.Rows.Count
If Len(Sheets("Data1").Name) > 0 Then
With xlWB
With InventoryListSheet
.Range("O2").Formula = "=(COUNTIFS(Data1!A:A,A2,Data1!G:G,""Y"",Data1!E:E,""<>"")/(COUNTIFS(Data1!A:A,A2,Data1!G:G,""Y"")))"
.Range("O2:O" & NumberofTags & "").FillDown
End With
End With
End If
If Len(Sheets("Data2").Name) > 0 Then
With xlWB
With InventoryListSheet
.Range("P2").Formula = "=(COUNTIFS(Data2!A:A,A2,Data2!G:G,""Y"",Data2!E:E,""<>"")/(COUNTIFS(Data2!A:A,A2,Data2!G:G,""Y"")))"
.Range("P2:P" & NumberofTags & "").FillDown
End With
End With
End If
xlWB.Save
xlWB.Close
DoCmd.SetWarnings True
End Sub
Have you tried this:
http://blogs.office.com/2009/03/12/excel-vba-performance-coding-best-practices/
or similarly this:
http://www.cpearson.com/excel/optimize.htm
where relevant.
Here I have some VBA code that outputs a ton of files into Excel files. My question is, from this, is there anyway for it to Format the excel file a bit? What I would like to do is make the Columns bold and make the columns fit the size of the header as well.
Sub OutPutXL()
Dim qdf As QueryDef
Dim rs As DAO.Recordset
Set qdf = CurrentDb.QueryDefs("OutputStudents")
Set rs = CurrentDb.OpenRecordset("Teachers")
Do While Not rs.EOF
qdf.SQL = "SELECT * FROM Students WHERE contact='" & rs!contact & "'"
''Output to Excel
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel8, _
qdf.Name, "C:\Users\chrisjones\Documents\ProjectionsFY14\Teachers\" _
& rs!contact & ".xls", True
rs.MoveNext
Loop
End Sub
this is a quick and dirty combination of Phil.Wheeler's Code and my previous input, for me this is working. Don't forget to add Excel's Object Library in your Access-Macro.
Sub doWhatIWantTheDirtyWay()
pathToFolder = "C:\Users\Dirk\Desktop\myOutputFolder\"
scaleFactor = 0.9
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = False
objExcel.DisplayAlerts = False
Set objFso = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFso.GetFolder(pathToFolder)
For Each objFile In objFolder.Files
If objFso.GetExtensionName(objFile.path) = "xls" Then
Set objWorkbook = objExcel.Workbooks.Open(objFile.path)
For Each sh In objWorkbook.Worksheets
If sh.UsedRange.Address <> "$A$1" Or sh.Range("A1") <> "" Then
With sh
columncount = .Cells(1, 256).End(xlToLeft).Column
For j = 1 To columncount
With .Cells(1, j)
i = Len(.Value)
.ColumnWidth = i * scaleFactor
.Font.Bold = True
End With
Next
End With
End If
Next
objWorkbook.Close True
End If
Next
objExcel.Quit
End Sub
Yes it is possible! This is hacked together from one of my codes, might need a bit of editing before it works...
'This deals with Excel already being open or not
On Error Resume Next
Set xl = GetObject(, "Excel.Application")
On Error GoTo 0
If xl Is Nothing Then
Set xl = CreateObject("Excel.Application")
End If
Set XlBook = GetObject(filename)
'filename is the string with the link to the file ("C:/....blahblah.xls")
'Make sure excel is visible on the screen
xl.Visible = True
XlBook.Windows(1).Visible = True
'xl.ActiveWindow.Zoom = 75
'Define the sheet in the Workbook as XlSheet
Set xlsheet1 = XlBook.Worksheets(1)
'Then have some fun!
with xlsheet1
.range("A1") = "some data here"
.columns("A:A").HorizontalAlignment = xlRight
.rows("1:1").font.bold = True
end with
'And so on...
I have come across this problem a couple of times as well. As #Remou said, you will need to open excel to format xls files, this modification of your code silently opens Excel and that should get you in the right direction. Remember to add a reference to the Microsoft Excel Object Library in your VBA project.
Sub OutPutXL()
Dim qdf As QueryDef
Dim rs As DAO.Recordset
Dim xl as Excel.Application
Dim wb as Object
Dim strFile as string
Set qdf = CurrentDb.QueryDefs("OutputStudents")
Set rs = CurrentDb.OpenRecordset("Teachers")
Set xl = New Excel.Application
xl.DisplayAlerts = False
Do While Not rs.EOF
qdf.SQL = "SELECT * FROM Students WHERE contact='" & rs!contact & "'"
'Output to Excel
strFile = "C:\Users\chrisjones\Documents\ProjectionsFY14\Teachers\" & rs!contact & ".xls"
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel8, qdf.Name, strFile, True
'Start formatting'
Set wb = xl.Workbooks.Open(strFile)
With wb.Sheets(qdf.name)
'Starting with a blank excel file, turn on the record macro function'
'Format away to hearts delight and save macro'
'Past code here and resolve references'
End With
wb.save
wb.close
set wb = Nothing
rs.MoveNext
Loop
xl.quit
set xl = Nothing
End Sub
You could (depending on the number of files) make a template for each file you are outputting. In the long run if someone needs to change the formatting they can change the template which is going to be easier on you now that you don't have to sift through a bunch of excel formatting garbage. You could even let a qualified end user do it.
It's one of the biggest problems I have with excel sheets if I wrote the VBA I am responsible until I die for it. This way (in theory) they should be able to change a column, without changing how the data is outputted, just presented without you.
+1 To open the excel file itself and format it using that automation though.