Link to home
Start Free TrialLog in
Avatar of da_mango_bros
da_mango_bros

asked on

INSTALIZE ARRAY/STRUCTURE AT DECLARATION - EXTREMELY DIFFICULT

hi.

working with these structures:

typedef struct tagINPUT {
  DWORD   type;
  union {
      MOUSEINPUT      mi;
      KEYBDINPUT      ki;
      HARDWAREINPUT   hi;
  };
} INPUT, *PINPUT;


typedef struct tagKEYBDINPUT {
  WORD      wVk;
  WORD      wScan;
  DWORD     dwFlags;
  DWORD     time;
  ULONG_PTR dwExtraInfo;
} KEYBDINPUT, *PKEYBDINPUT;


i want to get the following result:
type = INPUT_KEYBOARD
ki.wVk = VK_CAPITAL
ki.wScan = 0
ki.dwFlags = KEYEVENTF_EXTENDEDKEY
ki.time = 0
ki.dwExtraInfo = 0

But first union member is MOUSEINPUT mi, so this becomes very hard.

because i am using shared memory in a dll, i must assign the values  upon declaration.


if any1 has any solutions to this problem, then pls let me know

thanks in advance,

suma
Avatar of stefan73
stefan73
Flag of Germany image

Hi da_mango_bros,
> But first union member is MOUSEINPUT mi, so this becomes very hard.

do something like:

switch(type){
    case INPUT_MOUSE:
        /* Display mouse data mi.* */
        break;
    case INPUT_KEYBOARD:
        /* Display keyboard data ki.* */
        break;
    case INPUT_HARDWARE:
        /* Display hardware data hi.* */
        break;
    default:
        /* Error */
}

Cheers,
Stefan
Avatar of jkr
>>because i am using shared memory in a dll, i must assign the values  upon declaration.

You only need to initialize the values with arbitrary data. But, you can simply use

INPUT in = { INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0};
Avatar of da_mango_bros
da_mango_bros

ASKER

i originally tried to use:

INPUT input = {INPUT_KEYBOARD, {VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0}};

but because "mi" is first, i thought that {VK_CAPTIAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0} gets assigned to "mi", rather than to "ki" as required.

is there a way around this?

sefan73:
u misunderstood the question. i need to create a new instance of these structures, assign values to the variables, and then pass the new instances of the structures into functions as parameters.

because i am using shared memory in a dll, i cannot use:

int x;
x = 1;

but must use:

int x = 1;

this becomes difficult because of the union in one of the structures


 
You can use either

INPUT in = { INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0};

or

INPUT in = { INPUT_KEYBOARD, {VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0}};
i gave that a try and it didnt completly work.

here is my code from the HookProc of a keyboard hook, designed to toggle the capslock key each time a key is pressed:

//instalized in the shared memory section:

INPUT input[2] =
{
      {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0},
      {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0}
};

//in the HookProc:

SendInput(2, input, sizeof(INPUT));


it works the first time i press a key (regardless of the state of capslock), but after that it dosnt work for some reason

it must be something to do with the instalization, cuz when i tried creating new variables each time, it worked.
>>//in the HookProc:
>>SendInput(2, input, sizeof(INPUT));

So, why would you need a global shared variable for that, you can simply make the array a local variable of your hooc proc...
da_mango_bros,
> but because "mi" is first, i thought that {VK_CAPTIAL, 0, KEYEVENTF_EXTENDEDKEY,
> 0, 0} gets assigned to "mi", rather than to "ki" as required.

> is there a way around this?

Yes, move the "KEYBDINPUT      ki;" to the top of the union. Unions don't have an order, as every element starts at the same address, anyway.
Stefan
da_mango_bros,
The probably cleanest way is to define INPUT_UNDEFINED as a fourth possible value for type, indicating that there is no real data in the union.

Stefan
>>Yes, move the "KEYBDINPUT      ki;" to the top of the union

Might be a long way down the road. Do you thing mailing that request to Bill Gates directly could help speeding that up? :o)
jkr:

yea that works... but it is ineffecient to create new variables each time a key is pressed

this code works fine:

                                                                KEYBDINPUT keyDown;
                        keyDown.dwExtraInfo = 0;
                        keyDown.dwFlags = KEYEVENTF_EXTENDEDKEY;
                        keyDown.time = 0;
                        keyDown.wScan = 0;
                        keyDown.wVk = VK_CAPITAL;
                        
                        KEYBDINPUT keyUp;
                        keyUp.dwExtraInfo = 0;
                        keyUp.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
                        keyUp.time = 0;
                        keyUp.wScan = 0;
                        keyUp.wVk = VK_CAPITAL;

                        INPUT input[2];
                        input[0].type = input[1].type = INPUT_KEYBOARD;
                        input[0].ki = keyDown;
                        input[1].ki = keyUp;

                        SendInput(2, input, sizeof(INPUT));

but when i change the code, leaving it in the hook proc (i.e. local), to:

INPUT input[2] =
{
     {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0},
     {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0}
};

