2 Commits

Author SHA1 Message Date
Developer01
d03efbe3b3 MergCommit 2026-04-30 16:15:54 +02:00
Developer01
7d91b973ce Base Zooflow Filehandling 2026-04-30 16:04:31 +02:00
11 changed files with 1077 additions and 19 deletions

View File

@@ -76,6 +76,7 @@
<ItemGroup>
<Compile Include="Base\BaseClass.vb" />
<Compile Include="Base\BaseUtils.vb" />
<Compile Include="DocumentPathHandler.vb" />
<Compile Include="ECM\ECM.vb" />
<Compile Include="Encryption\Compression.vb" />
<Compile Include="Encryption\Encryption.vb" />
@@ -86,6 +87,7 @@
<Compile Include="FileWatcher\FileWatcherFilters.vb" />
<Compile Include="FileWatcher\FileWatcherProperties.vb" />
<Compile Include="IDB\Constants.vb" />
<Compile Include="Map_Drive.vb" />
<Compile Include="MimeEx.vb" />
<Compile Include="StringFunctions.vb" />
<Compile Include="WindowsEx.vb" />

346
Base/DocumentPathHandler.vb Normal file
View File

@@ -0,0 +1,346 @@
Imports System.IO
Imports DigitalData.Modules.Logging
''' <summary>
''' Zentrale Klasse für Dokumentenpfad-Verwaltung mit optionalem Laufwerks-Mapping und Temp-Kopie
''' </summary>
Public Class DocumentPathHandler
Private ReadOnly _Logger As Logger
Private ReadOnly _LogConfig As LogConfig
Private _mappedDrive As String = ""
Private _clsmapDrive As Map_Drive
Public Sub New(LogConfig As LogConfig)
_LogConfig = LogConfig
_Logger = LogConfig.GetLogger()
_clsmapDrive = New Map_Drive(LogConfig)
End Sub
Private Function EnsureTempFolder(ByRef tempFolder As String, ByRef errorMessage As String) As Boolean
Try
_Logger.Debug($"📂 Überprüfe Temp-Ordner: [{tempFolder}]")
If String.IsNullOrWhiteSpace(tempFolder) Then
_Logger.Debug("⚠️ Temp-Ordner nicht konfiguriert, verwende TEMP_DOCUMENT_FOLDER aus AppSettings")
tempFolder = TEMP_DOCUMENT_FOLDER
End If
' Fallback, falls global nichts konfiguriert ist
If String.IsNullOrWhiteSpace(tempFolder) Then
tempFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Digital Data",
"taskFLOW",
"DocumentViewer",
"Temp")
_Logger.Info($"⚠️ TEMP_DOCUMENT_FOLDER war leer - verwende Fallback: [{tempFolder}]")
TEMP_DOCUMENT_FOLDER = tempFolder
End If
If Not Directory.Exists(tempFolder) Then
Directory.CreateDirectory(tempFolder)
_Logger.Info($"📁 Temp-Ordner erstellt: [{tempFolder}]")
End If
If Not Directory.Exists(tempFolder) Then
errorMessage = $"Temp-Ordner konnte nicht erstellt werden: [{tempFolder}]"
Return False
End If
Return True
Catch ex As Exception
errorMessage = $"Fehler bei Temp-Ordner-Initialisierung: {ex.Message}"
_Logger.Error($"❌ {errorMessage}")
_Logger.Error(ex)
Return False
End Try
End Function
''' <summary>
''' Verarbeitet einen Dokumentenpfad: Optional Mapping, dann Temp-Kopie
''' </summary>
''' <param name="sourcePath">Quell-Pfad der Datei (UNC oder lokal)</param>
''' <param name="options">Optionen für die Verarbeitung</param>
''' <returns>DocumentPathResult mit finalem Pfad und Mapping-Info</returns>
Public Function ProcessDocumentPath(sourcePath As String, options As DocumentPathOptions) As DocumentPathResult
Dim result As New DocumentPathResult With {
.SourcePath = sourcePath,
.FinalPath = sourcePath,
.Success = False,
.WasMapped = False,
.WasCopiedToTemp = False
}
Try
' Validierung
If String.IsNullOrEmpty(sourcePath) Then
result.ErrorMessage = "Quell-Pfad ist leer"
_Logger.Error("❌ ProcessDocumentPath: Quell-Pfad ist leer")
Return result
End If
Dim workingPath As String = sourcePath
' ========== SCHRITT 1: OPTIONALES LAUFWERKS-MAPPING ==========
If options.EnableMapping AndAlso Not String.IsNullOrEmpty(options.WMSuffix) Then
Dim mappingResult = TryMapNetworkDrive(sourcePath, options)
If mappingResult.Success Then
workingPath = mappingResult.MappedPath
_mappedDrive = mappingResult.DriveLetter
result.WasMapped = True
result.MappedDrive = _mappedDrive
_Logger.Info($"✓ Laufwerk gemappt: {_mappedDrive}")
Else
_Logger.Warn("⚠️ Laufwerks-Mapping fehlgeschlagen - verwende Original-UNC-Pfad")
workingPath = sourcePath
End If
End If
' ========== SCHRITT 2: PRÜFEN OB DATEI EXISTIERT ==========
If Not File.Exists(workingPath) Then
result.ErrorMessage = $"Datei nicht gefunden: [{workingPath}]"
_Logger.Error($"❌ {result.ErrorMessage}")
' Cleanup bei Fehler
If result.WasMapped Then
UnmapDrive()
End If
Return result
End If
' ========== SCHRITT 3: OPTIONALE TEMP-KOPIE ==========
If options.CopyToTemp Then
_Logger.Debug($"📂 Starte Temp-Kopie für: [{workingPath}]")
Dim tempResult = CopyToTempFolder(workingPath, options.TempFolder)
If tempResult.Success Then
result.FinalPath = tempResult.TempPath
result.WasCopiedToTemp = True
result.TempPath = tempResult.TempPath
_Logger.Info($"✓ Datei in Temp kopiert: [{Path.GetFileName(tempResult.TempPath)}]")
' Laufwerk unmappen nach erfolgreicher Kopie
If result.WasMapped AndAlso options.UnmapAfterCopy Then
UnmapDrive()
result.WasMapped = False
End If
Else
_Logger.Warn($"⚠️ Temp-Kopie fehlgeschlagen: {tempResult.ErrorMessage}")
' WICHTIG: Nicht den gemappten Pfad behalten, wenn danach ungemappt wird.
' Fallback immer auf stabilen Originalpfad (UNC).
result.FinalPath = sourcePath
result.WasCopiedToTemp = False
result.TempPath = String.Empty
If result.WasMapped Then
UnmapDrive()
result.WasMapped = False
End If
End If
Else
result.FinalPath = workingPath
_Logger.Debug($"📄 Verwende Pfad ohne Temp-Kopie: [{workingPath}]")
End If
result.Success = True
Return result
Catch ex As Exception
result.ErrorMessage = $"Unerwarteter Fehler: {ex.Message}"
_Logger.Error($"❌ ProcessDocumentPath: {ex.Message}")
_Logger.Error(ex)
' Cleanup bei Exception
If result.WasMapped Then
UnmapDrive()
End If
Return result
End Try
End Function
''' <summary>
''' Versucht ein Netzlaufwerk zu mappen
''' </summary>
Private Function TryMapNetworkDrive(sourcePath As String, options As DocumentPathOptions) As MappingResult
Dim result As New MappingResult With {.Success = False}
Try
' Prüfen ob Pfad mit WMSUFFIX beginnt
If Not sourcePath.StartsWith(options.WMSuffix, StringComparison.OrdinalIgnoreCase) Then
_Logger.Debug($"📄 Pfad beginnt nicht mit WMSUFFIX - kein Mapping erforderlich")
Return result
End If
_Logger.Debug($"📂 WMSUFFIX erkannt - starte Laufwerks-Mapping")
' Laufwerk mappen
Dim mappedDrive As String = ""
If Not String.IsNullOrEmpty(options.SpecificDrive) AndAlso options.SpecificDrive.Length = 1 Then
' Spezifisches Laufwerk
If _clsmapDrive.MapSpecificDrive(options.SpecificDrive, options.DriveBlacklist, options.WMSuffix) Then
mappedDrive = options.SpecificDrive & ":"
End If
Else
' Automatisches Mapping
mappedDrive = _clsmapDrive.MapDriveAutomatic(options.DriveBlacklist, options.WMSuffix)
End If
If String.IsNullOrEmpty(mappedDrive) Then
_Logger.Warn("⚠️ Kein Laufwerk gemappt")
Return result
End If
' Pfad umschreiben
Dim relativePath As String = sourcePath.Substring(options.WMSuffix.Length)
If relativePath.StartsWith("\") Then
relativePath = relativePath.Substring(1)
End If
Dim mappedPath As String = mappedDrive.TrimEnd(":"c, "\"c) & ":\" & relativePath
_Logger.Debug($"📄 Original: [{sourcePath}]")
_Logger.Debug($"📄 Gemappt: [{mappedPath}]")
result.Success = True
result.DriveLetter = mappedDrive
result.MappedPath = mappedPath
Return result
Catch ex As Exception
_Logger.Error($"Fehler beim Mapping: {ex.Message}")
_Logger.Error(ex)
Return result
End Try
End Function
Private Function CopyToTempFolder(sourcePath As String, tempFolder As String) As TempCopyResult
Dim result As New TempCopyResult With {.Success = False}
Try
Dim configuredTempFolder As String = tempFolder
Dim globalTempFolder As String = TEMP_DOCUMENT_FOLDER
' Temp-Ordner validieren/initialisieren (kann tempFolder per ByRef setzen)
If Not EnsureTempFolder(tempFolder, result.ErrorMessage) Then
_Logger.Warn($"⚠️ {result.ErrorMessage}")
_Logger.Warn($"[TempCopy] InputTemp=[{If(String.IsNullOrWhiteSpace(configuredTempFolder), "<leer>", configuredTempFolder)}], GlobalTemp=[{If(String.IsNullOrWhiteSpace(globalTempFolder), "<leer>", globalTempFolder)}]")
Return result
End If
' NEU: Effektiven Zielordner immer transparent loggen
_Logger.Info($"[TempCopy] Effektiver Temp-Ordner: [{tempFolder}]")
_Logger.Debug($"[TempCopy] InputTemp=[{If(String.IsNullOrWhiteSpace(configuredTempFolder), "<leer>", configuredTempFolder)}], GlobalTemp=[{If(String.IsNullOrWhiteSpace(globalTempFolder), "<leer>", globalTempFolder)}]")
' Eindeutigen Dateinamen generieren
Dim originalFileName As String = Path.GetFileName(sourcePath)
Dim fileNameWithoutExt As String = Path.GetFileNameWithoutExtension(originalFileName)
Dim extension As String = Path.GetExtension(originalFileName)
Dim timestamp As String = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff")
Dim uniqueFileName As String = $"{fileNameWithoutExt}_{timestamp}{extension}"
Dim targetPath As String = Path.Combine(tempFolder, uniqueFileName)
_Logger.Debug($"📄 Kopiere nach Temp:")
_Logger.Debug($" Von: [{sourcePath}]")
_Logger.Debug($" Nach: [{targetPath}]")
File.Copy(sourcePath, targetPath, overwrite:=True)
result.Success = True
result.TempPath = targetPath
result.TempFileName = uniqueFileName
Return result
Catch ex As Exception
result.ErrorMessage = $"Fehler beim Kopieren: {ex.Message}"
_Logger.Error($"❌ {result.ErrorMessage}")
_Logger.Error(ex)
Return result
End Try
End Function
''' <summary>
''' Trennt das gemappte Laufwerk
''' </summary>
Public Sub UnmapDrive()
If Not String.IsNullOrEmpty(_mappedDrive) Then
Try
If _clsmapDrive.DisconnectNetworkDrive(_mappedDrive, force:=True) Then
_Logger.Info($"🔌 Laufwerk {_mappedDrive} getrennt")
Else
_Logger.Warn($"⚠️ Warnung beim Trennen von {_mappedDrive}")
End If
Catch ex As Exception
_Logger.Debug($"Fehler beim Trennen von {_mappedDrive}: {ex.Message}")
Finally
_mappedDrive = ""
End Try
End If
End Sub
''' <summary>
''' Cleanup-Methode (z.B. beim Schließen des Forms)
''' </summary>
Public Sub Cleanup()
UnmapDrive()
End Sub
#Region "Nested Classes für Optionen und Ergebnisse"
''' <summary>
''' Optionen für die Dokumentenpfad-Verarbeitung
''' </summary>
Public Class DocumentPathOptions
''' <summary>Soll Laufwerks-Mapping versucht werden?</summary>
Public Property EnableMapping As Boolean = False
''' <summary>WMSUFFIX für Mapping-Erkennung (z.B. "\\windream\objects")</summary>
Public Property WMSuffix As String = ""
''' <summary>Spezifischer Laufwerksbuchstabe (z.B. "V") oder leer für automatisch</summary>
Public Property SpecificDrive As String = ""
''' <summary>Blacklist für Laufwerksbuchstaben (z.B. "Y,M,V")</summary>
Public Property DriveBlacklist As String = ""
''' <summary>Soll Datei in Temp kopiert werden?</summary>
Public Property CopyToTemp As Boolean = False
''' <summary>Temp-Ordner-Pfad</summary>
Public Property TempFolder As String = ""
''' <summary>Laufwerk nach erfolgreicher Temp-Kopie unmappen?</summary>
Public Property UnmapAfterCopy As Boolean = True
End Class
''' <summary>
''' Ergebnis der Dokumentenpfad-Verarbeitung
''' </summary>
Public Class DocumentPathResult
Public Property Success As Boolean
Public Property SourcePath As String
Public Property FinalPath As String
Public Property ErrorMessage As String
Public Property WasMapped As Boolean
Public Property MappedDrive As String
Public Property WasCopiedToTemp As Boolean
Public Property TempPath As String
End Class
Private Class MappingResult
Public Property Success As Boolean
Public Property DriveLetter As String
Public Property MappedPath As String
End Class
Private Class TempCopyResult
Public Property Success As Boolean
Public Property TempPath As String
Public Property TempFileName As String
Public Property ErrorMessage As String
End Class
#End Region
End Class

559
Base/Map_Drive.vb Normal file
View File

@@ -0,0 +1,559 @@
Imports System.IO
Imports System.Runtime.InteropServices
Imports DigitalData.Modules.Logging
Public Class Map_Drive
Private ReadOnly _Logger As Logger
Private ReadOnly _LogConfig As LogConfig
#Region "Windows API Deklarationen"
<DllImport("mpr.dll", CharSet:=CharSet.Auto)>
Private Shared Function WNetAddConnection2(ByRef lpNetResource As NETRESOURCE,
ByVal lpPassword As String,
ByVal lpUsername As String,
ByVal dwFlags As Integer) As Integer
End Function
<DllImport("mpr.dll", CharSet:=CharSet.Auto)>
Private Shared Function WNetCancelConnection2(ByVal lpName As String,
ByVal dwFlags As Integer,
ByVal fForce As Boolean) As Integer
End Function
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Private Structure NETRESOURCE
Public dwScope As Integer
Public dwType As Integer
Public dwDisplayType As Integer
Public dwUsage As Integer
Public lpLocalName As String
Public lpRemoteName As String
Public lpComment As String
Public lpProvider As String
End Structure
Private Const RESOURCETYPE_DISK As Integer = 1
Private Const CONNECT_UPDATE_PROFILE As Integer = 1
Private Const ERROR_SUCCESS As Integer = 0
Private Const ERROR_ALREADY_ASSIGNED As Integer = 85
#End Region
Public Sub New(LogConfig As LogConfig)
_LogConfig = LogConfig
_Logger = LogConfig.GetLogger()
End Sub
''' <summary>
''' Struktur für Netzlaufwerk-Informationen
''' </summary>
Public Structure NetworkDriveInfo
Public DriveLetter As String
Public NetworkPath As String
Public DriveType As IO.DriveType
Public IsReady As Boolean
Public TotalSize As Long
Public FreeSpace As Long
End Structure
''' <summary>
''' Ermittelt den nächsten freien Laufwerksbuchstaben (alphabetisch absteigend von Z bis A)
''' </summary>
''' <param name="blacklist">Liste der nicht erlaubten Laufwerksbuchstaben (z.B. "Y,M,V")</param>
''' <returns>Nächster freier Laufwerksbuchstabe mit Doppelpunkt (z.B. "Z:") oder String.Empty wenn keiner frei</returns>
Public Function GetNextFreeDriveLetter(Optional blacklist As String = "") As String
Try
' Blacklist verarbeiten (Großbuchstaben ohne Doppelpunkte)
Dim blacklistArray As New List(Of Char)
If Not String.IsNullOrEmpty(blacklist) Then
For Each item In blacklist.Split(","c)
Dim letter = item.Trim().ToUpper().Replace(":", "")
If letter.Length = 1 AndAlso Char.IsLetter(letter(0)) Then
blacklistArray.Add(letter(0))
End If
Next
End If
' Alle aktuell verwendeten Laufwerksbuchstaben ermitteln
Dim usedDrives As New List(Of Char)
For Each drive As IO.DriveInfo In IO.DriveInfo.GetDrives()
Dim letter As Char = drive.Name(0)
usedDrives.Add(Char.ToUpper(letter))
Next
' Alphabetisch absteigend von Z bis A durchgehen
For i As Integer = Asc("Z"c) To Asc("A"c) Step -1
Dim currentLetter As Char = Chr(i)
' Prüfen ob Buchstabe verfügbar ist
If Not usedDrives.Contains(currentLetter) AndAlso Not blacklistArray.Contains(currentLetter) Then
_Logger.Debug($"Nächster freier Laufwerksbuchstabe gefunden: {currentLetter}:")
Return currentLetter & ":"
End If
Next
_Logger.Warn("Kein freier Laufwerksbuchstabe gefunden!")
Return String.Empty
Catch ex As Exception
_Logger.Error($"Fehler beim Ermitteln des nächsten freien Laufwerksbuchstabens: {ex.Message}")
_Logger.Error(ex)
Return String.Empty
End Try
End Function
''' <summary>
''' Prüft ob ein Laufwerksbuchstabe verfügbar ist (nicht verwendet und nicht in Blacklist)
''' </summary>
''' <param name="driveLetter">Zu prüfender Laufwerksbuchstabe</param>
''' <param name="blacklist">Liste der nicht erlaubten Laufwerksbuchstaben</param>
''' <returns>True wenn verfügbar, False wenn bereits verwendet oder in Blacklist</returns>
Public Function IsDriveLetterAvailable(driveLetter As String, Optional blacklist As String = "") As Boolean
Try
' Formatierung sicherstellen
driveLetter = driveLetter.Trim().ToUpper().Replace(":", "")
If driveLetter.Length <> 1 OrElse Not Char.IsLetter(driveLetter(0)) Then
_Logger.Warn($"Ungültiger Laufwerksbuchstabe: {driveLetter}")
Return False
End If
Dim letter As Char = driveLetter(0)
' Blacklist prüfen
If Not String.IsNullOrEmpty(blacklist) Then
For Each item In blacklist.Split(","c)
Dim blacklistedLetter = item.Trim().ToUpper().Replace(":", "")
If blacklistedLetter.Length = 1 AndAlso blacklistedLetter(0) = letter Then
_Logger.Debug($"Laufwerk {letter}: ist in der Blacklist")
Return False
End If
Next
End If
' Prüfen ob bereits verwendet
For Each drive As IO.DriveInfo In IO.DriveInfo.GetDrives()
If Char.ToUpper(drive.Name(0)) = letter Then
_Logger.Debug($"Laufwerk {letter}: ist bereits verwendet")
Return False
End If
Next
Return True
Catch ex As Exception
_Logger.Error($"Fehler beim Prüfen der Laufwerksverfügbarkeit: {ex.Message}")
_Logger.Error(ex)
Return False
End Try
End Function
''' <summary>
''' Mappt ein Netzlaufwerk mit automatischer Laufwerksbuchstabenwahl oder spezifischem Buchstaben
''' </summary>
''' <param name="driveLetter">Gewünschter Laufwerksbuchstabe (leer = automatisch den nächsten freien wählen)</param>
''' <param name="networkPath">UNC-Pfad des Netzwerkshares</param>
''' <param name="blacklist">Komma-getrennte Liste verbotener Laufwerksbuchstaben (z.B. "Y,M,V")</param>
''' <param name="userName">Optionaler Benutzername für Authentifizierung</param>
''' <param name="password">Optionales Passwort für Authentifizierung</param>
''' <param name="persistent">Soll das Mapping persistent sein?</param>
''' <returns>Verwendeter Laufwerksbuchstabe bei Erfolg, String.Empty bei Fehler</returns>
Public Function MapNetworkDrive(driveLetter As String,
networkPath As String,
Optional blacklist As String = "",
Optional userName As String = Nothing,
Optional password As String = Nothing,
Optional persistent As Boolean = True) As String
Try
Dim targetDriveLetter As String = ""
' Szenario 1: Kein Laufwerksbuchstabe angegeben -> Automatische Auswahl
If String.IsNullOrEmpty(driveLetter) Then
_Logger.Info("Kein Laufwerksbuchstabe angegeben - suche nächsten freien Buchstaben...")
targetDriveLetter = GetNextFreeDriveLetter(blacklist)
If String.IsNullOrEmpty(targetDriveLetter) Then
_Logger.Error("❌ Kein freier Laufwerksbuchstabe verfügbar!")
Return String.Empty
End If
_Logger.Info($"Automatisch gewählter Laufwerksbuchstabe: {targetDriveLetter}")
Else
' Szenario 2: Spezifischer Laufwerksbuchstabe angegeben
targetDriveLetter = driveLetter.Trim().ToUpper()
If Not targetDriveLetter.EndsWith(":") Then
targetDriveLetter &= ":"
End If
' ========== NEU: Prüfen ob Laufwerk verfügbar ist ==========
If Not IsDriveLetterAvailable(targetDriveLetter, blacklist) Then
_Logger.Warn($"⚠️ Laufwerk {targetDriveLetter} ist nicht verfügbar (bereits verwendet oder in Blacklist)")
' NICHT abbrechen - weiter versuchen (alte Logik beibehalten)
End If
' ========== ENDE NEU ==========
End If
' ========== NEU: Prüfung ob Laufwerk bereits existiert ==========
Dim driveExists As Boolean = False
Try
Dim driveInfo As New System.IO.DriveInfo(targetDriveLetter)
driveExists = driveInfo.IsReady
Catch
' Laufwerk existiert nicht - das ist OK
driveExists = False
End Try
' Nur trennen wenn Laufwerk wirklich existiert
If driveExists Then
_Logger.Debug($" Laufwerk {targetDriveLetter} existiert bereits - wird getrennt")
DisconnectNetworkDrive(targetDriveLetter, force:=True)
Else
_Logger.Debug($"✓ Laufwerk {targetDriveLetter} existiert noch nicht - kein Disconnect nötig")
End If
' ========== ENDE NEU ==========
' Laufwerk mappen (bestehende Logik)
If MapNetworkDriveInternal(targetDriveLetter, networkPath, userName, password, persistent) Then
_Logger.Info($"✓ Netzlaufwerk {targetDriveLetter} erfolgreich gemappt zu {networkPath}")
Return targetDriveLetter
Else
_Logger.Error($"❌ Fehler beim Mappen von {targetDriveLetter}")
Return String.Empty
End If
Catch ex As Exception
_Logger.Error($"❌ Fehler in MapNetworkDrive: {ex.Message}")
_Logger.Error(ex)
Return String.Empty
End Try
End Function
''' <summary>
''' Interne Methode zum tatsächlichen Mappen eines Netzlaufwerks
''' </summary>
Private Function MapNetworkDriveInternal(driveLetter As String,
networkPath As String,
userName As String,
password As String,
persistent As Boolean) As Boolean
Try
' Erst trennen falls vorhanden (ohne Fehler wenn nicht vorhanden)
DisconnectNetworkDrive(driveLetter, True)
' NETRESOURCE-Struktur vorbereiten
Dim netResource As New NETRESOURCE With {
.dwType = RESOURCETYPE_DISK,
.lpLocalName = driveLetter,
.lpRemoteName = networkPath
}
Dim flags As Integer = If(persistent, CONNECT_UPDATE_PROFILE, 0)
' WICHTIG: Credentials als Nothing übergeben = Verwende aktuelle Windows-Credentials
' Wenn der Share öffentlich oder mit aktuellen Credentials erreichbar ist, funktioniert es
Dim result As Integer = WNetAddConnection2(netResource, password, userName, flags)
Select Case result
Case ERROR_SUCCESS
_Logger.Debug($"✓ Laufwerk {driveLetter} erfolgreich gemappt")
Return True
Case 1326 ' ERROR_LOGON_FAILURE
_Logger.Error($"❌ Authentifizierungsfehler (1326): Anmeldung fehlgeschlagen für [{networkPath}]")
_Logger.Error($" → Der UNC-Pfad erfordert möglicherweise spezielle Credentials")
_Logger.Error($" → Oder der aktuelle Benutzer hat keine Berechtigung")
Return False
Case 53 ' ERROR_BAD_NETPATH
_Logger.Error($"❌ Netzwerkpfad nicht gefunden (53): [{networkPath}]")
Return False
Case 67 ' ERROR_BAD_NET_NAME
_Logger.Error($"❌ Netzwerkname ungültig (67): [{networkPath}]")
Return False
Case 85 ' ERROR_ALREADY_ASSIGNED
_Logger.Warn($"⚠️ Laufwerk {driveLetter} ist bereits zugewiesen (85)")
' Versuche es zu trennen und erneut zu verbinden
DisconnectNetworkDrive(driveLetter, force:=True)
System.Threading.Thread.Sleep(500) ' Kurze Pause
result = WNetAddConnection2(netResource, password, userName, flags)
If result = ERROR_SUCCESS Then
_Logger.Info($"✓ Laufwerk {driveLetter} nach erneutem Versuch erfolgreich gemappt")
Return True
Else
Return False
End If
Case Else
_Logger.Error($"❌ WNetAddConnection2 Error Code: {result}")
Return False
End Select
Catch ex As Exception
_Logger.Error($"Fehler in MapNetworkDriveInternal: {ex.Message}")
_Logger.Error(ex)
Return False
End Try
End Function
''' <summary>
''' Test-Funktion um UNC-Pfad-Zugriff zu prüfen
''' </summary>
Public Function TestUNCAccess(uncPath As String) As Boolean
Try
_Logger.Info($"🔍 Teste Zugriff auf UNC-Pfad: [{uncPath}]")
' Teste ob Pfad existiert und erreichbar ist
If System.IO.Directory.Exists(uncPath) Then
_Logger.Info($"✓ UNC-Pfad ist direkt erreichbar ohne Mapping")
' Teste Lese-Berechtigung
Try
Dim files = System.IO.Directory.GetFiles(uncPath)
_Logger.Info($"✓ Lese-Berechtigung vorhanden ({files.Length} Dateien gefunden)")
Return True
Catch permEx As UnauthorizedAccessException
_Logger.Error($"❌ Keine Lese-Berechtigung: {permEx.Message}")
Return False
End Try
Else
_Logger.Error($"❌ UNC-Pfad nicht erreichbar oder existiert nicht")
Return False
End If
Catch ex As Exception
_Logger.Error($"❌ Fehler beim Testen des UNC-Zugriffs: {ex.Message}")
_Logger.Error(ex)
Return False
End Try
End Function
''' <summary>
''' Trennt ein Netzlaufwerk mit Windows-API
''' </summary>
Public Function DisconnectNetworkDrive(driveLetter As String, Optional force As Boolean = True) As Boolean
Try
' Formatierung sicherstellen
driveLetter = driveLetter.Trim().ToUpper()
If Not driveLetter.EndsWith(":") Then
driveLetter &= ":"
End If
Dim driveExists As Boolean = False
Try
Dim driveInfo As New System.IO.DriveInfo(driveLetter)
driveExists = driveInfo.IsReady
Catch
driveExists = False
End Try
If Not driveExists Then
_Logger.Debug($" Laufwerk {driveLetter} existiert nicht - Disconnect übersprungen")
Return True ' Kein Fehler, da das gewünschte Ergebnis erreicht ist (Laufwerk ist nicht verbunden)
End If
Dim flags As Integer = CONNECT_UPDATE_PROFILE
Dim result As Integer = WNetCancelConnection2(driveLetter, flags, force)
If result = ERROR_SUCCESS Then
_Logger.Debug($"✓ Netzlaufwerk {driveLetter} erfolgreich getrennt")
Return True
ElseIf result = 2250 Then ' ERROR_NOT_CONNECTED
' Von WARN auf DEBUG herabgestuft, da es kein echter Fehler ist
_Logger.Debug($" Laufwerk {driveLetter} war nicht verbunden (Code 2250)")
Return True
ElseIf result = ERROR_ALREADY_ASSIGNED Then
_Logger.Debug($" Netzlaufwerk {driveLetter} war nicht verbunden")
Return True
Else
_Logger.Warn($"⚠️ Warnung beim Trennen von {driveLetter}: Error Code {result}")
Return False
End If
Catch ex As Exception
_Logger.Debug($"⚠️ Fehler beim Trennen von {driveLetter} (ignoriert): {ex.Message}")
Return False
End Try
End Function
''' <summary>
''' Ermittelt alle gemappten Netzlaufwerke
''' </summary>
Public Function GetMappedNetworkDrives() As List(Of NetworkDriveInfo)
Dim mappedDrives As New List(Of NetworkDriveInfo)
Try
For Each drive As IO.DriveInfo In IO.DriveInfo.GetDrives()
If drive.DriveType = IO.DriveType.Network Then
Dim driveInfo As New NetworkDriveInfo With {
.DriveLetter = drive.Name,
.NetworkPath = GetNetworkPath(drive.Name),
.DriveType = drive.DriveType,
.IsReady = drive.IsReady
}
If drive.IsReady Then
Try
driveInfo.TotalSize = drive.TotalSize
driveInfo.FreeSpace = drive.AvailableFreeSpace
Catch ex As Exception
_Logger.Debug($"Konnte Größeninformationen für {drive.Name} nicht ermitteln: {ex.Message}")
End Try
End If
mappedDrives.Add(driveInfo)
End If
Next
_Logger.Debug($"Insgesamt {mappedDrives.Count} Netzlaufwerk(e) gefunden")
Return mappedDrives
Catch ex As Exception
_Logger.Error($"Fehler beim Ermitteln der Netzlaufwerke: {ex.Message}")
_Logger.Error(ex)
Return mappedDrives
End Try
End Function
''' <summary>
''' Ermittelt den UNC-Pfad eines gemappten Laufwerks
''' </summary>
Public Function GetNetworkPath(driveLetter As String) As String
Try
driveLetter = driveLetter.Trim().ToUpper()
If Not driveLetter.EndsWith(":") Then
driveLetter &= ":"
End If
Dim network As Object = CreateObject("WScript.Network")
Dim enumDrives As Object = network.EnumNetworkDrives()
For i As Integer = 0 To enumDrives.Count - 1 Step 2
If enumDrives.Item(i).ToString.Equals(driveLetter, StringComparison.OrdinalIgnoreCase) Then
Return enumDrives.Item(i + 1).ToString()
End If
Next
Return String.Empty
Catch ex As Exception
_Logger.Debug($"Fehler beim Ermitteln des Netzwerkpfads für {driveLetter}: {ex.Message}")
Return String.Empty
End Try
End Function
''' <summary>
''' Prüft ob ein bestimmtes Laufwerk als Netzlaufwerk gemappt ist
''' </summary>
Public Shared Function IsDriveMapped(driveLetter As String) As Boolean
Try
driveLetter = driveLetter.Trim().ToUpper()
If Not driveLetter.EndsWith(":") Then
driveLetter &= ":"
End If
If Not driveLetter.EndsWith("\") Then
driveLetter &= "\"
End If
Dim driveInfo As New IO.DriveInfo(driveLetter)
Return driveInfo.DriveType = IO.DriveType.Network AndAlso driveInfo.IsReady
Catch ex As Exception
Return False
End Try
End Function
''' <summary>
''' Gibt eine formatierte Übersicht aller gemappten Netzlaufwerke zurück
''' </summary>
Public Function GetMappedDrivesInfo() As String
Dim result As New System.Text.StringBuilder()
Dim drives = GetMappedNetworkDrives()
If drives.Count = 0 Then
Return "Keine Netzlaufwerke gefunden."
End If
result.AppendLine($"Gemappte Netzlaufwerke ({drives.Count}):")
result.AppendLine(New String("-"c, 60))
For Each drive In drives
result.AppendLine($"Laufwerk: {drive.DriveLetter}")
result.AppendLine($" Pfad: {drive.NetworkPath}")
result.AppendLine($" Status: {If(drive.IsReady, "Verfügbar", "Nicht verfügbar")}")
If drive.IsReady AndAlso drive.TotalSize > 0 Then
Dim totalGB As Double = drive.TotalSize / (1024.0 ^ 3)
Dim freeGB As Double = drive.FreeSpace / (1024.0 ^ 3)
result.AppendLine($" Größe: {totalGB:N2} GB (Frei: {freeGB:N2} GB)")
End If
result.AppendLine()
Next
Return result.ToString()
End Function
''' <summary>
''' Mappt ein spezifisches Laufwerk (z.B. "V") mit Blacklist-Prüfung
''' </summary>
Public Function MapSpecificDrive(driveLetter As String, blacklist As String, networkPath As String) As Boolean
Try
' Formatierung sicherstellen
driveLetter = driveLetter.Trim().ToUpper().Replace(":", "")
If String.IsNullOrEmpty(driveLetter) OrElse driveLetter.Length <> 1 Then
_Logger.Warn($"⚠️ Ungültiger Laufwerksbuchstabe: [{driveLetter}]")
Return False
End If
Dim driveWithColon As String = driveLetter & ":"
' Prüfen ob Laufwerk verfügbar ist
If Not IsDriveLetterAvailable(driveWithColon, blacklist) Then
_Logger.Warn($"⚠️ Laufwerk {driveWithColon} ist nicht verfügbar (bereits verwendet oder in Blacklist)")
Return False
End If
' UNC-Pfad vorbereiten (ohne abschließenden Backslash)
Dim uncPath As String = networkPath.TrimEnd("\"c)
' Laufwerk mappen (OHNE Credentials, persistent=False für temporäres Mapping)
Dim result = MapNetworkDrive(driveWithColon, uncPath, blacklist, userName:=Nothing, password:=Nothing, persistent:=False)
If Not String.IsNullOrEmpty(result) Then
_Logger.Debug($"✓ Laufwerk {driveWithColon} erfolgreich gemappt zu [{uncPath}]")
Return True
Else
_Logger.Error($"❌ Fehler beim Mappen von {driveWithColon}")
Return False
End If
Catch ex As Exception
_Logger.Error($"Fehler in MapSpecificDrive: {ex.Message}")
_Logger.Error(ex)
Return False
End Try
End Function
''' <summary>
''' Mappt automatisch den nächsten freien Laufwerksbuchstaben (Z→A)
''' </summary>
Public Function MapDriveAutomatic(blacklist As String, networkPath As String) As String
Try
' UNC-Pfad vorbereiten (ohne abschließenden Backslash)
Dim uncPath As String = networkPath.TrimEnd("\"c)
_Logger.Debug($"🔍 Suche automatisch freien Laufwerksbuchstaben...")
_Logger.Debug($" Blacklist: [{blacklist}]")
_Logger.Debug($" Netzwerkpfad: [{uncPath}]")
' Automatisches Mapping (leer = automatische Auswahl, persistent=False)
Dim result = MapNetworkDrive("", uncPath, blacklist, userName:=Nothing, password:=Nothing, persistent:=False)
If Not String.IsNullOrEmpty(result) Then
_Logger.Debug($"✓ Automatisch gewähltes Laufwerk: {result}")
Return result
Else
_Logger.Error($"❌ Kein freier Laufwerksbuchstabe verfügbar")
Return String.Empty
End If
Catch ex As Exception
_Logger.Error($"Fehler in MapDriveAutomatic: {ex.Message}")
_Logger.Error(ex)
Return String.Empty
End Try
End Function
End Class

View File

@@ -2,6 +2,7 @@
Imports System.Web
Public Module ModuleExtensions
Public TEMP_DOCUMENT_FOLDER As String = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Documents")
Const UnixEraStartTicks As Long = 621355968000000000
' ======================================================

View File

@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' indem Sie "*" wie unten gezeigt eingeben:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("1.3.9.0")>
<Assembly: AssemblyFileVersion("1.3.9.0")>
<Assembly: AssemblyVersion("1.4.0.0")>
<Assembly: AssemblyFileVersion("1.4.0.0")>

View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5EBACBFA-F11A-4BBF-8D02-91461F2293ED}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>DigitalData.Modules.License</RootNamespace>
<AssemblyName>DigitalData.Modules.License</AssemblyName>
<FileAlignment>512</FileAlignment>
<MyType>Windows</MyType>
<TargetFrameworkVersion>net462</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>DigitalData.Modules.License.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>DigitalData.Modules.License.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.5.0.5\lib\net46\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Collections.Generic" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
<Import Include="System.Linq" />
<Import Include="System.Xml.Linq" />
<Import Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Compile Include="LicenseCreator.vb" />
<Compile Include="LicenseFile.vb" />
<Compile Include="LicenseLegacy.vb" />
<Compile Include="LicenseManagerLegacy.vb" />
<Compile Include="LicenseSchema.vb">
<DependentUpon>LicenseSchema.xsd</DependentUpon>
</Compile>
<Compile Include="LicensesLegacy.vb" />
<Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Application.myapp</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="My Project\Resources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="My Project\Settings.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="LicenseSchema.xsd">
<SubType>Designer</SubType>
</None>
<None Include="My Project\Application.myapp">
<Generator>MyApplicationCodeGenerator</Generator>
<LastGenOutput>Application.Designer.vb</LastGenOutput>
</None>
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<CustomToolNamespace>My</CustomToolNamespace>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Logging\Logging.vbproj">
<Project>{903b2d7d-3b80-4be9-8713-7447b704e1b0}</Project>
<Name>Logging</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<PropertyGroup>
<PostBuildEvent>powershell.exe -command "&amp; { &amp;'$(SolutionDir)copy-binary.ps1' '$(TargetPath)' '$(TargetFileName)' '$(ConfigurationName)' '$(ProjectName)' }"</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -16,6 +16,9 @@ EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Interfaces", "Interfaces\Interfaces.vbproj", "{AB6F09BF-E794-4F6A-94BB-C97C0BA84D64}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Jobs", "Jobs\Jobs.vbproj", "{39EC839A-3C30-4922-A41E-6B09D1DDE5C3}"
ProjectSection(ProjectDependencies) = postProject
{AB6F09BF-E794-4F6A-94BB-C97C0BA84D64} = {AB6F09BF-E794-4F6A-94BB-C97C0BA84D64}
EndProjectSection
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "License", "License\License.vbproj", "{5EBACBFA-F11A-4BBF-8D02-91461F2293ED}"
EndProject

View File

@@ -44,9 +44,6 @@
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<ItemGroup>
<Reference Include="DigitalData.Modules.Logging, Version=2.6.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Modules.Logging.2.6.5\lib\net462\DigitalData.Modules.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Office.Interop.Outlook">
<HintPath>M:\Bibliotheken\3rdParty\Office\Microsoft.Office.Interop.Outlook.dll</HintPath>
@@ -149,6 +146,10 @@
<Project>{6ea0c51f-c2b1-4462-8198-3de0b32b74f8}</Project>
<Name>Base</Name>
</ProjectReference>
<ProjectReference Include="..\Logging\Logging.vbproj">
<Project>{903b2d7d-3b80-4be9-8713-7447b704e1b0}</Project>
<Name>Logging</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<COMReference Include="stdole">

View File

@@ -7,4 +7,10 @@ Public Class Environment
Public Property Database As MSSQLServer
Public Property DatabaseIDB As MSSQLServer
Public Property Modules As New Dictionary(Of String, State.ModuleState)
Public Class Filehandling
Public CopyWMFile2Temp As Boolean = False
Public WM_SUFFIX As String = "\\WINDREAM\OBJECTS"
Public MAP_SHAREDRIVE As String = ""
Public MAP_BLACKLIST As String = ""
End Class
End Class

View File

@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("1.3.0.0")>
<Assembly: AssemblyFileVersion("1.3.0.0")>
<Assembly: AssemblyVersion("1.4.0.0")>
<Assembly: AssemblyFileVersion("1.4.0.0")>

View File

@@ -44,18 +44,6 @@
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<ItemGroup>
<Reference Include="DigitalData.Modules.Database">
<HintPath>..\Database\bin\Debug\DigitalData.Modules.Database.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Modules.EDMI.API">
<HintPath>M:\Bibliotheken\Digital Data\DD_Modules\DigitalData.Modules.EDMI.API.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Modules.Logging">
<HintPath>..\Logging\bin\Debug\DigitalData.Modules.Logging.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Modules.Windows">
<HintPath>..\Windows\bin\Debug\DigitalData.Modules.Windows.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.5.0.5\lib\net46\NLog.dll</HintPath>
@@ -135,6 +123,24 @@
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Database\Database.vbproj">
<Project>{eaf0ea75-5fa7-485d-89c7-b2d843b03a96}</Project>
<Name>Database</Name>
</ProjectReference>
<ProjectReference Include="..\EDMIAPI\EDMI.API.vbproj">
<Project>{25017513-0d97-49d3-98d7-ba76d9b251b0}</Project>
<Name>EDMI.API</Name>
</ProjectReference>
<ProjectReference Include="..\Logging\Logging.vbproj">
<Project>{903b2d7d-3b80-4be9-8713-7447b704e1b0}</Project>
<Name>Logging</Name>
</ProjectReference>
<ProjectReference Include="..\Windows\Windows.vbproj">
<Project>{5efaef9b-90b9-4f05-9f70-f79ad77fff86}</Project>
<Name>Windows</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<PropertyGroup>
<PostBuildEvent>powershell.exe -command "&amp; { &amp;'$(SolutionDir)copy-binary.ps1' '$(TargetPath)' '$(TargetFileName)' '$(ConfigurationName)' '$(ProjectName)' }"</PostBuildEvent>