Thread: ToUnicodeEx and dead keys inside system wide hooks

  1. #1
    Registered User
    Join Date
    Nov 2005
    Posts
    14

    ToUnicodeEx and dead keys inside system wide hooks

    Hello there. I'm trying to detect which character is produced whenever the user presses a key. For this, I'm using the ToUnicodeEx function, which translates the specified virtual-key code and keyboard state to the corresponding Unicode character, inside a keyboard system wide hook. However, every time the user presses dead keys, such as the ones producing '¨', '~' or '´', strange things happen. Here is sample code of the first version:

    Code:
    private static int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
    {
    	//indicates if any of underlaing events set e.Handled flag
    	bool handled = false;
    
    	if (nCode >= 0)
    	{
    		//read structure KeyboardHookStruct at lParam
    		KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
    
    		// raise KeyPress
    		if (s_KeyPress != null && wParam == WM_KEYDOWN)
    		{
    			bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
    			bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
    
    			byte[] keyState = new byte[256];
    			GetKeyboardState(keyState);
    			System.Text.StringBuilder sbString = new System.Text.StringBuilder();
    			IntPtr HKL = GetKeyboardLayout(0);
    
    			switch (ToUnicodeEx((uint)MyKeyboardHookStruct.VirtualKeyCode,
    				(uint)MyKeyboardHookStruct.ScanCode,
    				keyState,
    				sbString,
    				5,
    				(uint)MyKeyboardHookStruct.Flags,
    				HKL))
    			{
    				case 1:
    					char key = sbString.ToString()[0];
    					if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
    					KeyPressEventArgs e = new KeyPressEventArgs(key);
    					s_KeyPress.Invoke(null, e);
    					handled = handled || e.Handled;
    					break;
    			}
    		}
    	}
    
    	//if event handled in application do not handoff to other listeners
    	if (handled)
    		return -1;
    
    	//forward to other application
    	return CallNextHookEx(s_KeyboardHookHandle, nCode, wParam, lParam);
    }
    With this code I faced the following problem: Whenever I tried to type "á" in notepad, which requires '´' to be pressed before 'a', the result would be "´´a". Although the captured character by ToUnicodeEx was 'á', I can't admit that my application prevents the user from using characters such as 'á'.

    So, I worked around this problem by avoiding ToUnicodeEx to be called whenever a dead key is pressed. This way, I can avoid the ToUnicodeEx function from messing with the keyboard state. Here is the current version code:

    Code:
    private static int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
    {
    	//indicates if any of underlaing events set e.Handled flag
    	bool handled = false;
    
    	if (nCode >= 0)
    	{
    		//read structure KeyboardHookStruct at lParam
    		KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
    
    		// raise KeyPress
    		if (s_KeyPress != null && wParam == WM_KEYDOWN)
    		{
    			bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
    			bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
    
    			byte[] keyState = new byte[256];
    			GetKeyboardState(keyState);
    			System.Text.StringBuilder sbString = new System.Text.StringBuilder();
    			IntPtr HKL = GetKeyboardLayout(0);
    
    			if (!IsDeadKey((uint)MyKeyboardHookStruct.VirtualKeyCode))
    			{
    				switch (ToUnicodeEx((uint)MyKeyboardHookStruct.VirtualKeyCode,
    					(uint)MyKeyboardHookStruct.ScanCode,
    					keyState,
    					sbString,
    					5,
    					(uint)MyKeyboardHookStruct.Flags,
    					HKL))
    				{
    					case 1:
    						char key = sbString.ToString()[0];
    						if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
    						KeyPressEventArgs e = new KeyPressEventArgs(key);
    						s_KeyPress.Invoke(null, e);
    						handled = handled || e.Handled;
    						break;
    				}
    			}
    		}
    	}
    
    	//if event handled in application do not handoff to other listeners
    	if (handled)
    		return -1;
    
    	//forward to other application
    	return CallNextHookEx(s_KeyboardHookHandle, nCode, wParam, lParam);
    }
    
    static bool IsDeadKey(uint key)
    {
    	if ((MapVirtualKey(key, 2) & 2147483648) == 2147483648)
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    I though this solution would solve my problem, but I was wrong. The result has improved, but it's not quite what I wanted. Now, typing 'á' in notepad results in 'a'. Notice that the accent '´' is missing.

    Does anyone know how to work around this issue? My current method already captures the correct character, but prevents the user from producing characters with accents or diacritics. I think the KeyboardHookProc method just need a few changes, but I can't figure them out.

    Thanks in advance.

    PS: I tried the solution described in the following link: Keyboard hooks, ToUnicodeEx, MapVirtrualKeyEx. However, the result is quite the same as in my current version. That code only erases the keyboard buffer, which deletes the accents, therefore not allowing the user to use them.
    Last edited by RevengerPT; 07-09-2009 at 05:58 PM.

  2. #2
    Registered User hackterr's Avatar
    Join Date
    Aug 2009
    Location
    INDIA
    Posts
    19
    i think its unicode at fault

Popular pages Recent additions subscribe to a feed

Tags for this Thread