Screenshot Problem (Entweder Fensterschatten oder keine Titelleiste)

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

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von dive26.

    Screenshot Problem (Entweder Fensterschatten oder keine Titelleiste)

    Hallo Leute,

    ich möchte einen Screenshot meiner Form machen und diese in die Zwischenablage kopieren.
    Das geht grundsätzlich, aber nicht so sauber wie ich das gerne hätte.

    Dieser Code macht einen Screenshot der kompletten Form inkl. Fenstertitel und Rahmen, aber auch inkl. der bei Windows 10 und Windows 11 typischen Fensterschatten:

    VB.NET-Quellcode

    1. Using bmp As New Drawing.Bitmap(meForm.Size.Width, meForm.Size.Height)
    2. Using img As Drawing.Graphics = Drawing.Graphics.FromImage(bmp)
    3. img.CopyFromScreen(New Drawing.Point(meForm.Location.X, meForm.Location.Y), New Drawing.Point(0, 0), meForm.Size, CopyPixelOperation.SourceCopy)
    4. End Using
    5. Clipboard.SetImage(bmp)
    6. End Using


    Dieser Code macht nur einen Screenshot ohne Fenstertitel und ohne Rahmen:

    VB.NET-Quellcode

    1. Using bmp As New Bitmap(meForm.ClientSize.Width, meForm.ClientSize.Height)
    2. Using gr As Graphics = Graphics.FromImage(bmp)
    3. Dim hDC As IntPtr = gr.GetHdc()
    4. PrintWindow(meForm.Handle, hDC, PW_CLIENTONLY)
    5. gr.ReleaseHdc()
    6. End Using
    7. Clipboard.SetImage(bmp)
    8. End Using


    Grundsätzlich gefällt mir die zweite Variante ohne Rahmen besser.
    Ich habe auch schon herausgefunden wie ich die Fensterhöhe ermittle: Dim FensterTitelHoehe As Integer = SystemInformation.ToolWindowCaptionHeight

    Nun bin ich aber noch nicht draufgekommen, wie ich den Screenshot um die Höhe des FensterTitels weiter oben beginnen kann. Also wie ich die Position nach oben verschieben kann. Es muss ja in diesem Codeteil subtrahiert werden:

    VB.NET-Quellcode

    1. PrintWindow(meForm.Handle, hDC, PW_CLIENTONLY)


    Wie mache ich das Codetechnisch, dass ich da den Y-Point um FensterTitelHoehe verringere?

    LG Roland
    Bilder
    • mitzuvielrahmen.jpg

      354,4 kB, 1.024×617, 76 mal angesehen
    • ohnerahmen.jpg

      329,91 kB, 1.008×601, 81 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

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

    Da hab ich was für dich. Damit kannst du entweder mit oder ohne Fenster-Rahmen passgenau die Screenshot anfertigen. Mit der DWM-API recht einfach.

    Anwendung:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. NativeMethods.FormDrawToBitmap(Me, "filename")
    3. NativeMethods.FormDrawClientRectangleToBitmap(Me, "filename")
    4. End Sub


    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Public Class NativeMethods
    3. 'https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetwindowattribute
    4. ''' <summary>
    5. ''' Retrieves the current value of a specified Desktop Window Manager (DWM) attribute applied to a window. For programming guidance, and code examples, see Controlling non-client region rendering.
    6. ''' https://learn.microsoft.com/en-us/windows/desktop/dwm/composition-ovw#controlling-non-client-region-rendering
    7. ''' </summary>
    8. ''' <param name="hwnd">The handle to the window from which the attribute value is to be retrieved.</param>
    9. ''' <param name="dwAttribute">A flag describing which value to retrieve, specified as a value of the DWMWINDOWATTRIBUTE enumeration. This parameter specifies which attribute to retrieve, and the pvAttribute parameter points to an object into which the attribute value is retrieved.</param>
    10. ''' <param name="pvAttribute">A pointer to a value which, when this function returns successfully, receives the current value of the attribute. The type of the retrieved value depends on the value of the dwAttribute parameter. The DWMWINDOWATTRIBUTE enumeration topic indicates, in the row for each flag, what type of value you should pass a pointer to in the pvAttribute parameter.</param>
    11. ''' <param name="cbAttribute">The size, in bytes, of the attribute value being received via the pvAttribute parameter. The type of the retrieved value, and therefore its size in bytes, depends on the value of the dwAttribute parameter.</param>
    12. ''' <returns>Type: HRESULT, If the Function succeeds, it returns S_OK. Otherwise, it returns an HRESULT Error code.</returns>
    13. <DllImport("dwmapi.dll")>
    14. Public Shared Function DwmGetWindowAttribute(hwnd As IntPtr, dwAttribute As DWMWINDOWATTRIBUTE, <Out> ByRef pvAttribute As RECT, cbAttribute As UInteger) As UInteger
    15. End Function
    16. 'https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect
    17. ''' <summary>
    18. ''' Left: Specifies the x-coordinate of the upper-left corner of the rectangle.
    19. ''' Top: Specifies the y-coordinate of the upper-left corner of the rectangle.
    20. ''' Right: Specifies the x-coordinate of the lower-right corner of the rectangle.
    21. ''' Bottom: Specifies the y-coordinate of the lower-right corner of the rectangle.
    22. ''' </summary>
    23. Public Structure RECT
    24. Public Left As Integer
    25. Public Top As Integer
    26. Public Right As Integer
    27. Public Bottom As Integer
    28. End Structure
    29. ''' <summary>
    30. ''' https://learn.microsoft.com/en-us/windows/desktop/api/dwmapi/ne-dwmapi-dwmwindowattribute
    31. ''' </summary>
    32. <Flags>
    33. Public Enum DWMWINDOWATTRIBUTE As UInteger
    34. DWMWA_NCRENDERING_ENABLED = 1
    35. DWMWA_NCRENDERING_POLICY
    36. DWMWA_TRANSITIONS_FORCEDISABLED
    37. DWMWA_ALLOW_NCPAINT
    38. DWMWA_CAPTION_BUTTON_BOUNDS
    39. DWMWA_NONCLIENT_RTL_LAYOUT
    40. DWMWA_FORCE_ICONIC_REPRESENTATION
    41. DWMWA_FLIP3D_POLICY
    42. DWMWA_EXTENDED_FRAME_BOUNDS
    43. DWMWA_HAS_ICONIC_BITMAP
    44. DWMWA_DISALLOW_PEEK
    45. DWMWA_EXCLUDED_FROM_PEEK
    46. DWMWA_CLOAK
    47. DWMWA_CLOAKED
    48. DWMWA_FREEZE_REPRESENTATION
    49. DWMWA_PASSIVE_UPDATE_MODE
    50. DWMWA_USE_HOSTBACKDROPBRUSH
    51. DWMWA_USE_IMMERSIVE_DARK_MODE = 20
    52. DWMWA_WINDOW_CORNER_PREFERENCE = 33
    53. DWMWA_BORDER_COLOR
    54. DWMWA_CAPTION_COLOR
    55. DWMWA_TEXT_COLOR
    56. DWMWA_VISIBLE_FRAME_BORDER_THICKNESS
    57. DWMWA_SYSTEMBACKDROP_TYPE
    58. DWMWA_LAST
    59. End Enum
    60. ''' <summary>
    61. ''' Native RECT structure to System.Drawing.Rectangle structure
    62. ''' </summary>
    63. ''' <param name="rect">A RECT Structure</param>
    64. ''' <returns>A System.Drawing.Rectangle from RECT structure</returns>
    65. Public Shared Function RECT_ToRectangle(rect As RECT) As Rectangle
    66. Return New Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top)
    67. End Function
    68. ''' <summary>
    69. ''' Retrieves the extended frame bounds rectangle in screen space.
    70. ''' </summary>
    71. ''' <param name="form"></param>
    72. ''' <returns>A System.Drawing.Rectangle with the extended frame bounds rectangle in screen space</returns>
    73. Public Shared Function GetFormSize_DWMWA_EXTENDED_FRAME_BOUNDS(form As Form) As Rectangle
    74. Dim r As RECT
    75. DwmGetWindowAttribute(form.Handle, DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, r, CUInt(Marshal.SizeOf(r)))
    76. Return RECT_ToRectangle(r)
    77. End Function
    78. ''' <summary>
    79. ''' Makes a Screeshot of the Form and saves it as PNG file
    80. ''' </summary>
    81. ''' <param name="form">The form to take a screenshot of</param>
    82. ''' <param name="filename">The filename for the PNG file</param>
    83. Public Shared Sub FormDrawToBitmap(form As Form, filename As String)
    84. Dim r As Rectangle = GetFormSize_DWMWA_EXTENDED_FRAME_BOUNDS(form)
    85. Using bmp As New Bitmap(r.Width, r.Height)
    86. Using g As Graphics = Graphics.FromImage(bmp)
    87. g.CopyFromScreen(r.X, r.Y, 0, 0, bmp.Size)
    88. End Using
    89. bmp.Save(filename, Imaging.ImageFormat.Png)
    90. End Using
    91. End Sub
    92. ''' <summary>
    93. ''' Makes a Screeshot of the Form ClientRectangle and saves it as PNG file
    94. ''' </summary>
    95. ''' <param name="form">The form to take a screenshot of from the ClientRectangle</param>
    96. ''' <param name="filename">The filename for the PNG file</param>
    97. Public Shared Sub FormDrawClientRectangleToBitmap(form As Form, filename As String)
    98. Using bmp As New Bitmap(form.ClientRectangle.Width, form.ClientRectangle.Height)
    99. Using g As Graphics = Graphics.FromImage(bmp)
    100. Dim clientRectangleOnScreenLocation As Point = form.PointToScreen(form.ClientRectangle.Location)
    101. g.CopyFromScreen(clientRectangleOnScreenLocation.X, clientRectangleOnScreenLocation.Y, 0, 0, bmp.Size)
    102. End Using
    103. bmp.Save(filename, Imaging.ImageFormat.Png)
    104. End Using
    105. End Sub
    106. End Class

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BitBrösel“ ()

    dive26 schrieb:

    VB.NET-Quellcode

    1. New Drawing.Point(meForm.Location.X, meForm.Location.Y)
    machst Du meForm.Location.
    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!
    Vielen Dank lieber @BitBrösel
    Dein Code funktioniert wunderbar.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    @BitBrösel Dein Code versagt, wenn Teile der zu kopierenden Form aus dem Bildschirmbereich hereausgeschoben wurden.
    Und:
    Sieh Dier mal die Funktion Rectangle.FromLTRB(...) an.
    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!
    @RodFromGermany Ja, wenn was außerhalb des Screens ist kann das auch nicht gehen, wegen Graphics.CopyFromScreen.

    Mit Control.DrawToBitmap würde es möglicherweise gehen, aber da hat man dann den FensterRahmen in Win7 Optik. Gut dive26 fand das ohne Rahmen besser, könnte er nun auch mit Control.DrawToBitmap erst ein Bitmap anlegen, und dieses dann croppen, wenn das denn auch mit außerhalb des Screen funktioniert.

    Rectangle.FromLTRB ist mit noch nie aufgefallen. Werde ich in meine RECT_ToRectangle einsetzen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „BitBrösel“ ()

    BitBrösel schrieb:

    FensterRahmen in Win7 Optik
    Ich hab mal meine Snippets durchgeblättert, wenn da ein halber Button rausgeschoben ist, wird die Außen-Hälfte in Win-7-Optik dargestellt. Das sieht echt komisch aus.
    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!
    Ich habe mal vor ne Ewigkeit was eigenes probiert, und zwar aus der Console heraus.

    Vielleicht kannst es brauchen.

    PS. Die Rücksetzungen würde ich heute andersmachen.

    Freundliche Grüsse

    exc-jdbi
    Bilder
    • test.png

      106,76 kB, 632×545, 41 mal angesehen
    Dateien
    • ScreenShot.zip

      (11,63 kB, 31 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    Hi

    Gerade getestet. Auch rausgeschobene Fenster oder nur halb zu sehende Buttons werden hier drüber korrekt erfasst. blogs.windows.com/windowsdevel…ays-to-do-screen-capture/ Es wird das komplette Fenster, sofern nicht minimiert, erfasst und müsste dann entsprechend zugeschnitten werden.

    Setzt allerdings auch ein paar Kenntnisse in Direct3D voraus. Falls man den CapturePicker nicht benötigt und das Handle des Fensters bekannt ist, kann man auch direkt CreateForWindow -> learn.microsoft.com/en-us/wind…eminterop-createforwindow einsetzen. Ein paar Beispiele sind ja im ersten Link vorhanden und nein, es muss keine UWP-App sein um dieses zu nutzen.
    Mfg -Franky-
    @RodFromGermany, wer schiebt denn schon eine Form außerhalb des sichtbaren Bildschirmbereiches und macht dann einen Screenshot? Das ist nicht nur ungewöhnlich, sondern auch unnötig (und in meinen Augen ein Userfehler).

    Der Code von @BitBrösel macht das was ich möchte, auch wenn die Form über zwei Bildschirme verteilt ist. Siehe manuellem Screenshot.
    Ich muss aber dazusagen, dass ich auch nur Forms ohne Rahmen, aber mit Titelleiste verwende FixedDialog.

    Ich hatte vorher mit verschiedenen anderen Methoden herumprobiert. Und einige schalteten in der Tat die Windows-Rahmen auf Windows 7 Look um. Daher hatte ich diese auch gleich wieder verworfen.
    Bilder
    • IMG_20220925_223112.jpg

      2,4 MB, 3.648×2.736, 58 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    dive26 schrieb:

    wer schiebt denn schon eine Form außerhalb des sichtbaren Bildschirmbereiches und macht dann einen Screenshot?
    Einer, der ein Programm / einen Algorithmus ordentlich testet, dabei musst Du User-Fehlbedienungen in jedem Falle einbeziehen.
    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!
    @BitBrösel
    Deine Funktion hat auch noch einen weiteren Vorteil.
    Denn ich habe nicht alle Zeichenoperationen in die Paint-Routine gelegt. Somit würde bei jeder anderen Funktion die mit GDI+ gezeichneten Objekte beim ersten Screenshotversuch resettet. Daher liebe ich Deine Funktion.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at