Clever-Excel-Forum

Normale Version: Prüfung: IsFileClosed
Du siehst gerade eine vereinfachte Darstellung unserer Inhalte. Normale Ansicht mit richtiger Formatierung.
Seiten: 1 2
Hallo,

wenn mit VBA eine Excel-Datei geöffnet werden soll, muss diese geschlossen sein.

Was ist "best practice" das zu prüfen?

Der folgende Code nutzt verschieden Ansätze für die Prüfung. Am Anfang muss ein geeigneter Pfad (einige aber nicht zu viele) mit xls? angegeben werden und ein Verweis auf MS-Word gesetzt werden. Die "Vorbereitung" definiert die Titel.

Code:
'Prüfung, ob Datei verfügbar und geschlossen ist

Const Pfad As String = "C:\temp\" 'backslash am Ende
Dim FSO As Object
'Reference to Word.Application <<<<<<<<<<<<<<<<<<<

Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
        
Sub Vorbereitung()
Tit = Array("File", "OPEN LOCK", "Dir ('~$')", "API Tasks")
With Cells(1, 1).Resize(, 5)
    .Value = Tit
    .Font.Bold = True
    .Interior.Color = 15123099
End With
End Sub


Sub Liste_xls_Dateien()

Set FSO = CreateObject("Scripting.FileSystemObject")
Set FLD = FSO.GetFolder(Pfad) 'Folder

i = 1
For Each FL In FLD.Files
    If InStr(1, FL.Name, ".xls") > 0 Then
        'Debug.Print FL.Name
        i = i + 1
        Cells(i, 1) = FL.Name
        If fn_Lock(FL.Name) Then Cells(i, 2) = "closed" Else Cells(i, 2) = "open"
        If fn_Tilde(FL.Name) Then Cells(i, 3) = "closed" Else Cells(i, 3) = "open"
        If fn_API(FL.Name) Then Cells(i, 4) = "closed" Else Cells(i, 4) = "open"
        If fn_Task(FL.Name) Then Cells(i, 5) = "closed" Else Cells(i, 5) = "open"
    End If
Next
End Sub

Function fn_Lock(ByVal file) As Boolean 'True = closed
On Error Resume Next
ff = FreeFile

Open (Pfad & file) For Binary Access Read Lock Read As #ff
Close #ff

If Err.Number = 0 Then
    fn_Lock = True
Else
    fn_Lock = False
End If
Err.Clear
End Function

Function fn_Tilde(ByVal file) As Boolean

If Left(file, 2) = "~$" Then fn_Tilde = False: Exit Function
If Dir(Pfad & "~$" & file, vbHidden) = vbNullString Then
    fn_Tilde = True
Else
    fn_Tilde = False
End If
End Function

Function fn_API(ByVal file) As Boolean
    If FindWindow(vbNullString, file & " - Excel") = 0 Then
        fn_API = True
    Else
        fn_API = False
    End If
End Function

Function fn_Task(ByVal file) As Boolean ' benötigt Verweis zu Word
Dim Ts As Task
fn_Task = True
For Each Ts In Tasks
    If InStr(1, Ts.Name, file, vbTextCompare) > 0 Then fn_Task = False
Next Ts
End Function

Alls Ansätze haben Vor- und Nachteile. Wa nutzt Ihr?

mfg
Hallo Fennek,

ich bin ja ein API-Fan und da gefällt mir die API-Version ganz gut. Sollte dann, im Gegensatz zum Workbooks-Scan, auch bei mehreren Instanzen funktionieren?!

Aber bist Du sicher, dass das auch immer hinkommt. FindWindow möchte ja gern den genauen Fenstertext haben. Z.B. bei geteilten Fenstern (ok, seltener Sonderfall) klappt das m.E. schon  nicht.
Oder ist da eher eine WindowLike-Version angesagt.
Leider etwas aufwendiger....

Beispiel:
Code:

Private Declare PtrSafe Function EnumWindows Lib "user32" ( _
         ByVal lpEnumFunc As LongPtr, ByVal lParam As LongPtr) As Long
Private Declare PtrSafe Function GetWindowTextA Lib "user32" ( _
         ByVal hwnd As LongPtr, ByVal lpString As String, _
         ByVal cch As Long) As Long
Dim sWindowTextFrac As String
Dim bIsOpen         As Boolean

Sub Test()
 Debug.Print IsFileClosed("MyTool*.xls")
End Sub


