Search code examples
c++mfcfilesizefile-mapping

SetEndOfFile error 1224: ERROR_USER_MAPPED_FILE, even the file mapping closed successfully


I'm writing a program which logs out to the file with file-mapping.
When I want to read the log, Logger::Destroy() is called so that the content writed on file-mapped-view could be flushed to the physical file. The code is as follows:

int Logger::Destroy() {
    if (m_lpMapAddress) {
        auto bRet = UnmapViewOfFile(m_lpMapAddress);
        // succeed
    }

    if (m_hMapFile) {
        auto bRet = CloseHandle(m_hMapFile);
        // succeed
        m_hMapFile = NULL;
    }

    int nSize = m_lpCurAddress - m_lpMapAddress;
    if (nSize > 0
        && nSize < (1024 * 1024 * 16 * 2))
    {
        DWORD dwPtr = SetFilePointer(m_hFile, nSize, 0, FILE_BEGIN);

        ///// Succeed
        // if (dwPtr == INVALID_SET_FILE_POINTER)
        //  DbgViewOut(__FUNCTION__ " SetFilePointer error: %d \n", GetLastError());

        //// Error occurs :  "SetEndOfFile returned : 0 1224"
        BOOL bRet = SetEndOfFile(m_hFile);
        DbgViewOut(__FUNCTION__ " SetEndOfFile returned : %d %d\n", bRet, GetLastError()); 
    
        ....
    }

    m_lpMapAddress = m_lpCurAddress = NULL;

    return 0;
}

The problem is that SetEndOfFile() fails with ERROR_USER_MAPPED_FILE, even CloseHandle(m_hMapFile) succeed. So I googled microsoft's manuals about file mapping and I comment some of them.

https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle

Closing a handle to a file mapping can succeed even when there are file views that are still open. For more information, see Closing a File Mapping Object.

https://learn.microsoft.com/en-us/windows/win32/memory/closing-a-file-mapping-object

When each process finishes using the file mapping object and has unmapped all views, it must close the file mapping object's handle and the file on disk by calling CloseHandle. These calls to CloseHandle succeed even when there are file views that are still open. However, leaving file views mapped causes memory leaks.

It says that I can't believe the result of CloseHandle().
And I can't find out the solution.

Anyone could help me? Thanks.


Addition: I called Logger::Destroy() in std::lock_guard<std::mutex>, can this affect to the trouble?

: I've tested this, lock doesn't affect to it.


UPDATE: I've read When error 1224: ERROR_USER_MAPPED_FILE occurs?. And I think there isn't a very big difference. I append the code of Logger::Initialize() which initializes the filemapping.
And also, Logger::Initialize() and Logger::Destroy() both are in same process&thread, no need to be shared with others.

int Logger::Initialize()
{
  m_hFile = CreateFileA(
      m_zFileName.c_str()
      , GENERIC_READ | GENERIC_WRITE
      , FILE_SHARE_READ | FILE_SHARE_WRITE
      , NULL                          
      , CREATE_ALWAYS
      , FILE_ATTRIBUTE_NORMAL
      , NULL);

  if (m_hFile == INVALID_HANDLE_VALUE) {
      DbgViewOut(__FUNCTION__  " CreateFileA error: %d \n", GetLastError());
      return -1;
  }

  m_hMapFile = CreateFileMappingA(
      m_hFile
      , NULL
      , PAGE_READWRITE
      , 0
      , 1024 * 1024 * 16 * 2
      , m_zMapKey.c_str());

  if (!m_hMapFile) {
      DbgViewOut(__FUNCTION__  " CreateFileMapping error: %d \n", GetLastError());
      return -1;
  }

  m_hMapFile = OpenFileMappingA(FILE_MAP_WRITE, TRUE, m_zMapKey.c_str());
  if (m_hMapFile == NULL) {
      DbgViewOut(__FUNCTION__ " OpenFileMapping error: %d \n", GetLastError());
      return -1;
  }

  m_lpMapAddress = (BYTE*)MapViewOfFile(
      m_hMapFile              // handle to mapping object
      , FILE_MAP_ALL_ACCESS   // read/write
      , 0                 // high-order 32 bits of file offset
      , 0                 // low-order 32 bits of file offset
      , 0);                // number of bytes
  if (m_lpMapAddress == NULL) {
      DbgViewOut(__FUNCTION__ " MapViewOfFile error: %d \n", GetLastError());
      return -1;
  }

  m_lpCurAddress = m_lpMapAddress;

  return 0;
}

Solution

  • I'll answer my own question.

    The problem was that I called OpenFileMappingA() after CreateFileMappingA(), so the m_hMapFile returned by CreateFileMappingA() is leaking. I've removed OpenFileMappingA() and the problem disappeared.

    I used to know that it must be once opened after create handle, and I used to do that in memory sharing between processes, monthes ago. But during this arguement, I've realized that it's unecessary open handle after creating.

    Thank you very much, @RemyLebeau, for your detailed suggestion.

    int Logger::Initialize()
    {
        m_hFile = CreateFileA(
            m_zFileName.c_str()
            , GENERIC_READ | GENERIC_WRITE
            , FILE_SHARE_READ | FILE_SHARE_WRITE
            , NULL                          
            , CREATE_ALWAYS
            , FILE_ATTRIBUTE_NORMAL
            , NULL);
    
        if (m_hFile == INVALID_HANDLE_VALUE) {
            DbgViewOut(__FUNCTION__  " CreateFileA error: %d \n", GetLastError());
            return -1;
        }
    
    
        m_hMapFile = CreateFileMappingA(
            m_hFile
            , NULL
            , PAGE_READWRITE
            , 0
            , LINM_LOGGER_FILE_MAXSIZE * 2
            , m_zMapKey.c_str());
    
        if (!m_hMapFile) {
            DbgViewOut(__FUNCTION__  " CreateFileMapping error: %d \n", GetLastError());
            return -1;
        }
    
    //// This was the solution - CreateFileMappingA & OpenFileMappingA mustn't be used for same process or thread.
    //
    //  m_hMapFile = OpenFileMappingA(FILE_MAP_WRITE, TRUE, m_zMapKey.c_str());
    //  if (m_hMapFile == NULL) {
    //      DbgViewOut(__FUNCTION__ " OpenFileMapping error: %d \n", GetLastError());
    //      return -1;
    //  }
    
        m_lpMapAddress = (BYTE*)MapViewOfFile(
            m_hMapFile              // handle to mapping object
            , FILE_MAP_ALL_ACCESS   // read/write
            , 0                 // high-order 32 bits of file offset
            , 0                 // low-order 32 bits of file offset
            , 0);                // number of bytes
        if (m_lpMapAddress == NULL) {
            DbgViewOut(__FUNCTION__ " MapViewOfFile error: %d \n", GetLastError());
            return -1;
        }
    
        m_lpCurAddress = m_lpMapAddress;
    
        return 0;
    }