Des Öfteren kommt diese Frage auf, und eine definitive Antwort ist schwierig. Auch weil Windows und die typischen Anwendungen ein bewegliches Ziel sind:
Wie kann aus dem Rich-Client von enaio eine Datei im Format X geöffnet, bearbeitet und dafür automatisch ausgecheckt und nach der Bearbeitung automatisch wieder eingecheckt werden?
Vergleiche dazu z. B. die Diskussion Pdf Dateien automatisch einchecken aus dem letzten Sommer.
Unsere grundsätzliche Antwort ist es, dies von Tools wie Embedded Office, Embedded Documents etc. erledigen zu lassen, was auch im (enaio-) Web, auf Tablets etc. funktioniert und das Problem behebt. Wenn dies aber (noch) nicht in allen Unternehmen möglich ist, bleibt die Frage offen.
Für Office-Dokumente (XLSX, DOCX etc.) gibt es die Microsoft Office-Addins von OPTIMAL SYSTEMS. Solange Microsoft noch Lust hat, MS Office für den Desktop zu verkaufen, ist dies eine Option. Diese passen Word etc. so an, dass dies ermöglicht wird, weil dann sozusagen Word „selbst“ das Signal gibt, wenn ein Dokument wieder eingecheckt ist. Für andere Dokumentformate kann es sein, dass man im Regen steht.
Über eine externe Anwendung könnte dies automatisiert werden, z. B. so:
Jetzt kommt das eigentliche Problem: Früher haben die meisten Windows-Anwendungen einen WriteLock auf geöffnete Dokumente gesetzt, welcher zu erkennen war. Dies wäre ein Beispiel-checkout_edit_checkin-Script für solche Anwendungen:
Option Explicit
Dim FSO: Set FSO = CreateObject("Scripting.FileSystemObject")
Dim AX: Set AX = CreateObject("Optimal_AS.Application")
Main(): Function Main()
Dim DocIdAndType: Set DocIdAndType = GetPassedDocIdAndType()
Dim FileName
Dim AxResult: AxResult = AX.CheckOutDocument(DocIdAndType("id"), DocIdAndType("type"), FSO.GeTSpecialFolder(2).Path, FileName)
If AxResult <> 0 Then
MsgBox AX.GetLastError, 48, "Fehler beim Chekcout"
Exit Function
End If
Dim WShell: Set WShell = CreateObject("Wscript.Shell")
WShell.Run """" & FileName & """", 1, False
TimeOutWriteLock FileName
Do While Not IsFileWritable(FileName)
WScript.Sleep 500
Loop
AxResult = AX.CheckInDocument(DocIdAndType("id"), DocIdAndType("type"), FSO.GeTSpecialFolder(2).Path)
If AxResult <> 0 Then
AxResult = AX.UndoCheckOut(DocIdAndType("id"), DocIdAndType("type"))
If AxResult <> 0 Then
MsgBox AX.GetLastError, 48, "Fehler beim Checkin"
Exit Function
End If
End If
'Optional: Einen der denkbaren Worarounds für das Fehlen der osjxRefreshObjectInLists im AX-Objekt
WShell.AppActivate "enaio"
WScript.Sleep 200
WShell.SendKeys "{F5}"
End Function
Sub TimeOutWriteLock(FileName)
Dim i: For i = 1 To 30
WScript.Sleep 250
If Not IsFileWritable(FileName) Then
Exit Sub
End If
Next
End Sub
Function IsFileWritable(FilePath)
On Error Resume Next
Dim TS: Set TS = FSO.OpenTextFile(FilePath, 8, False)
If Err.Number = 0 Then
TS.Close
IsFileWritable = True
Else
IsFileWritable = False
End If
Err.Clear
On Error GoTo 0
End Function
Function GetPassedDocIdAndType()
Dim ArgObj: Set ArgObj = WScript.ArgumenTS
Dim IniFilePath: IniFilePath = ArgObj(0)
Dim osParamFile: Set osParamFile = FSO.OpenTextFile(IniFilePath)
Dim IniFileContent: IniFileContent = OsParamFile.ReadAll
OsParamFile.Close
FSO.DeleteFile IniFilePath, True
Dim DocIDs: DocIDs = Split(IniFileContent, vbCrLf)
Dim DocIdAndType: DocIdAndType = Split(DocIDs(0), ",")
Dim Result: Set Result = CreateObject("Scripting.Dictionary")
Result("id") = DocIdAndType(0)
Result("type") = DocIdAndType(1)
Set GetPassedDocIdAndType = Result
End Function
Allerdings machen dies immer weniger Anwendungen, sogar die MS-Tools notepad.exe, mswrite.exe und mspaint.exe, welche dies 30 Jahre getan haben, tun es heute nicht mehr (was ich grundsätzlich begrüsse). (Ganz abgesehen davon, dass Microsoft notepad derzeit massiv umbaut und Write/Paint abgekündigt und aus neuen Windows-Build verbannt hat…)
Deshalb müsste man für solche Anwendungen, wenn man sie mit Auto-Checkin nutzen will, einen anderen Erkennungsmechanismus nutzen, z. B. ob die Anwendung selbst noch läuft. Dies wäre dann ein Beispiel-checkout_edit_checkin-Script für solche nicht-sperrenden Anwendungen:
Option Explicit
Dim FSO: Set FSO = CreateObject("Scripting.FileSystemObject")
Dim AX: Set AX = CreateObject("Optimal_AS.Application")
Dim WMI: Set WMI = GetObject("winmgmts:\\.\root\cimv2")
Main(): Function Main()
Dim DocIdAndType: Set DocIdAndType = GetPassedDocIdAndType()
Dim FileName
Dim AxResult: AxResult = AX.CheckOutDocument(DocIdAndType("id"), DocIdAndType("type"), FSO.GetSpecialFolder(2).Path, FileName)
If AxResult <> 0 Then
MsgBox AX.GetLastError, 48, "Fehler beim Checkout"
Exit Function
End If
Dim WShell: Set WShell = CreateObject("Wscript.Shell")
Dim PidsBefore: Set PidsBefore = GetRunningPIDs()
WShell.Run """" & FileName & """", 1, False
Dim EditorPID: EditorPID = WaitForNewPID(PidsBefore, 10)
If EditorPID = -1 Then
MsgBox "Could not detect editor process.", 48, "Fehler"
Exit Function
End If
WaitForPIDExit EditorPID
AxResult = AX.CheckInDocument(DocIdAndType("id"), DocIdAndType("type"), FSO.GetSpecialFolder(2).Path)
If AxResult <> 0 Then
AxResult = AX.UndoCheckOut(DocIdAndType("id"), DocIdAndType("type"))
If AxResult <> 0 Then
MsgBox AX.GetLastError, 48, "Fehler beim Checkin"
Exit Function
End If
End If
WShell.AppActivate "enaio"
WScript.Sleep 200
WShell.SendKeys "{F5}"
End Function
Function GetRunningPIDs()
Dim Result: Set Result = CreateObject("Scripting.Dictionary")
Dim Process
For Each Process In WMI.ExecQuery("SELECT ProcessId FROM Win32_Process")
Result(Process.ProcessId) = 1
Next
Set GetRunningPIDs = Result
End Function
Function WaitForNewPID(PidsBefore, TimeoutSeconds)
Dim i, Process
For i = 1 To TimeoutSeconds * 4
WScript.Sleep 250
For Each Process In WMI.ExecQuery("SELECT ProcessId FROM Win32_Process")
If Not PidsBefore.Exists(Process.ProcessId) Then
WaitForNewPID = Process.ProcessId
Exit Function
End If
Next
Next
WaitForNewPID = -1
End Function
Sub WaitForPIDExit(PID)
Dim Running
Do
WScript.Sleep 500
Running = False
Dim Process
For Each Process In WMI.ExecQuery("SELECT ProcessId FROM Win32_Process WHERE ProcessId=" & PID)
Running = True
Next
Loop While Running
End Sub
Function GetPassedDocIdAndType()
Dim ArgObj: Set ArgObj = WScript.Arguments
Dim IniFilePath: IniFilePath = ArgObj(0)
Dim osParamFile: Set osParamFile = FSO.OpenTextFile(IniFilePath)
Dim IniFileContent: IniFileContent = osParamFile.ReadAll
osParamFile.Close
FSO.DeleteFile IniFilePath, True
Dim DocIDs: DocIDs = Split(IniFileContent, vbCrLf)
Dim DocIdAndType: DocIdAndType = Split(DocIDs(0), ",")
Dim Result: Set Result = CreateObject("Scripting.Dictionary")
Result("id") = DocIdAndType(0)
Result("type") = DocIdAndType(1)
Set GetPassedDocIdAndType = Result
End Function
Wenig überraschend ist aber auch dies weit weg von perfekt. Viele Anwendungen werden heute nicht zur Bearbeitung eines Dokuments geöffnet und dann wieder geschlossen. Vielmehr öffnet die gleiche Anwendung, wenn schon offen, weitere Dateien in Reitern, weiteren Fenstern etc. Dies tun Word, Excel, aber auch LibreOffice und viele andere eben genau so.
Sofern niemand hier eine andere, allgemeine Lösung kennt, muss man jede externe Anwendung und jedes Dateiformat separat betrachten und testen.