it dosnt work. clearly the last implementation is cleaner, replacing about 15 lines of code with 5


stefan73:

>> unions dont have an order

omg i never realized that... but can u explain why the code dosnt work then??

>>yea that works... but it is ineffecient to create new variables each time a key is pressed

No. Just create it as

static INPUT input[2] =
{
    {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0},
     {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0}
};

Anyway, you can keep that as a global variable, but the bottom line is that it does not have to be in a shared memory section...
static INPUT input[2] =
{
   {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0},
   {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0}
};

That is wrong as the parameters 2 - 5  get assigned to struct MOUSEINPUT and not to KEYINPUT, thus values like KEYEVENTF_EXTENDEDKEY will not properly assigned to input[0].ki.dwFlags but to input[0].mi.mouseData which has the same storage as input[0].ki.time. Look at this code:

bool operator==(const KEYBDINPUT& k1, const KEYBDINPUT& k2)
{
    return (k1.dwExtraInfo == k2.dwExtraInfo) && 
           (k1.dwFlags == k2.dwFlags) && 
           (k1.time == k2.time) && 
           (k1.wScan == k2.wScan) && 
           (k1.wVk == k2.wVk);
}


bool operator==(const INPUT& i1, const INPUT& i2)
{
    return (i1.type == i2.type) && (i1.ki == i2.ki);
}

void g()
{

    KEYBDINPUT keyDown;
    keyDown.dwExtraInfo = 0;
    keyDown.dwFlags = KEYEVENTF_EXTENDEDKEY;
    keyDown.time = 0;
    keyDown.wScan = 0;
    keyDown.wVk = VK_CAPITAL;
   
    KEYBDINPUT keyUp;
    keyUp.dwExtraInfo = 0;
    keyUp.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
    keyUp.time = 0;
    keyUp.wScan = 0;
    keyUp.wVk = VK_CAPITAL;

    INPUT input[2];
    input[0].type = input[1].type = INPUT_KEYBOARD;
    input[0].ki = keyDown;
    input[1].ki = keyUp;
    INPUT inputx[2] =
    {
        {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0},
        {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0}
    };

    bool b1 = (input[0] == inputx[0]);    // b1 = false;
    bool b2 = (input[1] == inputx[1]);    // b2 = false;
}

You may 'fix' the problem using this:

static INPUT input[2] =
{
   {INPUT_KEYBOARD, VK_CAPITAL, KEYEVENTF_EXTENDEDKEY, 0, 0, 0, 0},
   {INPUT_KEYBOARD, VK_CAPITAL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0, 0, 0}
};

Now KEYBDINPUT is correct as the first two members of KEYBDINPUT have WORD type and third is a DWORD whereas MOUSEINPUT starts with two LONG members. The two WORD members get mapped to the first LONG, and the DWORD get mapped to second LONG.

However, i would use the assign method because that isn't less tricky.

static INPUT input[2] =
{
   {INPUT_KEYBOARD, 0, 0, 0, 0, 0, 0},
   {INPUT_KEYBOARD, 0, 0, 0, 0, 0, 0}
};

static bool init = true;

if (init)
{
    init = false;
    KEYBDINPUT keyDown;
    keyDown.dwExtraInfo = 0;
    keyDown.dwFlags = KEYEVENTF_EXTENDEDKEY;
    keyDown.time = 0;
    keyDown.wScan = 0;
    keyDown.wVk = VK_CAPITAL;
   
    KEYBDINPUT keyUp;
    keyUp.dwExtraInfo = 0;
    keyUp.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
    keyUp.time = 0;
    keyUp.wScan = 0;
    keyUp.wVk = VK_CAPITAL;

    input[0].ki = keyDown;
    input[1].ki = keyUp;

}

Regards, Alex


thanks alex... i see what is going on now.

unfortunately i have to have the declaration in the shared memory section if i want it to be global, because im using a GLOBAL windows hook. (i tested this out btw)

now i have 2 options:

1) putting the stuff in the shared memory section requires instalization at declaration, so i must assign the extra members of MOUSEINPUT to 0 (zero). This is nice and effecient, because the variables are only created and instalized once.

2) putting the stuff in the HookProc means i can use the assign method, but this will result in ineffeciency (due to creating and assigning new variables each time), and also expand 5 lines of code into about 15.


my final question is this:

does assigning the extra members of MOUSEINPUT to 0 (zero), decrease performance at all, or is it completly acceptable?

i.e. is this advisable to use with SendInput:

static INPUT input[2] =
{
   {INPUT_KEYBOARD, VK_CAPITAL, KEYEVENTF_EXTENDEDKEY, 0, 0, 0, 0},
   {INPUT_KEYBOARD, VK_CAPITAL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0, 0, 0}
};

cheers, suma


Suma,

static member assignments wil be done only once. So with

static bool init = true;
if (init)
{
    init = false;
    // do something but only once
}

you are doing nothing that will harm performance. If you need shared memory just copy the well-defined structs from local memory to shared memory using memcpy.

