Double characters displayed when entering special characters when registering keystrokes in C #

I have an application that logs everything that the user clicks, but when I click special characters like ´ with a to get á , I get ´´a ; the same thing when I want to get à , then I get ``a , so all special characters are printed twice, and then after that a regular character is typed.

I always searched and can’t find anything. But I noticed that the problem lies in the ToAscii method, without which characters print correctly.

 public string GetString(IntPtr lParam, int vCode) { try { bool shift = Keys.Shift == Control.ModifierKeys || Console.CapsLock; string value = ""; KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure( lParam, typeof(KeyboardHookStruct)); byte[] keyState = new byte[256]; byte[] inBuffer = new byte[2]; DllClass.GetKeyboardState(keyState); var ascii= DllClass.ToAscii( MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags ); if (ascii == 1) { char key = (char)inBuffer[0]; if ((shift) && Char.IsLetter(key)) key = Char.ToUpper(key); value = key.ToString(); } return value; } catch (Exception) { return ""; } } 

Am I missing something or did something wrong? All other characters work fine, but these are special characters that appear as double characters.


EDIT:

Try instead of ToUnicode .

 [DllImport("USER32.DLL", CharSet = CharSet.Unicode)] public static extern int ToUnicode( uint virtualKey, uint scanCode, byte[] keyStates, [MarshalAs(UnmanagedType.LPArray)] [Out] char[] chars, int charMaxCount, uint flags); public string GetString(IntPtr lParam, int vCode) { try { bool shift = Keys.Shift == Control.ModifierKeys || Console.CapsLock; string value = ""; KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure( lParam, typeof(KeyboardHookStruct)); byte[] keyState = new byte[256]; byte[] inBuffer = new byte[2]; char[] chars = new char[2]; DllClass.GetKeyboardState(keyState); int val = 0; val = ToUnicode( (uint)MyKeyboardHookStruct.vkCode, (uint)MyKeyboardHookStruct.scanCode, keyState, chars, chars.Length, 0 ); val = ToUnicode( (uint)MyKeyboardHookStruct.vkCode, (uint)MyKeyboardHookStruct.scanCode, keyState, chars, chars.Length, 0 ); if (val == 1) { char key = (char)chars[0]; if ((shift) && Char.IsLetter(key)) key = Char.ToUpper(key); value = key.ToString(); } return value; } catch (Exception) { return ""; } } 

Someone PLEASE help me, I really need to understand this =/ .


EDIT:

 int val = -1; if (IsDeadKey((uint)vCode)) { while (val == -1) { val = ToUnicode( (uint)MyKeyboardHookStruct.vkCode, (uint)MyKeyboardHookStruct.scanCode, keyState, chars, chars.Length, 0 ); } } else val = ToUnicode( (uint)MyKeyboardHookStruct.vkCode, (uint)MyKeyboardHookStruct.scanCode, keyState, chars, chars.Length, 0 ); 

So, now I tried calling ToAscii or ToUnicode several times to reset the real character, but to no avail. Am I doing it wrong?

As for ASCII, first call ´ , I get -1 , so I call it again, then I get 1 ; and then I press as a to get á , but then I get only a . The same thing, if I use ToUnicode twice after each other, I get only a instead of á and so on ...

+2
source share
2 answers
  • The myth of ToAscii and ToUnicode

    In this question, you mentioned that you tried both ToAscii and ToUnicode without success. And I also looked for a question regarding:

    ToAscii / ToUnicode in a keyboard hook destroys dead keys

    I am not saying that any answer is right or wrong. However, we can think:

    • A (not so) difficult game

      In this game, I give the player a random inverted 50 percent coin at a time. One could argue to get one dollar bill from me if someone collected a pair of coins with 50 cents with one of them with its head and the other with its tail .

      If someone challenged, then who can reserve that 50 cents and the game will restart. If someone tried, but did not collect two, follow the rule, then I ask you to return to me those that I gave.

      How can I get one dollar bill from me without losing a coin?

    It is possible to travel in time ..


