Skip to main content

xll_utils/
xll.rs

1//! XLL-specific helpers.
2//!
3//! Provides functions for extracting XLL metadata, verifying required
4//! Excel entry points, and validating XLL files.
5
6use std::path::Path;
7
8use crate::error::Result;
9use crate::pe::parse_pe_file;
10use crate::types::{Architecture, PeFile};
11
12/// Information extracted from an XLL file.
13#[derive(Clone, Debug)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct XllInfo {
16    /// File path.
17    pub path: std::path::PathBuf,
18    /// XLL name (from filename).
19    pub name: String,
20    /// CPU architecture.
21    pub architecture: Architecture,
22    /// Whether `xlAutoOpen` is exported.
23    pub has_auto_open: bool,
24    /// Whether `xlAutoClose` is exported.
25    pub has_auto_close: bool,
26    /// Whether `xlAutoFree12` is exported.
27    pub has_auto_free: bool,
28    /// Whether `xlAddInManagerInfo12` is exported.
29    pub has_addin_manager_info: bool,
30    /// Total number of exports.
31    pub export_count: usize,
32    /// All exported function names (sorted).
33    pub exports: Vec<String>,
34}
35
36/// Extract metadata from an XLL file.
37///
38/// # Examples
39///
40/// ```no_run
41/// use xll_utils::xll::xll_info;
42///
43/// let info = xll_info("my_xll.xll").unwrap();
44/// println!("{}: {} exports", info.name, info.export_count);
45/// ```
46pub fn xll_info(path: impl AsRef<Path>) -> Result<XllInfo> {
47    let pe = parse_pe_file(path.as_ref())?;
48
49    let name = pe
50        .path
51        .file_stem()
52        .and_then(|n| n.to_str())
53        .unwrap_or("unknown")
54        .to_string();
55
56    let export_names = pe.export_names();
57
58    Ok(XllInfo {
59        path: pe.path.clone(),
60        name,
61        architecture: pe.architecture,
62        has_auto_open: export_names.iter().any(|n| n == "xlAutoOpen"),
63        has_auto_close: export_names.iter().any(|n| n == "xlAutoClose"),
64        has_auto_free: export_names.iter().any(|n| n == "xlAutoFree12"),
65        has_addin_manager_info: export_names.iter().any(|n| n == "xlAddInManagerInfo12"),
66        export_count: pe.exports.len(),
67        exports: export_names,
68    })
69}
70
71/// Check whether an XLL has the required Excel entry points.
72///
73/// Returns `true` if both `xlAutoOpen` and `xlAutoFree12` are exported.
74///
75/// # Examples
76///
77/// ```no_run
78/// use xll_utils::PeFile;
79/// use xll_utils::xll::verify_xll_entry_points;
80///
81/// let pe = PeFile::parse("my_xll.xll").unwrap();
82/// assert!(verify_xll_entry_points(&pe));
83/// ```
84pub fn verify_xll_entry_points(pe: &PeFile) -> bool {
85    let names = pe.export_names();
86    names.iter().any(|n| n == "xlAutoOpen") && names.iter().any(|n| n == "xlAutoFree12")
87}
88
89/// Check whether a file at the given path is a valid XLL (has required exports).
90///
91/// # Examples
92///
93/// ```no_run
94/// use xll_utils::xll::is_valid_xll;
95///
96/// let valid = is_valid_xll("my_xll.xll").unwrap();
97/// println!("valid XLL: {valid}");
98/// ```
99pub fn is_valid_xll(path: impl AsRef<Path>) -> Result<bool> {
100    let pe = parse_pe_file(path.as_ref())?;
101    Ok(verify_xll_entry_points(&pe))
102}