//***************************************************************************** // This version will force the data in cache out to disk for real. The code // can handle the different types of storage we might be sitting on based on // the open type. //***************************************************************************** HRESULT StgIO::WriteToDisk( // Return code. const void *pbBuff, // Buffer to write. ULONG cbWrite, // How much. ULONG *pcbWritten) // Return how much written. { ULONG cbWritten; // Buffer for write funcs. HRESULT hr = S_OK; // Pretty obvious. _ASSERTE(!IsReadOnly()); // Always need a buffer to write this data to. if (!pcbWritten) pcbWritten = &cbWritten; // Action taken depends on type of storage. switch (m_iType) { case STGIO_HFILE: case STGIO_HFILEMEM: { // Use the file system's move. _ASSERTE(m_hFile != INVALID_HANDLE_VALUE); // Do the write to disk. if (!::WriteFile(m_hFile, pbBuff, cbWrite, pcbWritten, 0)) hr = MapFileError(GetLastError()); } break; // Free the stream pointer. case STGIO_STREAM: { // Delegate write to stream code. hr = m_pIStream->Write(pbBuff, cbWrite, pcbWritten); } break; // We cannot write to fixed read/only memory or LoadLibrary module. case STGIO_HMODULE: case STGIO_MEM: #ifndef FEATURE_METADATA_STANDALONE_WINRT_RO case STGIO_SHAREDMEM: #endif _ASSERTE(0); hr = BadError(E_UNEXPECTED); break; // Weird to seek with no data. case STGIO_NODATA: default: _ASSERTE(0); break; } return (hr); }
//***************************************************************************** // Convert a string to UNICODE into the caller's buffer. //***************************************************************************** HRESULT StgPoolReadOnly::GetStringW( // Return code. ULONG iOffset, // Offset of string in pool. LPWSTR szOut, // Output buffer for string. int cchBuffer) // Size of output buffer. { LPCSTR pString; // The string in UTF8. int iChars; pString = GetString(iOffset); iChars = ::WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, szOut, cchBuffer); if (iChars == 0) return (BadError(HRESULT_FROM_NT(GetLastError()))); return (S_OK); }
//***************************************************************************** // Caller wants a pointer to a chunk of the file. This function will make sure // that the memory for that chunk has been committed and will load from the // file if required. This algorithm attempts to load no more data from disk // than is necessary. It walks the required pages from lowest to highest, // and for each block of unloaded pages, the memory is committed and the data // is read from disk. If all pages are unloaded, all of them are loaded at // once to speed throughput from disk. //***************************************************************************** HRESULT StgIO::GetPtrForMem( // Return code. ULONG cbStart, // Where to start getting memory. ULONG cbSize, // How much data. void *&ptr) // Return pointer to memory here. { int iFirst, iLast; // First and last page required. ULONG iOffset, iSize; // For committing ranges of memory. int i, j; // Loop control. HRESULT hr; // We need either memory (mmf or user supplied) or a backing store to // return a pointer. Call Read if you don't have these. if (!IsBackingStore() && m_pData == 0) return (PostError(BadError(E_UNEXPECTED))); // Validate the caller isn't asking for a data value out of range. if (!(ClrSafeInt<ULONG>::addition(cbStart, cbSize, iOffset) && (iOffset <= m_cbData))) return (PostError(E_INVALIDARG)); // This code will check for pages that need to be paged from disk in // order for us to return a pointer to that memory. if (IsBackingStore()) { // Backing store is bogus when in rewrite mode. if (m_bRewrite) return (PostError(BadError(E_UNEXPECTED))); // Must have the page map to continue. _ASSERTE(m_rgPageMap && m_iPageSize && m_pData); // Figure out the first and last page that are required for commit. iFirst = cbStart / m_iPageSize; iLast = (cbStart + cbSize - 1) / m_iPageSize; // Avoid confusion. ptr = 0; // Do a smart load of every page required. Do not reload pages that have // already been brought in from disk. //<REVISIT_TODO>@FUTURE: add an optimization so that when all pages have been faulted, we no // longer to a page by page search.</REVISIT_TODO> for (i=iFirst; i<=iLast; ) { // Find the first page that hasn't already been loaded. while (GetBit(m_rgPageMap, i) && i<=iLast) ++i; if (i > iLast) break; // Offset for first thing to load. iOffset = i * m_iPageSize; iSize = 0; // See how many in a row have not been loaded. for (j=i; i<=iLast && !GetBit(m_rgPageMap, i); i++) { // Safe: iSize += m_iPageSize; if (!(ClrSafeInt<ULONG>::addition(iSize, m_iPageSize, iSize))) { return PostError(E_INVALIDARG); } } // First commit the memory for this part of the file. if (::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset), iSize, MEM_COMMIT, PAGE_READWRITE) == 0) return (PostError(OutOfMemory())); // Now load that portion of the file from disk. if (FAILED(hr = Seek(iOffset, FILE_BEGIN)) || FAILED(hr = ReadFromDisk((void *) ((DWORD_PTR) m_pData + iOffset), iSize, 0))) { return (hr); } // Change the memory to read only to avoid any modifications. Any faults // that occur indicate a bug whereby the engine is trying to write to // protected memory. _ASSERTE(::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset), iSize, MEM_COMMIT, PAGE_READONLY) != 0); // Record each new loaded page. for (; j<i; j++) SetBit(m_rgPageMap, j, true); } // Everything was brought into memory, so now return pointer to caller. ptr = (void *) ((DWORD_PTR) m_pData + cbStart); } // Memory version or memory mapped file work the same way. else if (IsMemoryMapped() || (m_iType == STGIO_MEM) || (m_iType == STGIO_SHAREDMEM) || (m_iType == STGIO_HFILEMEM)) { if (!(cbStart <= m_cbData)) return (PostError(E_INVALIDARG)); ptr = (void *) ((DWORD_PTR) m_pData + cbStart); } // What's left?! Add some defense. else { _ASSERTE(0); ptr = 0; return (PostError(BadError(E_UNEXPECTED))); } return (S_OK); }