Base on [ Processing global mice and keyboard hooks in C # ] in CodeProject

and my very old answer: Konami code in C #

I made some changes to answer your question.

  • Code

     namespace Gma.UserActivityMonitor { using System.Diagnostics; using System.Windows.Forms; using System.Collections.Generic; using System.Linq; partial class HookManager { 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 var MyKeyboardHookStruct= (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); // raise KeyDown if(s_KeyDown!=null&&(wParam==WM_KEYDOWN||wParam==WM_SYSKEYDOWN)) { Keys keyData=(Keys)MyKeyboardHookStruct.VirtualKeyCode; KeyEventArgs e=new KeyEventArgs(keyData); s_KeyDown.Invoke(null, e); handled=e.Handled; } // raise KeyPress if(s_KeyPress!=null&&wParam==WM_KEYDOWN) { var keyText=GetString(lParam, nCode, ref handled); if(""!=keyText) { var keyChar=keyText.First(); Debug.Print("keyText => {0}", keyText); #if false if(AccentFormatter.Combination.Values.Contains(keyChar)) { SendKeys.Send("\b"+keyText); return -1; } #endif } } // raise KeyUp if(s_KeyUp!=null&&(wParam==WM_KEYUP||wParam==WM_SYSKEYUP)) { Keys keyData=(Keys)MyKeyboardHookStruct.VirtualKeyCode; KeyEventArgs e=new KeyEventArgs(keyData); s_KeyUp.Invoke(null, e); handled=handled||e.Handled; } } // 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); } public static String GetString(IntPtr lParam, int vCode, ref bool handled) { var MyKeyboardHookStruct= (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); bool isDownShift=((GetKeyState(VK_SHIFT)&0x80)==0x80?true:false); bool isDownCapslock=(GetKeyState(VK_CAPITAL)!=0?true:false); byte[] keyState=new byte[256]; GetKeyboardState(keyState); byte[] inBuffer=new byte[2]; var keyText=""; var ascii= ToAscii( MyKeyboardHookStruct.VirtualKeyCode, MyKeyboardHookStruct.ScanCode, keyState, inBuffer, MyKeyboardHookStruct.Flags ); if(ascii==1) { char key=(char)inBuffer[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; keyText=new String(new[] { e.KeyChar }); var sequence=KeySequence.Captured(e.KeyChar); if(null!=sequence) keyText=sequence.ToString(AccentFormatter.Default); } return keyText; } } public class KeySequence { public String ToString(IFormatProvider provider) { return null==provider ?new String(Sequence.Select(x => (char)x).ToArray()) :String.Format(provider, "{0}", Sequence); } public override String ToString() { return this.ToString(default(IFormatProvider)); } public bool Captures(int keyValue) { for(var i=Sequence.Length; i-->0; ) { if(Sequence[i]!=keyValue) { if(0==i) Count=0; continue; } if(Count!=i) continue; ++Count; break; } var x=Sequence.Length==Count; Count=x?0:Count; return x; } public KeySequence(int[] newSequence) { Sequence=newSequence; } public static KeySequence Captured(int keyValue) { return m_List.FirstOrDefault(x => x.Captures(keyValue)); } public int Count { private set; get; } public int[] Sequence { set; get; } static KeySequence() { m_List.AddRange( from x in AccentFormatter.Combination.Keys let intArray=x.Select(c => (int)c).ToArray() select new KeySequence(intArray) ); } static readonly List<KeySequence> m_List=new List<KeySequence>(); } public class AccentFormatter: IFormatProvider, ICustomFormatter { String ICustomFormatter.Format(String format, object arg, IFormatProvider formatProvider) { return GetAccent(new String((arg as int[]).Select(x => (char)x).ToArray())); } object IFormatProvider.GetFormat(Type formatType) { return typeof(ICustomFormatter)!=formatType?null:this; } public static String GetAccent(String input) { return Combination.Keys.Contains(input, StringComparer.OrdinalIgnoreCase) ?Combination[input].ToString() :""; } static AccentFormatter() { AcuteSymbol=((char)0xb4).ToString(); GraveSymbol=('`').ToString(); var ae=(char)0xe6; var oe=(char)0xf8; AcuteCandidates="acegiklmnoprsuwyz".ToArray().Concat(new[] { ae, oe }).ToArray(); GraveCandidates="aeinouwy".ToArray(); var lowerAcuteAccents=( new[] { 0xe1, 0x107, 0xe9, 0x1f5, 0xed, 0x1e31, 0x13a, 0x1e3f, 0x144, 0xf3, 0x1e55, 0x155, 0x15b, 0xfa, 0x1e83, 0xfd, 0x17a, 0x1fd, 0x1ff } ).Select( (x, i) => new { Key=AcuteSymbol+AcuteCandidates[i], Value=(char)x } ); var upperAcuteAccents=( new[] { 0xc1, 0x106, 0xc9, 0x1f4, 0xcd, 0x1e30, 0x139, 0x1e3e, 0x143, 0xd3, 0x1e54, 0x154, 0x15a, 0xda, 0x1e82, 0xdd, 0x179, 0x1fc, 0x1fe } ).Select( (x, i) => new { Key=AcuteSymbol+char.ToUpper(AcuteCandidates[i]), Value=(char)x } ); var lowerGraveAccents=( new[] { 0xe0, 0xe8, 0xec, 0x1f9, 0xf2, 0xf9, 0x1e81, 0x1ef3 } ).Select( (x, i) => new { Key=GraveSymbol+GraveCandidates[i], Value=(char)x } ); var upperGraveAccents=( new[] { 0xc0, 0xc8, 0xcc, 0x1f8, 0xd2, 0xd9, 0x1e80, 0x1ef2 } ).Select( (x, i) => new { Key=GraveSymbol+char.ToUpper(GraveCandidates[i]), Value=(char)x } ); Combination= lowerAcuteAccents .Concat(upperAcuteAccents) .Concat(lowerGraveAccents) .Concat(upperGraveAccents) .ToDictionary(x => x.Key, x => x.Value); } public static readonly Dictionary<String, char> Combination; public static readonly String AcuteSymbol, GraveSymbol; public static readonly char[] AcuteCandidates, GraveCandidates; public static readonly AccentFormatter Default=new AccentFormatter(); } } 

    First of all, with the code downloaded from CodeProject, find HookManager.Callbacks.cs and delete the entire KeyboardHookProc method .

    You can then save the code above in a new file, for example HookManager.Modified.cs , and add it to the Gma.UserActivityMonitor project or simply paste it at the end of HookManager.Callbacks.cs .

    Remember to install Gma.UserActivityMonitorDemo as a startup project . A target structure of 2.0 has been installed, and you may need to set a higher level.

  • Input → Output

    Vmh1a.jpg

  • About the accent

    As I searched, there are two types of general emphasis; they are a serious accent and a sharp accent .
    Diacritics of acute accent ´ , and possible letters áǽćéǵíḱĺḿńóǿṕŕśúẃýź .
    The deacon of serious accent - `` , and the possible letters are àèìǹòùẁỳ.

    Both options are possible in upper case or lower case, but I have not found a simple enough way to work with them in classes built into the structure. For example, I cannot use ToUpper or ToLower to get the opposite case with Ǹ and Ǹ , I even tried ToUpperInvariant and ToLowerInvariant . Thus, I prefer to use the hard-coded sequence in AccentFormatter , and you can change it to read from files or implement another custom formatter yourself.

  • Array location of hexadecimal representation

    In the code, I organized an acute accent sequence like:

      0xc1,0x106, 
     0xc9,0x1f4, 
     0xcd, 0x1e30, 0x139, 0x1e3e, 0x143, 
     0xd3,0x1e54,0x154,0x15a 
     0xda, 0x1e82, 0xdd, 0x179, 
     0x1fc, 0x1fe
    

    which the:

      ÁĆ
     ÉǴ
     ÍḰĹḾŃ
     ÓṔŔŚ
     ÚẂÝŹ
     ǼǾ
    

    Ǽ and Ǿ are placed behind because, and Ø do not have the corresponding character in ASCII (not extended).

    I preferred to save the code in ANSI with the default code page, since I have another CultureInfo from the languages ​​that is in the alphabet. And you can change them to exact characters for easy reading.

  • Send keys to active application

    If you want to send the key to the active application, make the following code change:

    change

     #if false 

    to

     #if !false 

    Inside a conditional compilation block, a code snippet

     if(AccentFormatter.Combination.Values.Contains(keyChar)) { SendKeys.Send("\b"+keyText); return -1; } 

    It uses the backspace character to erase the previous `` or '' entered as soon as it is recognized as a specific combination. Backspace is a time machine.

After this demo, I think you will learn how to modify them to integrate in your code.

+4
source

But I noticed that the problem lies in the ToAsciii method, without the correct character input.

What I was going to guess. I appreciate you doing the work for me! :-)

The problem is that these “special” characters are not ASCII characters. That is, they are actually some types of Unicode characters that are not part of the ASCII character set.

When you try to convert them to ASCII characters, the function seems to do everything possible by decomposing the code points that make up á into separate characters ´ and a .

Obviously not what you want. You want to treat á as one character, so you need to use Unicode. This is not a problem: Windows has been completely Unicode internally for at least a decade. ToAscii deprecated ToAscii function; instead, you will want to use MapVirtualKey or MapVirtualKeyEx to convert a virtual key code, you go through a low-level keyboard hook into a character value.

+5
source

All Articles