wininskit 0.1.2

Thin checked wrappers over the Win32 an installer needs: elevation, ACLs, services, the Restart Manager, the registry and shortcuts.
//! Exercises every wrapper against the live system, so a compile is not
//! mistaken for a working call. Run it from a normal prompt and then from an
//! elevated one: the elevation line and the ACL grant are the two that differ.

use std::path::PathBuf;

use wininskit::{Root, ServiceState};

fn main() {
    println!("elevated: {}", wininskit::is_elevated());

    match wininskit::running_from(std::path::Path::new(r"C:\Windows")) {
        Ok(found) => {
            println!("explorer.exe: {} running", found.len());
            for process in found.iter().take(2) {
                println!(
                    "  pid {} {}",
                    process.id,
                    process.image.as_deref().unwrap_or("<path unreadable>")
                );
            }
        }
        Err(error) => println!("running_from failed: {error}"),
    }

    let windows = PathBuf::from(r"C:\Windows\System32");
    match wininskit::running_from(&windows) {
        Ok(found) => println!("running from {}: {}", windows.display(), found.len()),
        Err(error) => println!("running_from failed: {error}"),
    }

    // Present on every Windows install, so this exercises the real path rather
    // than the not-installed shortcut.
    match wininskit::service_state("Schedule") {
        Ok(ServiceState::Running) => println!("service Schedule: running"),
        Ok(other) => println!("service Schedule: {other:?}"),
        Err(error) => println!("service_state failed: {error}"),
    }
    match wininskit::service_state("NoSuchServiceHere") {
        Ok(state) => println!("missing service reported {state:?}, expected an error"),
        Err(error) if error.is_not_found() => println!("missing service: correctly not found"),
        Err(error) => println!("missing service gave the wrong error: {error}"),
    }

    match wininskit::read_string(
        Root::LocalMachine,
        r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
        "ProductName",
    ) {
        Ok(Some(name)) => println!("product: {name}"),
        Ok(None) => println!("product: value missing"),
        Err(error) => println!("read_string failed: {error}"),
    }

    let scratch = std::env::temp_dir().join("wininskit-probe");
    let _ = std::fs::remove_dir_all(&scratch);
    if let Err(error) = std::fs::create_dir_all(&scratch) {
        println!("could not make {}: {error}", scratch.display());
        return;
    }

    // Needs administrator; from a normal prompt this is the expected refusal.
    match wininskit::grant_users_full_access(&scratch) {
        Ok(()) => println!("acl: granted Users full access to {}", scratch.display()),
        Err(error) => println!("acl: {error}"),
    }

    let link = scratch.join("Notepad.lnk");
    let target = PathBuf::from(r"C:\Windows\System32\notepad.exe");
    match wininskit::create_shortcut(&link, &target, None, None, Some(&target), Some("probe")) {
        Ok(()) => println!("shortcut: wrote {} ({} bytes)", link.display(), size(&link)),
        Err(error) => println!("shortcut failed: {error}"),
    }

    // The probe holds this file open itself, so the Restart Manager should name
    // this process. That makes it a real check and not just a call that returns.
    let held = scratch.join("held.bin");
    std::fs::write(&held, b"x").ok();
    let handle = std::fs::File::open(&held).expect("reopen");
    match wininskit::locking_processes(&[held.as_path()]) {
        Ok(found) if found.is_empty() => println!("restart manager: nothing holds the file"),
        Ok(found) => {
            println!("restart manager: {} holder(s)", found.len());
            for process in &found {
                println!("  pid {} {}", process.id, process.name);
            }
        }
        Err(error) => println!("locking_processes failed: {error}"),
    }
    drop(handle);

    let _ = wininskit::write_dword(Root::CurrentUser, r"Software\ZeroDensity\Probe", "Value", 7);
    match wininskit::read_string(Root::CurrentUser, r"Software\ZeroDensity\Probe", "Text") {
        Ok(None) => println!("registry: missing value correctly reported as absent"),
        Ok(Some(text)) => println!("registry: unexpected {text}"),
        Err(error) => println!("registry read failed: {error}"),
    }
    let _ = wininskit::write_string(
        Root::CurrentUser,
        r"Software\ZeroDensity\Probe",
        "Text",
        "hi",
    );
    match wininskit::read_string(Root::CurrentUser, r"Software\ZeroDensity\Probe", "Text") {
        Ok(Some(text)) => println!("registry: round-tripped {text:?}"),
        other => println!("registry round trip failed: {other:?}"),
    }
    match wininskit::delete_tree(Root::CurrentUser, r"Software\ZeroDensity\Probe") {
        Ok(()) => println!("registry: tree deleted"),
        Err(error) => println!("delete_tree failed: {error}"),
    }
    match wininskit::delete_tree(Root::CurrentUser, r"Software\ZeroDensity\Probe") {
        Ok(()) => println!("registry: deleting a missing tree is not an error"),
        Err(error) => println!("second delete_tree should have succeeded: {error}"),
    }

    let _ = std::fs::remove_dir_all(&scratch);
}

fn size(path: &std::path::Path) -> u64 {
    std::fs::metadata(path).map(|m| m.len()).unwrap_or(0)
}