I've got a project where I'm reading data from an Excel worksheet and saving it in Access tables (not a direct import--see this question if you're interested). My current problem is that any time I run my "import" and then try to open the workbook in Excel, it's "locked for editing" unless/until I close Access. I don't think this should be the case. My process is
Open the workbook with Automation.
Build a collection of sheet names.
Release the Automation objects.
If there's more than one sheet, get user input on which to process.
Open an ADO recordset on a specific range & read some data.
Release the recordset & connection.
Open an ADO recordset on a different specific range & read a bunch o' data.
Release the recordset & connection.
Close the controling form.
Steps 1 - 4 and 9 live in the form file, the remainder in a module.
Am I missing something? I think I've released all the references to the workbook....
If your step #1 includes something like this:
Dim objExcel As New Excel.Application
And later releasing the object is this:
Set objExcel = Nothing
Trying including this line just before you set the object variable to Nothing:
objExcel.Quit
It also helps to make the Excel application instance visible after starting it so you're less likely to leave Excel running unseen:
objExcel.Visible = True
Related
I have a large Excel file that uses PowerPivot to connect to several external data sources. The Excel file is stored in a SharePoint folder. I would like to be able to refresh the connections automatically overnight. To do this, I am using Windows Task Scheduler to trigger a VBScript each day at 2AM.
Previously, I had the script run a macro within the workbook to refresh all. This worked successfully until my organization recently updated to Microsoft Office 365 ProPlus. I am now struggling with the new read-only default setting for SharePoint. Following the update, whenever I open an Excel file from SharePoint, I see a yellow bar reading "Read-Only: We opened this workbook read-only from the server. Edit Workbook". When working with files manually, this has to be clicked before changes can be saved. I tried to research the programmatic workaround for this and found the recommendation to use the method .LockServerFile.
To streamline the trouble-shooting, I have tried to move the entire action into the VBScript script, rather than running a macro within the workbook. My code is below.
When I watch the process, the yellow "Read-Only" bar appears to pop up immediately before the save. I can run the process once with no apparent errors, but if I then manually open the file, I don't see the option to "Edit Workbook". If I manually change the file, save, and then run the script again, I get the error 800A03EC.
It seems that somehow the script is failing to correctly "release" the lock on the server file after it finishes.
Is there a way to fix this?
Set objExcel = CreateObject("Excel.Application")
Set objWorkbook = objExcel.Workbooks.Open("[[The Sharepoint Path]]")
objExcel.Application.DisplayAlerts = False
objExcel.Application.Visible = True
objExcel.ActiveWorkbook.LockServerFile
objExcel.ActiveWorkbook.EnableConnections
objExcel.ActiveWorkbook.Sheets(1).Range("P2").Value = Date
objExcel.ActiveWorkbook.RefreshAll
objExcel.ActiveWorkbook.Save
objExcel.ActiveWorkbook.Close false
objExcel.Application.Quit
set objworkbook = Nothing
set objExcel = Nothing
WScript.Quit
I bought a software (with a large database), and its output is a simple Excel workbook, not saved anywhere (no path), named generically "Book1", that simply pops up on my screen.
Every time I ask the software for this output, I need to copy the content of this workbook and paste into another workbook, a mother-workbook, as I named it, to consolidate all the data.
I have to repeat this action dozens of times a day, so I thought it would be a great idea to create some VBA code to automate this task.
So... I made a very simple one:
ActiveWorkbook.ActiveSheet.Range("A1:C32").Copy
Workbooks("Mother-Workbook.xlsm").Worksheets("Sheet1").Range("B6:D37").PasteSpecial Paste:=xlPasteValues
The problem is... Each time the software outputs a new workbook, it seems that it is created in a new instance of Excel, which my macro can't reach. I mean, I run the code, but nothing happens, because my mother-workbook doesn't find the generic, unsaved and located in another excel instance "Book1".
If I open the mother-workbook after the output is opened, OK, the code works, because both are in the same instance. But as I need to keep the mother-workbook open all the time, I can't do this. I don't want to save each new output file either. It would take me a lot of time.
I'm using the 2016 version of Excel, but already tried the 2010 as well. My OS is Windows 10 Pro.
Any thoughts?
This code should do it.
Dim xlapp As Object
Set xlapp = GetObject("Book1").Application
xlapp.ActiveWorkbook.ActiveSheet.Range("A1:C32").Copy
Workbooks("Mother-Workbook.xlsm").Worksheets("Sheet1").Range("B6:D37").PasteSpecial Paste:=xlPasteValues
xlapp.DisplayAlerts = False
xlapp.Quit
Note that you need to close "Book1" at the end of your code to make sure that the next time an Excel file is created it will also be called "Book1" and not "Book2". And might as well close the Excel instance while we are at it!
For more information on the GetObject function, you can have a look at this page
Thanks a lot, DecimalTurn and Patrick Lepelletier!
The GetObject really helped me. The "closing" command worked better like this:
Sub CollectA()
Dim oApp As Application
Dim oWb As Workbook
Set oWb = GetObject("Book1")
Set oApp = oWb.Parent
oWb.ActiveSheet.Range("A1:C32").Copy
Workbooks("Mother-Workbook.xlsm").Worksheets("Sheet1").Range("B6:D37").PasteSpecial Paste:=xlPasteValues
oWb.Close False
oApp.Quit
End Sub
Cheers!
So I basically have a VBS script that's supposed to post data to an Excel sheet asynchronously. I currently do this by using GetObject on the workbook's path like so:
Set xlBook = GetObject(strPath & "\Runner.xlsm")
This seems to work fine, except that the workbook will close at the end of the script if it was not open previously (not desired, I have a macro that will close and save the book when necessary).
This is similar to Question 7708039, EXCEPT I want to intentionally keep the excel instance OPEN, not force it to close (the reverse of his problem).
I think it's closing because the variables referencing the object get destroyed at the end of the script, but I can't figure out how to release those handles without destroying them (i.e. set to Nothing).
Instead of getting a reference to a specific workbook, have you tried getting a reference to Excel and then opening the workbook?
' 1a. Get an existing Excel instance...
Set Excel = GetObject(, "Excel.Application")
' 1b. Or, create one. Make it visible for testing.
Set Excel = CreateObject("Excel.Application")
Excel.Visible = True
' Load the workbook...
Set Workbook = Excel.Workbooks.Open(strPath & "\Runner.xlsm")
' Do stuff and save, if desired.
' Close workbook...
Workbook.Close
' Excel stays open. If you want to close Excel, use:
Excel.Quit
Per phd443322's two comments Comment 1 and Comment 2, this is apparently by design.
The solution here is to trick the object (in this case Excel) into thinking the user will need to interact with it or maintain interaction after the reference is destroyed.
Thus, the proper workaround is to make it interactive, in this case using:
xlApp.Visible = True
Thus Excel becomes visible and won't close just because the reference is destroyed.
Since I don't want this instance of Excel visible, I then have the VBS use xlApp.OnTime to call a macro (after one second, plenty of time for the VBS script to have exited) to hide the application window again.
This makes the application blink up on the screen for a second, but it's the best I can do in this instance.
I had the same problem using
xlApp.Visible = True
with this call on windows
wscript my-script.vbs C:/path/to/file.xml
But when I changed the separator from / to \ it worked:
wscript my-script.vbs C:\path\to\file.xml
So I'm importing data every day into Access to use for reporting. The data comes from several spreadsheets created by different individuals. Because those individuals like to format things incorrectly I created a macro that reformats their document so that it can be imported cleanly into Access for me to use. Works great but it gets tedious having to open up each Excel sheet to run this Macro.
What I'm trying to do is place the Excel Macro in Access and then run the formatting code before importing it all at once. I am a bit lost in approaching this. I'm aware of ways to run Macros already placed in Excel sheets but is there a way to run a macro that is stored in Access that works in excel. I also thought to maybe inject the Macro into the excel document and then run it.
To sum things up, what I'm hoping to do is from Access, store a macro, that can be used to alter Excel Files.
Is this at all possible? If so How? Is there another approach?
What you are asking to do is automate Excel from Access. Yes, you can do this. In Access, add a module, add a reference to the Microsoft Excel object model (Tools: References), and use this framework code to get you started:
Sub PrepExcelFileForImport()
Dim xl As Excel.Application
Dim wbk As Excel.Workbook
Dim wst As Excel.Worksheet
Set xl = CreateObject("Excel.Application")
With xl
.Visible = True
Set wbk = .Workbooks.Open("c:\temp\temp.xlsx")
Set wst = wbk.Worksheets("data")
With wst
' add your formatting code here, be sure to use qualified references, e.g.
.Rows(1).Font.Bold = True
End With
End With
wbk.Close SaveChanges:=True
xl.Quit
End Sub
I have an automated process that is mostly run in Access. But, in the middle, it puts some data in Excel to scrub it into the correct form (it's much faster than doing it in Access), and at the end it opens another Excel file and puts data from some Access queries into the Excel file. For these connections from Excel to Access, I accomplished them all by going into Excel and doing Data --> Get External Data --> From Access, then selecting the Access file and the query I want to get the data from and tell Excel to make it into a Table.
So, I do that one time and then I want to be able to run this automated process that simply refreshes the data. To do this refreshing of the data, I do a line like:
Worksheets("Data").Range("A1").ListObject.QueryTable.Refresh _
BackgroundQuery:=False
The problem is, half the time (and I can't figure out why it does it one time and not another), it says "Do you want to connect to path\filename?" Of course I do, how else would the table refresh? So, this stops the automation. Even if I click Yes, I still can't get it to continue on. If I click Yes, it opens up the Data Link Properties. After I click OK for that, it opens a window titled "Please Enter Microsoft Office Access Database Engine OLE DB Initialization Information". It has info in it, including the path and name of the data source I want to access, but if I click OK, it says, sorry that didn't work, would you like instead to connect to (and then it lists the exact same path and file name it just said didn't work). It repeats the steps I just mentioned, and after that it errors out.
In case it matters, here is the (basic idea) code I use to connect to Excel from Access:
Public Sub ExportToExcel()
Dim ObjXLApp As Object
Dim ObjXLBook As Object
Dim ExcelFilePath As String
ExcelFilePath = CurrentProject.Path & "\"
Set ObjXLApp = CreateObject("Excel.Application")
Set ObjXLBook = ObjXLApp.Workbooks.Open(ExcelFilePath & "filename.xlsm")
ObjXLApp.Visible = True
' Runs the "DataSetUp" macro in the Excel file.
ObjXLApp.Run ("DataSetUp")
' The DataSetUp macro saves the Excel file
' Quit Excel
ObjXLApp.Quit
' Free the memory
Set ObjXLBook = Nothing
Set ObjXLApp = Nothing
End Sub
I have no idea how to fix this! Any help would be much appreciated.
This may be happening because your access database is still open from which the new excel file needs to input data back into. The database cannot be open when this takes place, hense the reason why excel errors and asks for another location to connect to.
So, I would work on generating the needed scrubbing via vba inside access probably.