COM Structured Storage in .NET
Written by Harry Fairhead   
Wednesday, 03 February 2010
Article Index
COM Structured Storage in .NET
Opening structured storage
Using IStorage
Reading a stream
Reading JPEG data

Reading the JPEG

By comparison with reading the Catalog, reading one of the thumbnail files is very easy. If the files being thumbnailed are JPEGs then the thumbnails are also JPEGs but with 12 bytes of additional data added to the start. So to read the thumbnail corresponding to the element called "1" and store the result in a .JPG file all you have to do is open a new IStream:

IStream ThumbStream;
Is.OpenStream("1", IntPtr.Zero,
(uint)(STGM.READWRITE |
 STGM.SHARE_EXCLUSIVE),
0, out ThumbStream);

Read the first 12 bytes and throw them away:

byte[] bits = new byte[1000];
ThumbStream.Read(bits, 12,
new IntPtr(&count));

Open a BinaryWriter for the new JPEG file:

BinaryWriter JPEGfile =
new BinaryWriter(File.Open("1.jpg",
FileMode.Create));

Finally keep reading blocks of bits and writing them out to disk until there are no more to read:

do
{
ThumbStream.Read(bits, 1000,
new IntPtr(&count));
JPEGfile.Write(bits, 0, (int)count);
} while (count > 0);
JPEGfile.Close();

You now have a standard JPEG file containing the thumbnail stored on disk.

Creating a BitMap

If you want to work with the thumbnail in memory you probably want to convert it into a BitMap. This is relatively easy but first we need to reposition the stream back to the start, or in this case to the 12th byte. This can be done using the IStream Seek function which makes use of the Stream_Seek enumeration to indicate where the location is relative to. There isn't a predefined enumeration for this but it's easy to add one:

enum STREAM_SEEK : int
{
STREAM_SEEK_SET = 0,
STREAM_SEEK_CUR = 1,
STREAM_SEEK_END = 2
};

The call to the Seek function to position the reading location at the 12th byte is:

uint position;
ThumbStream.Seek(12,(int)
STREAM_SEEK.STREAM_SEEK_SET,
new IntPtr(&position));

Notice that we have to cast the Stream_Seek type to int – making it almost not worth using the enumeration. We now need to read in the entire JPEG file into a byte array and to do this we need to know the size of the stream. This can be found using the Stat function:

System.Runtime.InteropServices.
ComTypes.STATSTG fileinfo;
ThumbStream.Stat(out fileinfo, 0);

The StatSTG structure, fileinfo, gives lots of information about the stream including cbSize, which is the stream size in bytes. We can now create a buffer of exactly the right size and read the data in:

byte[] BitmapData=
new byte[fileinfo.cbSize-12];
ThumbStream.Read(BitmapData,
BitmapData.Length,
new IntPtr(&count));

To create a BitMap from this data we first need to convert it into a Stream, a MemoryStream to be precise, and then use the appropriate constructor:

 MemoryStream BitmapStream = new
MemoryStream(BitmapData);
Bitmap Jmap = new Bitmap(BitmapStream);
pictureBox1.Image = Jmap;
}

To display the result the Bitmap has been assigned to a PictureBox's Image property. Similar techniques can be used to save an existing JPEG back into the stream to change the thumbnail.

Conclusion

There are many other structured storage tasks, such as creating, writing and even deleting IStream elements, but the basic ideas are the same as for reading IStream elements. In principle when you investigate a structured storage file the IStreams that you find stored inside should be in standard format. That is, if you find a Tiff stored within a Word document then you can open and read it as if it was a standard Tiff stored in a file all on its own. In practice slight modifications to the stored files often make this more difficult and there is very little official documentation on how the formats have been modified.

Working with structured storage isn't difficult in principle, but in practice it can be frustrating.

To access the code for this project, once you have registered, click on CodeBin.

<ASIN:1572315482>

<ASIN:059652756X>

<ASIN:0130622966>

<ASIN:0321154932>



Last Updated ( Thursday, 04 February 2010 )