Skip to content

Commit 881d920

Browse files
amrbashirgezihuzi
authored andcommitted
refactor(single-instance): improve userdata ptr handling (tauri-apps#2453)
* fix(single-instance): fix null pointer dereference panic on Windows * fmt
1 parent 800318e commit 881d920

File tree

5 files changed

+68
-59
lines changed

5 files changed

+68
-59
lines changed

.changes/single-instance-nullpointer-deref.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
single-instance: patch
33
---
44

5-
Fixed an issue that caused `null pointer dereference occurred` errors on rust nightly.
5+
Fixed `null pointer dereference` panic on rust nightly on Windows.

plugins/single-instance/examples/vanilla/src-tauri/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
// SPDX-License-Identifier: MIT
44

55
fn main() {
6-
tauri_build::build()
6+
tauri_build::build()
77
}

plugins/single-instance/examples/vanilla/src-tauri/rustfmt.toml

Lines changed: 0 additions & 14 deletions
This file was deleted.

plugins/single-instance/examples/vanilla/src-tauri/src/main.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
// SPDX-License-Identifier: MIT
44

55
#![cfg_attr(
6-
all(not(debug_assertions), target_os = "windows"),
7-
windows_subsystem = "windows"
6+
all(not(debug_assertions), target_os = "windows"),
7+
windows_subsystem = "windows"
88
)]
99

1010
fn main() {
11-
tauri::Builder::default()
12-
.plugin(tauri_plugin_cli::init())
13-
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
14-
println!("{}, {argv:?}, {cwd}", app.package_info().name);
15-
}))
16-
.run(tauri::generate_context!())
17-
.expect("error while running tauri application");
11+
tauri::Builder::default()
12+
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
13+
println!("{}, {argv:?}, {cwd}", app.package_info().name);
14+
}))
15+
.run(tauri::generate_context!())
16+
.expect("error while running tauri application");
1817
}

plugins/single-instance/src/platform_impl/windows.rs

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,38 @@ use windows_sys::Win32::{
2020
},
2121
UI::WindowsAndMessaging::{
2222
self as w32wm, CreateWindowExW, DefWindowProcW, DestroyWindow, FindWindowW,
23-
RegisterClassExW, SendMessageW, GWL_STYLE, GWL_USERDATA, WINDOW_LONG_PTR_INDEX,
24-
WM_COPYDATA, WM_DESTROY, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
25-
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
23+
RegisterClassExW, SendMessageW, CREATESTRUCTW, GWLP_USERDATA, GWL_STYLE,
24+
WINDOW_LONG_PTR_INDEX, WM_COPYDATA, WM_CREATE, WM_DESTROY, WNDCLASSEXW, WS_EX_LAYERED,
25+
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
2626
},
2727
};
2828

29+
const WMCOPYDATA_SINGLE_INSTANCE_DATA: usize = 1542;
30+
2931
struct MutexHandle(isize);
32+
3033
struct TargetWindowHandle(isize);
3134

