5

C# Windows.Forms プロジェクトには、KeyPressed イベントを提供しないコントロールがあります (これは COM コントロール – ESRI マップです)。

KeyEventArgs構造体を含む KeyUp および KeyDown イベントのみを提供します。

現在アクティブなキーボード レイアウトなどを考慮して、KeyEventArgs の情報を表示可能な Unicode 文字に変換するにはどうすればよいですか?

4

3 に答える 3

11

トリックは、一連の user32.dll 関数を使用することです: GetWindowThreadProcessIdGetKeyboardLayoutGetKeyboardState、およびToUnicodeEx

  1. コントロール ハンドルでGetWindowThreadProcessId関数を使用して、関連するネイティブ スレッド ID を取得します。
  2. そのスレッド ID をGetKeyboardLayoutに渡して、現在のキーボード レイアウトを取得します。
  3. GetKeyboardStateを呼び出して、現在のキーボードの状態を取得します。これは、次のメソッドが修飾子の状態に応じて生成する文字を決定するのに役立ちます。
  4. 最後に、必要な仮想キー コードとスキャン コード (これら 2 つを同じにすることができます)、現在のキーボードの状態、文字列ホルダーとしての文字列ビルダー (結果を保持するため)、フラグなし (0) を指定してToUnicodeEx関数を呼び出します。現在のキーボード レイアウト ポインター。

結果がゼロでない場合は、最初に返された文字を返します。

  public class KeyboardHelper
    {
        [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern int ToUnicodeEx(
            uint wVirtKey,
            uint wScanCode,
            Keys[] lpKeyState,
            StringBuilder pwszBuff,
            int cchBuff,
            uint wFlags,
            IntPtr dwhkl);

        [DllImport("user32.dll", ExactSpelling = true)]
        internal static extern IntPtr GetKeyboardLayout(uint threadId);

        [DllImport("user32.dll", ExactSpelling = true)]
        internal static extern bool GetKeyboardState(Keys[] keyStates);

        [DllImport("user32.dll", ExactSpelling = true)]
        internal static extern uint GetWindowThreadProcessId(IntPtr hwindow, out uint processId);

        public static string CodeToString(int scanCode)
        {
            uint procId;
            uint thread = GetWindowThreadProcessId(Process.GetCurrentProcess().MainWindowHandle, out procId);
            IntPtr hkl = GetKeyboardLayout(thread);

            if (hkl == IntPtr.Zero)
            {
                Console.WriteLine("Sorry, that keyboard does not seem to be valid.");
                return string.Empty;
            }

            Keys[] keyStates = new Keys[256];
            if (!GetKeyboardState(keyStates))
                return string.Empty;

            StringBuilder sb = new StringBuilder(10);
            int rc = ToUnicodeEx((uint)scanCode, (uint)scanCode, keyStates, sb, sb.Capacity, 0, hkl);
            return sb.ToString();
        }
    }
于 2008-12-17T16:04:36.590 に答える
8

Itai Bar-Haim のソリューションは優れていますが、デッド キーに問題があります。

デッド キーのスキャン コードで CodeToString(int scanCode) を初めて呼び出すと、ToUnicodeEx() の戻りコードは -1 になり、メソッドは正しい値を返します。

ただし、次の呼び出しでは、ToUnicodeEx() は 2 を返します。結果の文字列には、予想される結果の前にデッド キーが含まれ、その後にメモリ ジャンク データが続きます。

たとえば、^ (デッド キー) を使用してメソッドを呼び出し、次に $ を使用してメソッドを呼び出します。最初の呼び出しは ^ (良い) を返しますが、2 番目の呼び出しは ^$azertyui を返します。

それを修正するには、次の 2 つのことを行う必要があります。

1) ToUnicodeEx() 戻りコードの絶対値を使用して、StringBuilder の長さを設定する必要があります。したがって、文字の後にジャンク データを取得することを回避できます。

2) ToUnicodeEx() の戻りコードが -1 の場合、メソッドの結果として StringBuilder の最初の文字を取得する必要があります。次に、デッド キーをキーボードの状態から削除する必要があります。スペース キーのスキャンコードを使用して ToUnicodeEx() を呼び出します。

別の問題が発生する可能性があります。ToUnicodeEx() を呼び出す前にデッド キーを押したユーザーによってキーボードの状態が変更された場合、返される文字列には、期待値の前にデッド キー文字が含まれます。私が見つけた回避策は、実際の呼び出しの前に、スペース キーのスキャンコードで ToUnicodeEx() を呼び出すことです。

この助けを願っています!

于 2009-05-06T14:20:14.137 に答える
0

KeysConverterとその ConvertToString メソッドを見てください。すべての KeyDown が KeyPress にマップされるわけではないことに注意してください。

于 2008-12-16T19:28:48.300 に答える