F# System.Runtime.InteropServices 对 SendInput 的本机库调用在 .net 框架中有效,但在 .net 核心中无效
F# System.Runtime.InteropServices native library call to SendInput works in .net framework but not in .net core
我正在将我为键绑定编写的一个小应用程序移植到 .net 核心,我已经 运行 跨越一个实例,其中 the same code behaves differently. I'm calling the SendInput function 在 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
相同的代码适用于我在 visual studio 中创建的 .net Framework 应用程序。现在,Marshal.GetLastWin32ErrorCode()
的输出是 87
,这显然意味着 ERROR_INVALID_PARAMETER
——不是很有帮助。我是 .net 和 F# 的新手,所以我不确定在这种情况下会有什么不同。我承认,即使获得此绑定代码也主要是反复试验。
如果有任何信息可以帮助我进行调试,我将不胜感激。
更新:我有一个我不满意的解决方法。我还不能解释为什么这会起作用——我需要阅读更多关于封送处理如何工作的信息。有了这个,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
我最后打开了一个 bug。
https://github.com/dotnet/runtime/issues/1515
这在 .net 中不是问题。显然,我的 .net 框架应用程序是 运行 作为 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 个可选字段指定字段偏移量,这也因平台而异。
我正在将我为键绑定编写的一个小应用程序移植到 .net 核心,我已经 运行 跨越一个实例,其中 the same code behaves differently. I'm calling the SendInput function 在 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
相同的代码适用于我在 visual studio 中创建的 .net Framework 应用程序。现在,Marshal.GetLastWin32ErrorCode()
的输出是 87
,这显然意味着 ERROR_INVALID_PARAMETER
——不是很有帮助。我是 .net 和 F# 的新手,所以我不确定在这种情况下会有什么不同。我承认,即使获得此绑定代码也主要是反复试验。
如果有任何信息可以帮助我进行调试,我将不胜感激。
更新:我有一个我不满意的解决方法。我还不能解释为什么这会起作用——我需要阅读更多关于封送处理如何工作的信息。有了这个,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
我最后打开了一个 bug。
https://github.com/dotnet/runtime/issues/1515
这在 .net 中不是问题。显然,我的 .net 框架应用程序是 运行 作为 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 个可选字段指定字段偏移量,这也因平台而异。