【问题标题】:GetFileAttributesW causes Access violation reading location x (GetLastError returns 8)GetFileAttributesW 导致访问冲突读取位置 x(GetLastError 返回 8)
【发布时间】:2021-08-03 02:26:19
【问题描述】:

我目前正在尝试实现一个将多个文件加载到内存中以进行进一步处理的程序。为了查看文件是否存在,我创建了一个使用 GetFileAttributesW 来检查文件是否确实存在的函数。

第一个文件被正确加载,但是一旦我尝试加载第二个文件,我就会遇到访问冲突(GetLastError 返回 8;ERROR_NOT_ENOUGH_MEMORY)。我可以安全地排除我没有足够的 RAM,因为文件是最大的。 500kb 大小,我从不加载超过 20 个(我有 16GB 的 RAM)。 我也有权访问他的文件等。

inline BOOL FileExists(const TCHAR* szPath)
{
    DWORD dwAttrib = GetFileAttributesW(szPath); // ERROR here (1st iteration everything is fine) 

    return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

.
.
.

FILE_DATA LoadFileIntoMemory(const TCHAR* FileName)
{
    PTCHAR FinalPath = VirtualAlloc(NULL, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    PTCHAR FilePath = L"C:\\Users\\invasi0nZ\\"; // 

    concat(FinalPath, FilePath, MAX_PATH);
    concat(FinalPath, FileName, MAX_PATH);

    if (!FileExists(FinalPath))
    {
        memset(FinalPath, 0, MAX_PATH);

        FilePath = L"C:\\Users\\invasi0nZ\\Documents\\";

        concat(FinalPath, FilePath, MAX_PATH);
        concat(FinalPath, FileName, MAX_PATH);
    }

    HANDLE File = CreateFileW(FinalPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    if (File == INVALID_HANDLE_VALUE)
    {
        return (FILE_DATA){ NULL, NULL };
    }

    int FileSize = GetFileSize(File, NULL);

    PBYTE RawFile = VirtualAlloc(NULL, FileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    DWORD dwRead;
    size_t FileSize = GetFileSize(File, NULL);

    ReadFile(File, RawFile, FileSize, &dwRead, NULL);

    CloseHandle(File);
    VirtualFree(FinalPath, sizeof(FinalPath), MEM_FREE);

    return (FILE_DATA) { RawFile, FileSize };
}

.
.
.

void LoadAllFiles(Array FileNames)
{
    for (int i = 0; i < FileNames.used; i++)
    {
        FILE_DATA file_data = LoadFileIntoMemory(FileNames.array[i].file_name);
        // Store file_data, etc.
    }
    // Do stuff with files here
}

据我所知,我正在关闭所有必需的句柄并在不破坏程序的情况下释放所有可能的东西。

非常感谢您!

【问题讨论】:

  • 那么,C++ 标签在这里做什么?如果这不是 C++ 问题,请不要这样标记。
  • 请显示完整的错误诊断,未删节。当您遇到访问冲突时,调用 GetLastError 毫无意义。
  • 您到底为什么要使用VirtualAlloc 作为通用分配器?如果您想要纯 WinAPI,请使用 HeapAllocGlobalAlloc 用于通用用途,SysAllocString 用于宽字符串。
  • 在您的固定大小分配的超级特定情况下,只需执行WCHAR FinalPath[MAX_PATH + 1];
  • 要修复您的错误,请使用调试器,在 GetFileAttributesW 调用上设置断点,并检查调用前立即存在的值​​。

标签: c winapi


【解决方案1】:

您没有为VirtualAlloc() 分配足够的内存,因此您的concat() 函数可能会导致缓冲区溢出。由于您在启用UNICODE 的情况下进行编译,因此TCHARwchar_t,大小为2 个字节。在为FinalPath 分配内存时,以及在使用memset() 清除FinalPath 时,您需要考虑该大小。

改变这个:

PTCHAR FinalPath = VirtualAlloc(NULL, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
...
memset(FinalPath, 0, MAX_PATH);

到这里:

PTCHAR FinalPath = VirtualAlloc(NULL, sizeof(TCHAR) * MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
...
memset(FinalPath, 0, sizeof(TCHAR) * MAX_PATH);

您的代码也存在其他问题。在使用返回的指针之前,您没有检查 VirtualAlloc() 是否成功。如果CreateFileW() 失败,您将泄漏分配的内存。而且你声明了FileSize 两次。

试试这个:

FILE_DATA LoadFileIntoMemory(const TCHAR* FileName)
{
    PTCHAR FinalPath = VirtualAlloc(NULL, sizeof(TCHAR) * MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!FinalPath)
        return (FILE_DATA){ NULL, 0 };

    PTCHAR FilePath = TEXT("C:\\Users\\invasi0nZ\\"); // 

    concat(FinalPath, FilePath, MAX_PATH);
    concat(FinalPath, FileName, MAX_PATH);

    if (!FileExists(FinalPath))
    {
        memset(FinalPath, 0, sizeof(TCHAR) * MAX_PATH);

        FilePath = TEXT("C:\\Users\\invasi0nZ\\Documents\\");

        concat(FinalPath, FilePath, MAX_PATH);
        concat(FinalPath, FileName, MAX_PATH);
    }

    HANDLE File = CreateFile(FinalPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (File == INVALID_HANDLE_VALUE)
    {
        VirtualFree(FinalPath, 0, MEM_RELEASE);
        return (FILE_DATA){ NULL, 0 };
    }

    VirtualFree(FinalPath, 0, MEM_RELEASE);

    DWORD FileSize = GetFileSize(File, NULL);
    if (FileSize == INVALID_FILE_SIZE)
    {
        CloseHandle(File);
        return (FILE_DATA){ NULL, 0 };
    }

    PBYTE RawFile = VirtualAlloc(NULL, FileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!RawFile)
    {
        CloseHandle(File);
        return (FILE_DATA){ NULL, 0 };
    }

    DWORD dwRead;
    if (!ReadFile(File, RawFile, FileSize, &dwRead, NULL))
    {
        VirtualFree(RawFile, 0, MEM_RELEASE);
        RawFile = NULL;
        dwRead = 0;
    }

    CloseHandle(File);

    return (FILE_DATA) { RawFile, dwRead };
}

话虽如此,您根本不需要FileExists(),因为CreateFile() 可以告诉您文件是否存在,从而避免在另一个进程之后创建/删除文件时代码中出现竞争条件您在打开它之前检查它的存在。

您还应该为FinalPath 删除VirtualAlloc(),您不需要动态分配该字符串。只需静态声明数组即可。

并且 Win32 API 具有将路径段连接在一起的功能,因此您无需编写自己的。它还具有查询用户配置文件和文档文件夹路径的功能,您不应该对这些路径进行硬编码。

试试类似的方法:

#include <windows.h>
#include <pathcch.h>
#include <shlobj.h>

HANDLE OpenFileInFolder(CSIDL FolderID, const TCHAR *FileName)
{
    TCHAR FilePath[MAX_PATH];

    HRESULT Res = SHGetFolderPath(NULL, FolderID, NULL, SHGFP_TYPE_CURRENT, FilePath);
    if (Res != S_OK)
    {
        SetLastError(Res);
        return INVALID_HANDLE_VALUE;
    }

    Res = PathCchCombine(FilePath, MAX_PATH, FilePath, FileName);
    if (Res != S_OK)
    {
        SetLastError(Res);
        return INVALID_HANDLE_VALUE;
    }

    return CreateFile(FilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}

FILE_DATA LoadFileIntoMemory(const TCHAR* FileName)
{
    HANDLE File = OpenFileInFolder(CSIDL_PROFILE, FileName);
    if ((File == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_FILE_NOT_FOUND))
        File = OpenFileInFolder(CSIDL_MYDOCUMENTS, FileName);

    if (File == INVALID_HANDLE_VALUE)
        return (FILE_DATA){ NULL, 0 };

    DWORD FileSize = GetFileSize(File, NULL);
    if (FileSize == INVALID_FILE_SIZE)
    {
        CloseHandle(File);
        return (FILE_DATA){ NULL, 0 };
    }

    PBYTE RawFile = VirtualAlloc(NULL, FileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!RawFile)
    {
        CloseHandle(File);
        return (FILE_DATA){ NULL, 0 };
    }

    DWORD dwRead;
    if (!ReadFile(File, RawFile, FileSize, &dwRead, NULL))
    {
        VirtualFree(RawFile, 0, MEM_RELEASE);
        RawFile = NULL;
        dwRead = 0;
    }

    CloseHandle(File);

    return (FILE_DATA) { RawFile, dwRead };
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 2016-05-28
    • 2017-01-09
    • 2014-11-09
    • 1970-01-01
    相关资源
    最近更新 更多