Imports System.IO Imports Microsoft.Office.Interop Imports Microsoft.Office.Interop.Outlook Public Class ClassDragDrop Public Shared files_dropped As String() Public Shared Event FilesDroppedReady(ByVal files As String()) ''' ''' Eindeutiges Drag-Format für DocID-Transfers zwischen frmNodeNavigation-Instanzen. ''' Muss mit frmNodeNavigation.DRAGDROP_FORMAT_DOCID identisch sein. ''' Public Shared ReadOnly DRAGDROP_FORMAT_DOCID As String = "DD_RecordOrganizer_DocID" ''' ''' Zeichen, die im Dateinamen Probleme bei DB-Import und Dateihandling verursachen können. ''' Private Shared ReadOnly ProblematicChars As Char() = { "'"c, "`"c, ";"c, "%"c, "&"c, "+"c, "#"c, "~"c, "{"c, "}"c, "["c, "]"c, "^"c, "="c, "@"c } Private Shared Function CheckAndSanitizeFilename(filePath As String) As String Try Dim dir As String = Path.GetDirectoryName(filePath) Dim originalName As String = Path.GetFileNameWithoutExtension(filePath) Dim ext As String = Path.GetExtension(filePath) ' ── 1) Unicode-Normalisierung NFD → NFC ────────────────────────────── Dim normalizedName As String = originalName.Normalize(System.Text.NormalizationForm.FormC) Dim hadNormalizationIssue As Boolean = (normalizedName <> originalName) If hadNormalizationIssue Then LOGGER?.Warn($"CheckAndSanitizeFilename: NFD-Encoding erkannt in [{originalName}{ext}]") Dim normMsg As String If USER_LANGUAGE <> "de-DE" Then normMsg = $"The filename contains Unicode characters in decomposed form (NFD encoding)." & vbCrLf & vbCrLf & $"File: {originalName}{ext}" & vbCrLf & vbCrLf & "This causes garbled characters in the database (e.g. ü → Ru¨ckbau)." & vbCrLf & vbCrLf & "Please rename the file and try again." & vbCrLf & "The import of this file has been aborted." Else normMsg = $"Der Dateiname enthält nicht sichtbare Unicode-Zeichen in dekomponiierter Form (NFD-Kodierung)." & vbCrLf & vbCrLf & $"Datei: {originalName}{ext}" & vbCrLf & vbCrLf & "Dies kann zu kryptischen Zeichen in der Datenbank (z.B. ü → Ru¨ckbau) oder Problemen beim Import nach windream führen." & vbCrLf & vbCrLf & "Ursache dafür kann sein, das die Datei aus einer NICHT Windows Umgebung erstellt wurde. (MacOS, Linux etc)" & vbCrLf & "Bitte benennen Sie die Datei um und versuchen Sie es erneut." & vbCrLf & "Der Import dieser Datei wurde abgebrochen." End If Dim normCaption As String = If(USER_LANGUAGE <> "de-DE", "Invalid Filename", "Ungültiger Dateiname") MessageBox.Show(normMsg, normCaption, MessageBoxButtons.OK, MessageBoxIcon.Warning) LOGGER?.Warn($"CheckAndSanitizeFilename: Import abgebrochen wegen NFD-Problem [{originalName}{ext}].") Return Nothing End If ' ── 2) Leerzeichen-Probleme prüfen (führend, abschließend, doppelt) ── Dim hasLeadingOrTrailingSpace As Boolean = (originalName <> originalName.Trim()) Dim hasDoubleSpace As Boolean = originalName.Contains(" ") ' zwei aufeinanderfolgende Leerzeichen If hasLeadingOrTrailingSpace OrElse hasDoubleSpace Then LOGGER?.Warn($"CheckAndSanitizeFilename: Leerzeichen-Problem in [{originalName}{ext}]") Dim spaceMsg As String If USER_LANGUAGE <> "de-DE" Then spaceMsg = $"The filename contains leading, trailing or consecutive spaces:" & vbCrLf & vbCrLf & $"File: ""{originalName}{ext}""" & vbCrLf & vbCrLf & "This can cause problems during database import or file comparisons." & vbCrLf & vbCrLf & "Please rename the file (remove extra spaces) and try again." & vbCrLf & "The import of this file has been aborted." Else spaceMsg = $"Der Dateiname enthält führende, abschließende oder doppelte Leerzeichen:" & vbCrLf & vbCrLf & $"Datei: ""{originalName}{ext}""" & vbCrLf & vbCrLf & "Dies kann beim Datenbankimport oder Dateivergleichen zu Problemen führen." & vbCrLf & vbCrLf & "Bitte benennen Sie die Datei um (Leerzeichen entfernen) und versuchen Sie es erneut." & vbCrLf & "Der Import dieser Datei wurde abgebrochen." End If Dim spaceCaption As String = If(USER_LANGUAGE <> "de-DE", "Invalid Filename", "Ungültiger Dateiname") MessageBox.Show(spaceMsg, spaceCaption, MessageBoxButtons.OK, MessageBoxIcon.Warning) LOGGER?.Warn($"CheckAndSanitizeFilename: Import abgebrochen wegen Leerzeichen-Problem [{originalName}{ext}].") Return Nothing End If ' ── 3) Problematische Sonderzeichen prüfen ─────────────────────────── Dim found As New System.Text.StringBuilder() For Each c As Char In ProblematicChars If originalName.IndexOf(c) >= 0 Then If found.Length > 0 Then found.Append(" ") found.Append($"'{c}'") End If Next If found.Length = 0 Then Return filePath End If LOGGER?.Warn($"CheckAndSanitizeFilename: Problematische Zeichen in [{originalName}{ext}]: {found}") Dim msg As String If USER_LANGUAGE <> "de-DE" Then msg = $"The filename contains characters that cause problems during database import or file handling:" & vbCrLf & vbCrLf & $"File: {originalName}{ext}" & vbCrLf & $"Problematic chars: {found}" & vbCrLf & vbCrLf & "Please rename the file and try again." & vbCrLf & "The import of this file has been aborted." Else msg = $"Der Dateiname enthält nicht sichtbare Zeichen, die beim Datenbankimport oder späteren Dateihandling in windream Probleme verursachen:" & vbCrLf & vbCrLf & $"Datei: {originalName}{ext}" & vbCrLf & $"Problematische Zeichen: {found}" & vbCrLf & vbCrLf & "Ursache dafür kann sein, das die Datei aus einer NICHT Windows Umgebung erstellt wurde. (MacOS, Linux etc)" & vbCrLf & "Bitte benennen Sie die Datei um und versuchen Sie es erneut." & vbCrLf & "Der Import dieser Datei wurde abgebrochen." End If Dim caption As String = If(USER_LANGUAGE <> "de-DE", "Invalid Filename", "Ungültiger Dateiname") MessageBox.Show(msg, caption, MessageBoxButtons.OK, MessageBoxIcon.Warning) LOGGER?.Warn($"CheckAndSanitizeFilename: Import abgebrochen für [{originalName}{ext}].") Return Nothing Catch ex As System.Exception LOGGER?.Warn("CheckAndSanitizeFilename Fehler: " & ex.Message) Return filePath End Try End Function Public Shared Function Drop_File(e As DragEventArgs) Try LOGGER.Debug("In Drop_File....") ClassHelper.DELETE_PMO_FILE_USER_OPEN_FILES() files_dropped = New String() {} Dim hasOutlookUnicode As Boolean = e.Data.GetDataPresent("FileGroupDescriptorW") Dim hasOutlookAnsi As Boolean = e.Data.GetDataPresent("FileGroupDescriptor") Dim hasOutlookContents As Boolean = e.Data.GetDataPresent("FileContents") Dim hasChromiumMime As Boolean = e.Data.GetDataPresent("Chromium Web Custom MIME Data Format") Dim hasFileNameW As Boolean = e.Data.GetDataPresent("FileNameW") OrElse e.Data.GetDataPresent("FileName") Dim hasFileDrop As Boolean = e.Data.GetDataPresent(DataFormats.FileDrop) '1) Klassische Outlook-Attachments: Descriptor + Contents If (hasOutlookUnicode OrElse hasOutlookAnsi) AndAlso hasOutlookContents Then ' ... dein bestehender Descriptor/Contents-Code ... ' Return True wenn erfolgreich End If '2) ATTACHMENT oder komplette Mail aus Outlook/WebView2: KEIN Descriptor+Contents, ABER FileDrop vorhanden If hasFileDrop AndAlso (hasChromiumMime OrElse hasFileNameW) AndAlso Not hasOutlookContents Then LOGGER?.Debug("WebView2/Outlook Attachment or Mail: try FileDrop, skip Outlook COM initially") Dim ok As Boolean = HandleFileDrop(e) If ok Then Return True LOGGER?.Warn("FileDrop vorhanden, aber leer. Fallback auf Outlook COM für komplette Mail.") ScheduleOutlookComFallback() Return True End If '3) Outlook Mail (.msg): Descriptor ohne Contents ODER Chromium/WebView2 Indikatoren – nur wenn KEIN FileDrop vorhanden If Not hasFileDrop AndAlso ((hasOutlookAnsi OrElse hasOutlookUnicode) OrElse hasChromiumMime OrElse hasFileNameW) Then Try Dim oApp As Outlook.Application = Nothing Try oApp = New Outlook.Application() Catch ex As System.Exception MsgBox("Fehler beim Initialisieren der Outlook-API:" & vbNewLine & ex.Message, MsgBoxStyle.Critical) GoTo CheckFileDrop End Try Dim explorer = oApp.ActiveExplorer If explorer IsNot Nothing AndAlso explorer.Selection IsNot Nothing AndAlso explorer.Selection.Count > 0 Then LOGGER?.Debug("Drop of msg (Outlook Explorer Selection)") For i As Integer = 1 To explorer.Selection.Count Dim myobj As Object = explorer.Selection.Item(i) If myobj Is Nothing Then Continue For SaveMailItemToTemp(myobj) Next Return True Else Dim inspector = oApp.ActiveInspector If inspector IsNot Nothing AndAlso inspector.CurrentItem IsNot Nothing Then LOGGER?.Debug("Drop of msg (ActiveInspector.CurrentItem) Fallback") SaveMailItemToTemp(inspector.CurrentItem) Return True Else LOGGER?.Warn("Outlook: Keine Auswahl im Explorer und kein ActiveInspector.CurrentItem verfügbar.") If hasFileNameW Then Dim namesObj As Object = e.Data.GetData(If(e.Data.GetDataPresent("FileNameW"), "FileNameW", "FileName"), True) Dim names As String() = TryCast(namesObj, String()) If names Is Nothing Then Dim nameSingle As String = TryCast(namesObj, String) If Not String.IsNullOrWhiteSpace(nameSingle) Then LOGGER?.Warn("Vorgeschlagener Name (ohne Inhalt): " & nameSingle) End If Else LOGGER?.Warn("Vorgeschlagene Namen (ohne Inhalt): " & String.Join("; ", names)) End If End If GoTo CheckFileDrop End If End If Catch ex As System.Exception LOGGER?.Warn("Outlook MSG-Drop Fehler: " & ex.Message) End Try End If CheckFileDrop: '4) Filesystem FileDrop (klassisch ODER WebView2 delayed rendering) If hasFileDrop Then If HandleFileDrop(e) Then Return True ScheduleOutlookComFallback() Return True End If '5) SCAN-StringFormat If e.Data.GetDataPresent(DataFormats.StringFormat) Then Dim Wert As String = TryCast(e.Data.GetData(DataFormats.StringFormat), String) If Not String.IsNullOrEmpty(Wert) Then Dim idx As Integer = files_dropped.Length ReDim Preserve files_dropped(idx) files_dropped(idx) = "@SCAN@" & Wert Return True End If End If Catch ex As System.Exception MsgBox("Unexpected Error in Drop_File: " & ex.Message, MsgBoxStyle.Critical) End Try LOGGER?.Warn("Drop_File: Kein extrahierbarer Inhalt. Bitte Attachment aus der Nachrichtenliste ziehen oder zunächst speichern.") Return False End Function ' FileDrop defensiv behandeln – erst ohne, dann mit autoConvert Private Shared Function HandleFileDrop(e As DragEventArgs) As Boolean Try Dim rawObj As Object = e.Data.GetData(DataFormats.FileDrop) Dim rawFiles As String() = TryCast(rawObj, String()) If Not (rawFiles Is Nothing OrElse rawFiles.Length = 0) Then For Each f In rawFiles LOGGER?.Info("FileDrop (raw) - File: " & f) AppendDroppedFile("@DROPFROMFSYSTEM@", f) Next Return True End If Dim convObj As Object = e.Data.GetData(DataFormats.FileDrop, True) Dim convFiles As String() = TryCast(convObj, String()) If Not (convFiles Is Nothing OrElse convFiles.Length = 0) Then For Each f In convFiles LOGGER?.Info("FileDrop (autoConvert) - File: " & f) AppendDroppedFile("@DROPFROMFSYSTEM@", f) Next Return True End If LOGGER?.Warn("FileDrop vorhanden, aber keine Dateien (raw/autoConvert leer).") Return False Catch ex As System.Exception LOGGER?.Warn("HandleFileDrop Fehler: " & ex.Message) Return False End Try End Function ''' ''' Hängt eine Datei an files_dropped an – mit vorheriger Sonderzeichen-Prüfung. ''' Gibt False zurück wenn der Dateiname ungültig ist. ''' Private Shared Function AppendDroppedFile(prefix As String, filePath As String) As Boolean Dim checkedPath As String = CheckAndSanitizeFilename(filePath) If checkedPath Is Nothing Then Return False End If Dim idx As Integer = files_dropped.Length ReDim Preserve files_dropped(idx) files_dropped(idx) = prefix & checkedPath Return True End Function Private Shared Sub SaveMailItemToTemp(ByVal mailObj As Object) Dim subj As String = "" Try subj = mailObj.Subject Catch subj = "NO_SUBJECT" End Try If String.IsNullOrWhiteSpace(subj) Then subj = "NO_SUBJECT" Dim safeName = subj.Replace("\", "-").Replace("/", "-").Replace(":", "") _ .Replace("?", "").Replace("!", "").Replace("%", "").Replace("$", "") Dim strFile As String = IO.Path.Combine(Path.GetTempPath(), safeName & ".msg") LOGGER?.Info("Drop of msg - File:" & strFile) Try mailObj.SaveAs(strFile) AppendDroppedFile("@OUTLOOK_MESSAGE@", strFile) Catch ex As System.Exception MsgBox("Error in Save Email2Tempfile" & vbNewLine & ex.Message, MsgBoxStyle.Critical) End Try End Sub Private Shared Sub ScheduleOutlookComFallback() Try Dim t As New Threading.Thread( Sub() Try Threading.Thread.Sleep(200) Dim maxRetries As Integer = 10 Dim saved As Boolean = False For attempt As Integer = 1 To maxRetries If TrySaveSelectedMailViaOutlook() Then LOGGER?.Info("Outlook COM Fallback: Mail gespeichert. Versuch " & attempt) saved = True Exit For End If LOGGER?.Debug("Outlook COM Fallback: Keine Auswahl, Retry " & attempt) Threading.Thread.Sleep(200) Next If saved Then Dim uiForm = If(System.Windows.Forms.Application.OpenForms.Count > 0, System.Windows.Forms.Application.OpenForms(0), Nothing) If uiForm IsNot Nothing Then uiForm.BeginInvoke( Sub() Try RaiseEvent FilesDroppedReady(files_dropped) Catch ex2 As System.Exception LOGGER?.Warn("FilesDroppedReady Invoke Fehler: " & ex2.Message) End Try End Sub) Else RaiseEvent FilesDroppedReady(files_dropped) End If Else LOGGER?.Warn("Outlook COM Fallback: Nach Retries keine Mail gespeichert.") End If Catch ex As System.Exception LOGGER?.Warn("Outlook COM Fallback Thread Fehler: " & ex.Message) End Try End Sub ) t.IsBackground = True t.SetApartmentState(Threading.ApartmentState.STA) t.Start() Catch ex As System.Exception LOGGER?.Warn("ScheduleOutlookComFallback Fehler: " & ex.Message) End Try End Sub Private Shared Function TrySaveSelectedMailViaOutlook() As Boolean Try Dim oApp As Outlook.Application = Nothing Try oApp = New Outlook.Application() Catch ex As System.Exception LOGGER?.Warn("Outlook COM Init fehlgeschlagen: " & ex.Message) Return False End Try Dim savedAny As Boolean = False Dim inspector = oApp.ActiveInspector If inspector IsNot Nothing AndAlso inspector.CurrentItem IsNot Nothing Then LOGGER?.Debug("Fallback: ActiveInspector.CurrentItem speichern") SaveMailItemToTemp(inspector.CurrentItem) savedAny = True End If If Not savedAny Then Dim explorer = oApp.ActiveExplorer If explorer IsNot Nothing AndAlso explorer.Selection IsNot Nothing AndAlso explorer.Selection.Count > 0 Then LOGGER?.Debug("Fallback: Explorer.Selection speichern") For i As Integer = 1 To explorer.Selection.Count Dim myobj As Object = explorer.Selection.Item(i) If myobj Is Nothing Then Continue For SaveMailItemToTemp(myobj) savedAny = True Next End If End If Return savedAny Catch ex As System.Exception LOGGER?.Warn("TrySaveSelectedMailViaOutlook Fehler: " & ex.Message) Return False End Try End Function Public Shared Sub Drag_enter(e As DragEventArgs) Try My.Settings.WD_INDEXDOKART_SAVE = "" ' ✅ DocID-Format hat Vorrang – Early Exit If e.Data.GetDataPresent(DRAGDROP_FORMAT_DOCID) Then e.Effect = DragDropEffects.Copy LOGGER?.Debug("DragEnter ... DocID-Format erkannt (Inter-Instance Transfer)") Return End If Dim hasOutlookUnicode As Boolean = e.Data.GetDataPresent("FileGroupDescriptorW") Dim hasOutlookAnsi As Boolean = e.Data.GetDataPresent("FileGroupDescriptor") Dim hasOutlookDescriptor As Boolean = hasOutlookUnicode OrElse hasOutlookAnsi Dim hasChromiumMime As Boolean = e.Data.GetDataPresent("Chromium Web Custom MIME Data Format") Dim hasFileNameW As Boolean = e.Data.GetDataPresent("FileNameW") OrElse e.Data.GetDataPresent("FileName") Dim hasOutlookLike As Boolean = hasOutlookDescriptor OrElse hasChromiumMime OrElse hasFileNameW Dim hasFileDrop As Boolean = e.Data.GetDataPresent(DataFormats.FileDrop) If hasOutlookLike Then e.Effect = DragDropEffects.Copy LOGGER?.Debug("DragEnter ... Outlook/WebView2 erkannt (Descriptor/Chromium/FileNameW)") ElseIf hasFileDrop Then e.Effect = DragDropEffects.Copy LOGGER?.Debug("DragEnter ... SimpleFileDrop") Else e.Effect = DragDropEffects.None LOGGER?.Debug("DragEnter ... Other FileFormat") End If LOGGER?.Debug("DragEnter Formats: " & String.Join(", ", e.Data.GetFormats())) Catch ex As System.Exception End Try End Sub End Class