static INPUT input[2] =
{
   {INPUT_KEYBOARD, 0, 0, 0, 0, 0, 0},
   {INPUT_KEYBOARD, 0, 0, 0, 0, 0, 0}
};

static bool init = true;
if (init)
{
    init = false;
    KEYBDINPUT keyDown;
    keyDown.dwExtraInfo = 0;
    keyDown.dwFlags = KEYEVENTF_EXTENDEDKEY;
    keyDown.time = 0;
    keyDown.wScan = 0;
    keyDown.wVk = VK_CAPITAL;
   
    KEYBDINPUT keyUp;
    keyUp.dwExtraInfo = 0;
    keyUp.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
    keyUp.time = 0;
    keyUp.wScan = 0;
    keyUp.wVk = VK_CAPITAL;

    input[0].ki = keyDown;
    input[1].ki = keyUp;

    // Get the shared memory somehow
    char* sharedMemoryPtr = createSharedMemory(...);
    memcpy(sharedMemoryPtr, input, sizeof(input));
}

Hope, that helps

Alex
da_mango_bros,
> >> unions dont have an order

> omg i never realized that... but can u explain why the code dosnt work then??

Just initialize it using an "undefined" state, as originally there is nothing in there and it's of no use to initialize your usion members with dummy data.

As soon as you get your first real data, you can set the values accordingly.

Stefan
so how do i instalize it undifined?
ASKER CERTIFIED SOLUTION
Avatar of sitbon
sitbon

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>Wow you people.. I am disappointed. There is a pretty simple solution...

Good that you came to save us. I thought some others have already been mentioned.
OK, here is another one, and the simplest yet:

change the stuct in the header file to

typedef struct tagINPUT {
  DWORD   type;
      KEYBDINPUT      ki;
} INPUT, *PINPUT;

Remove the union! The struct size/data is the same, it will NOT cause problems.
both of those solutions are exelent... ima use the class one because i prefer that.

sometimes there is an obvious solution but nobody can see it.

good stuff sitbon :)
just to help out any1 else who is reading this thread because they have the same problem:

using a function to perform the instilization does not work if u want to have a two-dimensional array

i used a class:

class Init
{
public:
      INPUT input[2];
      Init()
      {
            input[0].type = input[1].type = INPUT_KEYBOARD;
            input[0].ki.wVk = input[1].ki.wVk = VK_CAPITAL;
            input[0].ki.wScan = input[1].ki.wScan = VK_CAPITAL;
            input[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
            input[1].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
            input[0].ki.time = input[1].ki.time = 0;
            input[0].ki.dwExtraInfo = input[1].ki.dwExtraInfo = 0;
      }
};

the actual instalization is performed here:

Init init;

then later on, in the HookProc:

SendInput(2, init.input, sizeof(INPUT));


thanks to every1 who participated in the thread, esp. alex (itsmeandnobodyelse) and sitbon
>> typedef struct tagINPUT {
>>  DWORD   type;
>>      KEYBDINPUT      ki;
>>} INPUT, *PINPUT;

Redefining the struct tagINPUT to overcome initialisation problems is a brilliant idea ... at the first glance. If you change the original header file that came with your compiler you always have to think on that if you get any update or change the development platform. Even worse it is to make the changes to a copy that got first int include order. There will come a time where your program doesn't work although you didn't change anything and you have no idea (anymore) where it come from.

To save the brilliance of the idea, i would suggest the following:

Define a new type using the struct definition above. Define your variables using the new struct and cast the type when using it as an argument:

typedef struct tagINPUTX {
  DWORD   type;
      KEYBDINPUT      ki;
} INPUTX, *PINPUTX;

INPUTX input[2] =
{
     {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY, 0, 0},
     {INPUT_KEYBOARD, VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0, 0}
};

....

   SendInput(2, (PINPUT)input, sizeof(INPUTX));

Note, that sizeof(..) must use sizeof(INPUTX) as the new struct has a different size.

Regards, Alex


itsmeandnobodyelse, you're probably right, but the the point is this: if you change the header, compile your program, and say change it back, the only thing that would break it is re-arranging or changing the size of the INPUT struct... which is unlikely. I do agree that it it kind of a messy solution in a way, mainly in the sense of "what the hell did he do here?" when someone else reads it down the line :)

da mango - glad you liked my ideas :)
>> the only thing that would break it is re-arranging or changing the size of the INPUT struct... which is unlikely

No, much more likely he will get a new version of the compiler thus overwriting the changes, recompile the project - not remembering that there is a header that must be changed. If there are some years between there is a good chance to overlook that. And what makes it worse is that it will compile without problems. Changing a standard header isn't a good idea as long as you have a cheap alternative.

>> the only thing that would break it is re-arranging or changing the size of the INPUT

That is actually a minor problem as the size is an argument of SendInput.

Regards, Alex

I don't think you really get what I'm saying, but that's ok because the question is answered. It's obvious that changing the header causes those issues.