• C# 调用 Rust 来删除文件夹 ( 包含大量软链接 和 无效链接)


    最近遇到了一个 .Net 6 的大问题 (我感觉是一个 Bug)。
    Directory.Delete(path, recursive: true) 竟然删不掉 pnpm 安装的 node_modules(有大量的软链接 和 无效软链接)

    var path = "E:\\Work\\ReactProject\\hi-ice\\node_modules";
    // 第二个参数是 递归删除
    Directory.Delete(path, recursive: true);
    
    • 1
    • 2
    • 3

    执行结果如下:
    (卧槽,执行了这么长时间,还敢报错?微软自己的编程语言和标准库都不能完美处理微软自己的系统?后来大概看了下源码,代码倒是挺多,但就是删不成功。。)
    在这里插入图片描述

    又搜了一个普通的递归删除文件的代码(因为系统只能删除空文件夹,所以必须先删文件,再删文件夹)。

    执行结果如下:
    (果然比标准库的Directory.Delete 还垃圾。甚至基本的处理软链接得自己写。)
    在这里插入图片描述
    搞到这里我已经对 C# 绝望了。

    反正是自己的小工具,那我换个语言写吧。
    又去试了试 Java,更气人了,标准库连递归删除都没有,只能用第三方的库,
    试了试 commons-lang3hutool 也都报错了,看来这两个库内部实现也没有做到非常的完美。

    易语言 也报错了.

    NodeJs 的第三方库 rimraf,终于完美删除,然后我就用 NodeJs 写完了这个小工具。(后来大概看了一下源码,发现处理了很多情况,也算是一个久经沙场的库了。)

    接下来又试了几个语言
    Pythonshutil.rmtree(path),也完美删除。(不过只能删除文件夹,文件只能自己 if 判断下用 os.remove

    Golangos.RemoveAll 也完美删除。

    C++ 搜了下也是一堆写普通递归的代码,直接放弃,连新建项目都懒得搞了。

    Ruststd::fs::remove_dir_all,也完美删除。
    搞到这里,脑子里突然灵光一闪,
    既然 Rust 可以删除,那我直接编译成 DLL,让 C# 调用不就解决了 C# 的大问题。
    于是有了下面的 Rust 代码

    Cargo.toml

    [package]
    name = "rm_dir_lib"
    version = "0.1.0"
    edition = "2021"
    
    [lib]
    crate-type = ["cdylib"]
    
    [dependencies]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Rust 代码

    use std::{ffi::CStr, fs, os::raw::c_char, path::Path};
    
    #[no_mangle]
    pub extern "C" fn rs_rm_dir(path: *const c_char) -> bool {
        let c_str = unsafe {
            assert!(!path.is_null());
            CStr::from_ptr(path)
        };
    
        // c 字符串 转为 rust 字符串
        let path_str = c_str.to_str().unwrap().to_string();
    
        println!("地址:");
        println!("{}", path_str);
    
        let ret = {
            if Path::new(&path_str).exists() {
                match fs::remove_dir_all(&path_str) {
                    Ok(_) => true,
                    Err(err) => {
                        println!("{}", err);
                        return false;
                    }
                }
            } else {
                true
            }
        };
    
        if ret {
            // 检查一下是否真的删掉了
            return !Path::new(&path_str).exists();
        }
    
        return false;
    }
    
    // 测试
    #[cfg(test)]
    mod tests {
        use std::{ffi::CString, fs::create_dir};
    
        use super::*;
    
        #[test]
        fn test_rs_rm_dir() {
            let path = "E:\\Work\\ReactProject\\hi-ice\\node_modules";
    
            if !Path::new(&path).exists() {
                create_dir(&path).unwrap();
            }
            // 转为 CString
            let data = CString::new(path).unwrap();
    
            let res = rs_rm_dir(data.as_ptr());
    
            println!("返回值: {}", res);
            
            assert_eq!(res, true);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    C# 调用一下

    // 定义一下
    [
        DllImport("rm_dir_lib.dll",
        EntryPoint = "rs_rm_dir",
        CallingConvention = CallingConvention.Cdecl)
    ]
    public static extern bool RsRmDir([MarshalAs(UnmanagedType.LPUTF8Str)] string path);
    
    // 调用
    var path = "E:\\Work\\ReactProject\\hi-ice\\node_modules";
    var res = RsRmDir(path);
    Console.WriteLine(res);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    编译 Release,测试两遍,完美删除 ,速度也很稳定和 Rust 几乎相同,
    在这里插入图片描述
    Rust 编译出的 DLL 也只有 150 KB, 非常的不错啊,
    这个 DLL 可以在任何语言中用了.

    再来个 Java 调用例子:
    DLL 放到 resources 目录下

    import com.sun.jna.Library;
    import com.sun.jna.Native;
    
    public interface JNATestDll extends Library {
        JNATestDll dll = Native.load("rm_dir_lib", JNATestDll.class);
    
        public boolean rs_rm_dir(String path);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    
    public class Main {
    
        public static void main(String[] args) {
    
            long startTime = System.currentTimeMillis();
    
            JNATestDll.dll.rs_rm_dir("E:\\Work\\ReactProject\\hi-ice\\node_modules");
    
            long endTime = System.currentTimeMillis();
    
            System.out.printf("执行时长:%d 秒.", (endTime - startTime) / 1000);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    <dependency>
        <groupId>net.java.dev.jnagroupId>
        <artifactId>jnaartifactId>
        <version>5.12.1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    node篇 CommonJS规范
    【ZooKeeper】zookeeper源码7-FastLeaderElection网络通信机制&选票交换机制&lookForLeader执行选举
    Spring修炼之路(1)基础入门
    vb.net加本地SQL server数据库圣经
    天池Python练习05-列表操作
    一种用于多线程中间状态同步的屏障机制
    照片人像模糊怎么调?两分钟教会你
    Unsupervised Learning of Monocular Depth Estimation and Visual Odometry 论文阅读
    耗时半月,把牛客网最火Java面试题总结成PDF,涵盖所有面试高频题
    Android焦点之SurfaceFlinger的apply
  • 原文地址:https://blog.csdn.net/qq_37214567/article/details/126920748