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}