在c#中记录击键时输入特殊字符时显示双字符 [英] Double characters shown when typing special characters while logging keystrokes in c#

查看:245
本文介绍了在c#中记录击键时输入特殊字符时显示双字符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序记录用户按下的任何内容,但是当我用 a 按下' ,得到á,我得到''a ;同样的事情,当我想得到à,那么我得到``a ,所以所有特殊字符打字两次,然后正则字符后输入。



我搜索过,找不到任何东西。但我注意到,问题是在 ToAscii 方法,没有字符键入正确。

  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();
}

返回值;
}
catch(Exception)
{
return;
}
}

我错过了什么或做错了什么?所有其他字符工作完美,但它是特殊字符,作为双字符。






编辑



尝试使用 ToUnicode

  [DllImport(USER32.DLL ,CharSet = CharSet.Unicode)] 
public static extern int ToUnicode(
uint virtualKey,uint scanCode,byte [] keyStates,
[MarshalAs(UnmanagedType.LPArray)] [Out] ] 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();
}

返回值;
}
catch(Exception)
{
return;
}
}

有人请帮我,我真的需要out = /






编辑

  int val = -1; 

if(IsDeadKey((uint)vCode))
{
while(val == -1)
{
val = ToUnicode $ b(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
);

现在我已经尝试调用 ToAscii ToUnicode 几次刷新真实字符,但没有成功。我做错了吗?



像ASCII一样,首先调用'我得到 -1 ,所以我再次调用它,然后我得到 1 ;然后我按 a ,得到á,但是我只得到 / code>。同样的事情,如果我使用 ToUnicode 两次,我得到只是 a 而不是á ,等等...

解决方案


  • strong>关于 ToAscii ToUnicode 的神话



    在这个问题中,您提到您已尝试过 ToAscii ToUnicode ,但没有成功。我也搜索了一个相对的问题:



    急性口音

    急性口音的缩写是',可能的字母是áǽćéǵíḱĺḿńóǿṕŕśúẃýź

    重音符号的缩写是,可能的字母是àèìǹòùẁỳẁỳ。



    两者都可以是大写或小写,但是我没有找到一个足够简单的方法在类中使用它们是内置的框架。例如,我不能使用 ToUpper ToLower 获得与Ǹǹ,我甚至尝试了 ToUpperInvariant ToLowerInvariant 。因此,我选择在 AccentFormatter 中使用硬编码的序列,您可以将其更改为从文件读取,或自己实现另一个自定义格式化程序。

    将紧急重音序列排列为:

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

    $ b b

    这是:

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

    ǼǾ放在后面,因为ÆØ相应的ASCII字符(非扩展)。



    我更喜欢用默认代码页保存ANSI中的代码,因为我有一个不同的 CultureInfo 在字母表中的语言。并且您可能需要将其更改为准确的字符以提高可读性。

    将密钥发送到活动应用程序,然后在代码中进行以下更改:



    更改

      #if false 

      #if!false 

    编译块,代码片段是

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

    它使用一个退格字符来删除前面输入的``''一旦被识别为特定组合。 Backspace这里是时间机器。




这个演示后,我想你会知道如何修改它们合并在代码中。


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

I have searched for ever and can't find anything really. But I have noticed that the problem is in the ToAscii method , without that the characters are typed 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 doing something wrong? All other characters are working perfectly but it's the special characters that is coming as double chars.


EDIT:

Trying with ToUnicode instead.

[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 figure this out =/.


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 have tried calling the ToAscii or ToUnicode a couple of times to flush the real character but without success. Am I doing it wrong?

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

解决方案

  • The myth about ToAscii and ToUnicode

    In the question, you mentioned about that you've tried both ToAscii and ToUnicode with no success. And I also searched a question which is relative:

    ToAscii/ToUnicode in a keyboard hook destroys dead keys

    I'm not saying any answer is right or wrong. However, we can think about:

    • A (not so) tricky game

      In this game, I give the player a random flipped 50 cent coin at a time. One can challenge to get one dollar bill from me, if who collected a pair of 50 cent coins with one is a head and another is a tail.

      If one gave up to challenge, then who can reserve that 50 cent, and the game restarts. If who tried but did not collect two meet the rule, then I ask to return me those I've given.

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

    Maybe a time-travelling ..


Base on [Processing Global Mouse and Keyboard Hooks in C#] on CodeProject

and my very old answer: Konami Code in C#

I did some modification for answering 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 off, with the code downloaded from CodeProject, find HookManager.Callbacks.cs and delete the whole method KeyboardHookProc.

    Then You can save the code above in a new file such like HookManager.Modified.cs and add it in project Gma.UserActivityMonitor, or just paste it at the rear of HookManager.Callbacks.cs.

    Remember to set Gma.UserActivityMonitorDemo as startup project. It was set target framework to 2.0, and you might want to set to a higher.

  • Input → Output

  • About the accent

    As I searched there are two kind of general accent, they are grave accent and acute accent.
    The diacritic of acute accent is ´, and the possible letters are áǽćéǵíḱĺḿńóǿṕŕśúẃýź.
    The diacritic of grave accent is `` , and the possible letters areàèìǹòùẁỳ`.

    Both of them are possible in upper case or lower case, but I did not find an easy enough way works with them in classes those are built-in of the framework. For example, I cannot use ToUpper or ToLower to get a opposite case with Ǹ and ǹ, I even tried ToUpperInvariant and ToLowerInvariant. Thus I choose to use a hard-coded sequence in AccentFormatter, and you may change it to reading from files, or implement another custom formatter yourself.

  • Array arrangement of hexadecimal representation

    In the code, I arranged the acute accent sequence as:

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

    which is:

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

    The Ǽ and Ǿ are put at the rear because Æ and Ø do not have a corresponding character in ASCII(non-extended).

    I preferred to save the code in ANSI with a default code page, since I have a different CultureInfo from the languages which is in alphabet. And you might want to change them to exact characters for readability.

  • Send keys to the active application

    If you want to send the key to the active application, then do the following change in the code:

    change

    #if false
    

    to

    #if !false
    

    Inside the conditional compilation block, the code fragment is

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

    It use a backspace character to erase the previous typed `` or´` once it recognized as a specific combination. Backspace here is the time machine ..

After this demo, I guess you'll know how to modify them to merge in your code.

这篇关于在c#中记录击键时输入特殊字符时显示双字符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