windows: Implement cli and handle open_urls (#25412)

Closes #ISSUE

Release Notes:

- N/A
This commit is contained in:
张小白 2025-02-27 08:27:19 +08:00 committed by GitHub
parent 9822d9673c
commit 672a472a23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 290 additions and 33 deletions

View file

@ -33,10 +33,13 @@ util.workspace = true
tempfile.workspace = true
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
exec.workspace = true
exec.workspace = true
fork.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation.workspace = true
core-services = "0.2"
plist = "1.3"
[target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true

View file

@ -521,30 +521,108 @@ mod flatpak {
}
}
// todo("windows")
#[cfg(target_os = "windows")]
mod windows {
use anyhow::Context;
use release_channel::APP_IDENTIFIER;
use windows::{
core::HSTRING,
Win32::{
Foundation::{CloseHandle, GetLastError, ERROR_ALREADY_EXISTS, GENERIC_WRITE},
Storage::FileSystem::{
CreateFileW, WriteFile, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, OPEN_EXISTING,
},
System::Threading::CreateMutexW,
},
};
use crate::{Detect, InstalledApp};
use std::io;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process::ExitStatus;
struct App;
fn check_single_instance() -> bool {
let mutex = unsafe {
CreateMutexW(
None,
false,
&HSTRING::from(format!("{}-Instance-Mutex", *APP_IDENTIFIER)),
)
.expect("Unable to create instance sync event")
};
let last_err = unsafe { GetLastError() };
let _ = unsafe { CloseHandle(mutex) };
last_err != ERROR_ALREADY_EXISTS
}
struct App(PathBuf);
impl InstalledApp for App {
fn zed_version_string(&self) -> String {
unimplemented!()
format!(
"Zed {}{}{} {}",
if *release_channel::RELEASE_CHANNEL_NAME == "stable" {
"".to_string()
} else {
format!("{} ", *release_channel::RELEASE_CHANNEL_NAME)
},
option_env!("RELEASE_VERSION").unwrap_or_default(),
match option_env!("ZED_COMMIT_SHA") {
Some(commit_sha) => format!(" {commit_sha} "),
None => "".to_string(),
},
self.0.display(),
)
}
fn launch(&self, _ipc_url: String) -> anyhow::Result<()> {
unimplemented!()
fn launch(&self, ipc_url: String) -> anyhow::Result<()> {
if check_single_instance() {
std::process::Command::new(self.0.clone())
.arg(ipc_url)
.spawn()?;
} else {
unsafe {
let pipe = CreateFileW(
&HSTRING::from(format!("\\\\.\\pipe\\{}-Named-Pipe", *APP_IDENTIFIER)),
GENERIC_WRITE.0,
FILE_SHARE_MODE::default(),
None,
OPEN_EXISTING,
FILE_FLAGS_AND_ATTRIBUTES::default(),
None,
)?;
let message = ipc_url.as_bytes();
let mut bytes_written = 0;
WriteFile(pipe, Some(message), Some(&mut bytes_written), None)?;
CloseHandle(pipe)?;
}
}
Ok(())
}
fn run_foreground(&self, _ipc_url: String) -> io::Result<ExitStatus> {
unimplemented!()
fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
std::process::Command::new(self.0.clone())
.arg(ipc_url)
.arg("--foreground")
.spawn()?
.wait()
}
}
impl Detect {
pub fn detect(_path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
Ok(App)
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
let path = if let Some(path) = path {
path.to_path_buf().canonicalize()?
} else {
std::env::current_exe()?
.parent()
.context("no parent path for cli")?
.parent()
.context("no parent path for cli folder")?
.join("Zed.exe")
};
Ok(App(path))
}
}
}