当前位置: 代码网 > it编程>编程语言>Asp.net > C#中调用Windows API实现文件操作的代码实战

C#中调用Windows API实现文件操作的代码实战

2025年08月17日 Asp.net 我要评论
为何选择windows api?在c#开发中,文件操作通常依赖于system.io命名空间下的类(如file、filestream)。然而,这些类虽然封装了丰富的功能,但在某些场景下存在以下限制:性能

为何选择windows api?

在c#开发中,文件操作通常依赖于system.io命名空间下的类(如filefilestream)。然而,这些类虽然封装了丰富的功能,但在某些场景下存在以下限制:

  • 性能瓶颈:对于高频文件读写或大文件处理,托管代码可能无法满足实时性需求。
  • 底层控制缺失:无法直接操作文件句柄、文件锁、文件属性等底层功能。
  • 跨平台兼容性问题:某些windows特有的文件操作(如文件加密、访问控制列表)无法通过标准io类实现。

windows api(如createfilereadfilewritefile)提供了对文件系统的完全控制,是解决上述问题的终极方案。本文将通过真实项目级代码,带你从基础到高级掌握如何在c#中调用windows api实现文件操作!

一、windows api文件操作的核心函数

1. 核心api函数概览

函数名功能描述适用场景
createfile创建或打开文件/设备文件初始化、权限控制
readfile从文件中读取数据高效读取大文件
writefile向文件中写入数据实时日志记录、流式写入
setfilepointer移动文件指针随机访问文件内容
closehandle关闭文件句柄资源释放、异常安全处理

二、c#中调用windows api的基础准备

1. p/invoke声明

通过dllimport导入windows api函数,并定义其参数和返回值。

using system;
using system.runtime.interopservices;
using system.text;

// 导入kernel32.dll中的核心文件api
[dllimport("kernel32.dll", charset = charset.auto, setlasterror = true)]
public static extern intptr createfile(
    string lpfilename,               // 文件路径
    uint dwdesiredaccess,            // 访问模式(读/写/执行)
    uint dwsharemode,                // 共享模式
    intptr lpsecurityattributes,     // 安全属性(通常为intptr.zero)
    uint dwcreationdisposition,      // 创建/打开方式
    uint dwflagsandattributes,       // 文件属性(如file_attribute_normal)
    intptr htemplatefile             // 模板文件句柄(通常为intptr.zero)
);

[dllimport("kernel32.dll", setlasterror = true)]
public static extern bool readfile(
    intptr hfile,                    // 文件句柄
    byte[] lpbuffer,                 // 读取缓冲区
    uint nnumberofbytestoread,       // 要读取的字节数
    out uint lpnumberofbytesread,    // 实际读取的字节数
    intptr lpoverlapped              // 异步操作参数(同步操作设为intptr.zero)
);

[dllimport("kernel32.dll", setlasterror = true)]
public static extern bool writefile(
    intptr hfile,                    // 文件句柄
    byte[] lpbuffer,                 // 写入缓冲区
    uint nnumberofbytestowrite,      // 要写入的字节数
    out uint lpnumberofbyteswritten, // 实际写入的字节数
    intptr lpoverlapped              // 异步操作参数(同步操作设为intptr.zero)
);

[dllimport("kernel32.dll", setlasterror = true)]
public static extern bool closehandle(intptr hobject); // 关闭句柄

[dllimport("kernel32.dll", setlasterror = true)]
public static extern uint setfilepointer(
    intptr hfile,                    // 文件句柄
    int ldistancetomove,             // 移动偏移量(正负均可)
    intptr lpdistancetomovehigh,     // 高32位偏移量(通常为intptr.zero)
    uint dwmovemethod                // 移动方式(如file_begin)
);

2. 常量定义

// 文件访问模式
public const uint generic_read = 0x80000000;
public const uint generic_write = 0x40000000;

// 文件共享模式
public const uint file_share_read = 0x00000001;
public const uint file_share_write = 0x00000002;

// 文件创建方式
public const uint create_new = 1;           // 创建新文件(失败条件:文件已存在)
public const uint create_always = 2;        // 总是创建(覆盖已有文件)
public const uint open_existing = 3;        // 打开已有文件(失败条件:文件不存在)

// 文件移动方式
public const uint file_begin = 0;           // 从文件开头移动
public const uint file_current = 1;         // 从当前位置移动
public const uint file_end = 2;             // 从文件末尾移动

