F # System.Runtime.InteropServices Вызов родной библиотеки SendInput работает в. net инфраструктуре, но не в. net ядре - PullRequest
2 голосов
/ 08 января 2020

Я портирую небольшое приложение, которое я написал для привязки клавиш, на net ядро, и я столкнулся с экземпляром, где тот же код ведет себя по-разному. Я вызываю SendInput функцию в F # с этим объявлением

open System.Runtime.InteropServices

[<StructLayout(LayoutKind.Sequential)>]
type private MOUSEINPUT = struct
    val dx: int32
    val dy:int32
    val mouseData:uint32
    val dwFlags: uint32
    val time: uint32
    val dwExtraInfo: int
    new(_dx, _dy, _mouseData, _dwFlags, _time, _dwExtraInfo) = {dx=_dx; dy=_dy; mouseData=_mouseData; dwFlags=_dwFlags; time=_time; dwExtraInfo=_dwExtraInfo}
end

[<StructLayout(LayoutKind.Sequential)>]
type private KEYBDINPUT = struct
    val wVk: uint16
    val wScan: uint16
    val dwFlags: uint32
    val time: uint32
    val dwExtraInfo:int
    new(_wVk, _wScan, _dwFlags, _time, _dwExtraInfo) = {wVk =_wVk; wScan = _wScan; dwFlags = _dwFlags; time = _time; dwExtraInfo = _dwExtraInfo}
end

[<StructLayout(LayoutKind.Sequential)>]
type private HARDWAREINPUT = struct
    val uMsg: uint32
    val wParamL: uint16
    val wParamH: uint16
    new(_uMsg, _wParamL, _wParamH) = {uMsg = _uMsg; wParamL = _wParamL; wParamH = _wParamH}
end

[<StructLayout(LayoutKind.Explicit)>]
type private LPINPUT  = struct
    [<FieldOffset(0)>]
    val mutable ``type``:int // 1 is keyboard

    [<FieldOffset(4)>]
    val mutable mi : MOUSEINPUT

    [<FieldOffset(4)>]
    val mutable ki : KEYBDINPUT

    [<FieldOffset(4)>]
    val mutable hi : HARDWAREINPUT
end

module private NativeMethods =
    [<DllImport("user32.dll", SetLastError=true)>]
    extern uint32 SendInput(uint32 nInputs, LPINPUT* pInputs, int cbSize)

let appSignature = 0xA8969

let private createPressInput (code: int) =
    let mutable input = LPINPUT()
    input.``type`` <- InputModes.INPUT_KEYBOARD
    input.ki <- KEYBDINPUT(uint16  code, uint16 0, Dwords.KEYEVENTF_KEYDOWN, uint32 0, appSignature)
    input

let pressKey (code: int) =
    let input = createPressInput code
    NativeMethods.SendInput(uint32 1, &&input, Marshal.SizeOf(input)) |> ignore

Тот же код работает в приложении. net Framework, которое я создал в Visual Studio. Теперь вывод Marshal.GetLastWin32ErrorCode() равен 87, что, очевидно, означает ERROR_INVALID_PARAMETER - не очень полезно. Я новичок в. net и F #, поэтому я не уверен, что может быть иначе в этом контексте. Я признаю, что даже получение этого связующего кода было в основном методом проб и ошибок.

Буду признателен за любую информацию, которая может помочь мне отладить это.

ОБНОВЛЕНИЕ : I есть обходной путь, который меня не устраивает. Я пока не могу объяснить, почему это работает - мне нужно больше узнать о том, как работает маршалинг. При этом Marshal.GetLastWin32ErrorCode() равен 5, доступ запрещен. Он все еще посылает ключ, поэтому я не уверен, что эта ошибка должна означать. Тем не менее, вот оно. Разделение объединения из структуры, которую я использовал, в выделенный тип объединения, создание этого типа объединения LayoutKind.Explicit и создание хотя бы одного из полей FieldOffset(1) (но не поля, которое мне небезразлично) приводит к срабатыванию нажатий клавиш. Другие комбинации смещений полей приводят к тому, что что-то работает, но фактически не нажимает клавиши, что, как я предполагаю, означает, что его маршалинг происходит так, что не происходит видимых нажатий клавиш.

[<StructLayout(LayoutKind.Explicit)>]
type private InputUnion = struct
    [<FieldOffset(0)>]
    val mutable ki : KEYBDINPUT

    [<FieldOffset(1)>]
    val mutable mi : MOUSEINPUT

    [<FieldOffset(1)>]
    val mutable hi : HARDWAREINPUT 

end

[<StructLayout(LayoutKind.Sequential)>]
type private LPINPUT  = struct
    val ``type``:int // 1 is keyboard
    val u: InputUnion
    new(_type, _u) = {``type`` = _type;  u = _u}
end

1 Ответ

0 голосов
/ 10 января 2020

Я закончил тем, что открыл ошибку на этом.

https://github.com/dotnet/runtime/issues/1515

Это не было проблемой в. net. По-видимому, мое приложение. net Framework работало как 32-битное. Я объявил поле dwExtraInfo как int, что было хорошо для 32-битного. Моё основное приложение. net работало как 64-битное, и я должен был использовать UIntPtr для обработки различий между платформами по длине этого поля. Обновлен до этого кода, и он работает.

[<StructLayout(LayoutKind.Sequential)>]
type private MOUSEINPUT = struct
    val dx: int32
    val dy:int32
    val mouseData:uint32
    val dwFlags: uint32
    val time: uint32
    val dwExtraInfo: UIntPtr
    new(_dx, _dy, _mouseData, _dwFlags, _time, _dwExtraInfo) = {dx=_dx; dy=_dy; mouseData=_mouseData; dwFlags=_dwFlags; time=_time; dwExtraInfo=_dwExtraInfo}
end

[<StructLayout(LayoutKind.Sequential)>]
type private KEYBDINPUT = struct
    val wVk: uint16
    val wScan: uint16
    val dwFlags: uint32
    val time: uint32
    val dwExtraInfo: UIntPtr
    new(_wVk, _wScan, _dwFlags, _time, _dwExtraInfo) = {wVk =_wVk; wScan = _wScan; dwFlags = _dwFlags; time = _time; dwExtraInfo = _dwExtraInfo}
end

[<StructLayout(LayoutKind.Sequential)>]
type private HARDWAREINPUT = struct
    val uMsg: uint32
    val wParamL: uint16
    val wParamH: uint16
    new(_uMsg, _wParamL, _wParamH) = {uMsg = _uMsg; wParamL = _wParamL; wParamH = _wParamH}
end


[<StructLayout(LayoutKind.Explicit)>]
type private InputUnion = struct
    [<FieldOffset(0)>]
    val mutable mi : MOUSEINPUT

    [<FieldOffset(0)>]
    val mutable ki : KEYBDINPUT

    [<FieldOffset(0)>]
    val mutable hi : HARDWAREINPUT 
end

[<StructLayout(LayoutKind.Sequential)>]
type private LPINPUT  = struct
    val mutable ``type``: int // 1 is keyboard
    val mutable u: InputUnion
end

Основными отличиями являются определения полей dwExtraInfo. Кроме того, теперь существует явный тип для объединения вместо того, чтобы объединять все в один LPINPUT. Это позволяет мне избегать указания смещения поля для 3 необязательных полей, которое также зависит от платформы.

...