Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Utils/CompoundDocument.cs @ 17401

Last change on this file since 17401 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 28.5 KB
Line 
1/*******************************************************************************
2 * You may amend and distribute as you like, but don't remove this header!
3 *
4 * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
5 * See http://www.codeplex.com/EPPlus for details.
6 *
7 * Copyright (C) 2011  Jan Källman
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
17 * See the GNU Lesser General Public License for more details.
18 *
19 * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
20 * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
21 *
22 * All code and executables are provided "as is" with no warranty either express or implied.
23 * The author accepts no liability for any damage or loss of business that this product may cause.
24 *
25 * Code change notes:
26 *
27 * Author             Change            Date
28 *******************************************************************************
29 * Jan Källman    Added   01-01-2012
30 * Jan Källman      Added compression support 27-03-2012
31 *******************************************************************************/
32using System;
33using System.Collections.Generic;
34using System.Linq;
35using System.Text;
36using System.Runtime.InteropServices;
37using comTypes = System.Runtime.InteropServices.ComTypes;
38using System.IO;
39using System.Security;
40
41namespace OfficeOpenXml.Utils
42{
43#if !MONO
44    internal class CompoundDocument
45    {       
46        internal class StoragePart
47        {
48            public StoragePart()
49            {
50
51            }
52            internal Dictionary<string, StoragePart> SubStorage = new Dictionary<string, StoragePart>();
53            internal Dictionary<string, byte[]> DataStreams = new Dictionary<string, byte[]>();
54        }
55        internal StoragePart Storage = null;
56        internal CompoundDocument()
57        {
58            Storage = new CompoundDocument.StoragePart();
59        }
60        internal CompoundDocument(FileInfo fi)
61        {
62            Read(fi);
63        }
64        internal CompoundDocument(ILockBytes lb)
65        {
66            Read(lb);
67        }
68        internal CompoundDocument(byte[] doc)
69        {
70            Read(doc);
71        }
72        internal void Read(FileInfo fi)
73        {
74            var b = File.ReadAllBytes(fi.FullName);
75            Read(b);
76        }
77        [SecuritySafeCritical]
78        internal void Read(byte[] doc)
79        {
80            ILockBytes lb;
81            var iret = CreateILockBytesOnHGlobal(IntPtr.Zero, true, out lb);
82
83            IntPtr buffer = Marshal.AllocHGlobal(doc.Length);
84            Marshal.Copy(doc, 0, buffer, doc.Length);
85            UIntPtr readSize;
86            lb.WriteAt(0, buffer, doc.Length, out readSize);
87            Marshal.FreeHGlobal(buffer);
88
89            Read(lb);
90        }
91
92        [SecuritySafeCritical]
93        internal void Read(ILockBytes lb)
94        {
95            if (StgIsStorageILockBytes(lb) == 0)
96            {
97                IStorage storage = null;
98                if (StgOpenStorageOnILockBytes(
99                    lb,
100                    null,
101                    STGM.DIRECT | STGM.READ | STGM.SHARE_EXCLUSIVE,
102                    IntPtr.Zero,
103                    0,
104                    out storage) == 0)
105                {
106                    Storage = new StoragePart();
107                    ReadParts(storage, Storage);
108                    Marshal.ReleaseComObject(storage);
109                }
110            }
111            else
112            {
113                throw (new InvalidDataException(string.Format("Part is not a compound document")));
114            }
115        }
116        #region  Compression
117        /// <summary>
118        /// Compression using a run length encoding algorithm.
119        /// See MS-OVBA Section 2.4
120        /// </summary>
121        /// <param name="part">Byte array to decompress</param>
122        /// <returns></returns>
123        internal static byte[] CompressPart(byte[] part)
124        {
125            MemoryStream ms = new MemoryStream(4096);
126            BinaryWriter br = new BinaryWriter(ms);
127            br.Write((byte)1);
128
129            int compStart = 1;
130            int compEnd = 4098;
131            int decompStart = 0;
132            int decompEnd = part.Length < 4096 ? part.Length : 4096;
133
134            while (decompStart < decompEnd && compStart < compEnd)
135            {
136                byte[] chunk = CompressChunk(part, ref decompStart);
137                ushort header;
138                if (chunk == null || chunk.Length == 0)
139                {
140                    header = 4096 | 0x600;  //B=011 A=0
141                }
142                else
143                {
144                    header = (ushort)(((chunk.Length - 1) & 0xFFF));
145                    header |= 0xB000;   //B=011 A=1
146                    br.Write(header);
147                    br.Write(chunk);                   
148                }
149                decompEnd = part.Length < decompStart + 4096 ? part.Length : decompStart+4096;
150            }
151
152           
153            br.Flush();
154            return ms.ToArray();       
155        }
156        private static byte[] CompressChunk(byte[] buffer, ref int startPos)
157        {
158            var comprBuffer=new byte[4096];
159            int flagPos = 0;
160            int cPos=1;
161            int dPos = startPos;
162            int dEnd=startPos+4096 < buffer.Length? startPos+4096 : buffer.Length;
163            while(dPos<dEnd)
164            {
165                byte tokenFlags = 0;
166                for (int i = 0; i < 8; i++)
167                {
168                    if (dPos - startPos > 0)
169                    {
170                        int bestCandidate = -1;
171                        int bestLength = 0;
172                        int candidate = dPos - 1;
173                        int bitCount = GetLengthBits(dPos-startPos);
174                        int bits = (16 - bitCount);
175                        ushort lengthMask = (ushort)((0xFFFF) >> bits);
176
177                        while (candidate >= startPos)
178                        {
179                            if (buffer[candidate] == buffer[dPos])
180                            {
181                                int length = 1;
182
183                                while (buffer.Length > dPos + length && buffer[candidate + length] == buffer[dPos + length] && length < lengthMask && dPos+length < dEnd)
184                                {
185                                    length++;
186                                }
187                                if (length > bestLength)
188                                {
189                                    bestCandidate = candidate;
190                                    bestLength = length;
191                                    if (bestLength == lengthMask)
192                                    {
193                                        break;
194                                    }
195                                }
196                            }
197                            candidate--;
198                        }
199                        if (bestLength >= 3)    //Copy token
200                        {
201                            tokenFlags |= (byte)(1 << i);
202
203                            UInt16 offsetMask = (ushort)~lengthMask;
204                            ushort token = (ushort)(((ushort)(dPos - (bestCandidate+1))) << (bitCount) | (ushort)(bestLength - 3));
205                            Array.Copy(BitConverter.GetBytes(token), 0, comprBuffer, cPos, 2);
206                            dPos = dPos + bestLength;
207                            cPos += 2;
208                            //SetCopy Token                       
209                        }
210                        else
211                        {
212                            comprBuffer[cPos++] = buffer[dPos++];
213                        }
214                    }
215                   
216                    else
217                    {
218                        comprBuffer[cPos++] = buffer[dPos++];
219                    }
220                    if (dPos >= dEnd) break;
221                }
222                comprBuffer[flagPos] = tokenFlags;
223                flagPos = cPos++;
224            }
225            var ret = new byte[cPos - 1];
226            Array.Copy(comprBuffer, ret, ret.Length);
227            startPos = dEnd;
228            return ret;
229        }
230        internal static byte[] DecompressPart(byte[] part)
231        {
232            return DecompressPart(part, 0);
233        }
234        /// <summary>
235        /// Decompression using a run length encoding algorithm.
236        /// See MS-OVBA Section 2.4
237        /// </summary>
238        /// <param name="part">Byte array to decompress</param>
239        /// <param name="startPos"></param>
240        /// <returns></returns>
241        internal static byte[] DecompressPart(byte[] part, int startPos)
242        {
243
244            if (part[startPos] != 1)
245            {
246                return null;
247            }
248            MemoryStream ms = new MemoryStream(4096);
249            int compressPos = startPos + 1;
250            while(compressPos < part.Length-1)
251            {
252                DecompressChunk(ms, part, ref compressPos);
253            }
254            return ms.ToArray();
255        }
256        private static void DecompressChunk(MemoryStream ms, byte[] compBuffer, ref int pos)
257        {
258            ushort header = BitConverter.ToUInt16(compBuffer, pos);
259            int  decomprPos=0;
260            byte[] buffer = new byte[4198]; //Add an extra 100 byte. Some workbooks have overflowing worksheets.
261            int size = (int)(header & 0xFFF)+3;
262            int endPos = pos+size;
263            int a = (int)(header & 0x7000) >> 12;
264            int b = (int)(header & 0x8000) >> 15;
265            pos += 2;
266            if (b == 1) //Compressed chunk
267            {
268                while (pos < compBuffer.Length && pos < endPos)
269                {
270                    //Decompress token
271                    byte token = compBuffer[pos++];
272                    if (pos >= endPos)
273                        break;
274                    for (int i = 0; i < 8; i++)
275                    {
276                        //Literal token
277                        if ((token & (1 << i)) == 0)
278                        {
279                            ms.WriteByte(compBuffer[pos]); 
280                            buffer[decomprPos++] = compBuffer[pos++];
281                        }
282                        else //copy token
283                        {
284                            var t = BitConverter.ToUInt16(compBuffer, pos);
285                            int bitCount = GetLengthBits(decomprPos);
286                            int bits = (16 - bitCount);
287                            ushort lengthMask = (ushort)((0xFFFF) >> bits);
288                            UInt16 offsetMask = (ushort)~lengthMask;
289                            var length = (lengthMask & t) + 3;
290                            var offset = (offsetMask & t) >> (bitCount);
291                            int source = decomprPos - offset - 1;
292                            if (decomprPos + length >= buffer.Length)
293                            {
294                                // Be lenient on decompression, so extend our decompression
295                                // buffer. Excel generated VBA projects do encounter this issue.
296                                // One would think (not surprisingly that the VBA project spec)
297                                // over emphasizes the size restrictions of a DecompressionChunk.
298                                var largerBuffer = new byte[buffer.Length + 4098];
299                                Array.Copy(buffer, largerBuffer, decomprPos);
300                                buffer = largerBuffer;
301                            }
302                           
303                            // Even though we've written to the MemoryStream,
304                            // We still should decompress the token into this buffer
305                            // in case a later token needs to use the bytes we're
306                            // about to decompress.
307                            for (int c = 0; c < length; c++)
308                            {
309                                ms.WriteByte(buffer[source]); //Must copy byte-wise because copytokens can overlap compressed buffer.
310                                buffer[decomprPos++] = buffer[source++];
311                            }
312
313                            pos += 2;
314
315                        }
316                        if (pos >= endPos)
317                            break;
318                    }
319                }
320                return;
321            }
322            else //Raw chunk
323            {
324                ms.Write(compBuffer, pos, size);
325                pos += size;
326                return;
327            }
328        }
329        private static int GetLengthBits(int decompPos)
330        {
331            if (decompPos <= 16)
332            {
333                return 12;
334            }
335            else if (decompPos <= 32)
336            {
337                return 11;
338            }
339            else if (decompPos <= 64)
340            {
341                return 10;
342            }
343            else if (decompPos <= 128)
344            {
345                return 9;
346            }
347            else if (decompPos <= 256)
348            {
349                return 8;
350            }
351            else if (decompPos <= 512)
352            {
353                return 7;
354            }
355            else if (decompPos <= 1024)
356            {
357                return 6;
358            }
359            else if (decompPos <= 2048)
360            {
361                return 5;
362            }
363            else if (decompPos <= 4096)
364            {
365                return 4;
366            }
367            else
368            {
369                //We should never end up here, but if so this is the formula to calculate the bits...
370                return 12 - (int)Math.Truncate(Math.Log(decompPos - 1 >> 4, 2) + 1);
371            }
372        }
373        #endregion
374        #region "API declare"
375        [ComImport]
376        [Guid("0000000d-0000-0000-C000-000000000046")]
377        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
378        internal interface IEnumSTATSTG
379        {
380            // The user needs to allocate an STATSTG array whose size is celt.
381            [PreserveSig]
382            uint Next(
383                uint celt,
384                [MarshalAs(UnmanagedType.LPArray), Out]
385            System.Runtime.InteropServices.ComTypes.STATSTG[] rgelt,
386                out uint pceltFetched
387            );
388
389            void Skip(uint celt);
390
391            void Reset();
392
393            [return: MarshalAs(UnmanagedType.Interface)]
394            IEnumSTATSTG Clone();
395        }
396
397        [ComImport]
398        [Guid("0000000b-0000-0000-C000-000000000046")]
399        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
400        interface IStorage
401        {
402            void CreateStream(
403                /* [string][in] */ string pwcsName,
404                /* [in] */ uint grfMode,
405                /* [in] */ uint reserved1,
406                /* [in] */ uint reserved2,
407                /* [out] */ out comTypes.IStream ppstm);
408
409            void OpenStream(
410                /* [string][in] */ string pwcsName,
411                /* [unique][in] */ IntPtr reserved1,
412                /* [in] */ uint grfMode,
413                /* [in] */ uint reserved2,
414                /* [out] */ out comTypes.IStream ppstm);
415
416            void CreateStorage(
417                /* [string][in] */ string pwcsName,
418                /* [in] */ uint grfMode,
419                /* [in] */ uint reserved1,
420                /* [in] */ uint reserved2,
421                /* [out] */ out IStorage ppstg);
422
423            void OpenStorage(
424                /* [string][unique][in] */ string pwcsName,
425                /* [unique][in] */ IStorage pstgPriority,
426                /* [in] */ STGM grfMode,
427                /* [unique][in] */ IntPtr snbExclude,
428                /* [in] */ uint reserved,
429                /* [out] */ out IStorage ppstg);
430
431            void CopyTo(
432                [InAttribute] uint ciidExclude,
433                [InAttribute] Guid[] rgiidExclude,
434                [InAttribute] IntPtr snbExclude,
435                [InAttribute] IStorage pstgDest
436            );
437
438            void MoveElementTo(
439                /* [string][in] */ string pwcsName,
440                /* [unique][in] */ IStorage pstgDest,
441                /* [string][in] */ string pwcsNewName,
442                /* [in] */ uint grfFlags);
443
444            void Commit(
445                /* [in] */ uint grfCommitFlags);
446
447            void Revert();
448
449            void EnumElements(
450                /* [in] */ uint reserved1,
451                /* [size_is][unique][in] */ IntPtr reserved2,
452                /* [in] */ uint reserved3,
453                /* [out] */ out IEnumSTATSTG ppenum);
454
455            void DestroyElement(
456                /* [string][in] */ string pwcsName);
457
458            void RenameElement(
459                /* [string][in] */ string pwcsOldName,
460                /* [string][in] */ string pwcsNewName);
461
462            void SetElementTimes(
463                /* [string][unique][in] */ string pwcsName,
464                /* [unique][in] */ System.Runtime.InteropServices.ComTypes.FILETIME pctime,
465                /* [unique][in] */ System.Runtime.InteropServices.ComTypes.FILETIME patime,
466                /* [unique][in] */ System.Runtime.InteropServices.ComTypes.FILETIME pmtime);
467
468            void SetClass(
469                /* [in] */ Guid clsid);
470
471            void SetStateBits(
472                /* [in] */ uint grfStateBits,
473                /* [in] */ uint grfMask);
474
475            void Stat(
476                /* [out] */ out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg,
477                /* [in] */ uint grfStatFlag);
478
479        }
480        [ComVisible(false)]
481        [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("0000000A-0000-0000-C000-000000000046")]
482        internal interface ILockBytes
483        {
484            void ReadAt(long ulOffset, System.IntPtr pv, int cb, out UIntPtr pcbRead);
485            void WriteAt(long ulOffset, System.IntPtr pv, int cb, out UIntPtr pcbWritten);
486            void Flush();
487            void SetSize(long cb);
488            void LockRegion(long libOffset, long cb, int dwLockType);
489            void UnlockRegion(long libOffset, long cb, int dwLockType);
490            void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag);
491        }
492        [Flags]
493        internal enum STGM : int
494        {
495            DIRECT = 0x00000000,
496            TRANSACTED = 0x00010000,
497            SIMPLE = 0x08000000,
498            READ = 0x00000000,
499            WRITE = 0x00000001,
500            READWRITE = 0x00000002,
501            SHARE_DENY_NONE = 0x00000040,
502            SHARE_DENY_READ = 0x00000030,
503            SHARE_DENY_WRITE = 0x00000020,
504            SHARE_EXCLUSIVE = 0x00000010,
505            PRIORITY = 0x00040000,
506            DELETEONRELEASE = 0x04000000,
507            NOSCRATCH = 0x00100000,
508            CREATE = 0x00001000,
509            CONVERT = 0x00020000,
510            FAILIFTHERE = 0x00000000,
511            NOSNAPSHOT = 0x00200000,
512            DIRECT_SWMR = 0x00400000,
513        }
514
515        internal enum STATFLAG : uint
516        {
517            STATFLAG_DEFAULT = 0,
518            STATFLAG_NONAME = 1,
519            STATFLAG_NOOPEN = 2
520        }
521
522        internal enum STGTY : int
523        {
524            STGTY_STORAGE = 1,
525            STGTY_STREAM = 2,
526            STGTY_LOCKBYTES = 3,
527            STGTY_PROPERTY = 4
528        }
529
530        [DllImport("ole32.dll")]
531        private static extern int StgIsStorageFile(
532            [MarshalAs(UnmanagedType.LPWStr)] string pwcsName);
533        [DllImport("ole32.dll")]
534        private static extern int StgIsStorageILockBytes(
535            ILockBytes plkbyt);
536
537
538        [DllImport("ole32.dll")]
539        static extern int StgOpenStorage(
540            [MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
541            IStorage pstgPriority,
542            STGM grfMode,
543            IntPtr snbExclude,
544            uint reserved,
545            out IStorage ppstgOpen);
546
547        [DllImport("ole32.dll")]
548        static extern int StgOpenStorageOnILockBytes(
549            ILockBytes plkbyt,
550            IStorage pStgPriority,
551            STGM grfMode,
552            IntPtr snbEnclude,
553            uint reserved,
554            out IStorage ppstgOpen);
555        [DllImport("ole32.dll")]
556        static extern int CreateILockBytesOnHGlobal(
557            IntPtr hGlobal,
558            bool fDeleteOnRelease,
559            out ILockBytes ppLkbyt);
560
561        [DllImport("ole32.dll")]
562        static extern int StgCreateDocfileOnILockBytes(ILockBytes plkbyt, STGM grfMode, int reserved, out IStorage ppstgOpen);
563
564        #endregion
565        [SecuritySafeCritical]
566        internal static int IsStorageFile(string Name)
567        {
568            return StgIsStorageFile(Name);
569        }
570        [SecuritySafeCritical]
571        internal static int IsStorageILockBytes(ILockBytes lb)
572        {
573            return StgIsStorageILockBytes(lb);
574        }
575        [SecuritySafeCritical]
576        internal static ILockBytes GetLockbyte(MemoryStream stream)
577        {
578            ILockBytes lb;
579            var iret = CreateILockBytesOnHGlobal(IntPtr.Zero, true, out lb);
580            byte[] docArray = stream.GetBuffer();
581
582            IntPtr buffer = Marshal.AllocHGlobal(docArray.Length);
583            Marshal.Copy(docArray, 0, buffer, docArray.Length);
584            UIntPtr readSize;
585            lb.WriteAt(0, buffer, docArray.Length, out readSize);
586            Marshal.FreeHGlobal(buffer);
587
588            return lb;
589        }
590        [SecuritySafeCritical]
591        private MemoryStream ReadParts(IStorage storage, StoragePart storagePart)
592        {
593            MemoryStream ret = null;
594            comTypes.STATSTG statstg;
595
596            storage.Stat(out statstg, (uint)STATFLAG.STATFLAG_DEFAULT);
597
598            IEnumSTATSTG pIEnumStatStg = null;
599            storage.EnumElements(0, IntPtr.Zero, 0, out pIEnumStatStg);
600
601            comTypes.STATSTG[] regelt = { statstg };
602            uint fetched = 0;
603            uint res = pIEnumStatStg.Next(1, regelt, out fetched);
604
605            //if (regelt[0].pwcsName == "DataSpaces")
606            //{
607            //    PrintStorage(storage, regelt[0],"");
608            //}
609            while (res != 1)
610            {
611                foreach (var item in regelt)
612                {
613                    if (item.type == 1)
614                    {
615                        IStorage subStorage;
616                        storage.OpenStorage(item.pwcsName, null, STGM.DIRECT | STGM.READ | STGM.SHARE_EXCLUSIVE, IntPtr.Zero, 0, out subStorage);
617                        StoragePart subStoragePart=new StoragePart();
618                        storagePart.SubStorage.Add(item.pwcsName, subStoragePart);
619                        ReadParts(subStorage, subStoragePart);
620                    }
621                    else
622                    {
623                        storagePart.DataStreams.Add(item.pwcsName, GetOleStream(storage, item));                   
624                    }
625                }
626                res = pIEnumStatStg.Next(1, regelt, out fetched);
627            }
628            Marshal.ReleaseComObject(pIEnumStatStg);
629            return ret;
630        }
631        // Help method to print a storage part binary to c:\temp
632        //private void PrintStorage(IStorage storage, System.Runtime.InteropServices.ComTypes.STATSTG sTATSTG, string topName)
633        //{
634        //    IStorage ds;
635        //    if (topName.Length > 0)
636        //    {
637        //        topName = topName[0] < 'A' ? topName.Substring(1, topName.Length - 1) : topName;
638        //    }
639        //    storage.OpenStorage(sTATSTG.pwcsName,
640        //        null,
641        //        (uint)(STGM.DIRECT | STGM.READ | STGM.SHARE_EXCLUSIVE),
642        //        IntPtr.Zero,
643        //        0,
644        //        out ds);
645
646        //    System.Runtime.InteropServices.ComTypes.STATSTG statstgSub;
647        //    ds.Stat(out statstgSub, (uint)STATFLAG.STATFLAG_DEFAULT);
648
649        //    IEnumSTATSTG pIEnumStatStgSub = null;
650        //    System.Runtime.InteropServices.ComTypes.STATSTG[] regeltSub = { statstgSub };
651        //    ds.EnumElements(0, IntPtr.Zero, 0, out pIEnumStatStgSub);
652
653        //    uint fetched = 0;
654        //    while (pIEnumStatStgSub.Next(1, regeltSub, out fetched) == 0)
655        //    {
656        //        string sName = regeltSub[0].pwcsName[0] < 'A' ? regeltSub[0].pwcsName.Substring(1, regeltSub[0].pwcsName.Length - 1) : regeltSub[0].pwcsName;
657        //        if (regeltSub[0].type == 1)
658        //        {
659        //            PrintStorage(ds, regeltSub[0], topName + sName + "_");
660        //        }
661        //        else if(regeltSub[0].type==2)
662        //        {
663        //            File.WriteAllBytes(@"c:\temp\" + topName + sName + ".bin", GetOleStream(ds, regeltSub[0]));
664        //        }
665        //    }
666        //}    }
667        /// <summary>
668        /// Read the stream and return it as a byte-array
669        /// </summary>
670        /// <param name="storage"></param>
671        /// <param name="statstg"></param>
672        /// <returns></returns>
673        [SecuritySafeCritical]
674        private byte[] GetOleStream(IStorage storage, comTypes.STATSTG statstg)
675        {
676            comTypes.IStream pIStream;
677            storage.OpenStream(statstg.pwcsName,
678               IntPtr.Zero,
679               (uint)(STGM.READ | STGM.SHARE_EXCLUSIVE),
680               0,
681               out pIStream);
682
683            byte[] data = new byte[statstg.cbSize];
684            pIStream.Read(data, (int)statstg.cbSize, IntPtr.Zero);
685            Marshal.ReleaseComObject(pIStream);
686
687            return data;
688        }
689
690        [SecuritySafeCritical]
691        internal byte[] Save()
692        {
693            ILockBytes lb;
694            var iret = CreateILockBytesOnHGlobal(IntPtr.Zero, true, out lb);
695
696            IStorage storage = null;
697            byte[] ret = null;
698
699            //Create the document in-memory
700            if (StgCreateDocfileOnILockBytes(lb,
701                    STGM.CREATE | STGM.READWRITE | STGM.SHARE_EXCLUSIVE | STGM.TRANSACTED,
702                    0,
703                    out storage)==0)
704            {
705                foreach(var store in this.Storage.SubStorage)
706                {
707                    CreateStore(store.Key, store.Value, storage);
708                }
709                CreateStreams(this.Storage, storage);                               
710                lb.Flush();
711               
712                //Now copy the unmanaged stream to a byte array --> memory stream
713                var statstg = new comTypes.STATSTG();
714                lb.Stat(out statstg, 0);
715                int size = (int)statstg.cbSize;
716                IntPtr buffer = Marshal.AllocHGlobal(size);
717                UIntPtr readSize;
718                ret=new byte[size];
719                lb.ReadAt(0, buffer, size, out readSize);
720                Marshal.Copy(buffer, ret, 0, size);
721                Marshal.FreeHGlobal(buffer);
722            }
723            Marshal.ReleaseComObject(storage);
724            Marshal.ReleaseComObject(lb);
725
726            return ret;
727        }
728
729        private void CreateStore(string name, StoragePart subStore, IStorage storage)
730        {
731            IStorage subStorage;
732            storage.CreateStorage(name, (uint)(STGM.CREATE | STGM.WRITE | STGM.DIRECT | STGM.SHARE_EXCLUSIVE), 0, 0, out subStorage);
733            storage.Commit(0);
734            foreach (var store in subStore.SubStorage)
735            {
736                CreateStore(store.Key, store.Value, subStorage);
737            }
738           
739            CreateStreams(subStore, subStorage);
740        }
741
742        private void CreateStreams(StoragePart subStore, IStorage subStorage)
743        {
744            foreach (var ds in subStore.DataStreams)
745            {
746                comTypes.IStream stream;
747                subStorage.CreateStream(ds.Key, (uint)(STGM.CREATE | STGM.WRITE | STGM.DIRECT | STGM.SHARE_EXCLUSIVE), 0, 0, out stream);
748                stream.Write(ds.Value, ds.Value.Length, IntPtr.Zero);
749            }
750            subStorage.Commit(0);
751        }
752     
753    }
754#endif
755}
Note: See TracBrowser for help on using the repository browser.