// 文件属性
public const uint file_attribute_normal = 0x80; // 普通文件

三、基础操作:文件的创建与读写

1. 创建文件

public static intptr createfileexample(string filepath) {
    // 打开或创建文件(覆盖模式)
    intptr hfile = createfile(
        filepath,
        generic_write,                  // 写入权限
        0,                              // 不共享
        intptr.zero,
        create_always,                  // 总是创建新文件
        file_attribute_normal,
        intptr.zero
    );

    if (hfile == intptr.zero || hfile == new intptr(-1)) {
        throw new win32exception(marshal.getlastwin32error(), "创建文件失败");
    }

    return hfile;
}

2. 写入数据

public static void writefileexample(intptr hfile, string data) {
    byte[] buffer = encoding.utf8.getbytes(data); // 将字符串转为字节数组
    uint byteswritten;

    bool success = writefile(
        hfile,
        buffer,
        (uint)buffer.length,
        out byteswritten,
        intptr.zero
    );

    if (!success) {
        throw new win32exception(marshal.getlastwin32error(), "写入文件失败");
    }

    console.writeline($"成功写入 {byteswritten} 字节");
}

3. 读取数据

public static string readfileexample(intptr hfile, int buffersize = 1024) {
    byte[] buffer = new byte[buffersize];
    uint bytesread;

    bool success = readfile(
        hfile,
        buffer,
        (uint)buffersize,
        out bytesread,
        intptr.zero
    );

    if (!success) {
        throw new win32exception(marshal.getlastwin32error(), "读取文件失败");
    }

    return encoding.utf8.getstring(buffer, 0, (int)bytesread);
}

4. 关闭文件

public static void closefileexample(intptr hfile) {
    if (!closehandle(hfile)) {
        throw new win32exception(marshal.getlastwin32error(), "关闭文件失败");
    }
}

5. 完整示例:文件复制

public static void copyfileusingapi(string sourcepath, string destpath) {
    intptr hsource = createfile(
        sourcepath,
        generic_read,
        file_share_read,
        intptr.zero,
        open_existing,
        file_attribute_normal,
        intptr.zero
    );

    if (hsource == intptr.zero || hsource == new intptr(-1)) {
        throw new win32exception(marshal.getlastwin32error(), "打开源文件失败");
    }

    intptr hdest = createfileexample(destpath);

    try {
        byte[] buffer = new byte[4096]; // 4kb缓冲区
        uint bytesread;

        do {
            // 读取源文件
            if (!readfile(hsource, buffer, (uint)buffer.length, out bytesread, intptr.zero)) {
                throw new win32exception(marshal.getlastwin32error(), "读取源文件失败");
            }

            if (bytesread > 0) {
                // 写入目标文件
                uint byteswritten;
                if (!writefile(hdest, buffer, bytesread, out byteswritten, intptr.zero)) {
                    throw new win32exception(marshal.getlastwin32error(), "写入目标文件失败");
                }
            }
        } while (bytesread > 0); // 循环直到读取完成
    } finally {
        closehandle(hsource);
        closehandle(hdest);
    }
}

四、高级操作:文件指针控制与随机访问

1. 移动文件指针

public static void movefilepointerexample(intptr hfile, int offset, uint movemethod) {
    uint newpointer = setfilepointer(
        hfile,
        offset,
        intptr.zero,
        movemethod
    );

    if (newpointer == 0xffffffff) {
        throw new win32exception(marshal.getlastwin32error(), "移动文件指针失败");
    }

    console.writeline($"文件指针新位置: {newpointer} 字节");
}

2. 随机读写示例

public static void randomaccessexample(string filepath) {
    intptr hfile = createfile(
        filepath,
        generic_read | generic_write,
        0,
        intptr.zero,
        open_existing,
        file_attribute_normal,
        intptr.zero
    );

    if (hfile == intptr.zero || hfile == new intptr(-1)) {
        throw new win32exception(marshal.getlastwin32error(), "打开文件失败");
    }

    try {
        // 移动指针到文件开头
        movefilepointerexample(hfile, 0, file_begin);

        // 读取前10字节
        byte[] buffer = new byte[10];
        uint bytesread;
        readfile(hfile, buffer, 10, out bytesread, intptr.zero);
        console.writeline($"前10字节内容: {encoding.utf8.getstring(buffer, 0, (int)bytesread)}");

        // 移动指针到文件末尾
        movefilepointerexample(hfile, 0, file_end);

        // 写入新内容到末尾
        string newdata = " - 附加内容";
        byte[] newbuffer = encoding.utf8.getbytes(newdata);
        uint byteswritten;
        writefile(hfile, newbuffer, (uint)newbuffer.length, out byteswritten, intptr.zero);
        console.writeline($"成功追加 {byteswritten} 字节");
    } finally {
        closehandle(hfile);
    }
}

