如何在 F# 中程序不在焦点(在后台)时保持记录击键?
How to keep recording key strokes while program not in focus (in background) in F#?
下面的代码是一个更大的 ML 项目的一部分,但我希望它 运行 同时也被最小化。我怎样才能做到这一点?我想我需要 运行 它作为一个 windows 过程来实现这一点,但没有太多关于如何在 F# 中做到这一点的信息。或者有别的办法吗?
let rec f() =
let c = System.Console.ReadKey().KeyChar
printfn "%c" c
f()
f()
根据 Aaron 评论中的 link,我决定尝试移植。这对我有用。请注意,这肯定可以进一步清理。
我将以下内容放在一个名为 NativeHooks.fs
的文件中
namespace Native
open System
type HookProc = delegate of int * nativeint * nativeint -> nativeint
module User32 =
open System.Runtime.InteropServices
[<DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
extern nativeint SetWindowsHookEx(int idHook, HookProc lpfn, nativeint hMod, uint32 dwThreadId)
[<DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
[<MarshalAs(UnmanagedType.Bool)>]
extern bool UnhookWindowsHookEx(IntPtr hhk)
[<DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
extern nativeint CallNextHookEx(nativeint hhk, int nCode, nativeint wParam, nativeint lParam)
module Kernal32 =
open System.Runtime.InteropServices
[<DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
extern nativeint GetModuleHandle(string lpModuleName)
我的Program.fs然后看起来像这样:
open System
open System.Runtime.InteropServices
open System.Windows.Forms
open System.Diagnostics
open Native
// Credit: https://blogs.msdn.microsoft.com/toub/2006/05/03/low-level-keyboard-hook-in-c/
[<Literal>]
let WH_KEYBOARD_LL = 13
[<Literal>]
let WM_KEYDOWN = 0x0100
let mutable hookId = IntPtr.Zero
[<EntryPoint>]
let main argv =
let sprintKey k = KeysConverter().ConvertToString(k)
// installs a hook from WH_KEYBOARD_LL events to callback.
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
let setHook hookProc : nativeint =
using (Process.GetCurrentProcess()) (fun curProcess ->
using (curProcess.MainModule) ( fun curModule ->
let handle = Kernal32.GetModuleHandle(curModule.ModuleName)
User32.SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, handle, (0 |> uint32))
))
let callback = fun nCode wParam lParam ->
if (nCode >= 0 && wParam = (WM_KEYDOWN |> nativeint)) then
let vkCode = Marshal.ReadInt32(lParam)
printfn "%s" (sprintKey vkCode)
User32.CallNextHookEx(hookId, nCode, wParam, lParam)
hookId <- setHook(new HookProc(callback))
Application.Run()
User32.UnhookWindowsHookEx(hookId) |> ignore
0
我的 fsproj 文件:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="NativeHooks.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms" />
</ItemGroup>
</Project>
下面的代码是一个更大的 ML 项目的一部分,但我希望它 运行 同时也被最小化。我怎样才能做到这一点?我想我需要 运行 它作为一个 windows 过程来实现这一点,但没有太多关于如何在 F# 中做到这一点的信息。或者有别的办法吗?
let rec f() =
let c = System.Console.ReadKey().KeyChar
printfn "%c" c
f()
f()
根据 Aaron 评论中的 link,我决定尝试移植。这对我有用。请注意,这肯定可以进一步清理。 我将以下内容放在一个名为 NativeHooks.fs
的文件中namespace Native
open System
type HookProc = delegate of int * nativeint * nativeint -> nativeint
module User32 =
open System.Runtime.InteropServices
[<DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
extern nativeint SetWindowsHookEx(int idHook, HookProc lpfn, nativeint hMod, uint32 dwThreadId)
[<DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
[<MarshalAs(UnmanagedType.Bool)>]
extern bool UnhookWindowsHookEx(IntPtr hhk)
[<DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
extern nativeint CallNextHookEx(nativeint hhk, int nCode, nativeint wParam, nativeint lParam)
module Kernal32 =
open System.Runtime.InteropServices
[<DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)>]
extern nativeint GetModuleHandle(string lpModuleName)
我的Program.fs然后看起来像这样:
open System
open System.Runtime.InteropServices
open System.Windows.Forms
open System.Diagnostics
open Native
// Credit: https://blogs.msdn.microsoft.com/toub/2006/05/03/low-level-keyboard-hook-in-c/
[<Literal>]
let WH_KEYBOARD_LL = 13
[<Literal>]
let WM_KEYDOWN = 0x0100
let mutable hookId = IntPtr.Zero
[<EntryPoint>]
let main argv =
let sprintKey k = KeysConverter().ConvertToString(k)
// installs a hook from WH_KEYBOARD_LL events to callback.
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
let setHook hookProc : nativeint =
using (Process.GetCurrentProcess()) (fun curProcess ->
using (curProcess.MainModule) ( fun curModule ->
let handle = Kernal32.GetModuleHandle(curModule.ModuleName)
User32.SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, handle, (0 |> uint32))
))
let callback = fun nCode wParam lParam ->
if (nCode >= 0 && wParam = (WM_KEYDOWN |> nativeint)) then
let vkCode = Marshal.ReadInt32(lParam)
printfn "%s" (sprintKey vkCode)
User32.CallNextHookEx(hookId, nCode, wParam, lParam)
hookId <- setHook(new HookProc(callback))
Application.Run()
User32.UnhookWindowsHookEx(hookId) |> ignore
0
我的 fsproj 文件:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="NativeHooks.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms" />
</ItemGroup>
</Project>