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:
- create an compound file (StgCreateStorageEx; IStorage)
- create a "subfolder" in the compound file (CreateStorage)
- 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);
}