This can't be the best way to do it?

So I started writing a class that allows someone to simulate keyboard input based upon a string, now is parsing the string with a huge else/if in order to get the character's VK_KEY hexadecimal counterpart used in Windows' keyboard functions the best way of doing this?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <Windows.h>
#include <string>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

class kbd
{
    int * key_buffer;
    int string_len;
    public:
    void write(std::string s, bool enter)
    {
        string_len = s.length();
        if(enter)
        {
            key_buffer = new int[string_len + 1];
            key_buffer[string_len+1] = 0x0D; /*make the final item enter*/
        }
        else
        {
        key_buffer = new int[string_len];
        }
        for(int i = 0; i < string_len; i++)
        {
            if(s[i] == 'a')
            {
                key_buffer[i] = 0x41;
            }
            /*commence monster else/if to parse the string*/
        }
        //for loop using keyboard events, simulating keyboard input for each item in key_buffer 
        delete key_buffer;
    }
};


Also more or less clueless on dynamic memory, if I'm doing something else wrong just tell me.

It compiled fine but I never tested the semantics of it because the else/if wasn't done
Last edited on
If you meant to write
1
2
if(s[K] == 'a')
   key_buffer[K] = VK_KEY_A;
you could just do
1
2
if( isalpha(s[K]) )
   key_buffer[K] = toupper(s[K]);
For the others, it would depend on how you intend to represent them.
I think you've misunderstood...

0x41 is they virtual keycode that you pass to Windows' keyboard event functions for it to simulate pressing of the a key.

What I'm trying to do here is translate a string into a list of keycodes that the keyboard event can use, and all I'm wondering is if a huge else/if is the easiest way to do this.
Last edited on
I would use a std::map for that. Filling it would still be quite a long piece of code, but you could put that into a separate function in a separate file. Plus indexing a map will actually be faster than consecutive if's (but I don't think that you should worry about performance for this number of elements).

Just a note on ne555's comment. If you look up the virtual key codes for alphanumeric keys - the VK_KEY_* code will match the ascii code of the corresponding uppercase letter. Therefore the code snippet provided by ne555 will work for these characters (and for the others
it would depend on how you intend to represent them
)

Just to wrap it up:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// You may have this map in kbd class as a member, a static member, or outside of it
// It is up to you.  
std::map<char, int> charToVKMap;

void fillCharacterMap() 
{
   charToVKMap['a'] = VK_KEY_A;
   // ...
   charToVKMap['~'] = VK_KEY_RETURN; // you can use such a trick to remove the "enter" flag if you like  
   // ...
}

    void write(std::string s, bool enter)
    {
        // ...  
        for(int i = 0; i < string_len; i++)
        {
                key_buffer[i] = charToVKMap[s[i]];
        }
        // ...

    }


Couple more things for you to sort out:
1) You don't need a key buffer - with this map you can operate directly on the string.
2) (suppose you have a reason to have the key buffer) Why manual memory management? Using std::vector would be safer and cleaner and you will avoid mistakes such as incorrect memory cleanup in line 36 (should be delete [] key_buffer)
3) Why is key_buffer a member variable if it is only used in one function of the class and is not saved between function calls. This should be a local variable in this function. Same goes for string_len.
4) key_buffer[string_len+1] = 0x0D; is incorrect. The arrays are indexed from 0, so the index of the last element of array with length string_len+1 will be string_len: key_buffer[string_len] = 0x0D;
Last edited on
'A'==0x41==VK_KEY_A
The letters follow that.
'A'==0x41==VK_KEY_A
The letters follow that.


Sorry about that I'm quick to jump to conclusions, I didn't make the connection in my head as to what you were describing, though that is very helpful.



2) (suppose you have a reason to have the key buffer) Why manual memory management? Using std::vector would be safer and cleaner and you will avoid mistakes such as incorrect memory cleanup in line 36 (should be delete [] key_buffer)


I'd never used it before I just wanted to give it a go. I've also never used vectors, I'll almost definitely switch the dynamic memory out for vectors though.

3) Why is key_buffer a member variable if it is only used in one function of the class and is not saved between function calls. This should be a local variable in this function. Same goes for string_len.


I have them as member variables because I plan on implementing more functions that will use the same variables whether they retain their values between function calls I'll decide on later.

4) key_buffer[string_len+1] = 0x0D; is incorrect. The arrays are indexed from 0, so the index of the last element of array with length string_len+1 will be string_len: key_buffer[string_len] = 0x0D;


Thanks for pointing that out ^_^

Your map idea was great but before I'd read it I decided on implementing 2 arrays with a-z and 0-9 in them and the VK_KEY hex counterparts and assigning them like:

1
2
3
4
5
6
7
8
9
10
11
        for(int i = 0; i < string_len; i++)
        {
            for(int k = 0; k < 36; k++)
            {
                if(eng_letter_freq[k] == s[i])
                {
                    key_buffer[i] = eng_letter_freq_vk_hex[k];
                    break;
                }
            }
        }


