#excel #user-defined #udf #macro #write #function #help

xladd-derive

一个简单的宏,帮助在Rust中编写Excel UDF函数

14个版本

0.7.0 2022年1月17日
0.6.3 2021年4月27日
0.6.0 2021年3月4日
0.5.0 2021年1月8日
0.1.1 2020年3月29日

#358过程宏

每月下载 32

MIT 许可证

41KB
806

xladd-derive

宏,帮助您在Rust中轻松编写Excel用户定义函数

版本 0.7.0 发布说明

修复了崩溃错误,支持异步函数,并添加了对单线程函数的支持

版本 0.6.3 发布说明

当范围包含#NA或#DIV/0时,会填充f64::NAN。可能会破坏假设0.0的代码

版本 0.6.2 发布说明

如果从范围单元格返回xlTypeMulti,xladd会返回一个null值。

版本 0.6.1 发布说明

xladd中集成了一些小的错误修复

版本 0.6 发布说明

更新到使用ndarray 0.14,支持sref,允许在Excel中使用索引和偏移量范围。一些小的错误修复

版本 0.5 发布说明

  • 主要新功能是数组现在可以长到1078,576行,这是Excel支持的最大长度
  • 为了支持这一点,尽可能地对代码进行性能分析,以减少数组之间的复制。我相信还可以进行进一步改进
  • 现在支持异步UDF。它们在Excel中的表现不错,但拥有太多这样的函数会搞乱Excel的依赖关系管理。但是,函数被标记为线程安全,应该在Rust中(无全局变量)执行,因此您应该没问题。但要注意覆盖问题
  • 现在支持make_row和make_col变体数组,以帮助返回表格。
  • RTD和Ribbon Bar支持即将推出

版本 0.4 发布说明

新功能

  • 之前,当从Excel调用函数时,如果用户参数输入错误,会返回一个通用的“错误”。现在我们得到“函数[xxx]缺少参数[name]”,这是一个更好的用户体验。
  • 它还会捕获期望特定类型但无法解析的情况。
  • 已添加跟踪日志功能。如果启用了日志并且设置为LevelFilter::Trace级别,则可以获取每个函数调用、传递的参数和结果值的日志。即使禁用,这也对性能产生了一定的影响。建议创建一个Excel UDF(用户定义函数),例如enable_trace_logging,将日志输出到文件,以便在测试工作表中按需调用。
  • 在Excel中将NaN值转换为#N/A。当用户输入超出范围并产生NAN值时,我发现这很有用。而不是崩溃或跳过,我们在Excel中得到一个#NA,表示我们需要检查输入。
  • 添加了对log::* crate的依赖。

0.3版本发布说明

新功能

  • 主要新功能是可以通过NDArray使用2维数组进行输入和输出。
  • 添加了一个功能标志"使用_ndarray"。但我不知道如何将其自动传递给xladd,如果旧版本的crate没有这个功能标志?欢迎提交PR。这允许您使用Array2或Array2类型作为输入或输出参数。这解决了之前2维数组的问题,之前的解决方案最多只是权宜之计。仍然支持使用&[f64],如之前一样,对于单列或单行数据仍然有意义。仍然支持使用(Vec,usize)作为返回类型,但我认为它很丑,因为它并没有真正显示开发者的意图。

错误修复

  • 如果您指定了一个数组(vec或array2)并且您正在拖动一系列值,则第一个单元格实际上被发送为一个单独的f64,而不是一个范围。我之前没有处理这种情况,可能会导致崩溃。

0.2版本发布说明

  • 添加了一个新功能:使用prefix可以为您的函数命名。以前,所有导出的Excel函数都称为"xl_myfunction",现在通过设置prefix = "project_",您的导出将被重命名为project_myfunction。如果没有指定,则默认为"xl_"。
  • 添加了rename,它可以将Excel公开的函数重命名为指定的名称。前缀仍然有效。

为什么?

我发现通过拖放快速在Excel中创建示例GUI非常好,这对于原型设计非常有用。编写测试函数并能够交互式地传递各种数据给它是一种有用的功能。

背景

我发现xladd来自MarcusRainbow,它允许创建用于Excel的原始API用户定义函数。我对它并不完全满意,并向他的项目提交了几个PR,但没有收到任何回应,所以我fork了他的项目,并继续添加我认为合适的功能。

但仍然很痛苦,我想学习关于proc-macros的知识,所以创建了这个proc-macro crate,以便更容易地在Rust中编写函数。

用法

添加

[lib]
crate-type = ["cdylib"]

[dependencies]
xladd-derive= {"^0.4" }
xladd = {git="https://github.com/ronniec95/xladd" , features=["use_ndarray"] } # Needed to patch the old abandoned crate

