File Streams
This section provides detailed information about the Windows API functions and structures related to file stream operations. File streams offer a more abstract and powerful way to interact with files compared to traditional file handles, allowing for asynchronous operations, buffering, and advanced control over data flow.
Introduction to File Streams
File streams in Windows leverage technologies like the Asynchronous I/O (AIO) model and the I/O Completion Ports (IOCP) for high-performance, scalable file operations. They are particularly useful in server applications and scenarios requiring efficient handling of large files or a high volume of concurrent I/O requests.
Key Concepts
- Asynchronous I/O: Operations that do not block the calling thread, allowing for continuous execution of other tasks while I/O completes in the background.
- I/O Completion Ports (IOCP): A scalable mechanism for efficiently managing and dispatching I/O operations.
- Buffers: Memory areas used to temporarily store data during read and write operations, improving performance by reducing the number of direct disk accesses.
- Stream Handles: Objects representing an open file stream, providing methods for reading, writing, seeking, and querying file properties.
Core API Functions
Creating and Opening Streams
Functions to create new files or open existing ones for stream-based access.
-
CreateStreamOnFile
Creates or opens a file for stream-based access. This function is a modern replacement for older Win32 file creation functions.
HRESULT CreateStreamOnFile(PCWSTR pszFileName, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, IStream **ppstm); -
CreateFile2
A more robust and feature-rich version of
CreateFilethat can be used to create file stream objects.HANDLE CreateFile2(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams);
Reading and Writing Data
Functions for performing asynchronous and synchronous read and write operations.
-
ReadFile
Reads data from a file or device into a buffer. Can be used asynchronously.
BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); -
WriteFile
Writes data from a buffer to a file or device. Can be used asynchronously.
BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); -
ReadClassStream
Reads object data from a stream.
HRESULT ReadClassStream(IStream *pstm, CLSID *pclsid); -
WriteClassStream
Writes object data to a stream.
HRESULT WriteClassStream(IStream *pstm, REFCLSID rclsid);
Seeking and Positioning
Functions to move the read/write pointer within the file stream.
-
SetFilePointerEx
Moves the file pointer to a specified location within a file.
BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod); -
Seek
Moves the seek pointer of the stream to a specified position.
HRESULT Seek(IStream *pstm, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
Stream Properties and Control
Functions to get information about the stream and manage its state.
-
GetFileSizeEx
Retrieves the size of the specified file.
BOOL GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize); -
SetEndOfFile
Sets the specified file to the specified size.
BOOL SetEndOfFile(HANDLE hFile); -
LockFileEx
Locks a specified portion of a file for exclusive access.
BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockHigh, DWORD nNumberOfBytesToLockLow, LPOVERLAPPED lpOverlapped);
Using Streams with COM
The IStream interface is a fundamental part of COM that represents a stream of bytes. Many COM objects use IStream for serialization and data transfer. Understanding how to implement and use IStream is crucial for advanced COM programming.
Example: Reading from a Memory Stream
#include <windows.h>
#include <objbase.h>
#include <stdio.h>
// Assume IStream is already defined and available
int main() {
IStream *pStream = NULL;
ULARGE_INTEGER ulSize;
HRESULT hr;
// Create a memory stream (e.g., using SHCreateMemStream)
// For simplicity, let's assume pStream is already initialized with some data
// Get the size of the stream
if (SUCCEEDED(pStream->Seek(0, STREAM_SEEK_END, &ulSize))) {
printf("Stream size: %llu bytes\n", ulSize.QuadPart);
}
// Reset stream position to the beginning
if (SUCCEEDED(pStream->Seek(0, STREAM_SEEK_SET, NULL))) {
// Read data from the stream
char buffer[100];
ULONG bytesRead;
hr = pStream->Read(buffer, sizeof(buffer) - 1, &bytesRead);
if (SUCCEEDED(hr) && bytesRead > 0) {
buffer[bytesRead] = '\0'; // Null-terminate the buffer
printf("Read from stream: %s\n", buffer);
} else if (hr == S_FALSE) {
printf("End of stream reached.\n");
} else {
printf("Error reading from stream: HRESULT = %08X\n", hr);
}
}
// Release the stream
if (pStream) {
pStream->Release();
}
return 0;
}
Best Practices
- Always check the return values of API functions for errors.
- Use asynchronous I/O for performance-critical operations to avoid blocking threads.
- Properly manage memory buffers used for reading and writing.
- Close file handles and release COM interfaces when they are no longer needed to prevent resource leaks.
- Consider using higher-level abstractions like the Windows Runtime (WinRT) APIs for modern application development.