我有一个应用程序会记录用户按下的内容,但是当我按´
和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();
}
return value;
}
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] 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 "";
}
}
有人请帮助我,我真的需要弄清楚=/
。
编辑:
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
);
所以现在我尝试过多次调用ToAscii
或ToUnicode
来冲洗真实角色,但没有成功。我做错了吗?
对于ASCII,首先调用´
我得到-1
,所以我再次调用它,然后我得到1
;然后我像a
一样按,以获得á
,但之后我只获得a
。如果我相互使用ToUnicode
两次相同,我只会a
而不是á
,依此类推......
答案 0 :(得分:5)
但是我注意到问题出现在ToAsciii方法中,没有正确输入字符。
这正是我猜的。我感谢你为我做过腿部工作! : - )
问题是这些“特殊”字符是不是 ASCII字符。也就是说,它们实际上是某种类型的花式裤子Unicode字符,它们不属于ASCII字符集。
当您尝试将它们转换为ASCII字符时,该功能可能会尽力而为,将构成á
的代码点分解为单独的字符´
和a
。
显然这不是你想要的。您希望将á
视为单个字符,因此您需要使用Unicode。这不是一个真正的问题:Windows已经在内部使用了所有Unicode至少十年。抛弃过时的ToAscii
功能;相反,您将要使用MapVirtualKey
或MapVirtualKeyEx
将您通过低级键盘挂钩获得的虚拟键码转换为字符值。
答案 1 :(得分:4)
关于ToAscii
和ToUnicode
的神话
在这个问题中,您提到您已经尝试了ToAscii
和ToUnicode
但没有成功。我还搜索了一个相对的问题:
ToAscii/ToUnicode in a keyboard hook destroys dead keys
我不是说任何答案是对还是错。但是,我们可以考虑:
A(不那么)棘手的游戏
在这个游戏中,我一次给玩家一个随机翻转的50美分硬币。一个人可以挑战从我那里拿一美元的钞票,如果谁收集了一对50美分的硬币一个是头而另一个是尾巴。
如果一个人放弃了挑战,那么谁能保留50美分,游戏重新开始。如果谁尝试但没有收集两个符合规则,那么我要求归还给我那些我给的。
如何在不丢失任何硬币的情况下从我那里获得1美元的账单?
也许是时间旅行..
基于CodeProject上的[Processing Global Mouse and Keyboard Hooks in C#]
和我很老的回答:Konami Code in C#
我做了一些修改来回答你的问题。
代码
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();
}
}
首先,使用从CodeProject下载的代码,找到HookManager.Callbacks.cs
并删除整个方法 KeyboardHookProc
。
然后,您可以将上面的代码保存在HookManager.Modified.cs
这样的新文件中,并将其添加到项目Gma.UserActivityMonitor
中,或者只将其粘贴到HookManager.Callbacks.cs
的后面。
请务必将Gma.UserActivityMonitorDemo
设置为启动项目。它将目标框架设置为2.0,您可能希望设置为更高。
输入→输出
关于重音
我搜索时有两种一般的重音,它们是grave accent和acute accent。
急性重音的变音是´
,可能的字母是áǽćéǵíḱĺḿńóǿṕŕśúẃýź
。
重音的变音是“, and the possible letters are
àèìǹòùẁỳ`。
它们都可能是大写或小写的,但我没有找到一种简单的方法在框架内置的类中使用它们。例如,我无法使用ToUpper
或ToLower
来获得与Ǹ
和ǹ
相反的情况,我甚至尝试了ToUpperInvariant
和ToLowerInvariant
。因此,我选择在AccentFormatter
中使用硬编码序列,您可以将其更改为从文件中读取,或者自己实现另一个自定义格式化程序。
十六进制表示的数组排列
在代码中,我将急性重音序列排列为:
0xc1, 0x106, 0xc9, 0x1f4, 0xcd, 0x1e30, 0x139, 0x1e3e, 0x143, 0xd3, 0x1e54, 0x154, 0x15a, 0xda, 0x1e82, 0xdd, 0x179, 0x1fc, 0x1fe
是:
ÁĆ ÉǴ ÍḰĹḾŃ ÓṔŔŚ ÚẂÝŹ ǼǾ
Ǽ
和Ǿ
放在后面,因为Æ
和Ø
在ASCII(非扩展)中没有相应的字符。
我更喜欢使用默认代码页将代码保存在ANSI中,因为我在字母表中的语言中有不同的CultureInfo
。并且您可能希望将它们更改为精确的字符以便于阅读。
将密钥发送到活动应用
如果要将密钥发送到活动应用程序,请在代码中进行以下更改:
变化
#if false
到
#if !false
在条件编译块中,代码片段是
if(AccentFormatter.Combination.Values.Contains(keyChar)) {
SendKeys.Send("\b"+keyText);
return -1;
}
一旦识别为特定组合,它就会使用退格字符擦除之前键入的“or
”。退格这里是时间机器..
在此演示之后,我想您将知道如何修改它们以合并到您的代码中。