【问题标题】:MapViewOfFile returns Error 5 (ERROR_ACCESS_DENIED)MapViewOfFile 返回错误 5 (ERROR_ACCESS_DENIED)
【发布时间】:2018-03-04 22:54:54
【问题描述】:

我正在尝试开发一个使用共享内存在两个进程之间进行通信的系统。一个进程是 32 位应用程序并使用 32 位 dll 进行通信。另一个进程是 64 位应用程序并使用 64 位 dll,其中包含用于创建/打开共享内存的完全相同的代码。我这样做了,每当一个进程打开内存的句柄并且无法打开它时,它就会自动尝试创建内存。然后另一个进程将尝试相同的操作,因此首先运行代码的进程将创建内存,而另一个进程将打开一个已存在内存的句柄。

当共享内存由 64 位进程/dll 创建时,代码运行良好,但每当 32 位 dll 创建它时,调用 MapViewOfFile 时返回错误 5 (ERROR_ACCESS_DENIED)。

我已经检查过传递给任何函数的大小是否不同,例如,因为其中一个结构的大小不同,具体取决于它是为 32 位还是 64 位编译的。但是,情况并非如此,两个进程的大小始终相同。

我还尝试了this 答案中建议的代码,但没有成功。 有谁知道为什么代码有时会因错误 5 而失败?

这是我的代码:

static LPVOID lpMappedInputData = nullptr,
    lpMappedOutputData = nullptr;

static HANDLE hInputFileMapping = NULL,
    hOutputFileMapping = NULL;

HANDLE createOrOpenFileMapping(DWORD size, LPCSTR lpName)
{
    HANDLE hMapFile = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, lpName);

    if (!hMapFile)
        DEBUG_LOG("OpenFileMapping failed! Error code: %i - Attempting to create the file mapping instead...\n", GetLastError());

    if (!hMapFile)
    {
        hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, lpName);

        if (!hMapFile)
            DEBUG_LOG("CreateFileMapping failed! Error code: %i\n", GetLastError());
    }

    return hMapFile;
}

LPVOID mapViewOfFile(HANDLE hFileMappingObject, DWORD size)
{
    LPVOID lpMappedData = MapViewOfFile(hFileMappingObject, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size);

    if (!lpMappedData)
    {
        CloseHandle(hFileMappingObject);
        DEBUG_LOG("MapViewOfFile failed! Error code: %i\n", GetLastError());
    }

    return lpMappedData;
}

bool Initialize()
{
    DEBUG_LOG("Initializing the input file mapping...\n");

    hInputFileMapping = createOrOpenFileMapping(sizeof(InputData), "Local\\InputData");

    if (!hInputFileMapping)
        return false;

    lpMappedInputData = mapViewOfFile(hInputFileMapping, sizeof(InputData));

    if (!lpMappedInputData)
        return false;

    DEBUG_LOG("Initializing the output file mapping...\n");

    hOutputFileMapping = createOrOpenFileMapping(sizeof(OutputData), "Local\\OutputData");

    if (!hOutputFileMapping)
        return false;

    lpMappedOutputData = mapViewOfFile(hOutputFileMapping, sizeof(OutputData));

    if (!lpMappedOutputData)
        return false;

    return true;
}

void Deinitialize()
{
    DEBUG_LOG("Deinitializing the file mappings...\n");

    if (lpMappedInputData)
        UnmapViewOfFile(lpMappedInputData);

    if (hInputFileMapping)
        CloseHandle(hInputFileMapping);

    if (lpMappedOutputData)
        UnmapViewOfFile(lpMappedOutputData);

    if (hOutputFileMapping)
        CloseHandle(hOutputFileMapping);

    DEBUG_LOG("Successfully deinitialized the file mappings!\n");
}

