Screenshots und Windows 7 mit DirectX

Afritus

Mitglied
Jo danke, dein Tipp war gut ;)
Aber jetzt kommt folgender Fehler, den man am angehängten Bild sieht.
Btw: Wenn ich ingame die DRUCK-Taste drücke und dann in Paint einfüge, dann klappt das einwandfrei (auch bei Windows 7). Kann man eventuell diese Aktion mit ein paar Zeilen Code definieren? Einziger Nachteil wäre halt, dass ein Bild mit 1920x1080 einen kurzes Ruckeln beim Drücken der DRUCK-Taste verursacht.

MfG
 

Anhänge

  • Unbenannt.PNG
    Unbenannt.PNG
    23,8 KB · Aufrufe: 101

WorldRacer

Erfahrenes Mitglied
Visual Basic:
' Sende Drück-Tastendruck
SendKeys.SendWait("{PRTSC}")
' Hole Bild aus Zwischenablage
Dim img As Image = System.Windows.Forms.Clipboard.GetImage()

' Wenn in Zwischenablage ein Bild existiert, weise es der PictureBox zu.
' img.Save(Dateiname, System.Drawing.Imaging.ImageFormat.Jpeg); speichert das Bild.
If img IsNot Nothing Then
  pbImage.Image = img
End If

//edit: sorry, falschen text kopiert.
 

Afritus

Mitglied
So macht er auf einmal wieder nur Screenshots vom Desktop. Aber wenn ich die Drucktaste ingame drücke, dann bleibt das Spiel 2 Sekunden lang stehen, weshalb das sowieso keine gute Lösung ist.
Langsam bin ich echt am verzweifeln. Ist es tatsächlich unmöglich, mit ein paar Zeilen einen simplen Screenshot aus einer Vollbildanwendung hinzubekommen?

EDIT: Habe nun diesen Code hier:

Code:
Const KEYEVENTF_KEYUP = &H2
        Const VK_MENU = &H12
        Const VK_SNAPSHOT = &H2C


        keybd_event(VK_MENU, 0, 0, 0) ''ALT-Taste 
        keybd_event(VK_SNAPSHOT, 0, 0, 0) ''Druck-Taste 
        keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0)

        Dim img As Image = System.Windows.Forms.Clipboard.GetImage()
        If img IsNot Nothing Then
            img.Save(My.Computer.FileSystem.SpecialDirectories.Desktop & "/" & Format$(Now, "dd-MM-yy hh-mm-ss") & ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg)
        End If

Der funktioniert sogar und macht von allen Spielen auch Ingame-Screenshots. Nur ist die Qualität der erstellten Bilder anscheinend so hoch, dass das Spiel während der Erstellung des Screenshots 2 Sekunden lang ruckelt. Kann man nun Sceenshots mit ALT+DRUCK erstellen und gleichzeitig die Qualität runterschrauben?

MfG
 
Zuletzt bearbeitet:

Afritus

Mitglied
Habe mir nun das kostenlose Screenshot-Programm "Fraps" runtergeladen, das macht ruckelfrei schöne Screenshots von allen Spielen. Also muss es doch möglich sein, aber anscheinend nur mit DirectX / DirectDraw.
Nun habe ich im Internet geschaut und da finde ich DirectDraw-Tutorials nur für VB5/6. Für VB.NET 2008 ist da nichts dabei. Weiß denn wirklich keiner, wie man mit VB.NET 2008 über DirectX Screenshots erstellen kann?
Bitte, es ist extrem wichtig.
MfG
Afritus

Vorweg gibt es dieses Thema von mir bereits unter ".NET Cafe", aber anscheinend war das das falsche Forum. Bitte entschuldigt das Doppelthema...

Nun zum Problem: Das Aufnehmen von Screenshots hardwarebeschleunigter Spiele (z.B. Call of Duty, Counter-Strike) mit einem einfachen Code ist bei Windows 7 nicht mehr möglich (bei XP und Vista klappts noch). Nach vielem Suchen im Internet bin ich draufgekommen, dass es doch eine Möglichkeit gibt (hat mir das kostenlose Programme "FRAPS" bewiesen), und zwar über DirectX / DirectDraw. Nun habe ich im Internet nach DirectDraw-Tutorials gesucht, aber leider nur Tutorials für VB5/6 gefunden.
Also, wie kann ich mit VB.NET Screenshots über DirectX erstellen?
Wäre euch für eine hilfreiche Antwort extrem dankbar.

