| Crates.io | windows-rpc-macros |
| lib.rs | windows-rpc-macros |
| version | 0.0.6 |
| created_at | 2025-12-05 11:11:11.427308+00 |
| updated_at | 2025-12-15 20:48:36.238661+00 |
| description | Windows RPC for Rust |
| homepage | |
| repository | https://github.com/gadol21/windows-rpc-rs |
| max_upload_size | |
| id | 1968023 |
| size | 97,775 |
Windows RPC client and server library for Rust.
This crate, together with windows_rpc_macros, provides
a way to define Windows RPC interfaces using Rust traits and automatically generate all the
necessary client and server code. The generated code handles NDR (Network Data Representation)
marshalling, format strings, and Windows RPC runtime integration.
Define an RPC interface as a trait with the [rpc_interface] macro:
use windows_rpc::rpc_interface;
use windows_rpc::{ProtocolSequence, client_binding::ClientBinding};
#[rpc_interface(guid(0x12345678_1234_1234_1234_123456789abc), version(1.0))]
trait Calculator {
fn add(a: i32, b: i32) -> i32;
fn multiply(x: i32, y: i32) -> i32;
fn strlen(string: &str) -> u64;
fn greet(name: &str) -> String;
}
This generates three types:
CalculatorClient - for making RPC callsCalculatorServerImpl - trait to implement for the serverCalculatorServer<T> - generic server wrapper for RPC dispatchImplement the generated ServerImpl trait with static methods:
use windows_rpc::rpc_interface;
#[rpc_interface(guid(0x12345678_1234_1234_1234_123456789abc), version(1.0))]
trait Calculator {
fn add(a: i32, b: i32) -> i32;
fn greet(name: &str) -> String;
}
struct CalculatorImpl;
impl CalculatorServerImpl for CalculatorImpl {
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create server with the implementation type
let mut server = CalculatorServer::<CalculatorImpl>::new();
server.register("calculator_endpoint")?;
// Non-blocking: returns immediately, processes calls in background
server.listen_async()?;
println!("Server is running...");
// Keep the server running
std::thread::sleep(std::time::Duration::from_secs(60));
// Clean shutdown
server.stop()?;
Ok(())
}
Make RPC calls using the generated client:
use windows_rpc::rpc_interface;
use windows_rpc::{ProtocolSequence, client_binding::ClientBinding};
#[rpc_interface(guid(0x12345678_1234_1234_1234_123456789abc), version(1.0))]
trait Calculator {
fn add(a: i32, b: i32) -> i32;
fn greet(name: &str) -> String;
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client binding
let binding = ClientBinding::new(ProtocolSequence::Alpc, "calculator_endpoint")?;
let client = CalculatorClient::new(binding);
// Make RPC calls - integers
let result = client.add(10, 20);
println!("10 + 20 = {result}"); // Prints: 10 + 20 = 30
// Make RPC calls - strings
let greeting = client.greet("Alice");
println!("{greeting}"); // Prints: Hello, Alice!
Ok(())
}
Here's a more comprehensive example showcasing various string operations:
use windows_rpc::{rpc_interface, ProtocolSequence, client_binding::ClientBinding};
#[rpc_interface(guid(0xabcdef12_3456_7890_abcd_ef1234567890), version(1.0))]
trait StringService {
fn to_uppercase(text: &str) -> String;
fn reverse(text: &str) -> String;
fn count_words(text: &str) -> u32;
fn concat(a: &str, b: &str) -> String;
}
struct StringServiceImpl;
impl StringServiceServerImpl for StringServiceImpl {
fn to_uppercase(text: &str) -> String {
text.to_uppercase()
}
fn reverse(text: &str) -> String {
text.chars().rev().collect()
}
fn count_words(text: &str) -> u32 {
text.split_whitespace().count() as u32
}
fn concat(a: &str, b: &str) -> String {
format!("{}{}", a, b)
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Start server
let mut server = StringServiceServer::<StringServiceImpl>::new();
server.register("string_service")?;
server.listen_async()?;
// Create client
let client = StringServiceClient::new(
ClientBinding::new(ProtocolSequence::Alpc, "string_service")?
);
// Test string operations
println!("{}", client.to_uppercase("hello")); // Output: HELLO
println!("{}", client.reverse("hello")); // Output: olleh
println!("{}", client.count_words("hello world")); // Output: 2
println!("{}", client.concat("Hello, ", "World!")); // Output: Hello, World!
server.stop()?;
Ok(())
}
The following types can be used for parameters and return values:
| Rust Type | Parameters | Return Values | Notes |
|---|---|---|---|
i8, u8 |
✓ | ✓ | 8-bit integers |
i16, u16 |
✓ | ✓ | 16-bit integers |
i32, u32 |
✓ | ✓ | 32-bit integers |
i64, u64 |
✓ | ✓ | 64-bit integers |
&str |
✓ | ✗ | String input parameters |
String |
✗ | ✓ | String return values |
Currently only ALPC (Advanced Local Procedure Call) is supported via the ncalrpc
protocol sequence. This allows RPC communication between processes on the same machine.
MIDL_STUB_DESC, MIDL_SERVER_INFO, etc.)This library is currently limited in scope:
[in]) parameters and return values ([out]) are
supported. Input-output parameters are not available.The generated code produces standard Windows RPC interfaces that are compatible with MIDL-generated C/C++ clients and servers. You can use a Rust server with a C++ client (or vice versa) as long as the interface GUID, version, and method signatures match.
This crate uses unsafe code extensively to interact with the Windows RPC runtime.
The generated client and server code manages memory carefully to ensure:
However, bugs in this crate could lead to memory corruption or undefined behavior.
The server implementation uses:
{Interface}Server<T> is generic over the implementation type&self, making implementations statelessServer<ConcreteType> generates type-specific wrapper functions