【问题讨论】:

  • Bitness 没有任何作用,您需要关注控制这些进程的用户帐户或权限升级。您未在问题中提供的详细信息。
  • @HansPassant 那么你建议我怎么做?两个进程在完全相同的用户帐户下运行。
  • 尝试只使用文件的标准路径名,看看问题是否消失。我担心 "WOW64"(允许运行基于 32 位 Windows 的应用程序的 x86 模拟器)对 "Local\\" 的作用。即使它是同一个用户,但它是不同的安全上下文(我认为)。
  • 请注意,在调用GetLastError() 之前调用CloseHandle() 是一个错误。始终在 API 函数失败时立即保存错误代码,因为调用其他 API 函数可以修改错误代码。
  • @RbMm 实际上,非常肯定。

标签: c++ winapi memory shared


【解决方案1】:

win32 错误 - 这是大多数转换自原始 NTSTATUS 原生 api 调用结果的转换。但这种翻译不是单向的 - 有时许多不同的状态转换为相同的 win32 错误值。例如到相同的ERROR_ACCESS_DENIED 至少转换了 18(!) 个不同的 ntstatus。所以原始错误可能不是STATUS_ACCESS_DENIED,而是绝对另一个错误,与实际访问被拒绝没有任何共同之处。因为这样,如果 win32 api 调用是 shell 而不是原生调用 - 总是更好地调用 RtlGetLastNtStatus 以获取原始错误状态代码或直接调用原生 api(ZwMapViewOfSection 而不是 MapViewOfFile

很遗憾,我不知道ZwMapViewOfSection 返回的原始状态,但我猜这是STATUS_INVALID_VIEW_SIZE尝试为大于该部分的部分创建视图。 em>)。

在创建和映射部分使用sizeof(InputData)sizeof(OutputData) - 在代码中没有定义此结构,bat 基于

当共享内存由 64 位进程/dll 但每当 32 位 dll 创建它时,我得到 调用MapViewOfFile时返回错误5(ERROR_ACCESS_DENIED)。

我猜接下来 - 在 32 位代码中 InputDataOutputData 尺寸更小)比较 64 位代码)。这只是解释一切。如果 64 位代码首先创建部分,然后 32 位代码尝试映射更小的尺寸 - 这没关系。但是如果 32 位代码首先创建部分然后 64 位代码尝试映射大小大于该部分大小 - 我们得到 STATUS_INVALID_VIEW_SIZE 转换为 ERROR_ACCESS_DENIED

但是怎么样

我已经检查过尺寸..两者的尺寸总是相同的 进程。

不相信。我要复查。

作为单独的注释 - CreateFileMapping

创建或打开一个命名或未命名的文件映射对象 指定文件。

所以我们不需要特殊功能createOrOpenFileMapping 并首先调用OpenFileMappingA - 只需调用CreateFileMappingWA api 有什么用?)。并且createOrOpenFileMapping 的任何方式代码都具有引发条件 - 两个进程都可以同时调用OpenFileMappingA 并且都在这里失败。无论如何都打电话给CreateFileMappingA

但主要的 GetLastError() 有时不是最好的,ERROR_ACCESS_DENIED 并不总是 STATUS_ACCESS_DENIED 并且与实际访问被拒绝错误有关

【讨论】:

  • 正如我在评论中已经提到的,RtlGetLastNtStatus 返回 0xC000001F (STATUS_INVALID_VIEW_SIZE)。但是,当我从 32 位和 64 位应用程序检查两种结构的 sizeof 时,两种情况下的大小相同:i.gyazo.com/0c7e36db8484b011627152f2a42c61d4.png这怎么可能?
  • 为了避免你提到的两个进程同时调用 OpenFileMappingA 的问题,我应该怎么做呢?我想不出更好的解决方案,无论哪个进程先执行代码,这都必须有效。
  • 答案必须提供解决方案。我不觉得这提供了解决方案
  • @dtracers - 你觉得有趣的是什么?回答解释一切。解决方案很简单 - 使用相同的尺寸,如果你还不明白的话:)
  • @ccman32 - 当两个进程同时调用 OpenFileMappingA 时 - 但解释一下,在你的情况下,这根本就是毫无意义的调用。只需致电CreateFileMappingW
猜你喜欢
  • 2017-06-30
  • 2019-03-07
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-08
  • 2013-03-12
  • 2018-12-25
相关资源
最近更新 更多