MfG
Markus
 

Afritus

Mitglied
Jo, vielen Dank. Hoffe halt, dass mir jemand antworten kann, da es wirklich sehr wichtig ist.

EDIT: Also nochmal, so weit bin ich bis jetzt gekommen (habe auch SlimDX installiert):

Code:
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Data
Imports System.Linq
Imports System.Windows.Forms

Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim prcs As System.Diagnostics.Process() = System.Diagnostics.Process.GetProcessesByName("notepad")
        For Each prc As System.Diagnostics.Process In prcs
            Dim screenshot As Bitmap = Spazzarama.ScreenCapture.Direct3DCapture.CaptureWindow(prc.MainWindowHandle)


            screenshot.Save("C:\er.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
        Next

    End Sub
End Class



Namespace Spazzarama.ScreenCapture
    Public Module Direct3DCapture
        Sub New()
        End Sub
        Private _direct3D9 As New SlimDX.Direct3D9.Direct3D()
        Private _direct3DDeviceCache As New Dictionary(Of IntPtr, SlimDX.Direct3D9.Device)()

        ''' <summary>
        ''' Capture the entire client area of a window
        ''' </summary>
        ''' <param name="hWnd"></param>
        ''' <returns></returns>
        Public Function CaptureWindow(ByVal hWnd As IntPtr) As Bitmap
            Return CaptureRegionDirect3D(hWnd, NativeMethods.GetAbsoluteClientRect(hWnd))
        End Function

        ''' <summary>
        ''' Capture a region of the screen using Direct3D
        ''' </summary>
        ''' <param name="handle">The handle of a window</param>
        ''' <param name="region">The region to capture (in screen coordinates)</param>
        ''' <returns>A bitmap containing the captured region, this should be disposed of appropriately when finished with it</returns>
        Public Function CaptureRegionDirect3D(ByVal handle As IntPtr, ByVal region As Rectangle) As Bitmap
            Dim hWnd As IntPtr = handle
            Dim bitmap As Bitmap = Nothing

            ' We are only supporting the primary display adapter for Direct3D mode
            Dim adapterInfo As SlimDX.Direct3D9.AdapterInformation = _direct3D9.Adapters.DefaultAdapter
            Dim device As SlimDX.Direct3D9.Device

            '#Region "Get Direct3D Device"
            ' Retrieve the existing Direct3D device if we already created one for the given handle
            If _direct3DDeviceCache.ContainsKey(hWnd) Then
                device = _direct3DDeviceCache(hWnd)
            Else
                ' We need to create a new device
                ' Setup the device creation parameters
                Dim parameters As New SlimDX.Direct3D9.PresentParameters()
                parameters.BackBufferFormat = adapterInfo.CurrentDisplayMode.Format
                Dim clientRect As Rectangle = NativeMethods.GetAbsoluteClientRect(hWnd)
                parameters.BackBufferHeight = clientRect.Height
                parameters.BackBufferWidth = clientRect.Width
                parameters.Multisample = SlimDX.Direct3D9.MultisampleType.None
                parameters.SwapEffect = SlimDX.Direct3D9.SwapEffect.Discard
                parameters.DeviceWindowHandle = hWnd
                parameters.PresentationInterval = SlimDX.Direct3D9.PresentInterval.[Default]
                parameters.FullScreenRefreshRateInHertz = 0

                ' Create the Direct3D device
                device = New SlimDX.Direct3D9.Device(_direct3D9, adapterInfo.Adapter, SlimDX.Direct3D9.DeviceType.Hardware, hWnd, SlimDX.Direct3D9.CreateFlags.SoftwareVertexProcessing, parameters)
                _direct3DDeviceCache.Add(hWnd, device)
            End If
            '#End Region

            ' Capture the screen and copy the region into a Bitmap
            Using surface As SlimDX.Direct3D9.Surface = SlimDX.Direct3D9.Surface.CreateOffscreenPlain(device, adapterInfo.CurrentDisplayMode.Width, adapterInfo.CurrentDisplayMode.Height, SlimDX.Direct3D9.Format.A8R8G8B8, SlimDX.Direct3D9.Pool.SystemMemory)
                device.GetFrontBufferData(0, surface)

                bitmap = New Bitmap(SlimDX.Direct3D9.Surface.ToStream(surface, SlimDX.Direct3D9.ImageFileFormat.Bmp, New Rectangle(region.Left, region.Top, region.Right, region.Bottom)))
            End Using

            Return bitmap
        End Function
    End Module

#Region "Native Win32 Interop"
    ''' <summary>
    ''' The RECT structure defines the coordinates of the upper-left and lower-right corners of a rectangle.
    ''' </summary>
    <Serializable(), StructLayout(LayoutKind.Sequential)> _
    Friend Structure RECT
        Public Left As Integer
        Public Top As Integer
        Public Right As Integer
        Public Bottom As Integer

        Public Sub New(ByVal left As Integer, ByVal top As Integer, ByVal right As Integer, ByVal bottom As Integer)
            Me.Left = left
            Me.Top = top
            Me.Right = right
            Me.Bottom = bottom
        End Sub

        Public ReadOnly Property AsRectangle() As Rectangle
            Get
                Return New Rectangle(Me.Left, Me.Top, Me.Right - Me.Left, Me.Bottom - Me.Top)
            End Get
        End Property

        Public Shared Function FromXYWH(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) As RECT
            Return New RECT(x, y, x + width, y + height)
        End Function

        Public Shared Function FromRectangle(ByVal rect As Rectangle) As RECT
            Return New RECT(rect.Left, rect.Top, rect.Right, rect.Bottom)
        End Function
    End Structure

    <System.Security.SuppressUnmanagedCodeSecurity()> _
    Friend NotInheritable Class NativeMethods
        <DllImport("user32.dll")> _
        Friend Shared Function GetClientRect(ByVal hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
        End Function

        <DllImport("user32.dll")> _
        Friend Shared Function GetWindowRect(ByVal hWnd As IntPtr, ByRef lpRect As RECT) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function

        ''' <summary>
        ''' Get a windows client rectangle in a .NET structure
        ''' </summary>
        ''' <param name="hwnd">The window handle to look up</param>
        ''' <returns>The rectangle</returns>
        Friend Shared Function GetClientRect(ByVal hwnd As IntPtr) As Rectangle
            Dim rect As New RECT()
            GetClientRect(hwnd, rect)
            Return rect.AsRectangle
        End Function

        ''' <summary>
        ''' Get a windows rectangle in a .NET structure
        ''' </summary>
        ''' <param name="hwnd">The window handle to look up</param>
        ''' <returns>The rectangle</returns>
        Friend Shared Function GetWindowRect(ByVal hwnd As IntPtr) As Rectangle
            Dim rect As New RECT()
            GetWindowRect(hwnd, rect)
            Return rect.AsRectangle
        End Function

        Friend Shared Function GetAbsoluteClientRect(ByVal hWnd As IntPtr) As Rectangle
            Dim windowRect As Rectangle = NativeMethods.GetWindowRect(hWnd)
            Dim clientRect As Rectangle = NativeMethods.GetClientRect(hWnd)

            ' This gives us the width of the left, right and bottom chrome - we can then determine the top height
            Dim chromeWidth As Integer = CInt(((windowRect.Width - clientRect.Width) / 2))

            Return New Rectangle(New Point(windowRect.X + chromeWidth, windowRect.Y + (windowRect.Height - clientRect.Height - chromeWidth)), clientRect.Size)
        End Function
    End Class
#End Region
End Namespace

Aber leider erhalte ich folgenden Fehler (angehängtes Bild):
 

Anhänge

  • 50646d1265555297-unbenannt.png
    50646d1265555297-unbenannt.png
    23,8 KB · Aufrufe: 130
Zuletzt bearbeitet:

Afritus

Mitglied
Push!

Kann ja nicht sein, dass das wirklich keiner weiß. Ein einfacher DirectX-Screenshot mit VB.NET, wie er zum Beispiel mit kostenlosen Programmen (Fraps) möglich ist.
Wenn das wirklich unmöglich ist, so möge mir das bitte jemand hier reinschreiben. Ich kann aber einfach nicht dran glauben!
Ich hab Google totgesucht. Anscheinend wurde noch in keinem anderen Forum eine passable und funktionierende Lösung für dieses Problem gefunden.

MfG
Markus
 

Afritus

Mitglied
Danke für die Antwort!
Hab mir das von GameDev mal durchgelesen, das scheint ja tatsächlich zu funktionieren. Aber wie lautet denn da der Befehl fürs Screenshotmachen unter VB.NET?
Wieso genau brauche ich eine Shared Memory?
MfG