2010-02-12 20 views
17

Mi aplicación. se ejecutará en el sistema, intente controlar una tecla de acceso directo; cuando el usuario selecciona texto en cualquier ventana y presiona una tecla de acceso rápido, ¿cómo obtengo el texto seleccionado cuando aparece el mensaje WM_HOTKEY?¿Cómo obtengo el texto seleccionado de la ventana enfocada usando la API Win32 nativa?

para capturar el texto en el portapapeles, he intentado enviar Ctrl + C usando keybd_event() y SendInput() a la ventana activa (GetActiveWindow()) y la ventana forground (GetForegroundWindow()); intenté combinaciones entre estos; todo en vano. ¿Puedo obtener el texto seleccionado de la ventana enfocada en Windows con las API del sistema Win32?

+1

¿Estás seguro de que estás obteniendo el asa de la ventana correcta? Puedes probar esto con Spy ++. Además, ¿has probado el viejo WM_GETTEXT? – Luke

+0

¿Qué versión de Windows? –

+0

@ Aaron: Windows XP y posterior; ambos 32 y 64 bits; básicamente, estoy transfiriendo mi aplicación (http://artha.sourceforge.net/) a Windows y necesito esta característica para continuar. – legends2k

Respuesta

19

TL; DR: Sí, hay una forma de hacerlo utilizando las API del sistema win32, pero es difícil de implementar correctamente.

WM_COPY y WM_GETTEXT pueden funcionar, pero no en todos los casos. Dependen de que la ventana de recepción maneje la solicitud correctamente y, en muchos casos, no. Déjame revisar una posible forma de hacer esto. Puede que no sea tan simple como esperabas, pero ¿qué hay en el mundo lleno de aventuras de la programación de win32? Listo? De acuerdo. Vamonos.

Primero necesitamos obtener la identificación HWND de la ventana de destino. Hay muchas maneras de hacer esto. Uno de estos enfoques es el que mencionó anteriormente: obtener la ventana de primer plano y luego la ventana con foco, etc. Sin embargo, hay un enorme que muchos olvidan. Después de obtener la ventana de primer plano, debeAttachThreadInput para obtener la ventana con foco. De lo contrario, GetFocus() simplemente devolverá NULL.

Hay una manera mucho más fácil. Simplemente (señorita) use las funciones GUITREADINFO. Es mucho más seguro, ya que evita todos los peligros ocultos asociados con la conexión de su hilo de entrada con otro programa.

LPGUITHREADINFO lpgui = NULL; 
HWND target_window = NULL; 

if(GetGUIThreadInfo(NULL, lpgui)) 
    target_window = lpgui->hwndFocus; 
else 
{ 
    // You can get more information on why the function failed by calling 
    // the win32 function, GetLastError(). 
} 

El envío de las pulsaciones de teclado para copiar el texto es un poco más complicado ...

Vamos a utilizar SendInput en lugar de keybd_event porque es más rápido y, lo más importante, no se puede mal estado por entrada de usuario concurrente u otros programas que simulen pulsaciones de teclas.

Esto significa que el programa tendrá que ejecutarse en Windows XP o posterior, así que, ¡lo siento si está ejecutando 98!

// We're sending two keys CONTROL and 'V'. Since keydown and keyup are two 
// seperate messages, we multiply that number by two. 
int key_count = 4; 

INPUT* input = new INPUT[key_count]; 
for(int i = 0; i < key_count; i++) 
{ 
    input[i].dwFlags = 0; 
    input[i].type = INPUT_KEYBOARD; 
} 

input[0].wVK = VK_CONTROL; 
input[0].wScan = MapVirtualKey(VK_CONTROL, MAPVK_VK_TO_VSC); 
input[1].wVK = 0x56 // Virtual key code for 'v' 
input[1].wScan = MapVirtualKey(0x56, MAPVK_VK_TO_VSC); 
input[2].dwFlags = KEYEVENTF_KEYUP; 
input[2].wVK = input[0].wVK; 
input[2].wScan = input[0].wScan; 
input[3].dwFlags = KEYEVENTF_KEYUP; 
input[3].wVK = input[1].wVK; 
input[3].wScan = input[1].wScan; 

if(!SendInput(key_count, (LPINPUT)input, sizeof(INPUT))) 
{ 
    // You can get more information on why this function failed by calling 
    // the win32 function, GetLastError(). 
} 

There. Eso no fue tan malo, ¿verdad?

Ahora solo tenemos que echar un vistazo a lo que hay en el portapapeles. Esto no es tan simple como pensarías primero. El "portapapeles" en realidad puede contener múltiples representaciones de la misma cosa. La aplicación que está activa cuando copia al portapapeles tiene control sobre exactamente qué colocar en el portapapeles.

Al copiar texto de Microsoft Office, por ejemplo, coloca datos RTF en el portapapeles, junto con una representación de texto sin formato del mismo texto. De esa forma, puede pegarlo en el juego de palabras y el bloc de notas. Wordpad utilizaría el formato de texto enriquecido, mientras que el bloc de notas utilizaría el formato de texto sin formato.

Para este simple ejemplo, supongamos que solo nos interesa el texto sin formato.

if(OpenClipboard(NULL)) 
{ 
    // Optionally you may want to change CF_TEXT below to CF_UNICODE. 
    // Play around with it, and check out all the standard formats at: 
    // http://msdn.microsoft.com/en-us/library/ms649013(VS.85).aspx 
    HGLOBAL hglb = GetClipboardData(CF_TEXT); 
    LPSTR lpstr = GlobalLock(hglb); 

    // Copy lpstr, then do whatever you want with the copy. 

    GlobalUnlock(hglb); 
    CloseClipboard(); 
} 
else 
{ 
    // You know the drill by now. Check GetLastError() to find out what 
    // went wrong. :) 
} 

¡Y ya lo tienes! Solo asegúrese de copiar lpstr a alguna variable que quiera usar, no use lpstr directamente, ya que debemos ceder el control del contenido del portapapeles antes de cerrarlo.

La programación de Win32 puede ser bastante desalentadora al principio, pero después de un tiempo ... sigue siendo desalentador.

¡Salud!

+0

En primer lugar, muchas gracias por tomarse el tiempo para dar una respuesta tan detallada, explicando los matices! Lo intentaré y te llamaré. – legends2k

+0

Esto es todo lo que he tenido que pasar antes en proyectos anteriores que implican ganchos de teclado de bajo nivel. La mayoría solo copian/pegan de esos proyectos y agregan texto descriptivo. ¡Espero eso ayude! – kurige

+1

¡Funcionó! ¡Oh, gracias, después de mucho tiempo, finalmente funcionó! Anteriormente, cuando traté de usar SendInput, yo estaba haciendo lo mismo, pero el problema era la tecla de acceso directo para la que me había registrado. Me registré para un 'Ctrl + Alt + S' y en _WM_HOTKEY_, llamé a SendInput con' Ctrl + C'. Pero cuando el usuario presiona 'Ctrl + Alt + S',' Alt' seguirá bajando cuando virtualmente pase 'Ctrl + C'; cuando cambié la tecla rápida a 'Windows + S' funcionó perfectamente. En una tecla de acceso directo con 'Alt' en la combinación, cuando prácticamente desactivo 'Alt' (KEYEVENTF_KEYUP), funciona también. – legends2k

0

Pruebe SendMessage (WM_COPY, etc.).

+0

Lo intenté también :( – legends2k

3

Intente agregar un Sleep() después de cada SendInput(). Algunas aplicaciones no son tan rápidas para captar la entrada del teclado.

+0

Esto en combinación con la respuesta de kurige lo hizo funcionar! Gracias :) – legends2k

+0

Sí, por ejemplo Chrome no pudo poner el texto seleccionado en el portapapeles antes de que mi proceso lo lea. –

+3

Pero quizás sea mejor si registra su ventana en la cadena de portapapeles y agrega WM_DRAWCLIPBOARD a su aplicación WndProc. De esta forma, recibirá el mensaje después de que la ventana que recibe la entrada modifique el portapapeles. –

Cuestiones relacionadas