Also haven't tested the semantics/functionality of that code but it does compile fine.

As you'd guess eng_letter_freq is an array with the alphabet but its order is: http://en.wikipedia.org/wiki/Letter_frequency#Relative_frequencies_of_letters_in_the_English_language Just to make it the tiniest bit more efficient.


Your way was better but I've already done the tedious typing so what the heck.

Nope my way was an embarrassment to programmers and presented several bugs and your (ne555) 2 line implementation of:

1
2
3
4
        for(int i = 0; i < string_len; i++)
        {
            key_buffer[i] = toupper(s[i]);
        }


works perfectly (for plain text).
Last edited on
Come back again for feedback since I've finished with it now. I ended up re-writing it because the previous way I was doing it was just terrible.

Here it is:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <Windows.h>
#include <string>
#include <cstdlib>
#include <time.h>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

#define SHIFT 0x10
#define CTRL 0x11
#define ALT 0x12
#define ENTER 0x0D
#define BACKSPACE 0x08
#define NOMOD 0

class kbd
{
    BYTE * key_buffer;
    public:
    void write(std::string s, bool enter, bool human/*type in a human-like manner at a moderate typing speed*/)
    {
        srand(time(NULL));
        int buffer_size = s.length();
        key_buffer = new BYTE[buffer_size];
        for(int i = 0; i < buffer_size; i++)
        {
            key_buffer[i] = s[i];
        }
        for(int i = 0; i < buffer_size; i++)
        {
            if(isalpha(key_buffer[i]))
            {
                if(isupper(key_buffer[i]))
                {
                    press(key_buffer[i], human, SHIFT);
                }
                else
                {
                    press(toupper(key_buffer[i]), human, NOMOD);
                }
            }
            else
            {
                short scan = VkKeyScan(key_buffer[i]);
                key_buffer[i] = LOBYTE(scan);
                switch(HIBYTE(scan))
                {
                    case 1:
                        press(key_buffer[i], human, SHIFT);
                        break;
                    case 2:
                        press(key_buffer[i], human, CTRL);
                        break;
                    case 4:
                        press(key_buffer[i], human, ALT);
                        break;
                    default:
                        press(key_buffer[i], human, NOMOD);
                        break;
                }
            }
        }
        if(enter)
        {
            press(ENTER, human, NOMOD);
        }
        delete[] key_buffer;
        return;
    }
    void press(BYTE key, bool human, BYTE modKey)
    {
        if(human){Sleep(rand() % 40 + 10);} // approximate time taking for a user to move their finger from one key to the next.
        if(modKey)
        {
            if(human)
            {
                keybd_event(modKey, NULL, NULL, NULL); //modifier key down
                Sleep(rand() % 55 + 15);
                keybd_event(key, NULL, NULL, NULL); //key down
                Sleep(rand() % 15 + 5);
                keybd_event(key, NULL, KEYEVENTF_KEYUP, NULL); //key up
                Sleep(rand() % 55 + 15);
                keybd_event(modKey, NULL, KEYEVENTF_KEYUP, NULL); //modifier key up
            }
            else
            {
                keybd_event(modKey, NULL, NULL, NULL); //modifier key down
                keybd_event(key, NULL, NULL, NULL); //key down
                keybd_event(key, NULL, KEYEVENTF_KEYUP, NULL); //key up
                keybd_event(modKey, NULL, KEYEVENTF_KEYUP, NULL); //modifier key up
            }
            return;
        }
        else
        {
            if(human)
            {
                keybd_event(key, NULL, NULL, NULL);
                Sleep(rand() % 15 + 5);
                keybd_event(key, NULL, KEYEVENTF_KEYUP, NULL);
            }
            else
            {
                keybd_event(key, NULL, NULL, NULL);
                keybd_event(key, NULL, KEYEVENTF_KEYUP, NULL);
            }
            return;
        }
    }

};


I know that key_buffer doesn't need to be dynamically allocated memory, it did previously though. I'll change it to an array soon enough. (Also doesn't need to be a member of kbd but local to the write function, will change that too)

Didn't *really* need to define TRUE and FALSE because I'm certain Windows.h already does it, if it doesn't I just like caps :D.

Also would the #define s for SHIFT/ALT/CTRL be better in an enum rather than #define s?

Any improvements I could make?
Didn't *really* need to define TRUE and FALSE because I'm certain Windows.h already does it

No, because C++ already has true and false which are actually real bool literals and not integers.
I just like it in caps that's all (even though it is extremely redundant code), thought it would look cooler if you you were passing keyboard.write("somestuff", TRUE, TRUE) rather than keyboard.write("somestuff", true, true) or keyboard.write("somestuff", 1, 1)
Last edited on
Topic archived. No new replies allowed.