思路:
Cargo.toml添加如下的配置。
winapi = {version = "0.3.9",features = ["winuser","windef","ntdef","basetsd",
"minwindef","libloaderapi","wingdi","windowsx","errhandlingapi"]}
简单版本的思路代码如下:复杂的代码是第二个。这两个代码二选一。
extern crate winapi;
use winapi::um::winuser::{INPUT, INPUT_KEYBOARD, KEYBDINPUT, SendInput, KEYEVENTF_KEYUP};
const VK_A: u16 = 0x41; // "A"键的虚拟键码
// 定义一个按下键的函数
fn press_key(vk: u16) {
// 创建一个INPUT结构体来存储键盘输入数据
let mut input = INPUT {
// 设置输入类型为键盘输入
type_: INPUT_KEYBOARD,
// 将联合体初始化为零
u: unsafe { std::mem::zeroed() },
};
// 使用不安全代码来操作KEYBDINPUT结构体
unsafe {
// 将虚拟键码(vk)设置为输入参数
*input.u.ki_mut() = KEYBDINPUT {
// 设置虚拟键码(vk)
wVk: vk,
// 将扫描码设置为0(不使用)
wScan: 0,
// 将标志设置为0(无标志)
dwFlags: 0,
// 将时间设置为0(不使用)
time: 0,
// 将额外信息设置为0(不使用)
dwExtraInfo: 0usize,
};
// 将输入事件发送到系统
SendInput(
// cInputs:发送的输入事件数量(这里是1)
1,
// lpInputs:INPUT结构体数组的指针(这里是一个单独的INPUT结构体)
&mut input,
// cbSize:INPUT结构体的字节数(转换为i32)
std::mem::size_of::<INPUT>() as i32
);
}
}
// 定义一个释放键的函数
fn release_key(vk: u16) {
// 创建一个INPUT结构体来存储键盘输入数据
let mut input = INPUT {
// 设置输入类型为键盘输入
type_: INPUT_KEYBOARD,
// 将联合体初始化为零
u: unsafe { std::mem::zeroed() },
};
// 使用不安全代码来操作KEYBDINPUT结构体
unsafe {
// 将虚拟键码(vk)设置为输入参数
*input.u.ki_mut() = KEYBDINPUT {
// 设置虚拟键码(vk)
wVk: vk,
// 将扫描码设置为0(不使用)
wScan: 0,
// 将标志设置为KEYEVENTF_KEYUP(键释放)
dwFlags: KEYEVENTF_KEYUP,
// 将时间设置为0(不使用)
time: 0,
// 将额外信息设置为0(不使用)
dwExtraInfo: 0usize,
};
// 将输入事件发送到系统
SendInput(1, &mut input, std::mem::size_of::<INPUT>() as i32);
}
}
#[test]
fn main() {
press_key(VK_A);
release_key(VK_A);
}
以下是带窗口的单一的虚拟键盘按钮。
//虚拟键盘
extern crate winapi;
use std::ptr;
use winapi::um::winuser::{CreateWindowExW, DefWindowProcW, DispatchMessageW, GetMessageW, PostQuitMessage, RegisterClassW, ShowWindow, UpdateWindow, INPUT, INPUT_KEYBOARD, KEYBDINPUT, SendInput, KEYEVENTF_KEYUP, MSG, WNDCLASSW, WS_OVERLAPPEDWINDOW, WM_DESTROY, WM_COMMAND, BS_PUSHBUTTON, WS_CHILD, WS_VISIBLE, GetForegroundWindow, SetForegroundWindow, WS_EX_TOPMOST, WS_EX_NOACTIVATE, TranslateMessage, GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN, KLF_SETFORPROCESS, ActivateKeyboardLayout, LoadKeyboardLayoutW, KLF_ACTIVATE, LoadKeyboardLayoutA, KL_NAMELENGTH};
use winapi::shared::windef::{HWND, POINT};
use winapi::shared::minwindef::{UINT, WPARAM, LPARAM, LRESULT, HKL};
use winapi::um::libloaderapi::GetModuleHandleW;
const VK_A: u16 = 0x41; // "A"键的虚拟键码
const BUTTON_ID: i32 = 1;
fn press_key(vk: u16) {
// ... 使用虚拟键盘输入 ...
let mut input = INPUT {
type_: INPUT_KEYBOARD,
u: unsafe { std::mem::zeroed() },
};
unsafe {
*input.u.ki_mut() = KEYBDINPUT {
wVk: vk,
wScan: 0,
dwFlags: 0,
time: 0,
dwExtraInfo: 0usize,
};
SendInput(1, &mut input, std::mem::size_of::<INPUT>() as i32);
}
}
fn release_key(vk: u16) {
let mut input = INPUT {
type_: INPUT_KEYBOARD,
u: unsafe { std::mem::zeroed() },
};
unsafe {
*input.u.ki_mut() = KEYBDINPUT {
wVk: vk,
wScan: 0,
dwFlags: KEYEVENTF_KEYUP,
time: 0,
dwExtraInfo: 0usize,
};
SendInput(1, &mut input, std::mem::size_of::<INPUT>() as i32);
}
}
use winapi::um::winuser::{ UnloadKeyboardLayout, GetKeyboardLayout};
fn set_english_keyboard_layout() {
let hkl = unsafe { GetKeyboardLayout(0) };
println!("hkl:{:?}",hkl);
let keyboard_layout = 0x0409_u16; // English (United States)
unsafe {
let i = UnloadKeyboardLayout(hkl);
println!("卸载情况:{}",i);
LoadKeyboardLayoutW(&keyboard_layout as _ , 0); // Load English (United States) keyboard layout
ActivateKeyboardLayout(keyboard_layout as _, 0);
}
}
fn set_chinese_keyboard_layout() {
let hkl = unsafe { GetKeyboardLayout(0) };
println!("hkl:{:?}", hkl);
let keyboard_layout = 0x0804_u16; // Chinese (Simplified)
unsafe {
let i = UnloadKeyboardLayout(hkl);
println!("卸载情况:{}", i);
LoadKeyboardLayoutW(&keyboard_layout as _, 0); // Load Chinese (Simplified) keyboard layout
ActivateKeyboardLayout(keyboard_layout as _, 0);
}
}
unsafe extern "system" fn window_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match msg {
WM_DESTROY => {
PostQuitMessage(0);
0
}
WM_COMMAND => {
if wparam == BUTTON_ID as WPARAM {
let foreground_hwnd = GetForegroundWindow();
// set_english_keyboard_layout();
press_key(VK_A);
release_key(VK_A);
set_chinese_keyboard_layout();
SetForegroundWindow(foreground_hwnd);
}
0
}
_ => DefWindowProcW(hwnd, msg, wparam, lparam),
}
}
#[test]
fn main() {
let class_name = to_wstring("my_window");
let hinstance = unsafe { GetModuleHandleW(ptr::null()) };
let screen_width = unsafe { GetSystemMetrics(SM_CXSCREEN) };
let screen_height = unsafe { GetSystemMetrics(SM_CYSCREEN) };
let window_width = 500;
let window_height = 300;
let x = (screen_width - window_width) / 2;
let y = screen_height - window_height * 3 / 2;
let wnd_class = WNDCLASSW {
style: 0,
lpfnWndProc: Some(window_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: hinstance,
hIcon: ptr::null_mut(),
hCursor: ptr::null_mut(),
hbrBackground: ptr::null_mut(),
lpszMenuName: ptr::null(),
lpszClassName: class_name.as_ptr(),
};
unsafe {
RegisterClassW(&wnd_class);
let hwnd = CreateWindowExW(
WS_EX_TOPMOST | WS_EX_NOACTIVATE,
class_name.as_ptr(),
to_wstring("Virtual Keyboard").as_ptr(),
WS_OVERLAPPEDWINDOW,
x,
y,
window_width,
window_height,
ptr::null_mut(),
ptr::null_mut(),
hinstance,
ptr::null_mut(),
);
CreateWindowExW(
0,
to_wstring("button").as_ptr(),
to_wstring("Press A").as_ptr(),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
50, 50, 100, 30,
hwnd,
BUTTON_ID as _,
hinstance,
ptr::null_mut(),
);
ShowWindow(hwnd, 1);
UpdateWindow(hwnd);
let mut msg = MSG {
hwnd: ptr::null_mut(),
message: 0,
wParam: 0,
lParam: 0,
time: 0,
pt: POINT { x: 100, y: 100 },
};
while GetMessageW(&mut msg, ptr::null_mut(), 0, 0) > 0 {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
fn to_wstring(str: &str) -> Vec<u16> {
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
OsStr::new(str).encode_wide().chain(once(0)).collect()
}
效果