32-
const WMCOPYDATA_SINGLE_INSTANCE_DATA: usize = 1542;
35+
struct UserData<R: Runtime> {
36+
app: AppHandle<R>,
37+
callback: Box<SingleInstanceCallback<R>>,
38+
}
39+
40+
impl<R: Runtime> UserData<R> {
41+
unsafe fn from_hwnd_raw(hwnd: HWND) -> *mut Self {
42+
GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut Self
43+
}
3344

34-
pub fn init<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
45+
unsafe fn from_hwnd<'a>(hwnd: HWND) -> &'a mut Self {
46+
&mut *Self::from_hwnd_raw(hwnd)
47+
}
48+
49+
fn run_callback(&mut self, args: Vec<String>, cwd: String) {
50+
(self.callback)(&self.app, args, cwd)
51+
}
52+
}
53+
54+
pub fn init<R: Runtime>(callback: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
3555
plugin::Builder::new("single-instance")
3656
.setup(|app, _api| {
3757
#[allow(unused_mut)]
@@ -54,37 +74,35 @@ pub fn init<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
5474
let hwnd = FindWindowW(class_name.as_ptr(), window_name.as_ptr());
5575

5676
if !hwnd.is_null() {
57-
let data = format!(
58-
"{}|{}\0",
59-
std::env::current_dir()
60-
.unwrap_or_default()
61-
.to_str()
62-
.unwrap_or_default(),
63-
std::env::args().collect::<Vec<String>>().join("|")
64-
);
77+
let cwd = std::env::current_dir().unwrap_or_default();
78+
let cwd = cwd.to_str().unwrap_or_default();
79+
80+
let args = std::env::args().collect::<Vec<String>>().join("|");
81+
82+
let data = format!("{cwd}|{args}\0",);
83+
6584
let bytes = data.as_bytes();
6685
let cds = COPYDATASTRUCT {
6786
dwData: WMCOPYDATA_SINGLE_INSTANCE_DATA,
6887
cbData: bytes.len() as _,
6988
lpData: bytes.as_ptr() as _,
7089
};
90+
7191
SendMessageW(hwnd, WM_COPYDATA, 0, &cds as *const _ as _);
92+
7293
app.cleanup_before_exit();
7394
std::process::exit(0);
7495
}
7596
}
7697
} else {
7798
app.manage(MutexHandle(hmutex as _));
7899

79-
let hwnd = create_event_target_window::<R>(&class_name, &window_name);
80-
unsafe {
81-
SetWindowLongPtrW(
82-
hwnd,
83-
GWL_USERDATA,
84-
Box::into_raw(Box::new((app.clone(), f))) as _,
85-
)
100+
let userdata = UserData {
101+
app: app.clone(),
102+
callback,
86103
};
87-
104+
let userdata = Box::into_raw(Box::new(userdata));
105+
let hwnd = create_event_target_window::<R>(&class_name, &window_name, userdata);
88106
app.manage(TargetWindowHandle(hwnd as _));
89107
}
90108

@@ -116,37 +134,43 @@ unsafe extern "system" fn single_instance_window_proc<R: Runtime>(
116134
wparam: WPARAM,
117135
lparam: LPARAM,
118136
) -> LRESULT {
119-
let data_ptr = GetWindowLongPtrW(hwnd, GWL_USERDATA)
120-
as *mut (AppHandle<R>, Box<SingleInstanceCallback<R>>);
121-
122-
if data_ptr.is_null() {
123-
return DefWindowProcW(hwnd, msg, wparam, lparam);
124-
}
125-
126-
let (app_handle, callback) = &mut *data_ptr;
127-
128137
match msg {
138+
WM_CREATE => {
139+
let create_struct = &*(lparam as *const CREATESTRUCTW);
140+
let userdata = create_struct.lpCreateParams as *const UserData<R>;
141+
SetWindowLongPtrW(hwnd, GWLP_USERDATA, userdata as _);
142+
0
143+
}
144+
129145
WM_COPYDATA => {
130146
let cds_ptr = lparam as *const COPYDATASTRUCT;
131147
if (*cds_ptr).dwData == WMCOPYDATA_SINGLE_INSTANCE_DATA {
148+
let userdata = UserData::<R>::from_hwnd(hwnd);
149+
132150
let data = CStr::from_ptr((*cds_ptr).lpData as _).to_string_lossy();
133151
let mut s = data.split('|');
134152
let cwd = s.next().unwrap();
135153
let args = s.map(|s| s.to_string()).collect();
136-
callback(app_handle, args, cwd.to_string());
154+
155+
userdata.run_callback(args, cwd.to_string());
137156
}
138157
1
139158
}
140159

141160
WM_DESTROY => {
142-
let _ = Box::from_raw(data_ptr);
161+
let userdata = UserData::<R>::from_hwnd_raw(hwnd);
162+
drop(Box::from_raw(userdata));
143163
0
144164
}
145165
_ => DefWindowProcW(hwnd, msg, wparam, lparam),
146166
}
147167
}
148168

149-
fn create_event_target_window<R: Runtime>(class_name: &[u16], window_name: &[u16]) -> HWND {
169+
fn create_event_target_window<R: Runtime>(
170+
class_name: &[u16],
171+
window_name: &[u16],
172+
userdata: *const UserData<R>,
173+
) -> HWND {
150174
unsafe {
151175
let class = WNDCLASSEXW {
152176
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
@@ -187,7 +211,7 @@ fn create_event_target_window<R: Runtime>(class_name: &[u16], window_name: &[u16
187211
std::ptr::null_mut(),
188212
std::ptr::null_mut(),
189213
GetModuleHandleW(std::ptr::null()),
190-
std::ptr::null(),
214+
userdata as _,
191215
);
192216
SetWindowLongPtrW(
193217
hwnd,

0 commit comments

Comments
 (0)