diff --git a/Base/Base.vbproj b/Base/Base.vbproj index 841173a1..5c88776d 100644 --- a/Base/Base.vbproj +++ b/Base/Base.vbproj @@ -76,6 +76,7 @@ + @@ -86,6 +87,7 @@ + diff --git a/Base/DocumentPathHandler.vb b/Base/DocumentPathHandler.vb new file mode 100644 index 00000000..7dc21135 --- /dev/null +++ b/Base/DocumentPathHandler.vb @@ -0,0 +1,346 @@ +Imports System.IO +Imports DigitalData.Modules.Logging +''' +''' Zentrale Klasse für Dokumentenpfad-Verwaltung mit optionalem Laufwerks-Mapping und Temp-Kopie +''' +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 + ''' + ''' Verarbeitet einen Dokumentenpfad: Optional Mapping, dann Temp-Kopie + ''' + ''' Quell-Pfad der Datei (UNC oder lokal) + ''' Optionen für die Verarbeitung + ''' DocumentPathResult mit finalem Pfad und Mapping-Info + 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 + + ''' + ''' Versucht ein Netzlaufwerk zu mappen + ''' + 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), "", configuredTempFolder)}], GlobalTemp=[{If(String.IsNullOrWhiteSpace(globalTempFolder), "", 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), "", configuredTempFolder)}], GlobalTemp=[{If(String.IsNullOrWhiteSpace(globalTempFolder), "", 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 + + ''' + ''' Trennt das gemappte Laufwerk + ''' + 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 + + ''' + ''' Cleanup-Methode (z.B. beim Schließen des Forms) + ''' + Public Sub Cleanup() + UnmapDrive() + End Sub + +#Region "Nested Classes für Optionen und Ergebnisse" + + ''' + ''' Optionen für die Dokumentenpfad-Verarbeitung + ''' + Public Class DocumentPathOptions + ''' Soll Laufwerks-Mapping versucht werden? + Public Property EnableMapping As Boolean = False + + ''' WMSUFFIX für Mapping-Erkennung (z.B. "\\windream\objects") + Public Property WMSuffix As String = "" + + ''' Spezifischer Laufwerksbuchstabe (z.B. "V") oder leer für automatisch + Public Property SpecificDrive As String = "" + + ''' Blacklist für Laufwerksbuchstaben (z.B. "Y,M,V") + Public Property DriveBlacklist As String = "" + + ''' Soll Datei in Temp kopiert werden? + Public Property CopyToTemp As Boolean = False + + ''' Temp-Ordner-Pfad + Public Property TempFolder As String = "" + + ''' Laufwerk nach erfolgreicher Temp-Kopie unmappen? + Public Property UnmapAfterCopy As Boolean = True + End Class + + ''' + ''' Ergebnis der Dokumentenpfad-Verarbeitung + ''' + 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 diff --git a/Base/Map_Drive.vb b/Base/Map_Drive.vb new file mode 100644 index 00000000..ae937746 --- /dev/null +++ b/Base/Map_Drive.vb @@ -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" + + Private Shared Function WNetAddConnection2(ByRef lpNetResource As NETRESOURCE, + ByVal lpPassword As String, + ByVal lpUsername As String, + ByVal dwFlags As Integer) As Integer + End Function + + + Private Shared Function WNetCancelConnection2(ByVal lpName As String, + ByVal dwFlags As Integer, + ByVal fForce As Boolean) As Integer + End Function + + + 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 + ''' + ''' Struktur für Netzlaufwerk-Informationen + ''' + 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 + + ''' + ''' Ermittelt den nächsten freien Laufwerksbuchstaben (alphabetisch absteigend von Z bis A) + ''' + ''' Liste der nicht erlaubten Laufwerksbuchstaben (z.B. "Y,M,V") + ''' Nächster freier Laufwerksbuchstabe mit Doppelpunkt (z.B. "Z:") oder String.Empty wenn keiner frei + 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 + + ''' + ''' Prüft ob ein Laufwerksbuchstabe verfügbar ist (nicht verwendet und nicht in Blacklist) + ''' + ''' Zu prüfender Laufwerksbuchstabe + ''' Liste der nicht erlaubten Laufwerksbuchstaben + ''' True wenn verfügbar, False wenn bereits verwendet oder in Blacklist + 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 + + ''' + ''' Mappt ein Netzlaufwerk mit automatischer Laufwerksbuchstabenwahl oder spezifischem Buchstaben + ''' + ''' Gewünschter Laufwerksbuchstabe (leer = automatisch den nächsten freien wählen) + ''' UNC-Pfad des Netzwerkshares + ''' Komma-getrennte Liste verbotener Laufwerksbuchstaben (z.B. "Y,M,V") + ''' Optionaler Benutzername für Authentifizierung + ''' Optionales Passwort für Authentifizierung + ''' Soll das Mapping persistent sein? + ''' Verwendeter Laufwerksbuchstabe bei Erfolg, String.Empty bei Fehler + 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 + ''' + ''' Interne Methode zum tatsächlichen Mappen eines Netzlaufwerks + ''' + 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 + ''' + ''' Test-Funktion um UNC-Pfad-Zugriff zu prüfen + ''' + 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 + ''' + ''' Trennt ein Netzlaufwerk mit Windows-API + ''' + 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 + + ''' + ''' Ermittelt alle gemappten Netzlaufwerke + ''' + 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 + + ''' + ''' Ermittelt den UNC-Pfad eines gemappten Laufwerks + ''' + 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 + + ''' + ''' Prüft ob ein bestimmtes Laufwerk als Netzlaufwerk gemappt ist + ''' + 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 + + ''' + ''' Gibt eine formatierte Übersicht aller gemappten Netzlaufwerke zurück + ''' + 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 + ''' + ''' Mappt ein spezifisches Laufwerk (z.B. "V") mit Blacklist-Prüfung + ''' + 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 + + ''' + ''' Mappt automatisch den nächsten freien Laufwerksbuchstaben (Z→A) + ''' + 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 diff --git a/Base/ModuleExtensions.vb b/Base/ModuleExtensions.vb index 28dba280..bbd097bf 100644 --- a/Base/ModuleExtensions.vb +++ b/Base/ModuleExtensions.vb @@ -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 ' ====================================================== diff --git a/Base/My Project/AssemblyInfo.vb b/Base/My Project/AssemblyInfo.vb index a042a5c5..de248c93 100644 --- a/Base/My Project/AssemblyInfo.vb +++ b/Base/My Project/AssemblyInfo.vb @@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices ' indem Sie "*" wie unten gezeigt eingeben: ' - - + + diff --git a/License/Licensebackup.vbproj b/License/Licensebackup.vbproj new file mode 100644 index 00000000..d4e40afb --- /dev/null +++ b/License/Licensebackup.vbproj @@ -0,0 +1,134 @@ + + + + + Debug + AnyCPU + {5EBACBFA-F11A-4BBF-8D02-91461F2293ED} + Library + DigitalData.Modules.License + DigitalData.Modules.License + 512 + Windows + net462 + + + + true + full + true + true + bin\Debug\ + DigitalData.Modules.License.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + DigitalData.Modules.License.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + On + + + Binary + + + Off + + + On + + + + + ..\packages\NLog.5.0.5\lib\net46\NLog.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LicenseSchema.xsd + + + + + True + Application.myapp + True + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + Designer + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + + {903b2d7d-3b80-4be9-8713-7447b704e1b0} + Logging + + + + + powershell.exe -command "& { &'$(SolutionDir)copy-binary.ps1' '$(TargetPath)' '$(TargetFileName)' '$(ConfigurationName)' '$(ProjectName)' }" + + \ No newline at end of file diff --git a/Modules.sln b/Modules.sln index a39b3c42..15b537e6 100644 --- a/Modules.sln +++ b/Modules.sln @@ -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 diff --git a/Windows/Windows.vbproj b/Windows/Windows.vbproj index 9631cc62..7fdb3348 100644 --- a/Windows/Windows.vbproj +++ b/Windows/Windows.vbproj @@ -44,9 +44,6 @@ On - - ..\packages\DigitalData.Modules.Logging.2.6.5\lib\net462\DigitalData.Modules.Logging.dll - M:\Bibliotheken\3rdParty\Office\Microsoft.Office.Interop.Outlook.dll @@ -149,6 +146,10 @@ {6ea0c51f-c2b1-4462-8198-3de0b32b74f8} Base + + {903b2d7d-3b80-4be9-8713-7447b704e1b0} + Logging + diff --git a/ZooFlow/Environment.vb b/ZooFlow/Environment.vb index 10ba683f..b8367b6e 100644 --- a/ZooFlow/Environment.vb +++ b/ZooFlow/Environment.vb @@ -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 diff --git a/ZooFlow/My Project/AssemblyInfo.vb b/ZooFlow/My Project/AssemblyInfo.vb index b8a183ca..3fafdade 100644 --- a/ZooFlow/My Project/AssemblyInfo.vb +++ b/ZooFlow/My Project/AssemblyInfo.vb @@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices ' übernehmen, indem Sie "*" eingeben: ' - - + + diff --git a/ZooFlow/ZooFlow.vbproj b/ZooFlow/ZooFlow.vbproj index be801c79..236e7580 100644 --- a/ZooFlow/ZooFlow.vbproj +++ b/ZooFlow/ZooFlow.vbproj @@ -44,18 +44,6 @@ On - - ..\Database\bin\Debug\DigitalData.Modules.Database.dll - - - P:\Projekte DIGITAL DATA\DIGITAL DATA - Entwicklung\DLL_Bibliotheken\Digital Data\DD_Modules\DigitalData.Modules.EDMI.API.dll - - - ..\Logging\bin\Debug\DigitalData.Modules.Logging.dll - - - ..\Windows\bin\Debug\DigitalData.Modules.Windows.dll - ..\packages\NLog.5.0.5\lib\net46\NLog.dll @@ -135,6 +123,24 @@ + + + {eaf0ea75-5fa7-485d-89c7-b2d843b03a96} + Database + + + {25017513-0d97-49d3-98d7-ba76d9b251b0} + EDMI.API + + + {903b2d7d-3b80-4be9-8713-7447b704e1b0} + Logging + + + {5efaef9b-90b9-4f05-9f70-f79ad77fff86} + Windows + + powershell.exe -command "& { &'$(SolutionDir)copy-binary.ps1' '$(TargetPath)' '$(TargetFileName)' '$(ConfigurationName)' '$(ProjectName)' }"