Attached is a self-contained class that manages IStream objects. These IStreams can be created by the class and/or passed to the class for management and accessibility. The class creates an IStorage interface object and maintains owned streams within the IStorage object. This object can contain up to 2GB worth of stream data that is cached in virtual memory as needed. This allows heavy data to be cached outside the user's memory space, greatly reducing the potential of out-of-memory errors for projects that want to cache large data in memory vs. individual files.
Note: This class is not a full implementation of IStorage. It does not permanently save anything. It was designed as a run-time only virtual cache of data, destroyed on release.
The class organizes IStreams into 2 categories: Owned and External.
Each class method that allows creation of a stream has an option to create it as owned or external.
Owned Streams
The next post in this thread briefly describes each of the class methods/properties. The class is heavily commented.
This class is not very low level, but it does require some care if you are using a few of its more generic methods which allow you to pass/receive data via memory addresses. You must ensure you do not pass parameters that would cause the class to attempt to read or write past the memory space represented by the passed memory pointer. Crashes will occur. Bottom line. If you tell the class that the available memory exists for the pointer you provided, at least that amount of memory better exist.
Why would you use this? Briefly touched in first paragraph above. Consider a graphics program that allows various levels of 'undos'. Instead of keeping potentially large data in the user's memory space, you can store that data to IStreams and recall them on demand, as needed. If you need to back up source data while you are working with it, while making sure it doesn't get deleted by the user/system, store it an IStream and purge it or recall it as needed. Though 2GB is fairly large, it isn't never ending. This class may make it easy to abuse virtual memory, not the intent.
Couple of super-simple examples. Each IStream is provided a key, the key allows you to choose which IStream you want to access. In the examples below, it is assumed the class is declared at module/form level, public to your project. We'll say the an instance of the IStreamManager class is named: cStrmMgr. In each example, we are assuming the stream's seek pointer is at beginning of the stream. To be sure, we can always call the cStrmMgr.SetSeekPointer method
Example: Back up a 2D array and recall it
Example. Read a file into a backup stream
Example: Backup a RTF document from the RichtTextBox control
Example: Detach IStream from the class
Last example. Let's say you are managing undo stuff via a DIB you have a memory pointer for the bits: pDIBits
Known Limitations
1) 2 GB IStorage object. Trying to exceed this should result in failure
2) 2 GB IStream is largest that can be created and if done, likely to max out IStorage
3) Class does not expose hGlobal address if stream has one. Use GetHGlobalFromStream API
4) Undefined Streams can be added, i.e., length of zero on memory pointer of zero. These can be added to willy-nilly and the stream auto-expands as needed. The 2GB limitation still applies
After downloading the text file, simply remove the .txt extension
Note: This class is not a full implementation of IStorage. It does not permanently save anything. It was designed as a run-time only virtual cache of data, destroyed on release.
The class organizes IStreams into 2 categories: Owned and External.
Each class method that allows creation of a stream has an option to create it as owned or external.
Owned Streams
Streams that are part of the IStorage object. Owned streams should not be detached from the class. Once the class terminates, any owned streams are invalid
External StreamsThese are streams that are created in the class but opted to be created in the user's memory space on an hGlobal memory address. Additionally, any externally created stream that is passed to the class is considered external. External streams can always be detached from the class.
The next post in this thread briefly describes each of the class methods/properties. The class is heavily commented.
This class is not very low level, but it does require some care if you are using a few of its more generic methods which allow you to pass/receive data via memory addresses. You must ensure you do not pass parameters that would cause the class to attempt to read or write past the memory space represented by the passed memory pointer. Crashes will occur. Bottom line. If you tell the class that the available memory exists for the pointer you provided, at least that amount of memory better exist.
Why would you use this? Briefly touched in first paragraph above. Consider a graphics program that allows various levels of 'undos'. Instead of keeping potentially large data in the user's memory space, you can store that data to IStreams and recall them on demand, as needed. If you need to back up source data while you are working with it, while making sure it doesn't get deleted by the user/system, store it an IStream and purge it or recall it as needed. Though 2GB is fairly large, it isn't never ending. This class may make it easy to abuse virtual memory, not the intent.
Couple of super-simple examples. Each IStream is provided a key, the key allows you to choose which IStream you want to access. In the examples below, it is assumed the class is declared at module/form level, public to your project. We'll say the an instance of the IStreamManager class is named: cStrmMgr. In each example, we are assuming the stream's seek pointer is at beginning of the stream. To be sure, we can always call the cStrmMgr.SetSeekPointer method
Example: Back up a 2D array and recall it
Code:
' a Long array was sized as: myArray(0 to 49, 0 to 2499) and you want to save it
cStrmMgr.AddStreamFromPointer myArray(0,0), 500000, "Spreadsheet1"
' now lets say you want to recall that data
Dim bytesRead As Long
ReDim myArray(0 to 49, 0 to 2499)
' next line identifies the receiving buffer, bytes to read, variable to return bytes read, where to start reading & what Key
cStrmMgr.ReadStream myArray(0, 0), 500000, bytesRead, 0, "SpreadSheet1"
If bytesRead = 500000 Then ' read all data
Code:
cStrmMgr.AddStreamFromFile "C:\Temp\SomeFile.any", "History"
' Let's say you don't need the history file any longer and just want to purge it
cStrmMgr.ReleaseStream "History"
Code:
cStrmMgr.AddStreamFromStringVB RTB.TextRTF, "RTFbackup"
' and lets say you want to return that stream into a VB string:
Dim sText As String
cStrmMgr.SaveStreamToStringVB sText, "RTFbackup"
Code:
Dim myStream As IUnknown ' or IStream if you have such a TLB
Set myStream = cStrmMgr.StreamObject(myKey) ' get instance of stream
cStrmMgr.ReleaseStream myKey ' detach from class, no longer maintained by the class
Code:
cStrmMgr.AddStreamFromPointer pDIBits, picScanWidth * picHeight, "Undo1"
' And now let's say you want to apply that Undo back to the DIB directly
DIm bytesRead As Long, amoutToRead As Long
amountToRead = picScanWidth * picHeight
cStrmMgr.ReadStream pDIBits, amountToRead, bytesRead, 0, "Undo1"
If bytesRead = amountToRead Then ' read all data
1) 2 GB IStorage object. Trying to exceed this should result in failure
2) 2 GB IStream is largest that can be created and if done, likely to max out IStorage
3) Class does not expose hGlobal address if stream has one. Use GetHGlobalFromStream API
4) Undefined Streams can be added, i.e., length of zero on memory pointer of zero. These can be added to willy-nilly and the stream auto-expands as needed. The 2GB limitation still applies
After downloading the text file, simply remove the .txt extension