C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C# Windows API文件操作

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

作者:墨夶

在C#开发中,文件操作通常依赖于System.IO命名空间下的类,然而,这些类虽然封装了丰富的功能,但在某些场景下存在限制,Windows API是解决上述问题的终极方案,本文将通过真实项目级代码,带你从基础到高级掌握如何在C#中调用Windows API实现文件操作

为何选择Windows API?

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

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. 使用缓冲区优化读写

2. 错误处理最佳实践

3. 路径处理技巧

六、与.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. 文件锁定问题

2. 编码问题

3. 文件属性与权限

九、 Windows API的无限可能

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

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

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

Windows API的更多可能性

以上就是C#中调用Windows API实现文件操作的代码实战的详细内容,更多关于C# Windows API文件操作的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文