asdf_overlay_client/lib.rs
1//! Library for attaching `asdf-overlay` to a process and initiating IPC channel.
2//!
3//! By utilizing this library, you can render overlay from any process and control it via IPC.
4//! It's designed to give you maximum flexibility as you can keep most of the logic in this process.
5//!
6//! # Example
7//! ```no_run
8//! use std::path::Path;
9//! use std::time::Duration;
10//! use asdf_overlay_client::{inject, OverlayDll};
11//!
12//! #[tokio::main]
13//! async fn main() -> anyhow::Result<()> {
14//! let dll = OverlayDll {
15//! x64: Some(Path::new("asdf-overlay-x64.dll")),
16//! x86: Some(Path::new("asdf-overlay-x86.dll")),
17//! x86: Some(Path::new("asdf-overlay-arm64.dll")),
18//! };
19//!
20//! let (mut conn, mut events) = inject(
21//! 1234, // target process pid
22//! dll, // overlay dll paths
23//! Some(Duration::from_secs(10)), // timeout for injection and ipc connection
24//! ).await?;
25//!
26//! // Use `conn` to send requests to overlay, and `events` to receive events from the overlay.
27//!
28//! Ok(())
29//! }
30//!
31
32pub mod client;
33mod injector;
34#[cfg(feature = "surface")]
35pub mod surface;
36#[cfg(feature = "surface")]
37pub mod ty;
38
39pub use asdf_overlay_common as common;
40pub use asdf_overlay_event as event;
41
42use core::time::Duration;
43use std::path::Path;
44
45use anyhow::{Context, bail};
46use asdf_overlay_common::ipc::create_ipc_addr;
47use tokio::{net::windows::named_pipe::ClientOptions, select, time::sleep};
48
49use crate::client::{IpcClientConn, IpcClientEventStream};
50
51/// Paths to overlay DLLs for different architectures.
52#[derive(Debug, Clone, Copy, Default)]
53pub struct OverlayDll<'a> {
54 /// Path to DLL to be used for x64 applications.
55 pub x64: Option<&'a Path>,
56
57 /// Path to DLL to be used for x86 applications.
58 pub x86: Option<&'a Path>,
59
60 /// Path to DLL to be used for ARM64 applications.
61 pub arm64: Option<&'a Path>,
62}
63
64/// Inject overlay DLL into target process and create IPC connection.
65/// * If you didn't supply DLL path for the target architecture, it will return an error.
66/// * If injection or IPC connection fails, it will return an error.
67/// * If timeout is `None`, it may wait indefinitely.
68pub async fn inject(
69 pid: u32,
70 dll: OverlayDll<'_>,
71 timeout: Option<Duration>,
72) -> anyhow::Result<(IpcClientConn, IpcClientEventStream)> {
73 let module_handle =
74 injector::inject(pid, dll, timeout).context("failed to inject overlay DLL")?;
75 let ipc_addr = create_ipc_addr(pid, module_handle);
76
77 let connect = IpcClientConn::new(ClientOptions::new().open(ipc_addr)?);
78 let timeout = sleep(timeout.unwrap_or(Duration::MAX));
79 let conn = select! {
80 res = connect => res?,
81 _ = timeout => bail!("ipc client wait timeout"),
82 };
83
84 Ok(conn)
85}