windows_rpc/lib.rs
1//! Windows RPC client and server library for Rust.
2//!
3//! This crate, together with [`windows_rpc_macros`](https://docs.rs/windows-rpc-macros), provides
4//! a way to define Windows RPC interfaces using Rust traits and automatically generate all the
5//! necessary client and server code. The generated code handles NDR (Network Data Representation)
6//! marshalling, format strings, and Windows RPC runtime integration.
7//!
8//! # Features
9//!
10//! - **Simple trait-based interface definition** - Define RPC interfaces using familiar Rust syntax
11//! - **Automatic code generation** - Client and server code generated at compile time
12//! - **Type safety** - Full Rust type system integration
13//! - **NDR marshalling** - Automatic Network Data Representation encoding/decoding
14//! - **String support** - Native handling of string parameters and return values
15//! - **Integer types** - Support for i8, i16, i32, i64, u8, u16, u32, u64
16//! - **ALPC protocol** - Fast local RPC using Advanced Local Procedure Call
17//!
18//! # Quick Start
19//!
20//! Define an RPC interface as a trait with the [`rpc_interface`] macro:
21//!
22//! ```rust
23//! use windows_rpc::rpc_interface;
24//! use windows_rpc::{ProtocolSequence, client_binding::ClientBinding};
25//!
26//! #[rpc_interface(guid(0x12345678_1234_1234_1234_123456789abc), version(1.0))]
27//! trait Calculator {
28//! fn add(a: i32, b: i32) -> i32;
29//! fn multiply(x: i32, y: i32) -> i32;
30//! fn strlen(string: &str) -> u64;
31//! fn greet(name: &str) -> String;
32//! }
33//! ```
34//!
35//! This generates three types:
36//! - `CalculatorClient` - for making RPC calls
37//! - `CalculatorServerImpl` - trait to implement for the server
38//! - `CalculatorServer<T>` - generic server wrapper for RPC dispatch
39//!
40//! # Server Example
41//!
42//! Implement the generated `ServerImpl` trait with static methods:
43//!
44//! ```rust,no_run
45//! use windows_rpc::rpc_interface;
46//!
47//! #[rpc_interface(guid(0x12345678_1234_1234_1234_123456789abc), version(1.0))]
48//! trait Calculator {
49//! fn add(a: i32, b: i32) -> i32;
50//! fn greet(name: &str) -> String;
51//! }
52//!
53//! struct CalculatorImpl;
54//!
55//! impl CalculatorServerImpl for CalculatorImpl {
56//! fn add(a: i32, b: i32) -> i32 {
57//! a + b
58//! }
59//!
60//! fn greet(name: &str) -> String {
61//! format!("Hello, {}!", name)
62//! }
63//! }
64//!
65//! fn main() -> Result<(), Box<dyn std::error::Error>> {
66//! // Create server with the implementation type
67//! let mut server = CalculatorServer::<CalculatorImpl>::new();
68//! server.register("calculator_endpoint")?;
69//!
70//! // Non-blocking: returns immediately, processes calls in background
71//! server.listen_async()?;
72//!
73//! println!("Server is running...");
74//!
75//! // Keep the server running
76//! std::thread::sleep(std::time::Duration::from_secs(60));
77//!
78//! // Clean shutdown
79//! server.stop()?;
80//! Ok(())
81//! }
82//! ```
83//!
84//! # Client Example
85//!
86//! Make RPC calls using the generated client:
87//!
88//! ```rust,no_run
89//! use windows_rpc::rpc_interface;
90//! use windows_rpc::{ProtocolSequence, client_binding::ClientBinding};
91//!
92//! #[rpc_interface(guid(0x12345678_1234_1234_1234_123456789abc), version(1.0))]
93//! trait Calculator {
94//! fn add(a: i32, b: i32) -> i32;
95//! fn greet(name: &str) -> String;
96//! }
97//!
98//! fn main() -> Result<(), Box<dyn std::error::Error>> {
99//! // Create a client binding
100//! let binding = ClientBinding::new(ProtocolSequence::Alpc, "calculator_endpoint")?;
101//! let client = CalculatorClient::new(binding);
102//!
103//! // Make RPC calls - integers
104//! let result = client.add(10, 20);
105//! println!("10 + 20 = {result}"); // Prints: 10 + 20 = 30
106//!
107//! // Make RPC calls - strings
108//! let greeting = client.greet("Alice");
109//! println!("{greeting}"); // Prints: Hello, Alice!
110//!
111//! Ok(())
112//! }
113//! ```
114//!
115//! # Complete Example with String Operations
116//!
117//! Here's a more comprehensive example showcasing various string operations:
118//!
119//! ```rust,no_run
120//! use windows_rpc::{rpc_interface, ProtocolSequence, client_binding::ClientBinding};
121//!
122//! #[rpc_interface(guid(0xabcdef12_3456_7890_abcd_ef1234567890), version(1.0))]
123//! trait StringService {
124//! fn to_uppercase(text: &str) -> String;
125//! fn reverse(text: &str) -> String;
126//! fn count_words(text: &str) -> u32;
127//! fn concat(a: &str, b: &str) -> String;
128//! }
129//!
130//! struct StringServiceImpl;
131//!
132//! impl StringServiceServerImpl for StringServiceImpl {
133//! fn to_uppercase(text: &str) -> String {
134//! text.to_uppercase()
135//! }
136//!
137//! fn reverse(text: &str) -> String {
138//! text.chars().rev().collect()
139//! }
140//!
141//! fn count_words(text: &str) -> u32 {
142//! text.split_whitespace().count() as u32
143//! }
144//!
145//! fn concat(a: &str, b: &str) -> String {
146//! format!("{}{}", a, b)
147//! }
148//! }
149//!
150//! fn main() -> Result<(), Box<dyn std::error::Error>> {
151//! // Start server
152//! let mut server = StringServiceServer::<StringServiceImpl>::new();
153//! server.register("string_service")?;
154//! server.listen_async()?;
155//!
156//! // Create client
157//! let client = StringServiceClient::new(
158//! ClientBinding::new(ProtocolSequence::Alpc, "string_service")?
159//! );
160//!
161//! // Test string operations
162//! println!("{}", client.to_uppercase("hello")); // Output: HELLO
163//! println!("{}", client.reverse("hello")); // Output: olleh
164//! println!("{}", client.count_words("hello world")); // Output: 2
165//! println!("{}", client.concat("Hello, ", "World!")); // Output: Hello, World!
166//!
167//! server.stop()?;
168//! Ok(())
169//! }
170//! ```
171//!
172//! # Supported Types
173//!
174//! The following types can be used for parameters and return values:
175//!
176//! | Rust Type | Parameters | Return Values | Notes |
177//! |-----------|------------|---------------|-------|
178//! | `i8`, `u8` | ✓ | ✓ | 8-bit integers |
179//! | `i16`, `u16` | ✓ | ✓ | 16-bit integers |
180//! | `i32`, `u32` | ✓ | ✓ | 32-bit integers |
181//! | `i64`, `u64` | ✓ | ✓ | 64-bit integers |
182//! | `&str` | ✓ | ✗ | String input parameters |
183//! | `String` | ✗ | ✓ | String return values |
184//!
185//! # Protocol Support
186//!
187//! Currently only ALPC (Advanced Local Procedure Call) is supported via the `ncalrpc`
188//! protocol sequence. This allows RPC communication between processes on the same machine.
189//!
190//! # What This Library Does
191//!
192//! - Generates all MIDL stub metadata (`MIDL_STUB_DESC`, `MIDL_SERVER_INFO`, etc.)
193//! - Handles NDR 2.0 and NDR64 format strings for type marshalling
194//! - Manages RPC binding handles and server lifecycle
195//! - Converts between Rust types and Windows ABI types
196//! - Provides clean async (non-blocking) and sync (blocking) server modes
197//!
198//! # Limitations
199//!
200//! This library is currently limited in scope:
201//!
202//! - **Protocol**: Only local RPC (ALPC/ncalrpc) is supported. TCP, UDP, and named pipes
203//! are not yet implemented.
204//! - **Parameter direction**: Only input (`[in]`) parameters and return values (`[out]`) are
205//! supported. Input-output parameters are not available.
206//! - **Types**: Only primitive integers and strings are supported. No pointers, structs,
207//! arrays, unions, or other complex types.
208//! - **Security**: No interface security (authentication, authorization, encryption) is
209//! implemented.
210//! - **Exceptions**: SEH exceptions from the RPC runtime are not caught or handled.
211//! - **Callbacks**: RPC callbacks from server to client are not supported.
212//!
213//! # Interoperability
214//!
215//! The generated code produces standard Windows RPC interfaces that are compatible with
216//! MIDL-generated C/C++ clients and servers. You can use a Rust server with a C++ client
217//! (or vice versa) as long as the interface GUID, version, and method signatures match.
218//!
219//! # Safety
220//!
221//! This crate uses `unsafe` code extensively to interact with the Windows RPC runtime.
222//! The generated client and server code manages memory carefully to ensure:
223//!
224//! - RPC metadata structures remain valid for the lifetime of the client/server
225//! - String conversions between Rust and Windows types are handled correctly
226//! - Memory allocated by the server for return values is properly managed
227//! - Static trait methods are called correctly via monomorphization
228//!
229//! However, bugs in this crate could lead to memory corruption or undefined behavior.
230//!
231//! # Implementation Details
232//!
233//! The server implementation uses:
234//! - **Generic server structs**: `{Interface}Server<T>` is generic over the implementation type
235//! - **Static trait methods**: Server trait methods don't take `&self`, making implementations stateless
236//! - **Monomorphization**: Each instantiation of `Server<ConcreteType>` generates type-specific wrapper functions
237//! - **Extern "C" wrappers**: Generated wrapper functions bridge the RPC runtime to Rust static methods
238#![cfg(windows)]
239
240#[doc(hidden)]
241pub mod alloc;
242pub mod client_binding;
243pub mod server_binding;
244
245pub use windows_rpc_macros::rpc_interface;
246
247/// Protocol sequence for RPC communication.
248///
249/// Specifies the transport protocol used for RPC calls.
250///
251/// # Example
252///
253/// ```rust,no_run
254/// use windows_rpc::{ProtocolSequence, client_binding::ClientBinding};
255///
256/// # fn main() -> windows::core::Result<()> {
257/// // Connect using local RPC (ALPC)
258/// let binding = ClientBinding::new(ProtocolSequence::Alpc, "my_endpoint")?;
259/// # Ok(())
260/// # }
261/// ```
262#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
263pub enum ProtocolSequence {
264 /// ALPC (Advanced Local Procedure Call) - local RPC on the same machine.
265 ///
266 /// Uses the `ncalrpc` protocol sequence. This is the fastest option for
267 /// communication between processes on the same Windows machine.
268 Alpc,
269 // TODO: test and add
270 //Tcp,
271 //Udp,
272 //NamedPipe
273}
274
275impl ProtocolSequence {
276 fn to_pcwstr(self) -> windows::core::PCWSTR {
277 match self {
278 ProtocolSequence::Alpc => windows::core::w!("ncalrpc"),
279 }
280 }
281}