Win32编程

2、文件与目录

1、课程目标

  1. 掌握Windows文件操作API
  2. 理解文件属性和安全描述符
  3. 学会目录遍历和管理
  4. 掌握文件映射技术

2、名词解释

术语 英文 说明
句柄 Handle 内核对象的引用标识
文件映射 File Mapping 将文件映射到内存
重叠I/O Overlapped I/O 异步文件操作

3、使用工具

工具 用途
Process Monitor 监控文件操作
WinObj 查看内核对象

4、技术原理

4.1、文件操作流程

CreateFile -> ReadFile/WriteFile -> CloseHandle

4.2、访问模式

标志 说明
GENERIC_READ 读取权限
GENERIC_WRITE 写入权限
FILE_SHARE_READ 允许其他进程读取
CREATE_NEW 创建新文件
OPEN_EXISTING 打开已存在文件

5、代码实现

5.1、示例1:基本文件操作

#include <windows.h>
#include <stdio.h>

void FileOperations() {
    // 创建/打开文件
    HANDLE hFile = CreateFile(
        TEXT("test.txt"),
        GENERIC_READ | GENERIC_WRITE,
        0,              // 不共享
        NULL,           // 默认安全属性
        CREATE_ALWAYS,  // 总是创建
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Error: %d\n", GetLastError());
        return;
    }
    
    // 写入数据
    const char* data = "Hello, File API!";
    DWORD written;
    WriteFile(hFile, data, strlen(data), &written, NULL);
    
    // 移动文件指针到开头
    SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
    
    // 读取数据
    char buffer[256] = {0};
    DWORD read;
    ReadFile(hFile, buffer, sizeof(buffer)-1, &read, NULL);
    printf("Read: %s\n", buffer);
    
    // 获取文件大小
    DWORD fileSize = GetFileSize(hFile, NULL);
    printf("File size: %d bytes\n", fileSize);
    
    CloseHandle(hFile);
}

5.2、示例2:目录遍历

#include <windows.h>
#include <stdio.h>

void ListDirectory(const TCHAR* path) {
    TCHAR searchPath[MAX_PATH];
    wsprintf(searchPath, TEXT("%s\\*"), path);
    
    WIN32_FIND_DATA fd;
    HANDLE hFind = FindFirstFile(searchPath, &fd);
    
    if (hFind == INVALID_HANDLE_VALUE) {
        printf("Error: %d\n", GetLastError());
        return;
    }
    
    do {
        // 跳过 . 和 ..
        if (lstrcmp(fd.cFileName, TEXT(".")) == 0 ||
            lstrcmp(fd.cFileName, TEXT("..")) == 0) {
            continue;
        }
        
        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            printf("[DIR]  %ls\n", fd.cFileName);
            
            // 递归遍历子目录
            TCHAR subPath[MAX_PATH];
            wsprintf(subPath, TEXT("%s\\%s"), path, fd.cFileName);
            ListDirectory(subPath);
        } else {
            printf("[FILE] %ls (%d bytes)\n", 
                   fd.cFileName, fd.nFileSizeLow);
        }
    } while (FindNextFile(hFind, &fd));
    
    FindClose(hFind);
}

5.3、示例3:文件映射

#include <windows.h>
#include <stdio.h>

void FileMapping(const TCHAR* filename) {
    // 打开文件
    HANDLE hFile = CreateFile(filename, GENERIC_READ | GENERIC_WRITE,
        0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
    if (hFile == INVALID_HANDLE_VALUE) return;
    
    // 创建文件映射对象
    HANDLE hMapping = CreateFileMapping(
        hFile,
        NULL,           // 安全属性
        PAGE_READWRITE, // 访问模式
        0, 0,           // 映射大小(0=整个文件)
        NULL            // 名称
    );
    
    if (hMapping == NULL) {
        CloseHandle(hFile);
        return;
    }
    
    // 映射视图到内存
    LPVOID pView = MapViewOfFile(
        hMapping,
        FILE_MAP_ALL_ACCESS,
        0, 0,           // 偏移
        0               // 大小(0=全部)
    );
    
    if (pView) {
        // 直接操作内存即修改文件
        char* data = (char*)pView;
        printf("First 32 bytes: %.32s\n", data);
        
        // 修改数据
        memcpy(data, "Modified!", 9);
        
        // 刷新到磁盘
        FlushViewOfFile(pView, 0);
        
        UnmapViewOfFile(pView);
    }
    
    CloseHandle(hMapping);
    CloseHandle(hFile);
}

5.4、示例4:文件属性操作

#include <windows.h>
#include <stdio.h>

void FileAttributes(const TCHAR* filename) {
    // 获取属性
    DWORD attrs = GetFileAttributes(filename);
    
    if (attrs == INVALID_FILE_ATTRIBUTES) {
        printf("Error: %d\n", GetLastError());
        return;
    }
    
    printf("Attributes:\n");
    if (attrs & FILE_ATTRIBUTE_READONLY) printf("  Read-only\n");
    if (attrs & FILE_ATTRIBUTE_HIDDEN) printf("  Hidden\n");
    if (attrs & FILE_ATTRIBUTE_SYSTEM) printf("  System\n");
    if (attrs & FILE_ATTRIBUTE_DIRECTORY) printf("  Directory\n");
    
    // 设置隐藏属性
    SetFileAttributes(filename, attrs | FILE_ATTRIBUTE_HIDDEN);
    
    // 获取详细信息
    WIN32_FILE_ATTRIBUTE_DATA fad;
    GetFileAttributesEx(filename, GetFileExInfoStandard, &fad);
    
    printf("Size: %lld\n", 
           ((LONGLONG)fad.nFileSizeHigh << 32) | fad.nFileSizeLow);
}

6、课后作业

  1. 基础练习:实现文件复制函数
  2. 目录遍历:递归统计目录大小
  3. 文件映射:实现大文件的快速搜索
  4. 文件监控:使用ReadDirectoryChangesW监控目录变化