Capturar Foto de Juego !!

Iniciado por TrashAmbishion, 21 Junio 2016, 09:09 AM

0 Miembros y 1 Visitante están viendo este tema.

TrashAmbishion

Hola,

Estuve viendo que hay una forma q es lidiando con DirectX pero Electro en su SDK lo demuestra de otra manera (la cual toy mirando, por cierto si estas leyendo alguna posibilidad de obtener el codigo pa no importar la DLL es por tema de peso final del App los creditos siempre se mantendran)

Gracias cualquier ayuda

Eleкtro

#1
Hola

En el repositorio de SharpDX (el wrapper más completo de la API de DirectX para .NET) tienes un ejemplo para lo que parece ser un capturador de imágenes:

Ahora, no puedo darte información más precisa por que no utilizo SharpDX ...bueno, en realidad si, pero para cosas muy básicas que nada tienen que ver, así que es como si no lo usase.

De todas formas, SharpDX/DirectX siempre se hace tedioso de usar en caso de que no tengas demasiada experiencia en el tema (como es mi caso xD), y en ese caso yo no te lo recomendaría para una tarea que debería llevarse a cabo de forma simple. Con los métodos de captura de imagen de ElektroKit debería ser suficiente y no deberías tener problemas al usarlos.




Cita de: TrashAmbishion en 21 Junio 2016, 09:09 AMalguna posibilidad de obtener el codigo pa no importar la DLL es por tema

Supongo que ya lo sabrás, pero lo digo de todas formas: GitHub tiene un buscador para encontrar código dentro de un repositorio. Úsalo la próxima vez...

