Consulta Coordenadas de Ventana en segundo plano

Iniciado por rigorvzla, 17 Marzo 2019, 22:44 PM

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

rigorvzla

Hola amigos, tengo una gran pregunta y duda, estoy haciendo un autoclicker (si se que existen muchos por ahi) pero este cumple exigencias particulares, y es que pueda enviar el click  a una aplicacion especifica en segundo plano SIN colocar el raton osea el puntoro real.

el programa en si ya esta... la parte de enviar el click al proceso podria decirse que si, mas no se, como a un proceso especifico, o bueno en este caso la ventana de dicho proceso  se clicke en un punto especifico de dicha ventana, imagino que es el principio de enviar una pulsacion de tecla , el click, eso lo se pero no sabria como empezar con las coordenadas de laventana o mas aun en segundo plano (minimizada).

Gracias de antemano

Eleкtro

#1
Para enviar pulsaciones ya sea del teclado o el ratón a una ventana minimizada, puedes usar la función PostMessage de la API de Windows:


Pasos a seguir para enviar la pulsación del botón izquierdo del ratón a una ventana minimizada:


  • Localizar la ventana que pretendes clickar, ya sea la ventana principal de un proceso, o una ventana hija ( ej. la ventana de un botón ). Para esto último deberás documentarte sobre las funciones FindWindowEx, EnumWindows, EnumChildWindows y relacionadas, o bien puedes recurrir a la API administrada de Microsoft UI Automation:
  • Activar dicha ventana enviandole el mensaje de ventana WM_ACTIVATE.
  • Enviarle a esa ventana el mensaje WM_LBUTTONDOWN seguido de WM_LBUTTONUP; o solamente el mensaje BM_CLICK en el caso de tratarse de la ventana de un botón.

No caigas en el típico error de enviarle el mensaje WM_LBUTTONDOWN y WM_LBUTTONUP a la ventana principal de un proceso posicionando las coordenadas sobre un control/ventana hija para clickarlo, por que en principio eso no te va a funcionar así; tienes que enviarle los mensajes a la ventana exacta que pretendas clickar, insisto.




Aquí tienes todo lo necesario:

( código extraido de mi librería comercial DevCase for .NET Faamework y modificado para adaptarse a las circunstancias. )

NativeMethods.vb
Código (vbnet) [Seleccionar]
' ***********************************************************************
' Author   : ElektroStudios
' Modified : 14-March-2019
' ***********************************************************************

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Imports "

Imports System.Diagnostics.CodeAnalysis

#End Region

#Region " NativeMethods "

' Namespace DevCase.Interop.Unmanaged.Win32

''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Platform Invocation methods (P/Invoke), access unmanaged code.
''' <para></para>
''' This class does not suppress stack walks for unmanaged code permission.
''' <see cref="Global.System.Security.SuppressUnmanagedCodeSecurityAttribute"/> must not be applied to this class.
''' <para></para>
''' This class is for methods that can be used anywhere because a stack walk will be performed.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <remarks>
''' <see href="https://msdn.microsoft.com/en-us/library/ms182161.aspx"/>
''' </remarks>
''' ----------------------------------------------------------------------------------------------------
Public NotInheritable Class NativeMethods

#Region " Constructors "

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Prevents a default instance of the <see cref="NativeMethods"/> class from being created.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   <DebuggerNonUserCode>
   Private Sub New()
   End Sub

#End Region

