Dateivorschau per Interface IPreviewHandler

    • VB.NET
    • .NET (FX) 4.5–4.8

    Es gibt 55 Antworten in diesem Thema. Der letzte Beitrag () ist von -Franky-.

      Hi @-Franky-,

      so ein Dusel - Papierkorb von NAS noch nicht geleert :D

      Hier - als Anlage - ein paar Bilder, welche den IPreview (und mein Programm) killen, wenn ca 20 nacheinander an den IPreview übergeben werden.
      Aber mach Dir nicht zuviel Umstände - wer benutzt heute schon noch die Cliparts vom Office 98?
      Im Win-Explorer werden diese übrigens ohne Vorschaubild, also mit leeren Preview gezeigt.
      Der IRFan kann diese Bilder aber öffnen und anzeigen.
      Auch Paintshop lädt und zeigt diese Bilder an.

      Schönes Rest-Weekend wünsche :)
      Dateien
      P.S.
      Zitat:
      "WMF clipart files are NOT Windows wmf files. They are Aldus Placeable
      Metafiles. Aldus Placeable Metafiles are composed of a Windows wmf file
      with a 20 byte header. The header basically gives the file a suggested
      size.
      ...
      The wmf file format is a sequence of records describing how to draw
      an image. Each record converts into a specific Windows function call.
      Therefore a wmf file is actually a program.
      These are the Windows functions that an attacker can craft in a wmf file."


      Vielleicht liest der Win-Explorer den Header vor dem IPreview aus - und wenn dieser 20Bytes gross ist, wird das WMF nicht an die IPreview übergeben.
      @Dideldum Probiere mal ein paar kleine Änderungen. Zum einen sind die Funktionen IsAlphaBitmap und GetAlphaBitmapFrom... entfallen. Dafür gibt es was neues.
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Private Function GetBitmapFromHBitmap(nativeHBitmap As IntPtr) As Bitmap
      2. Dim bmp As Bitmap = Bitmap.FromHbitmap(nativeHBitmap)
      3. If Bitmap.GetPixelFormatSize(bmp.PixelFormat) = 32 Then
      4. Dim bmp2 As New Bitmap(GetAlphaBitmap(bmp))
      5. bmp.Dispose()
      6. Return bmp2
      7. End If
      8. Return bmp
      9. End Function
      10. Private Function GetAlphaBitmap(bmpIn As Bitmap) As Bitmap
      11. Dim bmpOut As Bitmap
      12. Dim bmpInData As BitmapData = bmpIn.LockBits(New Rectangle(0, 0, bmpIn.Width, bmpIn.Height), ImageLockMode.ReadOnly, bmpIn.PixelFormat)
      13. bmpOut = New Bitmap(bmpIn.Width, bmpIn.Height, bmpInData.Stride, PixelFormat.Format32bppArgb, bmpInData.Scan0)
      14. bmpIn.UnlockBits(bmpInData)
      15. Return bmpOut
      16. End Function


      Zum anderen könntest auch unter " ' Optional falls die PreviewHandler fehlschlagen!" folgendes machen
      Spoiler anzeigen

      VB.NET-Quellcode

      1. ' Optional falls die PreviewHandler fehlschlagen!
      2. If Not bolRet Then
      3. If m_PreviewFile.ToLower.EndsWith(".wmf") Then
      4. Dim bmp As New Bitmap(m_PreviewFile)
      5. If bmp IsNot Nothing Then
      6. If m_PreviewWindow.BackgroundImage IsNot Nothing Then m_PreviewWindow.BackgroundImage.Dispose()
      7. m_PreviewWindow.BackgroundImageLayout = ImageLayout.Center
      8. m_PreviewWindow.BackgroundImage = bmp
      9. bolRet = True
      10. End If
      11. Else
      12. Dim pIShellItemImageFactory As IntPtr
      13. If SHCreateItemFromParsingName(m_PreviewFile, IntPtr.Zero,
      14. New Guid(IID_IShellItemImageFactory),
      15. pIShellItemImageFactory) = S_OK Then
      16. Dim oIShellItemImageFactory As Object = Marshal.GetObjectForIUnknown(pIShellItemImageFactory)
      17. If oIShellItemImageFactory IsNot Nothing Then
      18. Dim hBitmap As IntPtr
      19. If CType(oIShellItemImageFactory, IShellItemImageFactory).GetImage(New Size(Math.Min(PrevSize.Width, PrevSize.Height),
      20. Math.Min(PrevSize.Width, PrevSize.Height)),
      21. SIIGBF.SIIGBF_RESIZETOFIT, hBitmap) = S_OK Then
      22. If hBitmap <> IntPtr.Zero Then
      23. If m_PreviewWindow.BackgroundImage IsNot Nothing Then m_PreviewWindow.BackgroundImage.Dispose()
      24. m_PreviewWindow.BackgroundImageLayout = ImageLayout.Center
      25. m_PreviewWindow.BackgroundImage = GetBitmapFromHBitmap(hBitmap)
      26. DeleteObject(hBitmap)
      27. bolRet = True
      28. End If
      29. End If
      30. Marshal.ReleaseComObject(oIShellItemImageFactory)
      31. End If
      32. Marshal.Release(pIShellItemImageFactory)
      33. End If
      34. End If
      35. End If


      Damit werden Dir zumindest Deine WMF, die nicht wollen, direkt geladen und auch als Bild angezeigt.
      Mfg -Franky-
      Wie cool ist das denn? 8o

      Viiiiielen Dank @-Franky- - Das ist hypersuperoberklasse! :thumbsup:
      So funktioniert es superklasse - auch mit den uralt WMFs.

      Kann man diese WMF bzw. alle Bilder denn auch Formfüllend anzeigen lassen?
      Diese werden im Gegensatz zu anderen Formaten wie z.B. Docs nicht füllend, sondern nur in deren vorgegebener Grösse angezeigt.

      Edit:
      Ich verwende schon:

      VB.NET-Quellcode

      1. Private Sub Resize_WindowsViewer()
      2. If tsc_WindowsViewer.Visible = True Then
      3. tsc_WindowsViewer_PictureBox.Size = tsc_WindowsViewer.ContentPanel.Size
      4. Try
      5. Preview.ResizePreview(tsc_WindowsViewer_PictureBox.Size)
      6. Catch ex As System.Exception
      7. Dim form_fehler As New ICE_Eingabe
      8. form_fehler.Initialisiere(False, My.Resources.ICE_Common_ICEFehler, String.Format(My.Resources.ICE_Main_WindowsPreview_Fehler, ex.Message), MessageBoxButtons.OK, 8)
      9. End Try
      10. End If
      11. End Sub

      Aber das wirkt bei Bildchen nicht, da die WMFs m.W. im Header eine Grösse vorgaben und die anderen Bilder auch klare Dimensionen haben.

      Ich durchblicke zwar nicht wirklich, was Du gemacht hast - aber es funzt perfekt! :D

      Besten Merci! ^^

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Dideldum“ ()

      @-Franky-,
      jo, da hast schon recht - ich würde das daher gerne umschaltbar machen - wie in meinem Pic-Editor.

      1. Originalgrösse
      2. Originalgrösse (zentrieren)
      3. Anzeigegrösse (Verhältnis beibehalten)
      4. Anzeigegrösse (Verhältnis ignorieren)

      Aber mit dem Preview.ResizePreview aus meinem vorherigen Script funzt es nicht bei Bildern, nur bei anderen Formaten.
      Weisst Du, wo ich da Hand anlegen könnte?
      Hi. Preview.ResizePreview funktioniert nicht bei allen Dateiformaten. Steht auch so als Kommentar im Code. Alternativ musst Du halt eine Weiche für Bilder einbauen damit Bilder nicht über den IPreviewHandler geladen werden. Bilder kannst ja ganz normal mit .NET Mitteln laden und passend skalieren. Allerdings verwendet VB.NET noch GDI+ für Bilder und da sind die Codecs fest vorgegeben. Also nicht erweiterbar. zB kannst Du per GDI+ keine HEIC/HEIF Bilder laden. Wenn dann würde ich auf die Windows Imaging Component (WIC) für Bilder wechseln. WIC kann von Haus aus mehr Bildformate lesen/schreiben und ist durch zusätzliche WIC-Codecs erweiterbar.
      Mfg -Franky-
      Hi @-Franky-

      danke Dir - ja, so habe ich das auch gelöst.
      Die User können bei unverschlüsselt gespeicherten Bildern wählen, ob sie diese im IPreview oder dem Pic-Editor angezeigt bekommen wollen.
      Und im Pic-Editor können die ja auch das Anzeigeformat auswählen.

      Zu dem WIC muss ich noch recherchieren - aber ich denke, mit der regulären PicBox reichen die üblichen Bildformate auch aus.

      Vielen Dank für Deine Hilfe :)

      Dideldum schrieb:

      Zu dem WIC muss ich noch recherchieren

      Vllt. mal nur so im Hinterkopf behalten falls Du das mal irgendwann benötigst. Auf meinem PC (Win10) kann WIC folgende Bildformate lesen:
      BMP Decoder: *.bmp;*.dib;*.rle
      GIF Decoder: *.gif
      ICO Decoder: *.ico;*.icon
      CUR Decoder: *.cur
      JPEG Decoder: *.jpeg;*.jpe;*.jpg;*.jfif;*.exif
      PNG Decoder: *.png
      TIFF Decoder: *.tiff;*.tif
      DNG Decoder: *.dng
      WMPhoto Decoder: *.wdp;*.jxr
      DDS Decoder: *.dds
      Microsoft HEIF Decoder: *.heic;*.heif;*.hif;*.avci;*.heics;*.heifs;*.avcs;*.avif;*.avifs
      Microsoft Webp Decoder: *.webp
      Microsoft Raw Image Decoder: *.3fr;*.ari;*.arw;*.bay;*.cap;*.cr2;*.cr3;*.crw;*.dcs;*.dcr;*.drf;*.eip;*.erf;*.fff;*.iiq;*.k25;*.kdc;*.mef;*.mos;*.mrw;*.nef;*.nrw;*.orf;*.ori;*.pef;*.ptx;*.pxn;*.raf;*.raw;*.rw2;*.rwl;*.sr2;*.srf;*.srw;*.x3f;*.dng
      Microsoft Camera Raw Decoder: *.arw;*.cr2;*.crw;*.erf;*.kdc;*.mrw;*.nef;*.nrw;*.orf;*.pef;*.raf;*.raw;*.rw2;*.rwl;*.sr2;*.srw;*.dng

      Und folgende schreiben:
      BMP Encoder: *.bmp;*.dib;*.rle
      GIF Encoder: *.gif
      JPEG Encoder: *.jpeg;*.jpe;*.jpg;*.jfif;*.exif
      PNG Encoder: *.png
      TIFF Encoder: *.tiff;*.tif
      WMPhoto Encoder: *.wdp;*.jxr
      DDS Encoder: *.dds
      Microsoft HEIF Encoder: *.heic;*.heif;*.hif
      Mfg -Franky-
      Hi @-Franky-

      Danke Dir extremst für Deine Tips und Hilfe :)

      Güte, Güte - die Raw-Formate kenne ich alle nicht.
      Zu den Heif- und anderen Formaten muss ich auch erst mal meinen Browser ausquetschen, ob der was brauchbares dazu ausspuckt. :D
      Habe diesen Thread auf alle Fälle im ToDo geparkt - für spätere Updates. ;)

      Ich finde im Web keine Infos, wie ich die Schaltfläche im IPreview, die im Bild oben angezeigt ist ("Einige Bilder wurden geblockt...") so unterstützen kann, damit beim Anklickern die fehlenden Bilder in der HTML-IPreview Vorschau geladen werden. ;(
      Irgendwie müsste das doch zu machen sein...
      Hast Du vielleicht auch dazu eine Idee?

      Beste Grüsse
      Bilder
      • IPreview.jpg

        32,81 kB, 1.213×105, 33 mal angesehen

      Dideldum schrieb:

      Ich finde im Web keine Infos, wie ich die Schaltfläche im IPreview,....
      Gute Frage. Du könntest das mal im Explorer in der Preview testen ob das dort funktioniert. Wenn es dort auch nicht funktioniert, dann geht das mit dem Code auch nicht.

      Das Heic/Heif Format ist dem Jpeg Format sehr ähnlich. Ist also auch ein verlustbehaftetes Verfahren. Die Bildqualität ist aber besser als bei Jpeg und die Dateigröße dabei viel kleiner.
      Mfg -Franky-
      Hi @-Franky-,

      Danke Dir :)

      Für das IPreview habe ich im englischsprachigen Web eine Erklärung gefunden, die sich eigentlich auch ergibt, wenn man den angezeigten Satz ganz genau liest und versteht (das hatte ich nicht) :D
      "Einige Bilder wurden blockiert, damit der Absender Ihren Computer nicht identifizieren kann. Öffnen Sie diese Element, um die Bilder anzuzeigen"
      Der Satzsinn ging wohl mit der Translation stiften.
      Ich habe "diese Element" (als Plural "diese Elemente") als die nicht angezeigten Bilder interpretiert - dabei ist damit die gesamte Datei (im Singular "dieses Element") gemeint.
      Und "Öffnen" bezieht sich auf eine Dritt-Anwendung, in welcher das HTML-Objekt geöffnet werden soll.
      Das IPreview unterbindet bei HTML immer die eingebundenen Bilder.

      EDIT: Das ist auch gar keine Schaltfläche, auf der die Info ausgegeben wird, sondern nur eine Textbox, in welcher der Inhalt bei MouseOver gehighlightet wird.

      Na, ist ja auch kein wirkliches Problem - in meiner Anwendung kann der Benutzer wählen, ob er die HTML-Dokumente im IPreview oder im CeFSharp (mit Bildern) angezeigt bekomen möchte.

      Ah, mit Bild-Formaten bin ich nicht wirklich innig.
      Bislang haben mir immer die Standard-".bmp", ".dib", ".emf", ".gif", ".ico", ".jpe", ".jpg", ".jpeg", ".pcd", ".png", ".tif", ".tiff", ".wmf"-Endung ausgereicht.
      Wofür braucht man denn soviel weitere Formate?

      Einen schönen Papa-Tag @all

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Dideldum“ ()

      Dideldum schrieb:

      Wofür braucht man denn soviel weitere Formate?

      Die ganzen RAW Formate kommen hauptsächlich bei Digitalkameras vor (CR2/3 sind zB von Canon) wo das aufgenommene Bild später noch bearbeitet werden soll und von vornherein keine Verfälschungen, zB durch Komprimierung wie bei Jpeg, der Bilddaten geben soll. In RAW Formaten sind auch viel mehr Informationen gespeichert als nur die reinen Pixel des Bildes und sind daher auch sehr groß. Da jeder Hersteller sein eigenes Süppchen bei RAW kocht, gibt es auch soviele unterschiedliche Formate. Windows selbst hat ja auch so einige Audio-, Video- und Bildformate hevorgebracht die sich irgendwie nicht durchgesetzt haben. Ich wüsste zb nicht wo WMPhoto noch oder überhaupt zum Einsatz kommt.
      Mfg -Franky-

      Dideldum schrieb:

      Wofür braucht man denn soviel weitere Formate?
      Das spiegelt auch eine historische Entwicklung wider.
      Am Anfang gab es nur Bitmap, Komprimierung gab es noch nicht.
      Die Komprimierung ging dann zwei Wege, verlustbehaftet (JPG) und verlustfrei (PNG);
      in der Mikroskopie wurde das Tiff-Format präferiert, weil da die weitere Bilder und viele Aufnahme-Informationen zusammen gepackt werden konnten;
      usw.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Hi Ihr Profis,

      ich versuche verdreifelt, irgendwie einen Mausklick in das Preview-Control auszuwerten.
      Aber weder das umgebende Toolstripcontrol, noch die Picturebox der Preview-Klasse wollen mitspielen.
      Hintergrund:
      Ich muss eine Variable setzen, sobald der User in das Preview-Fenster klickert.

      Kann mir da vielleicht jemand helfen?

      Beste Grüsse :)
      Hi. Du könntest versuchen ob Du per Marshal.QueryInterface vom IPreviewHandler ein Interface IOleWindow erstellen kannst. Das müsste man mal probieren. Wenn das geht, dann könntest Du per IOleWindow.GetWindow ein FensterHandle -> hWnd erhalten und man könnte das Fenster subclassen und auf entsprechende Messages reagieren. Das eigentliche Problem ist, in der MS-Doku ist nicht immer beschrieben welche Interfaces man noch per QueryInterface von einem Interface erstellen kann. Da hilft nur ausprobieren.

      Edit: Jau, Du kannst ein Interface IOleWindow vom Interface IPreviewHandler erstellen. Und zwar direckt hinter DoPreview.
      Spoiler anzeigen

      VB.NET-Quellcode

      1. If CType(m_IPreviewHandler, IPreviewHandler).DoPreview = S_OK Then
      2. Dim pIOleWindow As IntPtr
      3. Marshal.QueryInterface(pIPreviewHandler, New Guid(IID_IOleWindow), pIOleWindow)
      4. If pIOleWindow <> IntPtr.Zero Then
      5. CType(Marshal.GetObjectForIUnknown(pIOleWindow), IOleWindow).GetWindow(m_PreviewHandlerHwnd)
      6. Marshal.Release(pIOleWindow)
      7. End If
      8. bolRet = True
      9. End If


      m_PreviewHandlerHwnd ist eine neue Variable und enthält dann das FensterHandle vom IPreviewHandler. Dieses FensterHandle kannst Du dann in einem klassischem Subclassing per API nutzen. Hier müsstest dann wohl auf die Messages WM_LButton(Down/Up) reagieren wenn Du die linke Maustaste auswerten möchtest.

      Edit2: Eventuell kommt beim Subclassing die Message WM_PARENTNOTIFY. In LoWord(wParam) dann entsprechend WM_LBUTTONDOWN. Wenn das so stimmt, dann könntest eventuell auch die PictureBox mit .NET-Mitteln subclassen und hier schauen welche Messages ankommen. WM_PARENTNOTIFY sollte eigentlich den Parent, in dem Fall auf dem der Preview erstellt wird, informieren.

      Edit3: IOleWindow.GetWindow gibt mir das gleiche FensterHandle zurück, das auch die PictureBox besitzt. Von daher sollte es reichen wenn Du die PictureBox subclasst. Da könntest Dir eine eigene PictureBox Klasse erstellen die von PictureBox erbt und hier über Protected Overrides Sub WndProc(ByRef m As Message) -> m auswertest. Im Prinzip kannst Du es Dir aussuchen wie Du vorgehen möchtest. IOleWindow.GetWindow -> klassisches Subclassing per API, Subclassing einer eigenen PictureBox-Klasse per Protected Overrides Sub WndProc oder alternativ klassisches Subclassing der vorhhandenen PictureBox per API. Als APIs könntest Du SetWindowSubclass/RemoveWindowSubclass einsetzen.

      Edit4: Da mich das nun auch interessiert hat, hier mein Ansatz wie man es per klassischem Subclassing machen könnte. Zunächst brauchst Du 3 weitere APIs.
      Spoiler anzeigen

      VB.NET-Quellcode

      1. <DllImport("Comctl32.dll", EntryPoint:="SetWindowSubclass")>
      2. Private Shared Function SetWindowSubclass(<[In]> hWnd As IntPtr,
      3. <[In]> pfnSubclass As IntPtr,
      4. <[In]> uIdSubclass As UIntPtr,
      5. <[In]> dwRefData As UIntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
      6. End Function
      7. <DllImport("Comctl32.dll", EntryPoint:="RemoveWindowSubclass")>
      8. Private Shared Function RemoveWindowSubclass(<[In]> hWnd As IntPtr,
      9. <[In]> pfnSubclass As IntPtr,
      10. <[In]> uIdSubclass As UIntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
      11. End Function
      12. <DllImport("Comctl32.dll", EntryPoint:="DefSubclassProc")>
      13. <PreserveSig> Private Shared Function DefSubclassProc(<[In]> hWnd As IntPtr,
      14. <[In]> uMsg As Integer,
      15. <[In]> wParam As Integer,
      16. <[In]> lParam As Integer) As Integer
      17. End Function


      Entsprechend benötigst Du die passende Subclassproc für die APIs und zwei zusätzliche Funktionen zum aufsplitten der w/lParam in Hi/LoWord. Für letzters könnte man auch Structure verwenden.
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Private ReadOnly SubclassprocDelegateProc As New SubclassprocDelegate(AddressOf Subclassproc)
      2. Private Delegate Function SubclassprocDelegate(<[In]> hwnd As IntPtr,
      3. <[In]> uMsg As Integer,
      4. <[In]> wParam As Integer,
      5. <[In]> lParam As Integer,
      6. <[In]> uIdSubclass As UIntPtr,
      7. <[In]> dwRefData As UIntPtr) As Integer
      8. Private Function Subclassproc(hwnd As IntPtr,
      9. uMsg As Integer,
      10. wParam As Integer,
      11. lParam As Integer,
      12. uIdSubclass As UIntPtr,
      13. dwRefData As UIntPtr) As Integer
      14. Const WM_PARENTNOTIFY As Integer = &H210
      15. Const WM_LBUTTONDOWN As Integer = &H201
      16. Const WM_RBUTTONDOWN As Integer = &H204
      17. Const WM_MBUTTONDOWN As Integer = &H207
      18. Const WM_XBUTTONDOWN As Integer = &H20B
      19. Select Case uMsg
      20. Case WM_PARENTNOTIFY
      21. Select Case LoWord(wParam)
      22. Case WM_LBUTTONDOWN
      23. Debug.Print("LeftMouseButtonDown X = " & LoWord(lParam).ToString & " Y = " & HiWord(lParam).ToString)
      24. Case WM_MBUTTONDOWN
      25. Debug.Print("MiddleMouseButtonDown X = " & LoWord(lParam).ToString & " Y = " & HiWord(lParam).ToString)
      26. Case WM_RBUTTONDOWN
      27. Debug.Print("RightMouseButtonDown X = " & LoWord(lParam).ToString & " Y = " & HiWord(lParam).ToString)
      28. Case WM_XBUTTONDOWN
      29. Debug.Print("XMouseButtonDown XButton = " & HiWord(wParam).ToString & " X = " & LoWord(lParam).ToString & " Y = " & HiWord(lParam).ToString)
      30. End Select
      31. End Select
      32. Return DefSubclassProc(hwnd, uMsg, wParam, lParam)
      33. End Function
      34. Private Function HiWord(value As Integer) As Integer
      35. Return (value >> 16) And &HFFFF
      36. End Function
      37. Private Function LoWord(value As Integer) As Integer
      38. Return value And &HFFFF
      39. End Function


      Um das ganze Subclassing dann zu starten

      VB.NET-Quellcode

      1. If CType(m_IPreviewHandler, IPreviewHandler).DoPreview = S_OK Then
      2. SetWindowSubclass(m_PreviewWindow.Handle, Marshal.GetFunctionPointerForDelegate(SubclassprocDelegateProc), UIntPtr.Zero, UIntPtr.Zero)

      und zu stoppen in der Function DeletePreview

      VB.NET-Quellcode

      1. If m_IPreviewHandler IsNot Nothing Then
      2. RemoveWindowSubclass(m_PreviewWindow.Handle, Marshal.GetFunctionPointerForDelegate(SubclassprocDelegateProc), UIntPtr.Zero)

      Als Ergebnis solltest Du dann folgendes erhalten. Jenachdem welche Maustaste gedrückt wurde und Position der Maus.
      LeftMouseButtonDown X = 588 Y = 389
      MiddleMouseButtonDown X = 588 Y = 389
      RightMouseButtonDown X = 695 Y = 416
      XMouseButtonDown XButton = 1 X = 587 Y = 390
      XMouseButtonDown XButton = 2 X = 588 Y = 389

      Natürlich lassen sich hier auch andere WM_-Messages auswerten.
      Mfg -Franky-

      Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „-Franky-“ ()