Código (vbnet) [Seleccionar]
<SuppressUnmanagedCodeSecurity>
<DllImport("user32.dll")>
Public Shared Function SetForegroundWindow(ByVal hwnd As IntPtr
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

<SuppressUnmanagedCodeSecurity>
<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function ShowWindow(ByVal hwnd As IntPtr,
   <MarshalAs(UnmanagedType.I4)> ByVal nCmdShow As WindowState
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

<SuppressUnmanagedCodeSecurity>
<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function GetWindowRect(ByVal hwnd As IntPtr,
                              <Out> ByRef rect As Rect
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function


+

Código (vbnet) [Seleccionar]
Public Enum WindowState As Integer
   Normal = 1
End Enum


+


+

Código (vbnet) [Seleccionar]
Public Shared Function TakeScreenshotFromObject(ByVal hwnd As IntPtr, ...) As Image
   ' ...
End Function


o también:
Código (vbnet) [Seleccionar]
Public Shared Function TakeScreenshotFromRegion(ByVal rect As Rectangle, ...) As Image
    ' ...
End Function


Saludos








TrashAmbishion

Compadre ayer yo juraría que el

Código (vbnet) [Seleccionar]


    Public Shared Function TakeScreenshotFromRegion(ByVal rect As Rectangle, ...) As Image
        ' ...
    End Function



Me había funcionado ahora siempre me capturan el escritorio y no la pantalla del juego como tal.

Salu2 y gracias

Eleкtro

#3
Cita de: TrashAmbishion en 22 Junio 2016, 00:23 AMMe había funcionado ahora siempre me capturan el escritorio y no la pantalla del juego como tal.

Hola.

Si no he entendido mal, dices que te captura la región completa del escritorio en lugar de la región de la ventana del juego?, entonces probablemente le estés pasando valores que no corresponden a la ventana del juego.

En el momento en que vayas a llamar a la función, depura el código para revisar que esos valores sean correctos, examinando los autos con un breakpoint, o en tiempo de ejecución:

Código (vbnet) [Seleccionar]
Dim rc As Rectangle = ...
Debug.WriteLine(String.Format("[Rectangle] Location: {0} Size: {1}", rc.Location.ToString(), rc.Size.ToString()))


Si los valores son incorrectos, podrías publicar el código para intentar ayudarte a encontrar el fallo con el intento de captura. De lo contrario, si estás seguro que los valores son correctos, podrías intentar publicar un código que yo pueda reproducir desde aquí para solucionar algún posible bug con mi función, aunque en un principio no debe haber ninguno, es una metodología simple y administrada por el GDI+ de .NET.

Saludos








TrashAmbishion

Hola,

Bueno estuve mirando detenidamente tu codigo hace un rato y me di cuenta de que en la función tu mandas activar la ventana pasandole el handle previamente obtenido y bueno como yo tengo el juego maximizado no en modo ventana me parece que es ahi la cuestión, no obstante minimize el juego y ejecute el codigo y Buala funciono me activa la ventana toma la foto y se vuelve a minimizar, asi que la cosa esta ahi toy dandole vueltas para ver como modificarlo si tienes alguna solución al vuelo te lo agradecer aun mas.

Salu2

Eleкtro

#5
Cita de: TrashAmbishion en 23 Junio 2016, 05:38 AMBueno estuve mirando detenidamente tu codigo hace un rato y me di cuenta de que en la función tu mandas activar la ventana pasandole el handle previamente obtenido y bueno como yo tengo el juego maximizado no en modo ventana me parece que es ahi la cuestión, no obstante minimize el juego y ejecute el codigo y Buala funciono me activa la ventana toma la foto y se vuelve a minimizar, asi que la cosa esta ahi toy dandole vueltas para ver como modificarlo si tienes alguna solución al vuelo te lo agradecer aun mas.

Te refieres a esta función que toma un handle de ventana: TakeScreenshotFromObject(IntPtr, ...)

Pero según habias dicho, tu estás utilizando esta otra función que toma un Rectangle: TakeScreenshotFromRegion(Rectangle, ...)

¿En que quedamos? :P

Son códigos bastante diferentes entre sí. Por favor aclárame exactamente que función estás llamando si la del Handle o la del Rectangle, e intentaré reproducir el problema en otro juego maximizado. ¡Gracias por tu tiempo!.

PD: En caso de que te refieras a la función TakeScreenshotFromObject(IntPtr, ...), tal vez con comprobar si la ventana es visible y/o está maximizada sea suficiente para solucionarlo, al evitar esa llamada Win32 de ShowWindow ...que podría ser el problema.

Saludos








TrashAmbishion

 :o

Dios toy tonteando pues si se me olvido aclararte esa parte de que con la funcion:

Código (vbnet) [Seleccionar]
TakeScreenshotFromRegion(Rectangle, ...)

No me funciona pues tira la foto al escritorio y no al juego como tal lo voy a depurar en cuanto llegue a la casa y te cuento los valores que tiene.

Y con:

Código (vbnet) [Seleccionar]
TakeScreenshotFromObject(IntPtr, ...)

Por ahi sería la cosa pero da igual si no llamo a esa funcion me tira la foto al escritorio de igual manera no obstante sigo probando.

Salu2

Eleкtro

#7
Efectivamente he podido reproducir el error, habia un fallo con la función TakeScreenshotFromObject(IntPtr, ...), aunque es un fallo debido al código no administrado con la función ShowWindow, algo extraño que no he llegado a comprender del todo por qué motivo sucede ...para ser sinceros.

He reemplazado la metodología para evitar que se produzca ese extraño comportamiento, y lo he perfeccionado para soportar las ventanas con bordes invisible típicas de Windows 10, y también ventanas que puedan estar tapadas por el area no-cliente del escritorio (ej. la barra de tareas).




¿Estás usando las .dll de ElektroKit, o copiaste solo el código que necesitas?. Si necesitas que actualice la librería en GitHub para que puedas descargas las .dll con estos cambios ya aplicados, dímelo.

(el resto del código necesario está en namespace Elektro.Interop.Win32)
Código (vbnet) [Seleccionar]
Public Shared Function TakeScreenshotFromObject(ByVal hwnd As IntPtr,
                                               Optional ByVal includeMouse As Boolean = False,
                                               Optional ByVal pixelFormat As PixelFormat = PixelFormat.Format32bppArgb) As Image

   If Not NativeMethods.IsWindow(hwnd) Then
       Throw New ArgumentException(paramName:="hwnd", message:="The specified handle doesn't point to a win32 Window.")
       Return Nothing
   End If

   ' Restore and give focus to a minimized window.
   If NativeMethods.IsIconic(hwnd) Then
       NativeMethods.OpenIconicWindow(hwnd)
   End If

   ' Show a non-activated window and bring the window to z-top.
   Dim wndPlacement As WindowPlacement
   NativeMethods.GetWindowPlacement(hwnd, wndPlacement)
   Select Case wndPlacement.ShowCmd

       Case Elektro.Interop.Win32.Enums.WindowState.Minimize, Elektro.Interop.Win32.Enums.WindowState.ForceMinimize,
            Elektro.Interop.Win32.Enums.WindowState.ShowMinimized, Elektro.Interop.Win32.Enums.WindowState.ShowMinNoActive
           NativeMethods.ShowWindow(hwnd, Elektro.Interop.Win32.Enums.WindowState.Restore)

       Case Elektro.Interop.Win32.Enums.WindowState.Hide
           ' Avoid restoring a hidden window, just because the user didn't demanded this.
           If Not NativeMethods.IsWindowVisible(hwnd) Then
               Throw New ArgumentException(paramName:="hwnd", message:="The specified window it's hidden.")
               Return Nothing
           End If

       Case Else
           NativeMethods.SetForegroundWindow(hwnd)

   End Select
   NativeMethods.BringWindowToTop(hwnd)

   Dim rc As Win32.Types.Rect = GetRealWindowRect(hwnd)
   Dim scr As Screen = Screen.FromHandle(hwnd)
   rc = Rectangle.Intersect(rc, scr.WorkingArea)
   Return TakeScreenshotFromRegion(rc, includeMouse, pixelFormat)

End Function


Código (vbnet) [Seleccionar]

''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets the (non-client) <see cref="Rectangle"/> of a window.
''' <para></para>
''' This method supports a borderless <c>Windows 10</c> window.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private Shared Function GetRealWindowRect(ByVal hwnd As IntPtr) As Rectangle

   Dim rc As Win32.Types.Rect = Rectangle.Empty

   If IsWin10 Then
       Dim hResult As Integer
       hResult = NativeMethods.DwmGetWindowAttribute(hwnd, Elektro.Interop.Win32.Enums.DwmWindowAttribute.ExtendedFrameBounds, rc, Marshal.SizeOf(rc))
       If (DirectCast(hResult, HResult) <> Elektro.Interop.Win32.Enums.HResult.S_OK) Then
           Marshal.ThrowExceptionForHR(hResult)
       End If

   Else
       Dim result As Boolean
       Dim win32Err As Integer
       result = NativeMethods.GetWindowRect(hwnd, rc)
       win32Err = Marshal.GetLastWin32Error()
       If Not (result) Then
           Throw New Win32Exception(win32Err)
       End If

   End If

   Return rc

End Function


+

Código (vbnet) [Seleccionar]
<SuppressUnmanagedCodeSecurity>
<DllImport("dwmapi.dll", SetLastError:=False)>
Public Shared Function DwmGetWindowAttribute(ByVal hwnd As IntPtr,
                                            ByVal attribute As DwmWindowAttribute,
                                            ByRef refAttribute As Rect,
                                            ByVal sizeAttribute As Integer
) As Integer
End Function


+

Código (vbnet) [Seleccionar]
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets a value that determines whether the current operating system is <c>Windows 10</c>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Public Shared ReadOnly Property IsWin10() As Boolean
   <DebuggerStepThrough>
   Get
       Return (Environment.OSVersion.Platform = PlatformID.Win32NT) AndAlso (InternalIsWin10())
   End Get
End Property

Private Shared Function InternalIsWin10() As Boolean

   Using reg As RegistryKey = Microsoft.Win32.Registry.LocalMachine.
                              OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion", writable:=False)

       Dim productName As String = DirectCast(reg.GetValue("ProductName", "Error", RegistryValueOptions.None), String)
       Return productName.StartsWith("Windows 10", StringComparison.OrdinalIgnoreCase)

   End Using

End Function


Saludos








TrashAmbishion

Un crack  ;-) ;-) ;-)

Había copiado el codigo para hacerlo lo mas independiente posible no obstante estoy pensando poner la libreria y despues hago un joiner con una herramienta que es para los NET no recuerdo ahora la tengo por ahi, en caso futuro necesite añadirle algo eso lo veo despues.

Muchas gracias again !!

TrashAmbishion

#9
Hola,

El único comando que no me reconoce es:

Código (vbnet) [Seleccionar]
NativeMethods.DwmGetWindowAttribute

Me dice que no es un miembro de NativeMethods, baje la ultima version de la DLL y nada me sigue diciendo lo mismo.

En el primer codigo pones el Return a:

Código (vbnet) [Seleccionar]
Return TakeScreenshotFromRegion(rc, includeMouse, pixelFormat)

Esto es correcto ??

Salu2 y gracias de antemano !!