问题
I have a very simple chunk of code that is designed to simulate keyboard events. The simple example below should type "Cz" - the shift key goes down, the c key goes down, c comes up and shift comes up. Then the z key goes down and up.
It seems that sometimes the order gets muddled though. When I create a timer to call this routine every second, the output should be CzCzCzCz.... But here's what I get:
CZcZCZCzczCzczCzczCZCZCzCz
I'll run it again:
CzCzCzCzCZCzCZCzCZCzCZCzCZCzCzCz
Different. And equally wrong.
The code:
e1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, true);
CGEventPost(kCGSessionEventTap, e1);
CFRelease(e1);
e2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, true);
CGEventPost(kCGSessionEventTap, e2);
CFRelease(e2);
e3 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, false);
CGEventPost(kCGSessionEventTap, e3);
CFRelease(e3);
e4 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, e4);
CFRelease(e4);
e7 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);
CGEventPost(kCGSessionEventTap, e7);
CFRelease(e7);
e8 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false);
CGEventPost(kCGSessionEventTap, e8);
CFRelease(e8);
Is there something I'm missing in how to implement the keydown and keyup for the shift key? I think this might be a bug - where would I report it?
回答1:
I have found a reliable way to post modified keyboard events - it does not follow the example in Apple's documentation (which doesn't work) but seems to make sense, and most importantly, WORKS.
Rather than sending 'shift key down' and 'shift key up' messages (as instructed in the docs), you need to set a modifier flag on the keypress. Here's how to output an uppercase Z.
CGEventRef event1, event2;
event1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);//'z' keydown event
CGEventSetFlags(event1, kCGEventFlagMaskShift);//set shift key down for above event
CGEventPost(kCGSessionEventTap, event1);//post event
I'm then releasing the 'z' key for completeness (also setting the shift-flag on, though not sure if this is correct).
event2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false);
CGEventSetFlags(event2, kCGEventFlagMaskShift);
CGEventPost(kCGSessionEventTap, event2);
Finally (and bizarrely) you DO need to send the 'key up' event for the shift key:
e5 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, e5);
Don't forget to release your events once you're done with them.
I hope this is useful to someone - it took me a long time to get this to work.
回答2:
The cleanest way for this is bitwise OR'ing the current modifier flags with the flag of your desired modifier(s) , e.g.:
CGEventFlags flags = kCGEventFlagMaskShift;
CGEventRef ev;
CGEventSourceRef source = CGEventSourceCreate (kCGEventSourceStateCombinedSessionState);
//press down
ev = CGEventCreateKeyboardEvent (source, keyCode, true);
CGEventSetFlags(ev,flags | CGEventGetFlags(ev)); //combine flags
CGEventPost(kCGHIDEventTap,ev);
CFRelease(ev);
//press up
ev = CGEventCreateKeyboardEvent (source, keyCode, false);
CGEventSetFlags(ev,flags | CGEventGetFlags(ev)); //combine flags
CGEventPost(kCGHIDEventTap,ev);
CFRelease(ev);
CFRelease(source);
回答3:
I had a similar problem recently. Instead of passing NULL as the first argument of CGEventCreateKeyboardEvent, create an event source like this:
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
and use it while you are creating and posting events. CFRelease it when you are done with it.
回答4:
Any relation to this guy's bug?
回答5:
Like another comment referring to other solutions here, after using uppercase with the Shift mask, a successive call would cause any further intended non-shifted character to be turned into an shifted characters. I figured the call to CGEventCreateKeyboardEvent was somehow saving previous masks, so I purposefully clear the mask of the modifier:
CGEventRef event1, event2;
CGEventSourceRef source = CGEventSourceCreate (kCGEventSourceStateCombinedSessionState);
CGEventFlags flags;
event1 = CGEventCreateKeyboardEvent (source, keyCode, true);
if (upper)
flags = kCGEventFlagMaskShift | CGEventGetFlags(event1);
else
flags = ~kCGEventFlagMaskShift & CGEventGetFlags(event1);
CGEventSetFlags(event1, flags);
CGEventPost(kCGHIDEventTap,event1);
event2 = CGEventCreateKeyboardEvent (source, keyCode, false);
if (upper)
flags = kCGEventFlagMaskShift | CGEventGetFlags(event2);
else
flags = ~kCGEventFlagMaskShift & CGEventGetFlags(event2);
CGEventSetFlags(event2, flags);
CGEventPost(kCGHIDEventTap,event2);
CFRelease(event1);
CFRelease(event2);
CFRelease(source);
回答6:
reliable workaround:
+(void)runScript:(NSString*)scriptText
{
NSDictionary *error = nil;
NSAppleEventDescriptor *appleEventDescriptor;
NSAppleScript *appleScript;
appleScript = [[NSAppleScript alloc] initWithSource:scriptText];
appleEventDescriptor = [appleScript executeAndReturnError:&error];
}
[self runScript:@"tell application \"System Events\" to key code "c" using shift down"];
[self runScript:@"tell application \"System Events\" to key code "z"];
来源:https://stackoverflow.com/questions/2008126/cgeventpost-possible-bug-when-simulating-keyboard-events