Microsoft KB Archive/163253

= BUG: "Out of Memory" Error When Saving to an Existing File =

Article ID: 163253

Article Last Modified on 11/21/2006

-

APPLIES TO

 Microsoft Foundation Class Library 4.2, when used with:  Microsoft Visual C++ 4.0 Standard Edition

 Microsoft Visual C++ 4.1 Subscription

 Microsoft Visual C++ 4.2 Enterprise Edition

 Microsoft Visual C++ 4.2 Professional Edition</li></ul> </li></ul>

-

<div class="notice_section">

This article was previously published under Q163253

<div class="symptoms_section">

SYMPTOMS
When you save to an existing file in a Visual C++ MFC application, an "Out of memory" error occurs. The error occurs only when you save over an existing file on a non-NT platform, such as Windows 95, that is on a network. For example, if you run the Scribble sample on a Windows NT machine and try to save the scribble file to an existing file on a Windows 95 share, the error occurs.

<div class="cause_section">

CAUSE
MFC uses a class called CMirrorFile that protects a file in cases where an error may occur when you are writing to a file. MFC first writes out to a temporary file and then copies over to the final file when all writing is done.

In the CMirrorFile::Open function located in Doccore.cpp, the following code exists: DWORD dwLength = 0; PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL; GetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION,        NULL, dwLength, &dwLength); pSecurityDescriptor = (PSECURITY_DESCRIPTOR) new BYTE[dwLength]; In the case where a file is located on a non-NT share, the GetFileSecurity call fails. It may return a large value and this can cause the new statement to fail with an "out of memory" error. You need to select the return value for GetFileSecurity.

<div class="resolution_section">

RESOLUTION
There are a couple of ways to work around the problem.


 * Override the CDocument::GetFile function to return a CFile object rather than a CMirrorFile object. You won't get the protection feature of CMirrorFile which checks to see whether you can write the whole file to the disk first. This may not be a problem depending upon what the application does. See the code the CDocument::GetFile in \Msdev\Mfc\Src\Doccore.cpp to see the default implementation.
 * Create your own CMirrorFile class and check the return value of the GetFileSecurity call. If the call fails, ignore the code that modifies the securities of the file. For example, you can create your own CMirrorFile-derived class like the following code demonstrates:

**********
 * 1) include <afxpriv.h>
 * 2) include "c:\msdev\mfc\src\afximpl.h"

class CMyMirrorFile: public CMirrorFile {     virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags,      CFileException* pError = NULL); };

BOOL CMyMirrorFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags,     CFileException* pError) {   ASSERT(lpszFileName != NULL); m_strMirrorName.Empty;

CFileStatus status; if (nOpenFlags & CFile::modeCreate) //opened for writing {     if (CFile::GetStatus(lpszFileName, status)) {        CString strRoot; AfxGetRoot(lpszFileName, strRoot);
 * 1) ifndef _MAC

DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus; int nBytes = 0; if (GetDiskFreeSpace(strRoot, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus, &dwTotalClus)) {           nBytes = dwFreeClus*dwSecPerClus*dwBytesPerSec; }        if (nBytes > 2*status.m_size) // at least 2x free space avail {           // get the directory for the file TCHAR szPath[_MAX_PATH]; LPTSTR lpszName; GetFullPathName(lpszFileName, _MAX_PATH, szPath, &lpszName); *lpszName = NULL;

//let's create a temporary file name GetTempFileName(szPath, _T("MFC"), 0,              m_strMirrorName.GetBuffer(_MAX_PATH+1)); m_strMirrorName.ReleaseBuffer; }     }   }
 * 1) endif

if (!m_strMirrorName.IsEmpty &&     CFile::Open(m_strMirrorName, nOpenFlags, pError)) {     m_strFileName = lpszFileName; FILETIME ftCreate, ftAccess, ftModify; if (::GetFileTime((HANDLE)m_hFile, &ftCreate, &ftAccess, &ftModify)) {        AfxTimeToFileTime(status.m_ctime, &ftCreate); SetFileTime((HANDLE)m_hFile, &ftCreate, &ftAccess, &ftModify); }
 * 1) ifndef _MAC

// If GetFileSecurity returns an error, don't do anything with // file securities. DWORD dwLength = 0; PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL; if (GetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION, NULL, dwLength, &dwLength)) {           pSecurityDescriptor = (PSECURITY_DESCRIPTOR) new BYTE[dwLength]; if (::GetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION, pSecurityDescriptor, dwLength, &dwLength)) {            SetFileSecurity(m_strMirrorName, DACL_SECURITY_INFORMATION, pSecurityDescriptor); }         delete[] (BYTE*)pSecurityDescriptor; }     return TRUE; }  m_strMirrorName.Empty; return CFile::Open(lpszFileName, nOpenFlags, pError); }
 * 1) endif

Then you need to override CDocument::GetFile to return an object of your new derived class.

Please note that the work around uses the undocumented class CMirrorFile. The work around may have to be modified to work in a future version of MFC. Refer to this article again in the future for any updates and modifications that may be required for future versions.

<div class="status_section">

STATUS
Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article.

Keywords: kbbug kbcode kbdocview kbfileio KB163253

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.