#Region " User32.dll "

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Places (posts) a message in the message queue associated with the thread that created
   ''' the specified window and returns without waiting for the thread to process the message.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <remarks>
   ''' <see href="https://msdn.microsoft.com/es-es/library/windows/desktop/ms644944%28v=vs.85%29.aspx"/>
   ''' </remarks>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <param name="hWnd">
   ''' Handle to the window whose window procedure will receive the message.
   ''' <para></para>
   ''' If this parameter is <see cref="WindowMessages.HWND_Broadcast"/>,
   ''' the message is sent to all top-level windows in the system,
   ''' including disabled or invisible unowned windows, overlapped windows, and pop-up windows;
   ''' but the message is not sent to child windows.
   ''' </param>
   '''
   ''' <param name="message">
   ''' Specifies the message to be sent.
   ''' </param>
   '''
   ''' <param name="wparam">
   ''' Specifies additional message-specific information.
   ''' </param>
   '''
   ''' <param name="lparam">
   ''' Specifies additional message-specific information.
   ''' </param>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <returns>
   ''' If the function succeeds, the return value is <see langword="True"/>.
   ''' <para></para>
   ''' If the function fails, the return value is <see langword="False"/>.
   ''' <para></para>
   ''' To get extended error information, call <see cref="Marshal.GetLastWin32Error()"/>.
   ''' </returns>
   ''' ----------------------------------------------------------------------------------------------------
   <SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible", Justification:="Visible for API users")>
   <DllImport("User32.dll", SetLastError:=True)>
   Public Shared Function PostMessage(ByVal hWnd As IntPtr,
                                      ByVal message As Integer,
                                      ByVal wparam As IntPtr,
                                      ByVal lparam As IntPtr
   ) As <MarshalAs(UnmanagedType.Bool)> Boolean
   End Function

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Sends the specified message to a window or windows.
   ''' <para></para>
   ''' The SendMessage function calls the window procedure for the specified window
   ''' and does not return until the window procedure has processed the message.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <remarks>
   ''' <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms644950%28v=vs.85%29.aspx"/>
   ''' </remarks>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <param name="hWnd">
   ''' A handle to the window whose window procedure will receive the message.
   ''' </param>
   '''
   ''' <param name="msg">
   ''' The message to be sent.
   ''' </param>
   '''
   ''' <param name="wParam">
   ''' Additional message-specific information.
   ''' </param>
   '''
   ''' <param name="lParam">
   ''' Additional message-specific information.
   ''' </param>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <returns>
   ''' The return value specifies the result of the message processing; it depends on the message sent.
   ''' </returns>
   ''' ----------------------------------------------------------------------------------------------------
   <SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible", Justification:="Visible for API users")>
   <DllImport("User32.dll", SetLastError:=True)>
   Public Shared Function SendMessage(ByVal hWnd As IntPtr,
                                      ByVal msg As Integer,
                                      ByVal wParam As IntPtr,
                                      ByVal lParam As IntPtr
   ) As IntPtr
   End Function

#End Region

End Class

' End Namespace

#End Region


NativeUtil.vb
Código (vbnet) [Seleccionar]
' ***********************************************************************
' Author   : ElektroStudios
' Modified : 09-September-2017
' ***********************************************************************

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Imports "

#End Region

#Region " Native Util "

' Namespace DevCase.Interop.Unmanaged.Win32

''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Contains win32 related utilites.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <remarks>
''' <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx"/>
''' </remarks>
''' ----------------------------------------------------------------------------------------------------
Public NotInheritable Class NativeUtil

#Region " Constructors "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Prevents a default instance of the <see cref="NativeUtil"/> class from being created.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerNonUserCode>
       Private Sub New()
       End Sub

#End Region