五、性能优化与异常处理

1. 使用缓冲区优化读写

  • 大文件处理:增大缓冲区大小(如4kb或8kb)可减少系统调用次数。
  • 异步操作:通过lpoverlapped参数实现异步读写(需结合线程或回调)。

2. 错误处理最佳实践

  • 始终检查返回值:windows api函数失败时返回false0
  • 获取错误码:通过marshal.getlastwin32error()获取具体错误信息。
  • 资源释放:使用try-finally确保文件句柄关闭。

3. 路径处理技巧

  • 绝对路径:使用path.getfullpath()确保路径合法性。
  • 权限控制:通过lpsecurityattributes参数设置文件访问权限。

六、与.net io类的对比

特性windows api.net system.io 类
性能更快(直接调用内核)封装后略有性能损耗
底层控制完全控制文件句柄、指针等封装简化,但灵活性受限
易用性需手动管理资源和错误提供高级封装(如file.copy)
跨平台仅限windows跨平台支持(通过.net core)

七、实战场景:文件备份工具

public static void backupfile(string source, string backup) {
    try {
        // 创建备份文件
        intptr hbackup = createfileexample(backup);

        // 打开源文件
        intptr hsource = createfile(
            source,
            generic_read,
            file_share_read,
            intptr.zero,
            open_existing,
            file_attribute_normal,
            intptr.zero
        );

        if (hsource == intptr.zero || hsource == new intptr(-1)) {
            throw new win32exception(marshal.getlastwin32error(), "打开源文件失败");
        }

        try {
            byte[] buffer = new byte[8192]; // 8kb缓冲区
            uint bytesread;

            do {
                // 读取源文件
                if (!readfile(hsource, buffer, (uint)buffer.length, out bytesread, intptr.zero)) {
                    throw new win32exception(marshal.getlastwin32error(), "读取源文件失败");
                }

                if (bytesread > 0) {
                    // 写入备份文件
                    uint byteswritten;
                    if (!writefile(hbackup, buffer, bytesread, out byteswritten, intptr.zero)) {
                        throw new win32exception(marshal.getlastwin32error(), "写入备份文件失败");
                    }
                }
            } while (bytesread > 0);

            console.writeline("文件备份完成!");
        } finally {
            closehandle(hsource);
            closehandle(hbackup);
        }
    } catch (exception ex) {
        console.writeline($"备份失败: {ex.message}");
    }
}

八、注意事项与常见问题

1. 文件锁定问题

  • 共享模式:设置dwsharemode参数(如file_share_read)可允许多个进程同时访问文件。
  • 独占访问:若需独占文件,设置dwsharemode为0并捕获error_sharing_violation错误。

2. 编码问题

  • 文本文件:确保使用正确的编码(如utf-8)读写数据。
  • 二进制文件:直接操作字节数组,无需编码转换。

3. 文件属性与权限

  • 权限控制:通过lpsecurityattributes参数设置文件访问权限(如只读、隐藏)。
  • 文件属性:使用file_attribute_hidden等常量设置文件属性。

九、 windows api的无限可能

通过本文的实战指南,你应该已经掌握了:

  1. 如何通过p/invoke调用windows api实现文件操作
  2. 如何高效读写文件并控制文件指针
  3. 如何处理错误和优化性能

windows api 是c#开发者手中的瑞士军刀,它赋予你对文件系统的完全控制权。无论是开发高性能文件处理工具,还是实现windows特有的文件管理功能,windows api都能成为你的得力助手!

windows api的更多可能性

  • 文件加密:使用cryptprotectfilecryptunprotectfile实现文件级加密。
  • 访问控制:通过setsecurityinfo设置文件的访问控制列表(acl)。
  • 文件监控:使用readdirectorychangesw实时监控文件夹变化。

以上就是c#中调用windows api实现文件操作的代码实战的详细内容,更多关于c# windows api文件操作的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com