Function IsFileClosed(sWindowTitle As String) As Boolean
  sWindowTextFrac = sWindowTitle & "*Excel*"
  Call EnumWindows(AddressOf EnumWindowProc, 0)
  IsFileClosed = bIsOpen
End Function

Private Function EnumWindowProc(ByVal hwnd As LongPtr, lParam As LongPtr) As Long
' Scannt alle Fenster durch
  Dim sWinTxt As String * 260
  
  Call GetWindowTextA(hwnd, sWinTxt, 260)   ' Fenstertext holen
  If sWinTxt Like sWindowTextFrac & "*" Then
     bIsOpen = False: EnumWindowProc = 0    ' Scannen abbrechen
  Else
     bIsOpen = True:  EnumWindowProc = 1    ' Weiterscannen
  End If
End Function

_________
viele Grüße
Karl-Heinz
Hallöchen,

Zitat:wenn mit VBA eine Excel-Datei geöffnet werden soll, muss diese geschlossen sein.
... kam ein Wanderer des Weges und behauptete, das stimme nicht 21 Es sei denn, ...
@Schauan:

Die Excel-Datei liegt auf einem File-Server: Wenn Kollege A die Datei geöffnet hat, können die Anderen nicht schreibend darauf zugreifen.

@Volti

Schöne Lösung!

Bei API's bin ich sehr schwach, also wieder etwas gelernt.

Danke
@Fennek,

jupp, darauf wollte ich hinaus. Was in der Art fehlte in der Aufgabenbeschreibung Smile
Und noch was ohne Netz, wieder im Hinblick auf die ursprüngliche Aufgabenstellung, falls nur Du damit arbeiten willst. In der gleichen Instanz öffnen geht nicht, aber in einer neuen - wiederum nur schreibgeschützt. Wobei ggf. auch eine Rolle spielt, ob die bereits offenen Datei schreibgeschützt geöffnet wurde.
Ich weiß, noch keine Kuchenzeit und die Krümel(...) wieder.
Was ist der Nachteil von

Code:
Function IsOpen(sBasename As String) As Boolean
Dim wb As Workbook
On Error Resume Next
Set wb = Workbooks(sBasename)
On Error GoTo 0
IsOpen = Not wb Is Nothing
End Function

Sub Test_Open()
Dim wb As Workbook
Debug.Print IsOpen(ThisWorkbook.Name)
Debug.Print IsOpen("This_File_does_not_even_exist.xlsx")
End Sub

?
Hallöchen,
Damit stellst Du nur fest, dass die Datei von Dir in der Instanz mit dem Makro offen ist. Zudem gibt es den einen oder anderen der lieber in einer Schleife alle offenen abgleicht als das mit on error abzukürzen.
(13.03.2021, 15:21)schauan schrieb: [ -> ]Hallöchen,
Damit stellst Du nur fest, dass die Datei von Dir in der Instanz mit dem Makro offen ist. Zudem gibt es den einen oder anderen der lieber in einer Schleife alle offenen abgleicht als das mit on error abzukürzen.

Das reicht mir.
Wenn sie nicht offen ist, versuche ich sie lesend oder schreibend zu öffnen.
Wenn ich dann auf einen Fehler stoße, höre ich halt mit Meldung auf.
Bei Volti's IsFileClosed sehe ich mehr Probleme: wenn z. B. eine Event-Prozedur auch auf IsFileClosed prüft und mich genau beim Aufruf der ersten Prozedur unterbricht. Die Variable bOpen macht den Code nicht-"re-entrant".
@Volti,

die Fenster könntest Du eventuell so abfangen. Ich hab das davor noch auf die Anwendung eingeschränkt. Selbige könnte man auch als Parameter übergeben und damit das Makro flexibler gestalten. Ich würde nicht auf die Angabe der Anwendung verzichten, manche haben ja die tollsten Dateinamen ...

If sWinTxt Like "*Excel*" Then
sWinTxt = Split(sWinTxt, ":")(0) & " - Excel"

Ich bevorzuge übrigens die "normalen" Varianten also zum einen die Schleife zum Prüfen ob ich die in der laufenden Instanz offen habe und die mit Open und Access ob es anderwärtig geht. Dazwischen prüfe ich unter Umständen auch noch, ob es die Datei überhaupt am programmierten Ort gibt Smile
Hi André,

(13.03.2021, 20:17)schauan schrieb: [ -> ]Ich würde nicht auf die Angabe der Anwendung verzichten, manche haben ja die tollsten Dateinamen ...

ob das sooo eine tolle Idee ist!? Wink

[attachment=37695]

Gruß Uwe
Seiten: 1 2