compound files in c#

Compound files are an somewhat obsolete concept of storing several files and objects into one compound file (http://en.wikipedia.org/wiki/Compound_File_Binary_Format), old microsoft office documents are compound files.

Sadly there is no managed code for c# to use compound files, so one has to use a lot of native  Com calls, which makes stuff a bit messy.

After the jump I'll show the basic steps to write a simple compound file ...

Steps described in this article:

  1. create an compound file (StgCreateStorageEx; IStorage)
  2. create a "subfolder" in the compound file (CreateStorage)
  3. write different streams to the subfolder and the compound file (IStream)

At the PInvoke wiki (http://www.pinvoke.net/) you can find the necessary API signatures to call unmanaged code from c#. Get the signatures for: STGM, STGC, IEnumSTATSTG, IStream, IStorage and StgCreateStorageEx.

private static void CreateStorageFile(string filename, ref IStorage myIs)
    {
    //error handling for file creation omitted
    int result = StgCreateStorageEx(
                filename,
                STGM.DIRECT | STGM.READWRITE | STGM.SHARE_EXCLUSIVE,
                0,
                0,
                IntPtr.Zero,
                IntPtr.Zero,
                typeof(IStorage).GUID,
                ref myIs);
    }

private static void CreateStorageContainer(
    string containername,
    ref IStorage myIs,
    out IStorage myDataIs)
        {
            myIs.CreateStorage(
                containername,
                STGM.DIRECT | STGM.READWRITE | STGM.SHARE_EXCLUSIVE,
                0,
                0,
                out myDataIs);
        }

private static void WriteGeneric(
    String streamname,
    ref IStorage myIs,
    out IStream myStream,
    byte[] pv,
    uint cb,
    out uint pcbWritten)
        {
            myIs.CreateStream(streamname,
                               STGM.CREATE | STGM.READWRITE | STGM.SHARE_EXCLUSIVE,
                               0, 0,
                               out myStream);
            myStream.Write(pv, cb, out pcbWritten);
            myStream.Commit(STGC.DEFAULT);
        }
#region Helper Functions
public static byte[] StringToByteArray(string str)
        {
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            return enc.GetBytes(str);
        }

        private static string ByteArrayToString(byte[] arr)
        {
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            return enc.GetString(arr);
        }


        public static byte[] StructureToByteArray(object obj)
        {
            int len = Marshal.SizeOf(obj);
            byte[] arr = new byte[len];
            IntPtr ptr = Marshal.AllocHGlobal(len);
            Marshal.StructureToPtr(obj, ptr, true);
            Marshal.Copy(ptr, arr, 0, len);
            Marshal.FreeHGlobal(ptr);
            return arr;
        }
#endregion
public void BuildCompoundFile(string filename, byte[] dataIn1, byte[] dataIn2)
    {
    uint nWritten;
    byte[] a = new byte[0];


    IStorage myIs = null;
    IStorage myDataIs = null;
    IStream myStream = null;

    //Create compound file with filename
    CreateStorageFile(filename, ref myIs);
    //Create a container in the compound file
    CreateStorageContainer("Container1", ref myIs, out myDataIs);
    //Write Data in Container
    WriteGeneric("File1", ref myIs, out myStream, a, 0, out nWritten);
    WriteGeneric("FileDataIn1", ref myIs, out myStream,
        dataIn1, Convert.ToUInt16(dataIn1.Length), out nWritten);
    WriteGeneric("FileDataIn2", ref myIs, out myStream,
        dataIn2, Convert.ToUInt16(dataIn2.Length), out nWritten);
    }
By @Gerald in
Tags : #programming, #c#,