-3

I have made the following C++ program to highlight the last word before the cursor in a typing interface by sending Control+Shift+Left and then copy it to clipboard by sending Control+C.

#define WINVER 0x0500
#include <windows.h>
#include <Winuser.h>
using namespace std;int main() {


    // Create a generic keyboard event structure
    INPUT ip;
    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0;
    ip.ki.time = 0;
    ip.ki.dwExtraInfo = 0;

while(true) {

if( GetKeyState(VK_LMENU) & 0x8000 ) {
Sleep(200);


        // Press the "Ctrl" key
        ip.ki.wVk = VK_CONTROL;
        ip.ki.dwFlags = 0; // 0 for key press
        SendInput(1, &ip, sizeof(INPUT));

        // Press the "Shift" key
        ip.ki.wVk = VK_SHIFT;
        ip.ki.dwFlags = 0; // 0 for key press
        SendInput(1, &ip, sizeof(INPUT));

        // Press the "Left" key
        ip.ki.wVk = VK_LEFT;
        ip.ki.dwFlags = 0; // 0 for key press
        SendInput(1, &ip, sizeof(INPUT));

        // Release the "Left" key
        ip.ki.wVk = VK_LEFT;
        ip.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(1, &ip, sizeof(INPUT));

        // Release the "Shift" key
        ip.ki.wVk = VK_SHIFT;
        ip.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(1, &ip, sizeof(INPUT));

        // Press the "C" key
        ip.ki.wVk = 'C';
        ip.ki.dwFlags = 0; // 0 for key press
        SendInput(1, &ip, sizeof(INPUT));

        // Release the "C" key
        ip.ki.wVk = 'C';
        ip.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(1, &ip, sizeof(INPUT));

        // Release the "Ctrl" key
        ip.ki.wVk = VK_CONTROL;
        ip.ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(1, &ip, sizeof(INPUT));
}}}

This is meant to work when I press the Left-Alt key. It works fine for words like abc or hello but not with words like #abc or hello%hello. I need to make it work for the entire word. By "entire word" what I mean is any collection of characters that does not include spaces or line breaks.

If you can't solve my problem completely, please know that I am open to workarounds that may work differently or include certain limitations. But I really this so please help.

And please feel free to suggest edits to help me improve this question.

r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
javabhai
  • 23
  • 5
  • 2
    Is it necessary for you to do this my emulating input? It might be simpler to FindWindow for this textbox instead and use [Edit control messages](https://msdn.microsoft.com/en-us/library/windows/desktop/ff485923(v=vs.85).aspx) to do what you need. – r3mus n0x Mar 06 '18 at 06:14
  • @r3musn0x I am a little new to C++. Please explain what you mean in simpler words. – javabhai Mar 06 '18 at 06:18
  • Could you do `SHIFT+LEFT` and read the result until it contains an unwanted character, then do `SHIFT+RIGHT`once? – Yunnosch Mar 06 '18 at 06:25
  • @r3musn0x I think I understand. The functionality I posted here is a small part of a functionality I am trying to make which is meant to work in any/most typing interfaces, including input fields, search bars, notepad, word editors, code editors, the comment box I am writhing in right now, etc. Not a specific textbox or application – javabhai Mar 06 '18 at 06:25
  • @Yunnosch that would be excellent if I knew how to do that – javabhai Mar 06 '18 at 06:26
  • @Yunnosch if you do(or even slightly such that you can post me in the right direction) please post an answer or tell me in a comment – javabhai Mar 06 '18 at 06:27
  • 2
    @javabhai It is also a mistake to call `SendInput()` with `nInputs=1` and no delays between events. You should put all of the events into an array and send them all in one call to `SendInput()`. But r3mus is right, this is not a job to solve by simulating keyboard input. Sending window messages directly to the Edit control is better. Using [UI Automation](https://msdn.microsoft.com/en-us/library/windows/desktop/ee684009.aspx) is even better. – Remy Lebeau Mar 06 '18 at 06:30
  • @RemyLebeau I am a newbie in C++ and can't really understand what u mean – javabhai Mar 06 '18 at 06:34
  • You cannot be serious about the formatting. Anyway, see [Is it a bug to pass a single-element array to SendInput?](https://stackoverflow.com/q/46744894/1889329) Also read the documentation for [GetKeyState](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301.aspx). It explicitly calls out, that you need to run a message loop. Regardless, though, you are probably looking for [RegisterHotKey](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646309.aspx) and [UI Automation](https://msdn.microsoft.com/en-us/library/windows/desktop/ee684009.aspx) anyway. – IInspectable Mar 06 '18 at 07:03
  • Besides, when you claim, that it doesn't work with certain words, you are wrong. It works just fine for those examples. The problem is, that your interpretation of what constitutes a word is not the same interpretation of the target widget you have tested this on. I'm sure there are text editors that will agree with your interpretation, but not all will. You decided to offload finding word boundaries to widgets outside your control. You're going to have to live with their rules. Or find a solution that allows you to defined word boundaries. – IInspectable Mar 06 '18 at 07:12

1 Answers1

0

As IInspectable mentioned, obviously what you consider to be a word and what the text field considers to be a word are two different things and you can't really do anything about it. So instead of trying to simulate the input which is not getting you what you want anyway, you should try and retrieve a handle (HWND) for the text field using some combination of FindWindowEx calls.

Now I can't exactly tell you how to find the window you need because I don't know where it is on your system and which application it belongs to. But you might be able to get the required information to do this (window hierarchy and class names) using some tool such as Inspect.

After that you should be able to get the text from the text field and parse it to get the first word:

#include <Windows.h>

int main()
{
    /* You should adjust the following code to whatever criteria
       you are using to choose the text field */
    HWND appWindow = FindWindowEx(GetDesktopWindow(), NULL, NULL, "App window title");
    HWND editControl = FindWindowEx(appWindow, NULL, "EDIT", NULL);

    int size = GetWindowTextLength(editControl) + 1;
    char *text = new char[size];
    GetWindowText(editControl, text, size);

    int cursorPos = 0;
    SendMessage(editControl, EM_GETSEL, (WPARAM) &cursorPos, NULL);

    for (int i = cursorPos; i < size; ++i) {
        if (text[i] == ' ') {
            text[i] = '\0';
            break;
        }
    }

    char *word = &text[cursorPos];
    //do whatever you need with the word here

    delete[] text;
    return 0;
}

I didn't really have the opportunity to test this code and regarding copying the text to the clipboard: it seems to be a lot more complicated task to achieve using Win32 API which is in great detail described here.

r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
  • Not going to work with window-less controls. Those are very common. Well-known implementations are web pages rendered by all major browser, as well as Qt and WPF applications. That, and UWP applications. A better answer is UI Automation. – IInspectable Mar 06 '18 at 09:19
  • Yes, it does sound like a better answer, and if you have it please write it out :) – r3mus n0x Mar 06 '18 at 09:28