#Region " Public Methods "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Creates a (32-Bit Unsigned Integer) DWORD value from a LOWORD and a HIWORD.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <example> This is a code example.
       ''' <code>
       ''' Dim value as UInteger = MakeDWord(UShort.MaxValue, UShort.MaxValue)
       ''' </code>
       ''' </example>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="loWord">
       ''' The low-order WORD.
       ''' </param>
       '''
       ''' <param name="hiWord">
       ''' The high-order WORD.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <returns>
       ''' The resulting DWORD value.
       ''' </returns>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Shared Function MakeDWord(ByVal loWord As UShort, ByVal hiWord As UShort) As UInteger
           Dim loBytes As Byte() = BitConverter.GetBytes(loWord)
           Dim hiBytes As Byte() = BitConverter.GetBytes(hiWord)
           Dim combined As Byte() = loBytes.Concat(hiBytes).ToArray()

           Return BitConverter.ToUInt32(combined, 0)
       End Function

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Creates a lParam value from two <see cref="System.Int32"/> values.
       ''' <para></para>
       ''' You must call this method overload if you need to use negative values.
       ''' <para></para>
       ''' <seealso cref="Windows.Forms.Message.LParam"/>
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <remarks>
       ''' <see href="https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-makelparam"/>
       ''' </remarks>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <example> This is a code example.
       ''' <code>
       ''' Dim lParam as IntPtr = MakeLParam(Integer.MaxValue, Integer.MaxValue)
       ''' </code>
       ''' </example>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="lo">
       ''' The low-order <see cref="System.Int32"/> value.
       ''' </param>
       '''
       ''' <param name="hi">
       ''' The high-order <see cref="System.Int32"/> value.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <returns>
       ''' The resulting lParam value.
       ''' </returns>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Shared Function MakeLParam(ByVal lo As Integer, ByVal hi As Integer) As IntPtr
           Dim loBytes As Byte() = {BitConverter.GetBytes(lo)(0), BitConverter.GetBytes(lo)(1)}
           Dim hiBytes As Byte() = {BitConverter.GetBytes(hi)(0), BitConverter.GetBytes(hi)(1)}
           Dim combined As Byte() = loBytes.Concat(hiBytes).ToArray()

           Return New IntPtr(BitConverter.ToInt32(combined, 0))
       End Function

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Creates a lParam value from two <see cref="System.UInt16"/> values.
       ''' <para></para>
       ''' <seealso cref="Windows.Forms.Message.LParam"/>
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <remarks>
       ''' <see href="https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-makelparam"/>
       ''' </remarks>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <example> This is a code example.
       ''' <code>
       ''' Dim lParam as IntPtr = MakeLParam(UShort.MaxValue, UShort.MaxValue)
       ''' </code>
       ''' </example>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="loWord">
       ''' The low-order WORD.
       ''' </param>
       '''
       ''' <param name="hiWord">
       ''' The high-order WORD.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <returns>
       ''' The resulting lParam value.
       ''' </returns>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Shared Function MakeLParam(ByVal loWord As UShort, ByVal hiWord As UShort) As IntPtr
           Return New IntPtr(MakeDWord(loWord, hiWord))
       End Function

#End Region

   End Class

' End Namespace

#End Region


+

( código hecho al vuelo. )

Código (vbnet) [Seleccionar]
Const WM_ACTIVATE As Integer = &H6      ' https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-activate
Const WM_LBUTTONDOWN As Integer = &H201 ' https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-lbuttondown
Const WM_LBUTTONUP As Integer = &H202   ' https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-lbuttonup
Const MK_LBUTTON As Integer = &H1       ' https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-lbuttondown
Const BM_CLICK As Integer = &HF5        ' https://docs.microsoft.com/en-us/windows/desktop/controls/bm-click

Public Shared Sub SendLeftClickToWindow(ByVal hWnd As IntPtr, ByVal pt As Point)
   Dim wParam As IntPtr = New IntPtr(MK_LBUTTON)
   Dim lParam As IntPtr = NativeUtil.MakeLParam(pt.X, pt.Y)

   NativeMethods.SendMessage(hWnd, WM_ACTIVATE, IntPtr.Zero, IntPtr.Zero)

   NativeMethods.PostMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam)
   Thread.Sleep(100)
   NativeMethods.PostMessage(hWnd, WM_LBUTTONUP, IntPtr.Zero, lParam)
End Sub

Public Shared Sub SendLeftClickToButton(ByVal hWnd As IntPtr)
   NativeMethods.SendMessage(hWnd, WM_ACTIVATE, IntPtr.Zero, IntPtr.Zero)
   NativeMethods.PostMessage(hWnd, BM_CLICK, IntPtr.Zero, IntPtr.Zero)
End Sub





Ejemplo para enviar la pulsación del botón izquierdo del ratón a la ventana principal de un proceso:
Código (vbnet) [Seleccionar]
Dim hWnd As IntPtr = Process.GetProcessById(...).MainWindowHandle
Dim pt As New Point(0, 0)
SendLeftClickToWindow(hWnd, pt)


Ejemplo para enviar la pulsación del botón izquierdo del ratón a una ventana de tipo BUTTON:
Código (vbnet) [Seleccionar]
Dim hWnd As IntPtr = New IntPtr(...)
SendLeftClickToButton(hWnd)


Si necesitas usar coordenadas negativas (Int32), en principio la sobrecarga de la función NativeUtil.MakeLParam que he compartido debería funcionar correctamente (para ser sinceros no he testeado mucho esa sobrecarga), de lo contrario debes adaptar el código teniendo en cuenta lo siguiente:

Y aquí tienes algo más de info y ejemplos que publiqué en otra pregunta relacionada:

EDITO: acabo de darme cuenta de que eres el mismo usuario que publicó esa otra pregunta... joder entonces todo esto que expliqué ya deberías saberlo, jeje.

Eso es todo. Saludos.








rigorvzla

calla elektro como siempre dando en el clavo, si en efecto era el otro ususario que pregunto lo del enviar una pulsacion a un proceso jejejje y si ya esa parte la sabia, de todos modos esta perfecta tu explicacion ya que me pone mas claro la respuesta, por otro lado, me surgio una pregunta.

Es posible enviar un click a la posicion X. Y especifica de una ventana minimizada sin maximizar?

Ejemplo: Tengo una ventana de un proceso de "notepad" quiero hacer click en el centro de la ventana, esta ventana esta minimizada y aun asi no fallar al enviar el click al proceso u coordenada especifica de ESA ventana.

Lograr esto es posible?

Yo, gracias a tu respuesta al tema anterior, logre enviar la pulsacion de tecla a un proceso que elijo de un listbox y resulto maravilloso, pero en el caso del click (lo veo mas complicado ya que es necesario elegir una posicion para hacer dicho click y lo veo dificil) es otro tema mas complicado y no se si se puede lograr.

Gracias por esta respuesta me da pie por donde empezar  a investigar.

Eleкtro

#3
Cita de: rigorvzla en 18 Marzo 2019, 14:54 PM
Es posible enviar un click a la posicion X. Y especifica de una ventana minimizada sin maximizar?

Sí, eso es lo que te acabo de explicar como hacer. Siempre puedes diseñar un Form de prueba con el que practicar enviandole mensajes con PostMessage y ver el comportamiento del Form al recibir estos mensajes de entrada del ratón cuando está minimizada, controlando los eventos Form.MouseDown, Form.MouseUp y Form.MouseClick o directamente el procedimiento de ventana WndProc. Pero vamos, que eso ya lo comprobé yo con una ventana minimizada y te aseguro que se puede clickar en X,Y coordenadas sin necesidad de maximizarla, de la forma que ya expliqué.

De todas formas siempre existe la rara posibilidad de que dependiendo de como haya sido programada una aplicación, la app pueda ignorar el input del teclado y/o ratón intencionadamente cuando está minimizada. Como alternativa a esto, siempre puedes colocar la ventana fuera de los límites visibles de la pantalla (off-screen), en vez de minimizarla.

Saludos.








rigorvzla

Perfecto entocnes me pondre a leer esto para poder entebnderlo y teniendo en cuenta que programo en C# debere convertir todo esto para q pueda funcionarme gracias nuevamente elektro me pondre a ello inmediatamnete