问题
I am trying to write a macro in Word so that I can save some information into an Excel file somewhere else in my computer. For this reason I wrote this:
Dim exlApp As Object
Dim exlWbk As Object
Set exlApp = CreateObject("Excel.Application")
Set exlWbk = exlApp.Workbooks.Open(FileName:="D:\database.xlsx")
exlWbk.ActiveSheet.Cells(1, 1).Value = "some info"
exlWbk.Close SaveChanges:=True
Set exlWbk = Nothing
exlApp.Quit
Set exlApp = Nothing
The code works perfectly fine for me, except when the Excel file in question (database.xlsx) is already opened by the user. In that case, running the macro will prompt me to save the new changes into a new copy of my excel file, which is not what I want. I want the new changes to be included in the current Excel file without creating a second copy of it.
Since the above code presented some problems, I wrote this:
Dim exlApp As Object
Dim exlWbk As Object
Set exlApp = CreateObject("Excel.Application")
Set exlWbk = exlApp.GetObject("D:\database.xlsx")
exlWbk.ActiveSheet.Cells(1, 1).Value = "some info"
exlWbk.Save
Set exlWbk = Nothing
exlApp.Quit
Set exlApp = Nothing
But nothing changed. I know there are some ways to figure out whether my Excel file is open or not, but the problem is that I don't know how to change my code if I find out that file is open.
How can I determine whether a workbook is open in Excel so that it can be edited, or open the file in order to edit it if it's closed?
回答1:
According to the documentation, GetObject(filename)
will pick up the existing file if it's already open or, optionally, open the file if it is not open:
When this code is executed, the application associated with the specified pathname is started, and the object in the specified file is activated.
If Excel is not running, by default nothing will be visible when GetObject(filename)
executes. Excel will be opened, the file will be opened and changed. There's a real danger, therefore, that the instance of Excel and the workbook will "hang" in memory, which could be seen in the Windows "Task Manager". Repeated running of such code can eventually crash Windows, so care must be taken to clean things up correctly on each iteration.
Since the question also stipulates that the file could be opened already by a user, it's necessary to determine that, as well as whether the Excel application is already running.
The following code sample demonstrates how this can be done. The assumption is that neither the applicaton nor the file is open. Then it tests whether Excel is already running.
Set xlApp = GetObject(, "Excel.Application")
Notice the difference in the syntax: instead of the fileName
there's a comma, followed by the name of the application. This will check whether the application is available; if it's not, an error will be triggered. Therefore, On Error Resume Next
precedes GetObject
, which means the error will be ignored.
Since ignoring errors is dangerous, the next line Or Error GoTo 0
turns errors back on.
If GetObject
is not successful, the variable xlApp
could not be instantiated and its "value" is Nothing
. If Not xlApp Is Nothing
executes if xlApp
could be instantiated and the Boolean appAlreadyOpen
is set to true so that we know to not quit Excel when the code finishes. It also checks whether the required workbook is already open. If it is, xlWb
can be instantiated and fileAlreadyOpened set to true.
If xlWb
could not be instantiated, either because the Excel application was not running or the workbook was not yet open, GetObject(fileName)
is executed. The workbook will be opened, in the existing instance of Excel if already running or in a new instance of Excel. At the end of this code block two lines are commented: should the newly started Excel application be made visible and remain open when the code ends, uncomment them.
The workbook can then be edited.
Lastly, things need to be cleaned up. The Booleans are checked and if not true, the workbook and possibly the application are closed. Very important are the last two lines that release these objects from memory. If the code creates any other objects, such as Range
s, these should also be released, in the reverse order they are instantiated.
Sub GetFileOpenedOrClosed()
Dim xlApp As Object ' Excel.Application
Dim xlWB As Object, wb As Object ' Excel.Workbook
Dim fileName As String
Dim fileAlreadyOpen As Boolean, appAlreadyOpen As Boolean
fileName = "C:\Test\SampleChart.xlsx"
fileAlreadyOpen = False
appAlreadyOpen = False
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
On Error GoTo 0
If Not xlApp Is Nothing Then
appAlreadyOpen = True
For Each wb In xlApp.Workbooks
If wb.FullName = fileName Then
Set xlWB = wb
fileAlreadyOpen = True
Exit For
End If
Next
End If
If xlWB Is Nothing Then
Set xlWB = GetObject(fileName)
Set xlApp = xlWB.Application
xlWB.Windows(1).Visible = True 'So that the window is not hidden when file is opened again
'xlApp.Visible = True
'xlApp.UserControl = True
End If
xlWB.Worksheets(1).Cells(7, 1).value = "some other info"
If Not fileAlreadyOpen Then
xlWB.Save
xlWB.Close
End If
If Not appAlreadyOpen Then
xlApp.Quit
End If
Set xlWB = Nothing
Set xlApp = Nothing
End Sub
来源:https://stackoverflow.com/questions/60119174/edit-a-workbook-whether-open-or-closed-from-word-vba