TolikPylypchuk / SharpHook

SharpHook provides a cross-platform global keyboard and mouse hook, event simulation, and text entry simulation for .NET
https://sharphook.tolik.io
MIT License
342 stars 32 forks source link

Inconsistent input and output characters #113

Closed Timskt closed 1 month ago

Timskt commented 4 months ago

I have two computers. Computer 1 is remotely connected to Computer 2 through VDI. I wrote a WPF program to output the input string to the Notepad of Computer 2 by calling the SimulateTextEntry method. This function works normally on Computer 1 and can output the correct string. However, when I enable the software on Computer 1, call the same method, and move the mouse cursor to Notepad of Computer 2, the output string does not match the input character. In English input method, the input string "11111" becomes "nnnnn=9" on computer 2

TolikPylypchuk commented 4 months ago

Hi! Thanks for posting this issue! This certainly seems weird, but could it be possible that it's an issue with an underlying Windows API, and not with SharpHook?

Could you please try using SendInput directly to simulate text entry to see whether there is the same problem? If there is, then I don't think I'll be able to do much about it.

Timskt commented 4 months ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CRApp {
    public static class KeyboardInput {
        [DllImport("user32.dll")]
        static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs, int cbSize);
        [StructLayout(LayoutKind.Sequential)]
        struct INPUT {
            public uint type;
            public MOUSEKEYBDHARDWAREINPUT mkhi;
        }
        [StructLayout(LayoutKind.Explicit)]
        struct MOUSEKEYBDHARDWAREINPUT {
            [FieldOffset(0)]
            public HARDWAREINPUT hi;
            [FieldOffset(0)]
            public KEYBDINPUT ki;
            [FieldOffset(0)]
            public MOUSEINPUT mi;
        }
        [StructLayout(LayoutKind.Sequential)]
        struct KEYBDINPUT {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }
        [StructLayout(LayoutKind.Sequential)]
        struct HARDWAREINPUT {
            public uint uMsg;
            public ushort wParamL;
            public ushort wParamH;
        }
        [StructLayout(LayoutKind.Sequential)]
        struct MOUSEINPUT {
            public int dx;
            public int dy;
            public uint mouseData;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }
        public static int InputText(string text) {
            if (string.IsNullOrEmpty(text)) {
                return 0;
            }
            INPUT[] inputs = new INPUT[text.Length * 2];
            for (int i = 0, j = 0; i < text.Length; i++, j += 2) {
                ushort ch = text[i];
                inputs[j] = new INPUT {
                    type = 1,
                        mkhi = new MOUSEKEYBDHARDWAREINPUT {
                            ki = new KEYBDINPUT {
                                wVk = 0,
                                    wScan = ch,
                                    dwFlags = 0x0004,
                                    time = 0,
                                    dwExtraInfo = IntPtr.Zero
                            }
                        }
                };
                inputs[j + 1] = new INPUT {
                    type = 1,
                        mkhi = new MOUSEKEYBDHARDWAREINPUT {
                            ki = new KEYBDINPUT {
                                wVk = 0,
                                    wScan = ch,
                                    dwFlags = 0x0004 | 0x0002,
                                    time = 0,
                                    dwExtraInfo = IntPtr.Zero
                            }
                        }
                };
            }
            uint result = SendInput((uint) inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
            return result > 0 ? text.Length : 0;
        }
    }
}

I used the above code, but it still doesn't work properly on computer 2. When I tried using Python code, it worked fine on computer 2. Therefore, I envisioned converting strings into hexstrings, and Python code only needed to map hexstrings.

demo

from pynput.keyboard import Key, Controller, KeyCode
import time
import binascii

keyboard = Controller()

time.sleep(1)

kMap = {
    '~': '`',
    '!': '1',
    '@': '2',
    '#': '3',
    '$': '4',
    '%': '5',
    '^': '6',
    '&': '7',
    '*': '8',
    '(': '9',
    ')': '0',
    '_': '-',
    '+': '=',
    '{': '[',
    '}': ']',
    '|': '\\',
    ':': ';',
    '\"': '\'',
    '<': ',',
    '>': '.',
    '?': '/',
}

def type(s:str):
    for c in s:
        if ord(c) > 127:
            continue
        if c == '\n':
            c = Key.enter

        shift = kMap.get(c) is not None
        if shift:
            c = kMap.get(c)

        if shift:
            with keyboard.pressed(Key.shift):
                time.sleep(0.01)
                keyboard.tap(c)
                time.sleep(0.01)
        else:
            keyboard.tap(c)
            # time.sleep(0.01)

s =  """你好"""

type(s)
TolikPylypchuk commented 3 months ago

Apparently, SendInput doesn't send Unicode characters correctly through VDI, at least that's what I think. Your code seems correct - if it doesn't work, then I don't really think I can help here, as I've never worked with VDI and currently don't have time to research it. I think you will have more luck asking around on forums related to VDI, why this happens.

Regarding the Python code - it appears to do something completely different - it just simulates pressing and releasing the specified keys (and you've also added basic Shift handling), but correct me if I'm wrong though. This is not what SharpHook (and SendInput) does when simulating text entry - it simulates the Unicode characters directly so there is no dependency on whether the Shift key is pressed or not, or a dependency on the current keyboard layout.

I'm going to keep this issue open for some time, but as I've said, I don't think I'll be able to do anything about it, so any help is appreciated.

TolikPylypchuk commented 1 month ago

I'm closing this issue due to inactivity and inability to fix it, but we can continue the discussion, and I will reopen it if anything comes up.