windows: Implement cli
and handle open_urls
(#25412)
Closes #ISSUE Release Notes: - N/A
This commit is contained in:
parent
9822d9673c
commit
672a472a23
7 changed files with 290 additions and 33 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2653,6 +2653,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"util",
|
"util",
|
||||||
|
"windows 0.58.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -370,7 +370,7 @@ zeta = { path = "crates/zeta" }
|
||||||
#
|
#
|
||||||
|
|
||||||
aho-corasick = "1.1"
|
aho-corasick = "1.1"
|
||||||
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", rev = "03c2907b44b4189aac5fdeaea331f5aab5c7072e"}
|
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", rev = "03c2907b44b4189aac5fdeaea331f5aab5c7072e" }
|
||||||
any_vec = "0.14"
|
any_vec = "0.14"
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
arrayvec = { version = "0.7.4", features = ["serde"] }
|
arrayvec = { version = "0.7.4", features = ["serde"] }
|
||||||
|
@ -544,7 +544,7 @@ tree-sitter-cpp = "0.23"
|
||||||
tree-sitter-css = "0.23"
|
tree-sitter-css = "0.23"
|
||||||
tree-sitter-elixir = "0.3"
|
tree-sitter-elixir = "0.3"
|
||||||
tree-sitter-embedded-template = "0.23.0"
|
tree-sitter-embedded-template = "0.23.0"
|
||||||
tree-sitter-gitcommit = {git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9"}
|
tree-sitter-gitcommit = { git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9" }
|
||||||
tree-sitter-go = "0.23"
|
tree-sitter-go = "0.23"
|
||||||
tree-sitter-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "6efb59652d30e0e9cd5f3b3a669afd6f1a926d3c", package = "tree-sitter-gomod" }
|
tree-sitter-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "6efb59652d30e0e9cd5f3b3a669afd6f1a926d3c", package = "tree-sitter-gomod" }
|
||||||
tree-sitter-gowork = { git = "https://github.com/zed-industries/tree-sitter-go-work", rev = "acb0617bf7f4fda02c6217676cc64acb89536dc7" }
|
tree-sitter-gowork = { git = "https://github.com/zed-industries/tree-sitter-go-work", rev = "acb0617bf7f4fda02c6217676cc64acb89536dc7" }
|
||||||
|
@ -619,6 +619,7 @@ features = [
|
||||||
"Win32_Storage_FileSystem",
|
"Win32_Storage_FileSystem",
|
||||||
"Win32_System_Com",
|
"Win32_System_Com",
|
||||||
"Win32_System_Com_StructuredStorage",
|
"Win32_System_Com_StructuredStorage",
|
||||||
|
"Win32_System_Console",
|
||||||
"Win32_System_DataExchange",
|
"Win32_System_DataExchange",
|
||||||
"Win32_System_LibraryLoader",
|
"Win32_System_LibraryLoader",
|
||||||
"Win32_System_Memory",
|
"Win32_System_Memory",
|
||||||
|
@ -639,7 +640,7 @@ features = [
|
||||||
# TODO livekit https://github.com/RustAudio/cpal/pull/891
|
# TODO livekit https://github.com/RustAudio/cpal/pull/891
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
|
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
|
||||||
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls"}
|
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
split-debuginfo = "unpacked"
|
split-debuginfo = "unpacked"
|
||||||
|
|
|
@ -33,10 +33,13 @@ util.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
|
||||||
exec.workspace = true
|
exec.workspace = true
|
||||||
fork.workspace = true
|
fork.workspace = true
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-foundation.workspace = true
|
core-foundation.workspace = true
|
||||||
core-services = "0.2"
|
core-services = "0.2"
|
||||||
plist = "1.3"
|
plist = "1.3"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
windows.workspace = true
|
||||||
|
|
|
@ -521,30 +521,108 @@ mod flatpak {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo("windows")
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod 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 crate::{Detect, InstalledApp};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::ExitStatus;
|
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 {
|
impl InstalledApp for App {
|
||||||
fn zed_version_string(&self) -> String {
|
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 {
|
impl Detect {
|
||||||
pub fn detect(_path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
|
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
|
||||||
Ok(App)
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,15 @@ pub static RELEASE_CHANNEL: LazyLock<ReleaseChannel> =
|
||||||
_ => panic!("invalid release channel {}", *RELEASE_CHANNEL_NAME),
|
_ => panic!("invalid release channel {}", *RELEASE_CHANNEL_NAME),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The app identifier for the current release channel, Windows only.
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub static APP_IDENTIFIER: LazyLock<&str> = LazyLock::new(|| match *RELEASE_CHANNEL {
|
||||||
|
ReleaseChannel::Dev => "Zed-Editor-Dev",
|
||||||
|
ReleaseChannel::Nightly => "Zed-Editor-Nightly",
|
||||||
|
ReleaseChannel::Preview => "Zed-Editor-Preview",
|
||||||
|
ReleaseChannel::Stable => "Zed-Editor-Stable",
|
||||||
|
});
|
||||||
|
|
||||||
/// The Git commit SHA that Zed was built at.
|
/// The Git commit SHA that Zed was built at.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppCommitSha(pub String);
|
pub struct AppCommitSha(pub String);
|
||||||
|
|
|
@ -173,6 +173,22 @@ fn fail_to_open_window(e: anyhow::Error, _cx: &mut App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let run_foreground = args.foreground;
|
||||||
|
|
||||||
|
#[cfg(all(not(debug_assertions), target_os = "windows"))]
|
||||||
|
if run_foreground {
|
||||||
|
unsafe {
|
||||||
|
use windows::Win32::System::Console::{AttachConsole, ATTACH_PARENT_PROCESS};
|
||||||
|
|
||||||
|
if run_foreground {
|
||||||
|
let _ = AttachConsole(ATTACH_PARENT_PROCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
menu::init();
|
menu::init();
|
||||||
zed_actions::init();
|
zed_actions::init();
|
||||||
|
|
||||||
|
@ -217,7 +233,10 @@ fn main() {
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
!crate::zed::windows_only_instance::check_single_instance()
|
!crate::zed::windows_only_instance::check_single_instance(
|
||||||
|
open_listener.clone(),
|
||||||
|
run_foreground,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
@ -574,7 +593,6 @@ fn main() {
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
|
|
||||||
let args = Args::parse();
|
|
||||||
let urls: Vec<_> = args
|
let urls: Vec<_> = args
|
||||||
.paths_or_urls
|
.paths_or_urls
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1012,6 +1030,11 @@ struct Args {
|
||||||
/// Instructs zed to run as a dev server on this machine. (not implemented)
|
/// Instructs zed to run as a dev server on this machine. (not implemented)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
dev_server_token: Option<String>,
|
dev_server_token: Option<String>,
|
||||||
|
|
||||||
|
/// Run zed in the foreground, only used on Windows, to match the behavior of the behavior on macOS.
|
||||||
|
#[arg(long)]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
foreground: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -1,31 +1,173 @@
|
||||||
use release_channel::ReleaseChannel;
|
use std::{sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use clap::Parser;
|
||||||
|
use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use release_channel::APP_IDENTIFIER;
|
||||||
|
use util::ResultExt;
|
||||||
use windows::{
|
use windows::{
|
||||||
core::HSTRING,
|
core::HSTRING,
|
||||||
Win32::{
|
Win32::{
|
||||||
Foundation::{GetLastError, ERROR_ALREADY_EXISTS},
|
Foundation::{CloseHandle, GetLastError, ERROR_ALREADY_EXISTS, GENERIC_WRITE, HANDLE},
|
||||||
System::Threading::CreateEventW,
|
Storage::FileSystem::{
|
||||||
|
CreateFileW, ReadFile, WriteFile, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE,
|
||||||
|
OPEN_EXISTING, PIPE_ACCESS_INBOUND,
|
||||||
|
},
|
||||||
|
System::{
|
||||||
|
Pipes::{
|
||||||
|
ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, PIPE_READMODE_MESSAGE,
|
||||||
|
PIPE_TYPE_MESSAGE, PIPE_WAIT,
|
||||||
|
},
|
||||||
|
Threading::CreateMutexW,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn retrieve_app_instance_event_identifier() -> &'static str {
|
use crate::{Args, OpenListener};
|
||||||
match *release_channel::RELEASE_CHANNEL {
|
|
||||||
ReleaseChannel::Dev => "Local\\Zed-Editor-Dev-Instance-Event",
|
|
||||||
ReleaseChannel::Nightly => "Local\\Zed-Editor-Nightly-Instance-Event",
|
|
||||||
ReleaseChannel::Preview => "Local\\Zed-Editor-Preview-Instance-Event",
|
|
||||||
ReleaseChannel::Stable => "Local\\Zed-Editor-Stable-Instance-Event",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_single_instance() -> bool {
|
pub fn check_single_instance(opener: OpenListener, run_foreground: bool) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
CreateEventW(
|
CreateMutexW(
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
false,
|
&HSTRING::from(format!("{}-Instance-Mutex", *APP_IDENTIFIER)),
|
||||||
&HSTRING::from(retrieve_app_instance_event_identifier()),
|
|
||||||
)
|
)
|
||||||
.expect("Unable to create instance sync event")
|
.expect("Unable to create instance sync event")
|
||||||
};
|
};
|
||||||
let last_err = unsafe { GetLastError() };
|
let first_instance = unsafe { GetLastError() } != ERROR_ALREADY_EXISTS;
|
||||||
last_err != ERROR_ALREADY_EXISTS
|
|
||||||
|
if first_instance {
|
||||||
|
// We are the first instance, listen for messages sent from other instances
|
||||||
|
std::thread::spawn(move || with_pipe(|url| opener.open_urls(vec![url])));
|
||||||
|
} else if !run_foreground {
|
||||||
|
// We are not the first instance, send args to the first instance
|
||||||
|
send_args_to_instance().log_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
first_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_pipe(f: impl Fn(String)) {
|
||||||
|
let pipe = unsafe {
|
||||||
|
CreateNamedPipeW(
|
||||||
|
&HSTRING::from(format!("\\\\.\\pipe\\{}-Named-Pipe", *APP_IDENTIFIER)),
|
||||||
|
PIPE_ACCESS_INBOUND,
|
||||||
|
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||||
|
1,
|
||||||
|
128,
|
||||||
|
128,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if pipe.is_invalid() {
|
||||||
|
log::error!("Failed to create named pipe: {:?}", unsafe {
|
||||||
|
GetLastError()
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(message) = retrieve_message_from_pipe(pipe)
|
||||||
|
.context("Failed to read from named pipe")
|
||||||
|
.log_err()
|
||||||
|
{
|
||||||
|
f(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve_message_from_pipe(pipe: HANDLE) -> anyhow::Result<String> {
|
||||||
|
unsafe { ConnectNamedPipe(pipe, None)? };
|
||||||
|
let message = retrieve_message_from_pipe_inner(pipe);
|
||||||
|
unsafe { DisconnectNamedPipe(pipe).log_err() };
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve_message_from_pipe_inner(pipe: HANDLE) -> anyhow::Result<String> {
|
||||||
|
let mut buffer = [0u8; 128];
|
||||||
|
unsafe {
|
||||||
|
ReadFile(pipe, Some(&mut buffer), None, None)?;
|
||||||
|
}
|
||||||
|
let message = std::ffi::CStr::from_bytes_until_nul(&buffer)?;
|
||||||
|
Ok(message.to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This part of code is mostly from crates/cli/src/main.rs
|
||||||
|
fn send_args_to_instance() -> anyhow::Result<()> {
|
||||||
|
let Args { paths_or_urls, .. } = Args::parse();
|
||||||
|
let (server, server_name) =
|
||||||
|
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
|
||||||
|
let url = format!("zed-cli://{server_name}");
|
||||||
|
|
||||||
|
let mut paths = vec![];
|
||||||
|
let mut urls = vec![];
|
||||||
|
for path in paths_or_urls.into_iter() {
|
||||||
|
match std::fs::canonicalize(&path) {
|
||||||
|
Ok(path) => paths.push(path.to_string_lossy().to_string()),
|
||||||
|
Err(error) => {
|
||||||
|
if path.starts_with("zed://")
|
||||||
|
|| path.starts_with("http://")
|
||||||
|
|| path.starts_with("https://")
|
||||||
|
|| path.starts_with("file://")
|
||||||
|
|| path.starts_with("ssh://")
|
||||||
|
{
|
||||||
|
urls.push(path);
|
||||||
|
} else {
|
||||||
|
log::error!("error parsing path argument: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let exit_status = Arc::new(Mutex::new(None));
|
||||||
|
let sender: JoinHandle<anyhow::Result<()>> = std::thread::spawn({
|
||||||
|
let exit_status = exit_status.clone();
|
||||||
|
move || {
|
||||||
|
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
|
||||||
|
let (tx, rx) = (handshake.requests, handshake.responses);
|
||||||
|
|
||||||
|
tx.send(CliRequest::Open {
|
||||||
|
paths,
|
||||||
|
urls,
|
||||||
|
wait: false,
|
||||||
|
open_new_workspace: None,
|
||||||
|
env: None,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
while let Ok(response) = rx.recv() {
|
||||||
|
match response {
|
||||||
|
CliResponse::Ping => {}
|
||||||
|
CliResponse::Stdout { message } => log::info!("{message}"),
|
||||||
|
CliResponse::Stderr { message } => log::error!("{message}"),
|
||||||
|
CliResponse::Exit { status } => {
|
||||||
|
exit_status.lock().replace(status);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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 = url.as_bytes();
|
||||||
|
let mut bytes_written = 0;
|
||||||
|
WriteFile(pipe, Some(message), Some(&mut bytes_written), None)?;
|
||||||
|
CloseHandle(pipe)?;
|
||||||
|
}
|
||||||
|
sender.join().unwrap()?;
|
||||||
|
if let Some(exit_status) = exit_status.lock().take() {
|
||||||
|
std::process::exit(exit_status);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue