Foro de elhacker.net

Programación => .NET (C#, VB.NET, ASP) => Mensaje iniciado por: TrashAmbishion en 21 Junio 2016, 09:09 am



Título: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 21 Junio 2016, 09:09 am
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


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 21 Junio 2016, 10:35 am
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:
  • https://github.com/sharpdx/SharpDX-Samples/tree/ed7e80cdcac145cf1864c6b9358a558e5fe8deb6/Desktop/Direct3D11.1/ScreenCapture

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.



alguna 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
  1. <SuppressUnmanagedCodeSecurity>
  2. <DllImport("user32.dll")>
  3. Public Shared Function SetForegroundWindow(ByVal hwnd As IntPtr
  4. ) As <MarshalAs(UnmanagedType.Bool)> Boolean
  5. End Function
  6.  
  7. <SuppressUnmanagedCodeSecurity>
  8. <DllImport("user32.dll", SetLastError:=True)>
  9. Public Shared Function ShowWindow(ByVal hwnd As IntPtr,
  10.    <MarshalAs(UnmanagedType.I4)> ByVal nCmdShow As WindowState
  11. ) As <MarshalAs(UnmanagedType.Bool)> Boolean
  12. End Function
  13.  
  14. <SuppressUnmanagedCodeSecurity>
  15. <DllImport("user32.dll", SetLastError:=True)>
  16. Public Shared Function GetWindowRect(ByVal hwnd As IntPtr,
  17.                               <Out> ByRef rect As Rect
  18. ) As <MarshalAs(UnmanagedType.Bool)> Boolean
  19. End Function

+

Código
  1. Public Enum WindowState As Integer
  2.    Normal = 1
  3. End Enum

+

  • https://github.com/ElektroStudios/ElektroKit/blob/54636a57e20c90186b7915102809b0a2bb134ea0/Solution/Elektro.Interop/Win32/Types/Rect.vb

+

Código
  1. Public Shared Function TakeScreenshotFromObject(ByVal hwnd As IntPtr, ...) As Image
  2.    ' ...
  3. End Function

o también:
Código
  1. Public Shared Function TakeScreenshotFromRegion(ByVal rect As Rectangle, ...) As Image
  2.    ' ...
  3. End Function

Saludos


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 22 Junio 2016, 00:23 am
Compadre ayer yo juraría que el

Código
  1.  
  2.    Public Shared Function TakeScreenshotFromRegion(ByVal rect As Rectangle, ...) As Image
  3.        ' ...
  4.    End Function
  5.  
  6.  

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

Salu2 y gracias


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 22 Junio 2016, 07:51 am
Me 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
  1. Dim rc As Rectangle = ...
  2. 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


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 23 Junio 2016, 05:38 am
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


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 23 Junio 2016, 06:27 am
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.

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


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 23 Junio 2016, 18:53 pm
 :o

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

Código
  1. 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
  1. 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


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 24 Junio 2016, 10:02 am
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
  1. Public Shared Function TakeScreenshotFromObject(ByVal hwnd As IntPtr,
  2.                                                Optional ByVal includeMouse As Boolean = False,
  3.                                                Optional ByVal pixelFormat As PixelFormat = PixelFormat.Format32bppArgb) As Image
  4.  
  5.    If Not NativeMethods.IsWindow(hwnd) Then
  6.        Throw New ArgumentException(paramName:="hwnd", message:="The specified handle doesn't point to a win32 Window.")
  7.        Return Nothing
  8.    End If
  9.  
  10.    ' Restore and give focus to a minimized window.
  11.    If NativeMethods.IsIconic(hwnd) Then
  12.        NativeMethods.OpenIconicWindow(hwnd)
  13.    End If
  14.  
  15.    ' Show a non-activated window and bring the window to z-top.
  16.    Dim wndPlacement As WindowPlacement
  17.    NativeMethods.GetWindowPlacement(hwnd, wndPlacement)
  18.    Select Case wndPlacement.ShowCmd
  19.  
  20.        Case Elektro.Interop.Win32.Enums.WindowState.Minimize, Elektro.Interop.Win32.Enums.WindowState.ForceMinimize,
  21.             Elektro.Interop.Win32.Enums.WindowState.ShowMinimized, Elektro.Interop.Win32.Enums.WindowState.ShowMinNoActive
  22.            NativeMethods.ShowWindow(hwnd, Elektro.Interop.Win32.Enums.WindowState.Restore)
  23.  
  24.        Case Elektro.Interop.Win32.Enums.WindowState.Hide
  25.            ' Avoid restoring a hidden window, just because the user didn't demanded this.
  26.            If Not NativeMethods.IsWindowVisible(hwnd) Then
  27.                Throw New ArgumentException(paramName:="hwnd", message:="The specified window it's hidden.")
  28.                Return Nothing
  29.            End If
  30.  
  31.        Case Else
  32.            NativeMethods.SetForegroundWindow(hwnd)
  33.  
  34.    End Select
  35.    NativeMethods.BringWindowToTop(hwnd)
  36.  
  37.    Dim rc As Win32.Types.Rect = GetRealWindowRect(hwnd)
  38.    Dim scr As Screen = Screen.FromHandle(hwnd)
  39.    rc = Rectangle.Intersect(rc, scr.WorkingArea)
  40.    Return TakeScreenshotFromRegion(rc, includeMouse, pixelFormat)
  41.  
  42. End Function

Código
  1. ''' ----------------------------------------------------------------------------------------------------
  2. ''' <summary>
  3. ''' Gets the (non-client) <see cref="Rectangle"/> of a window.
  4. ''' <para></para>
  5. ''' This method supports a borderless <c>Windows 10</c> window.
  6. ''' </summary>
  7. ''' ----------------------------------------------------------------------------------------------------
  8. Private Shared Function GetRealWindowRect(ByVal hwnd As IntPtr) As Rectangle
  9.  
  10.    Dim rc As Win32.Types.Rect = Rectangle.Empty
  11.  
  12.    If IsWin10 Then
  13.        Dim hResult As Integer
  14.        hResult = NativeMethods.DwmGetWindowAttribute(hwnd, Elektro.Interop.Win32.Enums.DwmWindowAttribute.ExtendedFrameBounds, rc, Marshal.SizeOf(rc))
  15.        If (DirectCast(hResult, HResult) <> Elektro.Interop.Win32.Enums.HResult.S_OK) Then
  16.            Marshal.ThrowExceptionForHR(hResult)
  17.        End If
  18.  
  19.    Else
  20.        Dim result As Boolean
  21.        Dim win32Err As Integer
  22.        result = NativeMethods.GetWindowRect(hwnd, rc)
  23.        win32Err = Marshal.GetLastWin32Error()
  24.        If Not (result) Then
  25.            Throw New Win32Exception(win32Err)
  26.        End If
  27.  
  28.    End If
  29.  
  30.    Return rc
  31.  
  32. End Function

+

Código
  1. <SuppressUnmanagedCodeSecurity>
  2. <DllImport("dwmapi.dll", SetLastError:=False)>
  3. Public Shared Function DwmGetWindowAttribute(ByVal hwnd As IntPtr,
  4.                                             ByVal attribute As DwmWindowAttribute,
  5.                                             ByRef refAttribute As Rect,
  6.                                             ByVal sizeAttribute As Integer
  7. ) As Integer
  8. End Function

+

Código
  1. ''' ----------------------------------------------------------------------------------------------------
  2. ''' <summary>
  3. ''' Gets a value that determines whether the current operating system is <c>Windows 10</c>.
  4. ''' </summary>
  5. ''' ----------------------------------------------------------------------------------------------------
  6. Public Shared ReadOnly Property IsWin10() As Boolean
  7.    <DebuggerStepThrough>
  8.    Get
  9.        Return (Environment.OSVersion.Platform = PlatformID.Win32NT) AndAlso (InternalIsWin10())
  10.    End Get
  11. End Property
  12.  
  13. Private Shared Function InternalIsWin10() As Boolean
  14.  
  15.    Using reg As RegistryKey = Microsoft.Win32.Registry.LocalMachine.
  16.                               OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion", writable:=False)
  17.  
  18.        Dim productName As String = DirectCast(reg.GetValue("ProductName", "Error", RegistryValueOptions.None), String)
  19.        Return productName.StartsWith("Windows 10", StringComparison.OrdinalIgnoreCase)
  20.  
  21.    End Using
  22.  
  23. End Function

Saludos


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 24 Junio 2016, 18:52 pm
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 !!


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 25 Junio 2016, 04:04 am
Hola,

El único comando que no me reconoce es:

Código
  1. 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
  1. Return TakeScreenshotFromRegion(rc, includeMouse, pixelFormat)

Esto es correcto ??

Salu2 y gracias de antemano !!


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 27 Junio 2016, 16:45 pm
Código
  1. 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.

La función Win32 DwmGetWindowAttribute no estaba incluida en la versión actual de ElektroKit en GitHub, ¡por eso te la enseñé aparte, para que la copiases directamente en tu código! xD. Tal vez me faltó eliminar el "Nativemethods." del ejemplo que te mostré para evitar esta confusión.

Simplemente elimina lo de "NativeMethods." y llama a la función DwmGetWindowAttribute que te mostré (TuClase.DwmGetWindowAttribute(...)).



En el primer codigo pones el Return a:
Código
  1. Return TakeScreenshotFromRegion(rc, includeMouse, pixelFormat)

Esto es correcto ??

Si, es correcto.

En la versión no-simplificada que usaste al principio (el código que sacaste de GitHub), TakeScreenshotFromObject y TakeScreenshotFromRegion hacian practicamente lo mismo, tenian una parte del código idéntica/duplicada, así que lo he refactorizado para que la función TakeScreenshotFromObject simplemente obtenga el Rectangle que debe pasarle a la función TakeScreenshotFromRegion para que se encarge del resto.



Ya he actualizado la librería de ElektroKit:
  • http://foro.elhacker.net/net/elektrokit_framework_v17_complementos_para_el_nucleo_de_net_framework-t444997.0.html

Disculpa la demora pero he tenido que modificar todos los archivos (casi 1.000) en esta actualización y me ha llevado varios días solamente para hacer eso.

PD: En GitHub puedes encontrar el código actualizado:
  • https://github.com/ElektroStudios/ElektroKit/blob/1118151e1c34bcc51d1f6a003b563676db69ab30/Solution/Elektro.Imaging/Tools/ImageUtil.vb

Saludos


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 27 Junio 2016, 19:12 pm
Hola,

Código
  1. Os.Win10

Me da que no reconoce ese metodo, supongo que tengo que importar otra de las librerias quizas System o Core.

Dime si me equivoco asumo que esto verifica si el sistema es Windows 10 y entonces continua....

Esto lo puedo resolver de otra forma pero quiero estar seguro antes.

Salu2 y gracias


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 27 Junio 2016, 19:34 pm
Código
  1. Os.Win10
Me da que no reconoce ese metodo, supongo que tengo que importar otra de las librerias quizas System o Core.

Para usar la class "OS":
Código:
Imports Elektro.System.Tools.SystemInfo

Por cierto, lo siento mucho por las complicaciones o los mareos que veo que te estoy causando, jeje.



Dime si me equivoco asumo que esto verifica si el sistema es Windows 10 y entonces continua....

Esto lo puedo resolver de otra forma pero quiero estar seguro antes.

Efectivamente, pero te recomiendo usar ElektroKit para realizar esa comprobación, o al menos sacar el código relacionado y copiarlo/pegarlo en tu código, el cual por cierto ya te lo mostré en mi respuesta anterior...

Citar
Código
  1. Public Shared ReadOnly Property IsWin10() As Boolean
  2.    <DebuggerStepThrough>
  3.    Get
  4.        Return (Environment.OSVersion.Platform = PlatformID.Win32NT) AndAlso (InternalIsWin10())
  5.    End Get
  6. End Property
  7.  
  8. Private Shared Function InternalIsWin10() As Boolean
  9.  
  10.    Using reg As RegistryKey = Microsoft.Win32.Registry.LocalMachine.
  11.                               OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion", writable:=False)
  12.  
  13.        Dim productName As String = DirectCast(reg.GetValue("ProductName", "Error", RegistryValueOptions.None), String)
  14.        Return productName.StartsWith("Windows 10", StringComparison.OrdinalIgnoreCase)
  15.  
  16.    End Using
  17.  
  18. End Function

TEN CUIDADO si no sigues esta sugerencia y lo vas a intentar detectar manualmente con otro código distinto, se requiere una metodología especial como por ejemplo la que mostré mediante el registro.

Metodologías como por ejemplo el uso de la propiedad Environment.OSVersion , recurrir a WMI, o a la API GetVersion/GetVersionEx, son metodologías que están consideradas obsoletas y te darán resultados contradictorios, incluyendo las siguientes APIs de ayuda para detectar las versiones de Windows y de Windows 10 ...la cual primero necesita que la app tenga un manifiesto específico:
 
  • https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972%28v=vs.85%29.aspx

No existe una solución eficiente por parte de Microsoft ...por el momento.

Como ya digo, te recomiendo utilizar ElektroKit para evaluar la versión del OS, o lo que es lo mismo: ¡ese código de arriba!.

Saludos!


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 27 Junio 2016, 20:13 pm
Aaaaaaaaaaaaaaa  :¬¬  :¬¬

Creo que el dios de la programacion me tiene odio GGG

Retifico...el post que hice hace nada por si ya lo habias leido.. me confundi de funcion te hablaba de la de "region" cuando era la de "object", de igual manera tampoco me funciono.

Incluso hice que iniciara minimizado y nada..

La fui depurando y toma el handle correctamente, detecta las dimensiones de la pantalla y nada tira la foto al escritorio.

Salu2

PD: Me imginaba que no seria tan sencillo ademas pensadolo bien si ya estoy importando las estructuras que mas da 2 DLL mas GGG asi lo hago todo nativo.

Nada de disculpas si yo soy el que te las tiene que pedir por chivarte tanto.



Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 27 Junio 2016, 21:13 pm
No se como ayudarte más, lo he probado con varios juegos de Blizzard maximizados (fullscreen) y minimizados y me funciona correctamente (con el código aactualizado que ya te mostré), en mi caso se toma la foto del juego.

Dime que de que juego se trata o... pásame por privado el proyecto de Visual Studio para intentar depurarla a ver si tal vez la aplicación está ganando el foco y por eso no funciona o... ¡no se por qué!.

Saludos


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 29 Junio 2016, 15:54 pm
Hola,

Disculpa la demora te comento que el juego es el Battle Field 3 es de shotting como supongo sabras y sino bueno ya lo sabes gggg.

Acabo de sentarme en un Pc con Windows 7 y probe el programa con un juego se llama Mahjong o algo asi que lo puedo poner pantalla completa y el codigo fue un éxito rotundo, ahora mismo voy a otro pc con windows 8 pero otra version diferente a la mia para probarlo y te contaré probare el mismo juego y otro mas que encuentre.

Te cuento los resultados.


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 30 Junio 2016, 04:06 am
Confirmado es en dependencia del tipo de juego..

Los minigames como dinerdash o cualquiera de esos del mago nico si funciona pero cuando juegas juegos mas potentes que asumo tenga que ver con el DirectX que es el q se mete por medio ya se chiva el codigo....

Alguna idea entonces con alguna de los proyectos que trabajan esa area..??

Salu2


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 1 Julio 2016, 19:06 pm
Confirmado es en dependencia del tipo de juego..

Tú mismo lo has dicho. El problema está en como trabaja/renderiza ese juego en específico, Battlefield 3 (y el resto de juegos que sean similares en ese sentido).

Lo he descargado para probarlo y lo primero que me he dado cuenta es que el juego no puede trabajar a una resolución nativa de 1920x1080px, sino que trabaja en una resolución bastante menor, y entonces cuando juegas en modo fullscreen el juego reescala hasta una resolución mayor. Esto es importante, y es lo que causa que no se pueda obtener una imagen real y al tamaño esperado, por que para reescalar la pantalla se vuelve negra o se queda una imagen estática de la ventana del juego antes de ponerlo en fullscreen. Si el juego trabajase a una resolución nativa igual que la de la pantalla, entonces la imagen al menos se tomaria a pantalla completa.

He probado varios capturadores de pantalla profesionales para intentar confirmar que realmente se trata del juego y no de mi algoritmo. Estos han sido los resultados:

Primero de nada, si estando dentro del juego pulsamos la tecla "Imprimir pantalla" para dejar que Windows tome la captura, este es el resultado:
(http://i.imgur.com/WeBlA6dl.png) (http://i.imgur.com/WeBlA6d.png)
( Una imagen a 1920x1080. La parte blanca imagino que es la superfie de renderizado que el juego no puede rellenar, al no trabajar a esa resolución. )

Esta imagen fue tomada con WinSnap, una captura de pantalla con retardo de 10 segundos, estando dentro del juego. Exactamente el mismo resultado que una captura de Windows:
(http://i.imgur.com/A1PAtzdl.jpg) (http://i.imgur.com/A1PAtzd.jpg)

Esta es una imagen tomada con ElektroKit, el resultado fue exactamente el mismo:
(http://i.imgur.com/DC9p1Unl.jpg) (http://i.imgur.com/DC9p1Un.jpg)

El código utilizado fue este:
Código
  1. Imports System.Drawing.Imaging
  2.  
  3. Imports Elektro.Application.Types
  4. Imports Elektro.Core.IO.Enums
  5. Imports Elektro.Imaging.Tools
  6.  
  7. Public NotInheritable Class Form1 : Inherits Form
  8.  
  9.    ' Fullscreen capture.
  10.    Friend WithEvents HotkeyScreenshotType1 As New Hotkey(HotkeyModifiers.None, Keys.D1)
  11.  
  12.    ' BF3 window capture.
  13.    Friend WithEvents HotkeyScreenshotType2 As New Hotkey(HotkeyModifiers.None, Keys.D2)
  14.  
  15.    Private Sub HotkeyScreenshotType1_Press(ByVal sender As Object, ByVal e As HotkeyPressEventArgs) _
  16.    Handles HotkeyScreenshotType1.Press
  17.  
  18.        Static imgIndex As Integer = 0
  19.        Dim path As String = String.Format(".\Fullscreen_{0:00}.png", Interlocked.Increment(imgIndex))
  20.  
  21.        Using screenshot As Image = ImageUtil.TakeScreenshotFromDesktop(includeMouse:=False)
  22.            screenshot.Save(path, ImageFormat.Png)
  23.        End Using
  24.        ' Process.Start(path)
  25.  
  26.    End Sub
  27.  
  28.    Private Sub HotkeyScreenshotType2_Press(ByVal sender As Object, ByVal e As HotkeyPressEventArgs) _
  29.    Handles HotkeyScreenshotType2.Press
  30.  
  31.        Static imgIndex As Integer = 0
  32.  
  33.        Dim name As String = "bf3"
  34.        Dim proc As Process = Process.GetProcessesByName(name).Single()
  35.        Dim hwnd As IntPtr = proc.MainWindowHandle
  36.        Dim path As String = String.Format(".\{0}_{1:00}.png", name, Interlocked.Increment(imgIndex))
  37.  
  38.        Using screenshot As Image = ImageUtil.TakeScreenshotFromObject(hwnd, includeMouse:=False)
  39.            screenshot.Save(path, ImageFormat.Png)
  40.        End Using
  41.        ' Process.Start(path)
  42.  
  43.    End Sub
  44.  
  45. End Class



No creo que pueda haber una solución "directa" a este tipo de problema. Sabiendo que la resolución nativa de ese juego es 1280x720px, lo único que se me ocurre que puedes hacer es recortar la imagen para eliminar la superficie o canvas blanco, y redimensionar la imagen cortada.

El siguiente código producirá esta imagen a 1920x1080p o la resolución que sea tu pantalla...:
(http://i.imgur.com/8fxHiYkl.jpg) (http://i.imgur.com/8fxHiYk.jpg)

Código
  1. Imports System.Drawing.Imaging
  2.  
  3. Imports Elektro.Application.Types
  4. Imports Elektro.Core.IO.Enums
  5. Imports Elektro.Imaging.Extensions.Image
  6. Imports Elektro.Imaging.Tools
  7.  
  8. Public NotInheritable Class Form1 : Inherits Form
  9.  
  10.    Friend WithEvents HotkeyScreenshot As New Hotkey(HotkeyModifiers.None, Keys.D0)
  11.  
  12.    Private Sub HotkeyScreenshot_Press(ByVal sender As Object, ByVal e As HotkeyPressEventArgs) _
  13.    Handles HotkeyScreenshot.Press
  14.  
  15.        Static imgIndex As Integer = 0
  16.  
  17.        Dim procName As String = "bf3"
  18.        Dim proc As Process = Process.GetProcessesByName(procName).Single()
  19.        Dim hwnd As IntPtr = proc.MainWindowHandle
  20.        Dim gameRes As New Size(1280, 720)
  21.        Dim scr As Screen = Screen.PrimaryScreen
  22.        Dim path As String = String.Format(".\{0}_{1:00}.png", procName, Interlocked.Increment(imgIndex))
  23.  
  24.        If (scr.Bounds.Width > gameRes.Width) AndAlso (scr.Bounds.Height > gameRes.Height) Then
  25.            Using screenshot As Image = ImageUtil.TakeScreenshotFromRegion(scr.Bounds, includeMouse:=True),
  26.                  cutted As Image = screenshot.Cut(gameRes.Width, gameRes.Height, 0, 0),
  27.                  resized As Image = cutted.Resize(scr.Bounds.Size)
  28.  
  29.                resized.Save(path, ImageFormat.Png)
  30.            End Using
  31.  
  32.        Else
  33.            Using screenshot As Image = ImageUtil.TakeScreenshotFromRegion(scr.Bounds, includeMouse:=True)
  34.                screenshot.Save(path, ImageFormat.Png)
  35.            End Using
  36.  
  37.        End If
  38.  
  39.        ' Process.Start(path)
  40.  
  41.    End Sub
  42.  
  43. End Class

Pero ese código de nada sirve ya que aun faltaría solucionar otro problema que no he sido capaz de lograr, ni Windows, ni el resto de capturadores profesionales: actualizar la imagen del juego.
 
Cuando quieras tomar una captura del juego, primero debes minimizar el juego y maximizarlo de nuevo para que se guarde una imagen (estática) del juego en ese preciso momento y la captura tome esa imagen, de lo contrario siempre tomará la misma imagen (si nunca restauraste la ventana del juego entonces no habrá imagen, será una imagen negra o blanca). Al parecer no es posible capturar una imagen real, sino la última imagen del juego cuando lo restauras de esta forma, no se si me explico, pero así es como funciona.

Saludos!


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 2 Julio 2016, 06:20 am
Hola,

Joder bro no sabes cuanto te agradezco las molestias.

No se si habras usando el programa FRAPS es que el uso para hacer capturas y grabar video de los juegos en tiempo real y funciona muy bien.

Te cuento que si la captura las hace asi me sirve porque la mayoria de los cheaters usan AimBots que les hace señalizaciones en la pantalla y como veo la imagen a pequeña escala aqui asumo que cuando la tire yo aqui se vean bien con ese detalle del blanco que al final no es tan importante.

De todas formas lo voy a probar y te cuento..

Salu2 y muchisimas gracias desde ya..


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 2 Julio 2016, 15:11 pm
No se si habras usando el programa FRAPS es que el uso para hacer capturas y grabar video de los juegos en tiempo real y funciona muy bien.

Es bueno saber que existe un programa que pueda hacerlo, intentaré investigar sobre la metodología que utiliza ese programa aunque es un programa de pago así que no creo que me digan nada al respecto :-/.

También me faltó probar otra cosa diferente; el wrapper de DirectX para .NET, SharpDX, tiene un ejemplo en GitHub para capturar imágenes, aunque no me lo he mirado detalladamente pero quizás esa sea la solución para poder tomar capturas de pantalla de juegos como este en fullscreen, quiero decir, con un algoritmo basado en DirectX.

https://github.com/sharpdx/SharpDX-Samples/blob/master/Desktop/Direct3D11.1/ScreenCapture/Program.cs

Cuando tenga un rato intentaré buscar una solución con todo eso.

Saludos


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 2 Julio 2016, 20:26 pm
Traigo buenas noticias, he probado a hacer la captura con SharpDX y funciona de maravilla :). He usado el mismo ejemplo que el de GitHub, si necesitas ayuda con eso lo traduciré a VB.NET y te mostraré una función de uso genérico (hoy o mañana).

Saludos!


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 2 Julio 2016, 22:33 pm
Hola,

Muchas gracias espero entonces con el codigo que me dejastes anteriormente me tira la foto cuando esta en FullScreen en Negro completamente lo que no mire es en que resolucion estaba trabajando.

Y la otra me daba error me decia que no encontraba esa funcion o algo similar.

Bueno nada ya espero entonces por esta que trabaja a la par con DirectX.

Salu2 y gracias

PD: De todas formas aun quisiera sino te molesta tratar de ejecutar con exito el tuyo para tenerlo como otra opción. Despues vemos eso con mas calma para decirte el error exacto que me genera con la 2da funcion.


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 3 Julio 2016, 17:42 pm
Muchas gracias espero entonces con el codigo que me dejastes anteriormente me tira la foto cuando esta en FullScreen en Negro completamente lo que no mire es en que resolucion estaba trabajando.

Se toma en negro por que como intenté explicarte antes de tomar la captura primero debes minimizar y maximizar el juego para que la imagen estática del juego se actualice. Es complicado esta mierd@ de DirectX y no comprendo del todo como funciona en ese sentido, pero hay que hacer eso ...si utilizamos la metodología Win32/GDI.



De todas formas aun quisiera sino te molesta tratar de ejecutar con exito el tuyo para tenerlo como otra opción.

Lamentablemente eso no va a ser posible, no he logrdo solucionar el problema de no poder "actualizar" la imagen de forma automatizada (quiero decir, sin minimizar y maximizar manualmente la ventana del juego), y he probado técnicas que en un principio deberian funcionar (funciones: ShowWindow, SetForegroundwindow, etc) pero no, eso no funciona, DirectX parece trabajar de una forma compleja la ventana.

Si ni siquiera Windows por si solo ni tampoco (algunas)aplicaciones profesionales pueden tomar una captura en condiciones del juego cuando éste está en fullscreen, mucho menos soy capaz de hacerlo yo sin ser un experto en la materia de gráficos. Es complicado al menos.

Es necesario evitar las limitaciones gráficas del modelo Win32 y recurrir a una metodología más potente como DirectX para tomar ese tipo de captura, por ejemplo usando el wrapper de SharpDX para .NET.



Bueno nada ya espero entonces por esta que trabaja a la par con DirectX.

Aquí tienes. Con esta metodología basada en DirectX ya si que no va a haber problemas para tomar la captura del juego en fullscreen.

Código
  1. Imports System.Drawing.Imaging
  2. Imports System.IO
  3.  
  4. Imports SharpDX
  5. Imports SharpDX.Direct3D11
  6. Imports SharpDX.DXGI
  7. Imports Device = SharpDX.Direct3D11.Device
  8. Imports MapFlags = SharpDX.Direct3D11.MapFlags
  9.  
  10. 'Imports Elektro.Imaging.Tools
  11.  
  12. Namespace Test
  13.  
  14.    Public NotInheritable Class SharpDXUtil
  15.  
  16.        ''' ----------------------------------------------------------------------------------------------------
  17.        ''' <summary>
  18.        ''' Capture the screen output using the default graphics card adapter.
  19.        ''' <para></para>
  20.        ''' This DirectX based methodology is useful to take screenshot of games that are running in full screen.
  21.        ''' <para></para>
  22.        ''' However, using this methodology for other common desktop screen captures will produce unexpected results (such as wrong colors);
  23.        ''' so for common screenshots you should use the methods exposed in <see cref="Elektro.Imaging.Tools.ImageUtil"/> instead.
  24.        ''' </summary>
  25.        ''' ----------------------------------------------------------------------------------------------------
  26.        ''' <returns>
  27.        ''' The resulting <see cref="System.Drawing.Image"/>.
  28.        ''' </returns>
  29.        ''' ----------------------------------------------------------------------------------------------------
  30.        Public Shared Function TakeScreenshot() As Image
  31.  
  32.            Return SharpDXUtil.TakeScreenshot(adapterIndex:=0)
  33.  
  34.        End Function
  35.  
  36.        ''' ----------------------------------------------------------------------------------------------------
  37.        ''' <summary>
  38.        ''' Captures the screen output using the specified graphics card adapter.
  39.        ''' <para></para>
  40.        ''' This DirectX based methodology is useful to take screenshot of games that are running in full screen.
  41.        ''' <para></para>
  42.        ''' However, using this methodology for other common desktop screen captures will produce unexpected results (such as wrong colors);
  43.        ''' so for common screenshots you should use the methods exposed in <see cref="Elektro.Imaging.Tools.ImageUtil"/> instead.
  44.        ''' </summary>
  45.        ''' ----------------------------------------------------------------------------------------------------
  46.        ''' <param name="adapterIndex">
  47.        ''' The index of the graphics card adapter.
  48.        ''' <para></para>
  49.        ''' Set this value to <c>0</c> if you don't have more than one graphics card.
  50.        ''' </param>
  51.        ''' ----------------------------------------------------------------------------------------------------
  52.        ''' <returns>
  53.        ''' The resulting <see cref="System.Drawing.Image"/>.
  54.        ''' </returns>
  55.        ''' ----------------------------------------------------------------------------------------------------
  56.        Public Shared Function TakeScreenshot(ByVal adapterIndex As Integer) As Image
  57.  
  58.            Dim factory As Factory1
  59.            Dim adapter As Adapter1
  60.            Dim device As Device
  61.            Dim output As Output
  62.            Dim output1 As Output1
  63.            Dim scrSize As Size
  64.            Dim textureDesc As Texture2DDescription
  65.            Dim screenTexture As Texture2D
  66.            Dim duplicatedOutput As OutputDuplication
  67.            Dim captureDone As Boolean = False
  68.            Dim capture As Bitmap = Nothing
  69.            Dim i As Integer = 0
  70.  
  71.            factory = New Factory1()
  72.            adapter = factory.GetAdapter1(adapterIndex)
  73.            device = New Device(adapter)
  74.            output = adapter.GetOutput(adapterIndex)
  75.            output1 = output.QueryInterface(Of Output1)()
  76.            scrSize = New Size(width:=CType(output.Description.DesktopBounds, SharpDX.Rectangle).Width,
  77.                               height:=CType(output.Description.DesktopBounds, SharpDX.Rectangle).Height)
  78.  
  79.            ' Create Staging texture CPU-accessible.
  80.            textureDesc = New Texture2DDescription() With {
  81.                .CpuAccessFlags = CpuAccessFlags.Read,
  82.                .BindFlags = BindFlags.None,
  83.                .Format = Format.B8G8R8A8_UNorm,
  84.                .Width = scrSize.Width,
  85.                .Height = scrSize.Height,
  86.                .OptionFlags = ResourceOptionFlags.None,
  87.                .MipLevels = 1,
  88.                .ArraySize = 1,
  89.                .SampleDescription = New SampleDescription With {.Count = 1, .Quality = 0},
  90.                .Usage = ResourceUsage.Staging
  91.            }
  92.            screenTexture = New Texture2D(device, textureDesc)
  93.            duplicatedOutput = output1.DuplicateOutput(device)
  94.  
  95.            While Not (captureDone)
  96.                Try
  97.                    Dim screenResource As DXGI.Resource = Nothing
  98.  
  99.                    Dim duplicateFrameInformation As OutputDuplicateFrameInformation
  100.  
  101.                    ' Try to get duplicated frame within given time.
  102.                    duplicatedOutput.AcquireNextFrame(10000, duplicateFrameInformation, screenResource)
  103.  
  104.                    If (i > 0) Then
  105.                        ' Copy resource into memory that can be accessed by the CPU.
  106.                        Using screenTexture2D As Texture2D = screenResource.QueryInterface(Of Texture2D)()
  107.                            device.ImmediateContext.CopyResource(screenTexture2D, screenTexture)
  108.                        End Using
  109.  
  110.                        ' Get the desktop capture texture.
  111.                        Dim mapSource As DataBox = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None)
  112.  
  113.                        ' Create the bitmap.
  114.                        capture = New System.Drawing.Bitmap(scrSize.Width, scrSize.Height, PixelFormat.Format32bppArgb)
  115.                        Dim boundsRect As New System.Drawing.Rectangle(0, 0, scrSize.Width, scrSize.Height)
  116.  
  117.                        ' Copy pixels from screen capture Texture to GDI bitmap.
  118.                        Dim mapDest As BitmapData = capture.LockBits(boundsRect, ImageLockMode.WriteOnly, capture.PixelFormat)
  119.                        Dim sourcePtr As IntPtr = mapSource.DataPointer
  120.                        Dim destPtr As IntPtr = mapDest.Scan0
  121.  
  122.                        For y As Integer = 0 To (scrSize.Height - 1)
  123.                            ' Copy a single line.
  124.                            Utilities.CopyMemory(destPtr, sourcePtr, (scrSize.Width * 4))
  125.  
  126.                            ' Advance pointers.
  127.                            sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch)
  128.                            destPtr = IntPtr.Add(destPtr, mapDest.Stride)
  129.                        Next y
  130.  
  131.                        capture.UnlockBits(mapDest)
  132.                        device.ImmediateContext.UnmapSubresource(screenTexture, 0)
  133.                        captureDone = True
  134.                    End If
  135.  
  136.                    screenResource.Dispose()
  137.                    duplicatedOutput.ReleaseFrame()
  138.  
  139.                Catch e As SharpDXException
  140.                    If (e.ResultCode.Code <> DXGI.ResultCode.WaitTimeout.Result.Code) Then
  141.                        Throw e
  142.                    End If
  143.  
  144.                End Try
  145.  
  146.                i += 1
  147.  
  148.            End While
  149.  
  150.            If (duplicatedOutput IsNot Nothing) Then
  151.                duplicatedOutput.Dispose()
  152.            End If
  153.  
  154.            If (screenTexture IsNot Nothing) Then
  155.                screenTexture.Dispose()
  156.            End If
  157.  
  158.            If (output1 IsNot Nothing) Then
  159.                output1.Dispose()
  160.            End If
  161.  
  162.            If (output IsNot Nothing) Then
  163.                output.Dispose()
  164.            End If
  165.  
  166.            If (device IsNot Nothing) Then
  167.                device.Dispose()
  168.            End If
  169.  
  170.            If (adapter IsNot Nothing) Then
  171.                adapter.Dispose()
  172.            End If
  173.  
  174.            If (factory IsNot Nothing) Then
  175.                factory.Dispose()
  176.            End If
  177.  
  178.            Return capture
  179.  
  180.        End Function
  181.  
  182.    End Class
  183.  
  184. End Namespace

Ejemplo de uso:
Código
  1. Test.SharpDXUtil.TakeScreenshot().Save("C:\Screenshot.png")

PD: Creo no hace falta decirlo, pero por si acaso: Necesitas descargar la librería de SharpDX y para usar el código de arriba debes referenciar los siguientes ensamblados: SharpDX.dll, SharpDX.Direct3D11.dll, SharpDX.DXGI.dll y SharpDX.Mathematics.dll

Saludos


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 5 Julio 2016, 16:54 pm
Disculpa la demora es que tuve problemas con el internet.

En cuanto llegue a casa lo pruebo.

Salu2 y millon de gracias.


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 6 Julio 2016, 03:19 am
Hola,

El código funciona genial mientras no me meta en un juego  :xD :xD

Me da un "access denaid" investigaré a ver que puede estar sucediendo.

Aqui te pongo una captura del error y te pongo la linea en la que ocurre.

Código
  1. duplicatedOutput = output1.DuplicateOutput(device)


(https://i.imgsafe.org/c5c7c97fa5.png)


Título: Re: Capturar Foto de Juego !!
Publicado por: Eleкtro en 6 Julio 2016, 03:36 am
Yo ahí no puedo intervenir, todo eso es controlado por SharpDX y es un problema específico (ya que a mi no me ocurre). De todas formas deduzco que ese código genérico HRESULT está indicando un acceso denegado al intentar leer o escribir en la memoria. Publica el problema en la sección de problemas de SharpDX en GitHub: https://github.com/sharpdx/sharpdx/issues

Saludos!


Título: Re: Capturar Foto de Juego !!
Publicado por: TrashAmbishion en 6 Julio 2016, 16:49 pm
Publicado, ahora resta esperar, mientras sigo con los remiendos usando FRAPS y simulando la tecla de F10. !

 :xD :xD :xD

No encuentro entre los ISSUES de alla nada relacionado con esto, me falto probar con las otras DLL's que viene en el mismo paquete quien sabe y si rula !

Salu2 y gracias !