DawnLauncher/rust/windows.rs
2023-11-03 15:17:45 +08:00

674 lines
21 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use base64::{engine::general_purpose, Engine as _};
use clipboard_win::{formats, get_clipboard};
use image::{imageops::flip_vertical, ImageBuffer, ImageFormat, Rgba};
use napi::{
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
JsFunction,
};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
io::Cursor,
path::Path,
process::Command,
sync::atomic::{AtomicBool, Ordering},
};
use windows::{
core::{ComInterface, HSTRING, PCSTR, PCWSTR},
w,
Win32::{
Foundation::{HWND, LPARAM, LRESULT, MAX_PATH, POINT, RECT, SIZE, WPARAM},
Graphics::Gdi::{
GetMonitorInfoW, GetObjectW, MonitorFromWindow, BITMAP, MONITORINFO,
MONITOR_DEFAULTTONEAREST,
},
Storage::FileSystem::SearchPathW,
System::{
Com::{
CoCreateInstance, CoInitializeEx, CoUninitialize, IPersistFile,
CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED, STGM_READ,
},
Environment::GetEnvironmentVariableW,
SystemInformation::GetSystemDirectoryW,
},
UI::{
Input::Ime::{
ImmGetContext, ImmReleaseContext, ImmSetConversionStatus, IME_CMODE_ALPHANUMERIC,
IME_SMODE_AUTOMATIC,
},
Shell::{
BHID_SFUIObject, IContextMenu, IShellItem, IShellItemImageFactory, IShellLinkW,
SHCreateItemFromParsingName, SHEmptyRecycleBinW, SHQueryUserNotificationState,
ShellExecuteW, ShellLink, CMF_NORMAL, CMINVOKECOMMANDINFO,
QUNS_ACCEPTS_NOTIFICATIONS, QUNS_APP, QUNS_BUSY, QUNS_NOT_PRESENT,
QUNS_PRESENTATION_MODE, QUNS_QUIET_TIME, QUNS_RUNNING_D3D_FULL_SCREEN,
SHERB_NOSOUND, SIIGBF_ICONONLY, SLGP_UNCPRIORITY,
},
WindowsAndMessaging::{
CallNextHookEx, CreatePopupMenu, DestroyMenu, FindWindowW, GetClassNameW,
GetCursorPos, GetForegroundWindow, GetSystemMetrics, GetWindowRect, SendMessageW,
SetForegroundWindow, SetWindowsHookExW, TrackPopupMenu, WindowFromPoint, HHOOK,
MSLLHOOKSTRUCT, SC_MONITORPOWER, SM_CXSCREEN, SM_CYSCREEN, SW_NORMAL,
SW_SHOWDEFAULT, TPM_NONOTIFY, TPM_RETURNCMD, WH_MOUSE_LL, WM_LBUTTONDOWN,
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SYSCOMMAND,
},
},
},
};
// 获取图标并转为BASE64
pub fn get_file_icon(path: &str) -> Option<String> {
// 返回信息
let mut base64 = None;
// HSTRING
let path = HSTRING::from(path);
// PCWSTR
let path: PCWSTR = PCWSTR(path.as_ptr());
// unsafe
unsafe {
// Init
let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED);
}
// IShellItemImageFactory
let result = unsafe {
SHCreateItemFromParsingName::<PCWSTR, Option<_>, IShellItemImageFactory>(path, None)
};
if let Ok(shell_item_image_factory) = result {
if let Some(mut image_buffer) = get_file_icon_image_buffer(&shell_item_image_factory, 256) {
// 判断像素点,是否是小图标
let mut transparency: f64 = 0_f64;
let mut non_transparency: f64 = 0_f64;
for y in 0..image_buffer.height() {
for x in 0..image_buffer.width() {
let pixel = image_buffer.get_pixel(x, y);
let alpha = pixel[3]; // 获取像素的 Alpha 通道值
if alpha == 0 {
// 透明
transparency += 1_f64;
} else {
// 不透明
non_transparency += 1_f64;
}
}
}
// 计算如果透明区域大于百分之70就代表是小图标
let proportion =
(transparency / (transparency + non_transparency) * 100_f64).round() as u32;
if proportion >= 70 {
// 获取小图标
if let Some(image_buffer_small) =
get_file_icon_image_buffer(&shell_item_image_factory, 48)
{
image_buffer = image_buffer_small;
}
}
// 翻转图片
image_buffer = flip_vertical(&image_buffer);
// 转码
base64 = Some(image_buffer_to_base64(image_buffer));
}
}
unsafe {
// UnInit
CoUninitialize();
}
base64
}
// 获取图标并转为ImageBuffer
fn get_file_icon_image_buffer(
shell_item_image_factory: &IShellItemImageFactory,
size: i32,
) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
// 获取图片
let result =
unsafe { shell_item_image_factory.GetImage(SIZE { cx: size, cy: size }, SIIGBF_ICONONLY) };
if let Ok(h_bitmap) = result {
// 转为BITMAP
let mut bitmap: BITMAP = BITMAP::default();
unsafe {
GetObjectW(
h_bitmap,
std::mem::size_of::<BITMAP>() as i32,
Some(&mut bitmap as *mut _ as _),
);
}
// 转换ImageBuffer
let width: u32 = bitmap.bmWidth as u32;
let height = bitmap.bmHeight as u32;
let pixel_data: &[u8] = unsafe {
std::slice::from_raw_parts(bitmap.bmBits as *const u8, (width * height * 4) as usize)
};
let result = ImageBuffer::<Rgba<u8>, _>::from_raw(width, height, pixel_data.to_vec());
if let Some(mut image_buffer) = result {
// 将ImageBuffer的颜色通道顺序从BGRA转为RGB
for pixel in image_buffer.pixels_mut() {
let b = pixel[0];
let r = pixel[2];
pixel[0] = r;
pixel[2] = b;
}
return Some(image_buffer);
}
}
None
}
/**
* imageBuffer转BASE64
*/
fn image_buffer_to_base64(image_buffer: ImageBuffer<Rgba<u8>, Vec<u8>>) -> String {
// imageBufferData
let mut image_buffer_data = Cursor::new(Vec::new());
// write
image_buffer
.write_to(&mut image_buffer_data, ImageFormat::Png)
.unwrap();
// 转码
format!(
"data:image/png;base64,{}",
general_purpose::STANDARD.encode(image_buffer_data.into_inner())
)
}
/**
* 运行
*/
pub fn shell_execute(
operation: String,
file: String,
params: String,
start_location: Option<String>,
) {
// dir
let dir = start_location.unwrap_or_else(|| {
// 判断是否是文件夹
let path = Path::new(&file);
if path.is_dir() {
// 文件夹
file.clone()
} else {
// 文件 获取上一级目录
path.parent().unwrap().display().to_string()
}
});
// HSTRING
let operation = HSTRING::from(operation.as_str());
let file = HSTRING::from(file.as_str());
let params = HSTRING::from(params.as_str());
let dir = HSTRING::from(dir.as_str());
// PCWSTR
let operation = PCWSTR(operation.as_ptr());
let file = PCWSTR(file.as_ptr());
let params = PCWSTR(params.as_ptr());
let dir = PCWSTR(dir.as_ptr());
unsafe {
// execute
ShellExecuteW(None, operation, file, params, dir, SW_SHOWDEFAULT);
}
}
/**
* 获取快捷方式信息
*/
pub fn get_shortcut_file_info(path: &str) -> Option<HashMap<String, String>> {
// HSTRING
let path = HSTRING::from(path);
unsafe {
// Init
let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED);
}
// IShellLinkW
let shell_link_result: Result<IShellLinkW, windows::core::Error> =
unsafe { CoCreateInstance(&ShellLink, None, CLSCTX_INPROC_SERVER) };
if let Ok(shell_link) = shell_link_result {
// IPersistFile
let persist_file_result: Result<IPersistFile, windows::core::Error> = shell_link.cast();
if let Ok(persist_file) = persist_file_result {
let load_result = unsafe {
// 加载路径
persist_file.Load(PCWSTR(path.as_ptr()), STGM_READ)
};
if let Ok(()) = load_result {
// 获取目标
let mut target_buffer = [0u16; MAX_PATH as usize];
let _ = unsafe {
shell_link.GetPath(
&mut target_buffer,
std::ptr::null_mut(),
SLGP_UNCPRIORITY.0 as u32,
)
};
// 获取参数
let mut arguments_buffer = [0u16; MAX_PATH as usize];
let _ = unsafe { shell_link.GetArguments(&mut arguments_buffer) };
// map
let mut map = HashMap::with_capacity(2);
map.insert(String::from("target"), u16_to_string(&target_buffer));
map.insert(String::from("arguments"), u16_to_string(&arguments_buffer));
return Some(map);
}
}
}
unsafe {
// UnInit
CoUninitialize();
}
None
}
/**
* 运行命令
*/
pub fn system_item_execute(target: &str, params: Option<&str>) {
if target == "static:TurnOffMonitor" {
// 关闭显示器
turn_off_monitor()
} else {
let mut file = target.to_string();
if !target.starts_with("shell:") {
// 如果不是shell开头就查询路径
file = search_path(target).unwrap_or(target.to_string());
}
let file = HSTRING::from(file);
let params = match params {
Some(p) => HSTRING::from(p),
_ => HSTRING::new(),
};
// 获取系统盘路径当作工作目录
let mut buffer = [0u16; MAX_PATH as usize];
unsafe {
GetSystemDirectoryW(Some(&mut buffer));
}
let dir = u16_to_string(&buffer);
let dir = HSTRING::from(dir);
// execute
unsafe {
ShellExecuteW(
None,
w!("open"),
PCWSTR(file.as_ptr()),
PCWSTR(params.as_ptr()),
PCWSTR(dir.as_ptr()),
SW_SHOWDEFAULT,
);
}
}
}
/**
* 关闭显示器
*/
pub fn turn_off_monitor() {
unsafe {
let _ = SendMessageW(
FindWindowW(PCWSTR::null(), PCWSTR::null()),
WM_SYSCOMMAND,
WPARAM(SC_MONITORPOWER as usize),
LPARAM(2),
);
}
}
/**
* 打开文件所在位置
*/
pub fn open_file_location(path: &str) {
let _ = Command::new("explorer").arg("/select,").arg(path).spawn();
}
/**
* 资源管理器菜单
*/
pub fn explorer_context_menu(window: i32, path: &str, x: i32, y: i32) {
// IShellItem
let path = HSTRING::from(path);
if let Ok(shell_item) =
unsafe { SHCreateItemFromParsingName::<_, _, IShellItem>(PCWSTR(path.as_ptr()), None) }
{
// IContextMenu
if let Ok(context_menu) =
unsafe { shell_item.BindToHandler::<_, IContextMenu>(None, &BHID_SFUIObject) }
{
// Menu
if let Ok(menu) = unsafe { CreatePopupMenu() } {
// 写入菜单
if let Ok(()) =
unsafe { context_menu.QueryContextMenu(menu, 0, 1, 0x7FFF, CMF_NORMAL) }
{
// HWND
let hwnd = HWND(window as isize);
// 弹出菜单
let res = unsafe {
SetForegroundWindow(hwnd);
TrackPopupMenu(menu, TPM_RETURNCMD | TPM_NONOTIFY, x, y, 0, hwnd, None)
};
unsafe {
DestroyMenu(menu);
}
if res.as_bool() {
let mut info = CMINVOKECOMMANDINFO::default();
info.cbSize = std::mem::size_of::<CMINVOKECOMMANDINFO>() as u32;
info.hwnd = hwnd;
info.lpVerb = PCSTR((res.0 - 1) as *mut u8);
info.nShow = SW_NORMAL.0 as i32;
let _ = unsafe { context_menu.InvokeCommand(&info) };
}
}
}
}
}
}
/**
* 搜索路径
*/
pub fn search_path(name: &str) -> Option<String> {
let name = HSTRING::from(name);
let mut buffer = [0u16; MAX_PATH as usize];
let result = unsafe {
SearchPathW(
PCWSTR::null(),
PCWSTR(name.as_ptr()),
PCWSTR::null(),
Some(&mut buffer),
None,
)
};
if result > 0 {
Some(u16_to_string(&buffer))
} else {
None
}
}
/**
* 数组转String
*/
fn u16_to_string(slice: &[u16]) -> String {
let mut vec = vec![];
for s in slice {
if *s > 0 {
vec.push(*s);
}
}
String::from_utf16_lossy(&vec)
}
/**
* 获取环境变量
*/
pub fn get_env_by_name(name: &str) -> Option<String> {
let name = HSTRING::from(name);
let mut buffer = [0u16; MAX_PATH as usize];
let result = unsafe { GetEnvironmentVariableW(PCWSTR(name.as_ptr()), Some(&mut buffer)) };
if result > 0 {
Some(u16_to_string(&buffer))
} else {
None
}
}
/**
* 是否有全屏窗口
*/
fn is_fullscreen_window() -> bool {
// 获取当前活动窗口的句柄
let foreground_window = unsafe { GetForegroundWindow() };
// 获取活动窗口的位置信息
let mut window_rect = RECT::default();
unsafe { GetWindowRect(foreground_window, &mut window_rect) };
// 获取包含活动窗口的显示器句柄
let monitor = unsafe { MonitorFromWindow(foreground_window, MONITOR_DEFAULTTONEAREST) };
// 获取显示器信息
let mut monitor_info = MONITORINFO::default();
monitor_info.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
unsafe { GetMonitorInfoW(monitor, &mut monitor_info) };
// 获取屏幕的尺寸
let screen_width = unsafe { GetSystemMetrics(SM_CXSCREEN) };
let screen_height = unsafe { GetSystemMetrics(SM_CYSCREEN) };
// 比较窗口位置和显示器尺寸来判断是否处于全屏模式
if window_rect.left <= 0
&& window_rect.top <= 0
&& window_rect.right >= screen_width
&& window_rect.bottom >= screen_height
&& monitor_info.rcMonitor.left == 0
&& monitor_info.rcMonitor.top == 0
&& monitor_info.rcMonitor.right == screen_width
&& monitor_info.rcMonitor.bottom == screen_height
{
// 获取窗口类名
let mut buffer = [0u16; MAX_PATH as usize];
unsafe { GetClassNameW(foreground_window, &mut buffer) };
// 转为String
let name = u16_to_string(&buffer);
if name != "WorkerW" {
return true;
}
}
false
}
/**
* 是否是全屏模式
*/
pub fn is_fullscreen() -> bool {
if let Ok(state) = unsafe { SHQueryUserNotificationState() } {
if state == QUNS_NOT_PRESENT {
// 非全屏(机器锁定/屏幕保护程序/用户切换)
return false;
} else if state == QUNS_BUSY {
// 全屏F11 全屏,我试过的所有视频游戏都使用它)
return is_fullscreen_window();
} else if state == QUNS_RUNNING_D3D_FULL_SCREEN {
// 全屏Direct3D 应用程序以独占模式运行,即全屏)
return true;
} else if state == QUNS_PRESENTATION_MODE {
// 全屏(一种用于显示全屏演示文稿的特殊模式)
return true;
} else if state == QUNS_ACCEPTS_NOTIFICATIONS {
// 不是全屏
return false;
} else if state == QUNS_QUIET_TIME {
// 不是全屏
return false;
} else if state == QUNS_APP {
// 不是全屏
return false;
}
}
false
}
/**
* 切换英文输入法
*/
pub fn switch_english(window: i32) {
// 窗口句柄
let hwnd = HWND(window as isize);
// 获取输入法上下文
let imc = unsafe { ImmGetContext(hwnd) };
// 设置输入法的首选转换模式为英文
unsafe { ImmSetConversionStatus(imc, IME_CMODE_ALPHANUMERIC, IME_SMODE_AUTOMATIC) };
// 释放输入法上下文
unsafe { ImmReleaseContext(hwnd, imc) };
}
// 是否回调
static mut MOUSE_HOOK_CALL: AtomicBool = AtomicBool::new(false);
// ThreadsafeFunction
static mut TSFN: Option<ThreadsafeFunction<String>> = None;
// 全局鼠标HOOK
static mut MOUSE_HOOK: Option<HHOOK> = None;
// 鼠标事件
#[derive(Debug, Serialize, Deserialize)]
struct MouseEvent {
event: String,
x: i32,
y: i32,
button: i32,
mouse_data: u32,
}
/**
* HOOK回调方法
*/
unsafe extern "system" fn mouse_proc(code: i32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
if code >= 0 && MOUSE_HOOK_CALL.load(Ordering::Relaxed) {
// 鼠标坐标
let msll_struct = lparam.0 as *const MSLLHOOKSTRUCT;
let x = (*msll_struct).pt.x;
let y = (*msll_struct).pt.y;
let mouse_data = (*msll_struct).mouseData;
// 参数
let param = wparam.0 as u32;
// 事件
let mut event = String::from("");
// 按键类型
let mut button = -1;
// 判断事件
if param == WM_MOUSEMOVE {
// 鼠标移动
event.push_str("mousemove");
} else {
// 鼠标操作
if param == WM_LBUTTONUP || param == WM_RBUTTONUP || param == WM_MBUTTONUP {
event.push_str("mouseup");
} else if param == WM_LBUTTONDOWN || param == WM_RBUTTONDOWN || param == WM_MBUTTONDOWN
{
event.push_str("mousedown");
} else if param == WM_MOUSEWHEEL || param == WM_MOUSEHWHEEL {
event.push_str("mousewheel");
}
// 按键类型
if param == WM_LBUTTONUP || param == WM_LBUTTONDOWN {
button = 1;
} else if param == WM_RBUTTONUP || param == WM_RBUTTONDOWN {
button = 2;
} else if param == WM_MBUTTONUP || param == WM_MBUTTONDOWN {
button = 3;
} else if param == WM_MOUSEWHEEL {
button = 0;
} else if param == WM_MOUSEHWHEEL {
button = 1;
}
}
if event != "" {
if let Some(func) = TSFN.as_ref() {
let mouse_event = MouseEvent {
event,
x,
y,
mouse_data,
button,
};
func.call(
Ok(serde_json::to_string(&mouse_event).unwrap()),
ThreadsafeFunctionCallMode::NonBlocking,
);
}
}
}
return CallNextHookEx(MOUSE_HOOK.unwrap(), code, wparam, lparam);
}
/**
* 创建鼠标hook
*/
pub fn create_mouse_hook(callback: JsFunction) {
// 创建回调
if let Ok(threadsafe_function) =
callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))
{
unsafe { TSFN = Some(threadsafe_function) };
// 创建鼠标HOOK
if let Ok(hook) = unsafe { SetWindowsHookExW(WH_MOUSE_LL, Some(mouse_proc), None, 0) } {
unsafe {
MOUSE_HOOK = Some(hook);
MOUSE_HOOK_CALL.store(true, Ordering::Relaxed);
};
}
}
}
/**
* 启用鼠标HOOK
*/
pub fn enable_mouse_hook() {
unsafe { MOUSE_HOOK_CALL.store(true, Ordering::Relaxed) }
}
/**
* 禁用鼠标HOOK
*/
pub fn disable_mouse_hook() {
unsafe { MOUSE_HOOK_CALL.store(false, Ordering::Relaxed) }
}
/**
* 获取鼠标点击的窗口ClassName
*/
pub fn get_cursor_pos_window_class_name() -> String {
// 获取鼠标位置
let mut point: POINT = POINT::default();
unsafe {
GetCursorPos(&mut point);
}
// 获取鼠标所在的窗口句柄
let hwnd = unsafe { WindowFromPoint(point) };
// 获取窗口的ClassName
let mut buffer = [0u16; MAX_PATH as usize];
unsafe {
GetClassNameW(hwnd, &mut buffer);
};
// 返回
u16_to_string(&buffer)
}
/**
* 获取剪切板文件列表
*/
pub fn get_clipboard_file_list() -> Vec<String> {
match get_clipboard(formats::FileList) {
Ok(vec) => vec,
Err(_) => vec![],
}
}
/**
* 剪切板是否存在BITMAP
*/
pub fn clipboard_has_bitmap() -> bool {
match get_clipboard(formats::Bitmap) {
Ok(_) => true,
Err(_) => false,
}
}
/**
* 获取剪切板BITMAP的BASE64
*/
pub fn get_clipboard_bitmap_base64() -> Option<String> {
match get_clipboard(formats::Bitmap) {
Ok(data) => Some(format!(
"data:image/bmp;base64,{}",
general_purpose::STANDARD.encode(data)
)),
Err(_) => None,
}
}
/**
* 清空回收站
*/
pub fn empty_recycle_bin(window: i32) {
// HWND
let hwnd = HWND(window as isize);
// 清空回收站
unsafe {
let _ = SHEmptyRecycleBinW(hwnd, None, SHERB_NOSOUND);
};
}