|
1 |
| -use std::{ |
2 |
| - io::{BufRead, BufReader, Result, Write}, |
3 |
| - path::Path, |
4 |
| -}; |
| 1 | +use std::io::Result; |
5 | 2 |
|
6 |
| -use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; |
7 |
| -use windows_sys::Win32::UI::{ |
8 |
| - Input::KeyboardAndMouse::{SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT}, |
9 |
| - WindowsAndMessaging::{AllowSetForegroundWindow, ASFW_ANY}, |
10 |
| -}; |
11 |
| -use winreg::{enums::HKEY_CURRENT_USER, RegKey}; |
| 3 | +use windows_registry::CURRENT_USER; |
12 | 4 |
|
13 | 5 | use crate::ID;
|
14 | 6 |
|
15 |
| -pub fn register<F: FnMut(String) + Send + 'static>(scheme: &str, handler: F) -> Result<()> { |
16 |
| - listen(handler)?; |
| 7 | +mod windows_single; |
17 | 8 |
|
18 |
| - let hkcu = RegKey::predef(HKEY_CURRENT_USER); |
19 |
| - let base = Path::new("Software").join("Classes").join(scheme); |
| 9 | +pub fn register(scheme: &str) -> Result<()> { |
| 10 | + let key_base = format!("Software\\Classes\\{}", scheme); |
20 | 11 |
|
21 |
| - let exe = crate::current_exe()? |
| 12 | + let exe = dunce::simplified(&crate::current_exe()?) |
22 | 13 | .display()
|
23 |
| - .to_string() |
24 |
| - .replace("\\\\?\\", ""); |
| 14 | + .to_string(); |
25 | 15 |
|
26 |
| - let (key, _) = hkcu.create_subkey(&base)?; |
27 |
| - key.set_value( |
| 16 | + let key_reg = CURRENT_USER.create(&key_base)?; |
| 17 | + key_reg.set_string( |
28 | 18 | "",
|
29 | 19 | &format!(
|
30 |
| - "URL:{}", |
| 20 | + "URL:{} protocol", |
31 | 21 | ID.get().expect("register() called before prepare()")
|
32 | 22 | ),
|
33 | 23 | )?;
|
34 |
| - key.set_value("URL Protocol", &"")?; |
| 24 | + key_reg.set_string("URL Protocol", "")?; |
35 | 25 |
|
36 |
| - let (icon, _) = hkcu.create_subkey(base.join("DefaultIcon"))?; |
37 |
| - icon.set_value("", &format!("{},0", &exe))?; |
| 26 | + let icon_reg = CURRENT_USER.create(format!("{key_base}\\DefaultIcon"))?; |
| 27 | + icon_reg.set_string("", &format!("{exe},0"))?; |
38 | 28 |
|
39 |
| - let (cmd, _) = hkcu.create_subkey(base.join("shell").join("open").join("command"))?; |
| 29 | + let cmd_reg = CURRENT_USER.create(format!("{key_base}\\shell\\open\\command"))?; |
40 | 30 |
|
41 |
| - cmd.set_value("", &format!("{} \"%1\"", &exe))?; |
| 31 | + cmd_reg.set_string("", &format!("\"{exe}\" \"%1\""))?; |
42 | 32 |
|
43 | 33 | Ok(())
|
44 | 34 | }
|
45 | 35 |
|
46 | 36 | pub fn unregister(scheme: &str) -> Result<()> {
|
47 |
| - let hkcu = RegKey::predef(HKEY_CURRENT_USER); |
48 |
| - let base = Path::new("Software").join("Classes").join(scheme); |
49 |
| - |
50 |
| - hkcu.delete_subkey_all(base)?; |
| 37 | + CURRENT_USER.remove_tree(format!("Software\\Classes\\{}", scheme))?; |
51 | 38 |
|
52 | 39 | Ok(())
|
53 | 40 | }
|
54 | 41 |
|
55 |
| -pub fn listen<F: FnMut(String) + Send + 'static>(mut handler: F) -> Result<()> { |
56 |
| - std::thread::spawn(move || { |
57 |
| - let listener = |
58 |
| - LocalSocketListener::bind(ID.get().expect("listen() called before prepare()").as_str()) |
59 |
| - .expect("Can't create listener"); |
60 |
| - |
61 |
| - for conn in listener.incoming().filter_map(|c| { |
62 |
| - c.map_err(|error| log::error!("Incoming connection failed: {}", error)) |
63 |
| - .ok() |
64 |
| - }) { |
65 |
| - // Listen for the launch arguments |
66 |
| - let mut conn = BufReader::new(conn); |
67 |
| - let mut buffer = String::new(); |
68 |
| - if let Err(io_err) = conn.read_line(&mut buffer) { |
69 |
| - log::error!("Error reading incoming connection: {}", io_err.to_string()); |
70 |
| - }; |
71 |
| - buffer.pop(); |
72 |
| - |
73 |
| - handler(buffer); |
74 |
| - } |
75 |
| - }); |
| 42 | +pub fn listen<F: FnMut(String) + Sync + Send + 'static>(mut handler: F) -> Result<()> { |
| 43 | + windows_single::init(Box::new(move |args, _| { |
| 44 | + handler(args.join(" ")); |
| 45 | + })); |
76 | 46 |
|
77 | 47 | Ok(())
|
78 | 48 | }
|
79 | 49 |
|
80 | 50 | pub fn prepare(identifier: &str) {
|
81 |
| - if let Ok(mut conn) = LocalSocketStream::connect(identifier) { |
82 |
| - // We are the secondary instance. |
83 |
| - // Prep to activate primary instance by allowing another process to take focus. |
84 |
| - |
85 |
| - // A workaround to allow AllowSetForegroundWindow to succeed - press a key. |
86 |
| - // This was originally used by Chromium: https://bugs.chromium.org/p/chromium/issues/detail?id=837796 |
87 |
| - dummy_keypress(); |
88 |
| - |
89 |
| - let primary_instance_pid = conn.peer_pid().unwrap_or(ASFW_ANY); |
90 |
| - unsafe { |
91 |
| - let success = AllowSetForegroundWindow(primary_instance_pid) != 0; |
92 |
| - if !success { |
93 |
| - log::warn!("AllowSetForegroundWindow failed."); |
94 |
| - } |
95 |
| - } |
96 |
| - |
97 |
| - if let Err(io_err) = conn.write_all(std::env::args().nth(1).unwrap_or_default().as_bytes()) |
98 |
| - { |
99 |
| - log::error!( |
100 |
| - "Error sending message to primary instance: {}", |
101 |
| - io_err.to_string() |
102 |
| - ); |
103 |
| - }; |
104 |
| - let _ = conn.write_all(b"\n"); |
105 |
| - std::process::exit(0); |
106 |
| - }; |
107 | 51 | ID.set(identifier.to_string())
|
108 | 52 | .expect("prepare() called more than once with different identifiers.");
|
109 | 53 | }
|
110 |
| - |
111 |
| -/// Send a dummy keypress event so AllowSetForegroundWindow can succeed |
112 |
| -fn dummy_keypress() { |
113 |
| - let keyboard_input_down = KEYBDINPUT { |
114 |
| - wVk: 0, // This doesn't correspond to any actual keyboard key, but should still function for the workaround. |
115 |
| - dwExtraInfo: 0, |
116 |
| - wScan: 0, |
117 |
| - time: 0, |
118 |
| - dwFlags: 0, |
119 |
| - }; |
120 |
| - |
121 |
| - let mut keyboard_input_up = keyboard_input_down; |
122 |
| - keyboard_input_up.dwFlags = 0x0002; // KEYUP flag |
123 |
| - |
124 |
| - let input_down_u = INPUT_0 { |
125 |
| - ki: keyboard_input_down, |
126 |
| - }; |
127 |
| - let input_up_u = INPUT_0 { |
128 |
| - ki: keyboard_input_up, |
129 |
| - }; |
130 |
| - |
131 |
| - let input_down = INPUT { |
132 |
| - r#type: INPUT_KEYBOARD, |
133 |
| - Anonymous: input_down_u, |
134 |
| - }; |
135 |
| - |
136 |
| - let input_up = INPUT { |
137 |
| - r#type: INPUT_KEYBOARD, |
138 |
| - Anonymous: input_up_u, |
139 |
| - }; |
140 |
| - |
141 |
| - let ipsize = std::mem::size_of::<INPUT>() as i32; |
142 |
| - unsafe { |
143 |
| - SendInput(2, [input_down, input_up].as_ptr(), ipsize); |
144 |
| - }; |
145 |
| -} |
0 commit comments