Reputation: 89
This continues from a previous question. I tried the suggested fix to check if an Excel file is open locally from an Outlook macro (Office 2010).
Public Sub UpdateFileIndex(ByVal FullFilePath As String, ByVal DocNo As String)
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.WorkSheet
On Error Resume Next
Set xlApp = GetObject(FullFilePath).Application
Debug.Print "Error = " & Err
If Err.Number = 0 Then ' Workbook is open locally
' Do stuff
ElseIf Err.Number = 429 Then ' Workbook is not open locally
' Do different stuff
End If
' Do a bunch of other stuff
End Sub
Now for open or closed files given by FullFilePath
(e.g. "C:\Data\Data.xlsx"
):
Set xlApp = GetObject(FullFilePath).Application
gives me 0 error either way. (i.e. it opens the file if it's not open.)
Set xlApp = GetObject(Dir(FullFilePath)).Application
gives me -214722120 for both cases. (Automation error)
Set xlApp = GetObject(, "Excel.Application")
gives me 0 when open and 429 when not open. See below.
Set xlApp = GetObject(Dir(FullFilePath), "Excel.Application")
gives me 432 for both cases. (File name or class name not found during Automation operation)
Set xlApp = GetObject(FullFilePath, "Excel.Application")
gives me 432 for both cases.
So the only case that works is the initially suggested fix (see link at top), which cannot find the file unless it's in the first instance of Excel open locally, which may not always be the case (i.e. it may be open in a second instance).
Ultimately I'd like to check if the file is open on the network, and if it is check if it's open locally.
Upvotes: 2
Views: 7941
Reputation: 3456
The following only requires the workbook filename, not the full path:
Sub IsOpen()
With CreateObject("Word.Application")
If .Tasks.exists("Workbook.xlsb") Then MsgBox "The Workbook is open"
.Quit
End With
End Sub
This will succeed even if the workbook is open in a different instance of Excel.
(Yes, you use a Word.Application
object even though you're interested in Excel....)
If you want to check a file by fully-qualified path use the function in this answer.
Upvotes: 1
Reputation: 1670
you can check if file is open or not and get object if it is open
Public Shared Function isFileAlreadyOpen(ByVal xlFileName As String) As Boolean
Return CBool(Not getIfBookOpened(xlFileName) Is Nothing)
End Function
Public Shared Function getIfBookOpened(ByVal xlFileName As String) As Excel.Workbook
Dim wbBook As Excel.Workbook
Dim xlProcs() As Process = Process.GetProcessesByName("EXCEL")
If xlProcs.Count > 0 Then
Dim xlApp As Excel.Application = CType(System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"), Excel.Application)
For Each wbBook In xlApp.Workbooks
If wbBook.FullName.ToUpper = xlFileName.ToUpper Then
Return wbBook
Exit For
End If
Next
End If
Return Nothing
End Function
or
Public Shared Function getOrOpenBook(ByVal xlFileName As String) As Excel.Workbook
Return System.Runtime.InteropServices.Marshal.BindToMoniker(xlFileName)
End Function
Upvotes: -1
Reputation: 149325
If you have multiple Excel instances open then this is what I suggest.
Logic
GetObject
unfortunately will return the same instance every time unless you close that Excel instance. Also there is no reliable way to get it to loop through all Excel instances. Talking of reliability, I would turn your attention towards APIs. The 3 APIs that we will use is FindWindowEx
, GetDesktopWindow
and AccessibleObjectFromWindow&
See this example (TRIED AND TESTED in EXCEL 2010)
Option Explicit
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function AccessibleObjectFromWindow& Lib "oleacc" _
(ByVal hwnd&, ByVal dwId&, riid As GUID, xlWB As Object)
Private Const OBJID_NATIVEOM = &HFFFFFFF0
Private Type GUID
lData1 As Long
iData2 As Integer
iData3 As Integer
aBData4(0 To 7) As Byte
End Type
Sub Sample()
Dim Ret
Dim oXLApp As Object, wb As Object
Dim sPath As String, sFileName As String, SFile As String, filewithoutExt As String
Dim IDispatch As GUID
sPath = "C:\Users\Chris\Desktop\"
sFileName = "Data.xlsx": filewithoutExt = "Data"
SFile = sPath & sFileName
Ret = IsWorkBookOpen(SFile)
'~~> If file is open
If Ret = True Then
Dim dsktpHwnd As Long, hwnd As Long, mWnd As Long, cWnd As Long
SetIDispatch IDispatch
dsktpHwnd = GetDesktopWindow
hwnd = FindWindowEx(dsktpHwnd, 0&, "XLMAIN", vbNullString)
mWnd = FindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
While mWnd <> 0 And cWnd = 0
cWnd = FindWindowEx(mWnd, 0&, "EXCEL7", filewithoutExt)
hwnd = FindWindowEx(dsktpHwnd, hwnd, "XLMAIN", vbNullString)
mWnd = FindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
Wend
'~~> We got the handle of the Excel instance which has the file
If cWnd > 0 Then
'~~> Bind with the Instance
Call AccessibleObjectFromWindow(cWnd, OBJID_NATIVEOM, IDispatch, wb)
'~~> Work with the file
With wb.Application.Workbooks(sFileName)
'
'~~> Rest of the code
'
End With
End If
'~~> If file is not open
Else
On Error Resume Next
Set oXLApp = GetObject(, "Excel.Application")
'~~> If not found then create new instance
If Err.Number <> 0 Then
Set oXLApp = CreateObject("Excel.Application")
End If
Err.Clear
On Error GoTo 0
Set wb = oXLApp.Workbooks.Open(SFile)
'
'~~> Rest of the code
'
End If
End Sub
Private Sub SetIDispatch(ByRef ID As GUID)
With ID
.lData1 = &H20400
.iData2 = &H0
.iData3 = &H0
.aBData4(0) = &HC0
.aBData4(1) = &H0
.aBData4(2) = &H0
.aBData4(3) = &H0
.aBData4(4) = &H0
.aBData4(5) = &H0
.aBData4(6) = &H0
.aBData4(7) = &H46
End With
End Sub
'~~> Function to check if file is open
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
Upvotes: 6
Reputation: 149325
To see if an Excel file is open or not, you can use this function.
Sub Sample()
Dim Ret
Dim sFile As String
sFile = "C:\Users\Chris\Desktop\Data.xlsx"
Ret = IsWorkBookOpen(sFile)
If Ret = True Then
MsgBox "File is Open"
Else
MsgBox "File is not Open"
End If
End Sub
'~~> Function to check if file is open
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
Upvotes: 2