到您的Cargo.toml中

编写一个Rust函数,并添加以下注解#[xl_func()],例如

// These imports are needed from xladd
use xladd::registrator::Reg;
use xladd::variant::Variant;
use xladd::xlcall::LPXLOPER12;
use xladd_derive::xl_func;
use log::*; // Needed from 0.4.* onwards to give tracing

#[xl_func()]
fn add(arg1: f64, arg2: f64) -> Result<f64, Box<dyn std::error::Error>> {
    // No comments in this method, defaults will be allocated
    Ok(arg1 + arg2)
}

/// This function adds any number of values together
/// * v - array of f64
/// * ret - returns the sum0
#[xl_func()]
fn add_array(v: &[f64]) -> Result<f64, Box<dyn std::error::Error>> {
    // Comments will be filled into the Excel function dialog box
    Ok(v.iter().sum())
}

/// This function adds any number of values together
/// * v - array of f64
/// * ret - returns the sum
#[xl_func(category="MyCategory")] // Custom attribute to assign function to a particular category
fn add_array_v2(v: &[f64]) -> Result<(Vec<f64>, usize), Box<dyn std::error::Error>> {
    // Comments will be filled into the Excel function dialog box
    // This returns a 2d array to excel using a (vec,usize) tuple. Note that if v.len() / columns != 0 you will have missing values
    Ok((v.to_vec(), 2))
}

use ndarray::Array2;
/// 2d arrays can now be accepted and returned opening up a lot more possibilities for interfacing with Excel
#[xl_func(category = "OptionPricing", prefix = "my", rename = "baz")]
fn add_f64_2(a: Array2<f64>) -> Result<Array2<f64>, Box<dyn std::error::Error>> {
    Ok(Array2::from_elem([2, 2], 0.0f64))
}

目前有一些限制,我希望将来去除它们

您的函数的返回类型可以是任何基本类型的Result>

  • f32
  • f64
  • i32
  • i64
  • bool
  • String(所有者)

  • (Vec<[基本类型]>,usize)

第二个参数是列数。这允许Excel处理二维数据的数组。该宏将根据数组的尺寸计算行数。

我在想,如果有人需要的话,可以让输入的 &[] 数组也成为一个元组。

参数以LPXLOPER12形式传递,然后被强制转换为Rust类型。强制转换错误将通过trace!()日志报告。如果你通过命令行使用env-logger或simplelog运行Excel,可以将这些输出到文件中进行调试。

文档

以下是对文档注释的解释方式

/// This is a normal multiline comment that 
/// will be used as a description of the function
/// * arg1 - This argument must be formatted with a `* <name> -` and will be used in the argument description
/// * ret - This is a special return type argument which will appended to the description of the function

多线程

Excel会使用机器上所有的核心,但它依赖于你的UDFs是线程安全的。Rust是支持多线程的,但如果你正在读写文件,要小心。

如果你想通过属性来控制这个方面,请告诉我。

与Excel注册

当Excel启动时,它会调用你的.dll中的这个函数。宏为你生成register_*函数,所以请遵循以下模板。如果有人知道如何自动从proc-macro中确定这些,请与我联系,或者提交一个PR。

// For excel to register this XLL ensure `no_mangle` is specified 
#[no_mangle]
pub extern "stdcall" fn xlAutoOpen() -> i32 {
    let reg = Reg::new();
    register_add(&reg);
    1 // Must return 1 to signal to excel SUCCESS
}

xladd依赖

由于我似乎无法联系到xladd crate的原作者MarcusRainbow,所以我创建了一个分支。因此,在Cargo.toml中,你需要添加github依赖项xladd = { git ="https://github.com/ronniec95/xladd"}。如果这有问题,请告诉我,我可以看看是否有更好的方法。

不安全代码 & Excel兼容性

由于xladd包调用Windows C api,因此它是固有不安全的。此包使用LPXLOPER12 api来与Excel兼容,因此兼容2007年以后的版本。如果插件不工作,请检查你的Excel是否与你的rustc编译器具有相同的位数(32位或64位)。在许多组织中,Excel通常安装为32位,而你的rustc编译器可能是64位。这当然不会工作。

尚未处理

异步方法。我没有这个需求,尤其是由于网络和IO类型的工作通常在Excel内部完成得更好。

我还想添加RTD支持,这样你就可以订阅实时数据。

调试

在VSCode中,你可以创建一个调试配置,并将program更改为

 "program": "C:/Program Files/Microsoft Office/root/Office16/EXCEL.EXE",

这将启动Excel,但你可以设置断点在你的代码中。

依赖项

~1.9–2.7MB
~53K SLoC