Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3044_variableScaling/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/VBA/ExcelVbaProject.cs @ 17387

Last change on this file since 17387 was 12463, checked in by ascheibe, 10 years ago

#2399 fixed EPPlus so that it compiles on Linux

File size: 47.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 * All code and executables are provided "as is" with no warranty either express or implied.
10 * The author accepts no liability for any damage or loss of business that this product may cause.
11 *
12 * If you want to understand this code have a look at the Office VBA File Format Structure Specification (MS-OVBA.PDF) or
13 * http://msdn.microsoft.com/en-us/library/cc313094(v=office.12).aspx
14 *
15 * * Code change notes:
16 *
17 * Author             Change            Date
18 *******************************************************************************
19 * Jan Källman    Added   26-MAR-2012
20 *******************************************************************************/
21using System;
22using System.Collections.Generic;
23using System.Globalization;
24using System.Linq;
25using System.Text;
26using System.IO;
27using OfficeOpenXml.Utils;
28using System.Security.Cryptography.Pkcs;
29using System.Security.Cryptography.X509Certificates;
30using System.Security.Cryptography;
31using System.Text.RegularExpressions;
32
33namespace OfficeOpenXml.VBA
34{
35    /// <summary>
36    /// Represents the VBA project part of the package
37    /// </summary>
38    public class ExcelVbaProject
39    {
40        const string schemaRelVba = "http://schemas.microsoft.com/office/2006/relationships/vbaProject";
41        internal const string PartUri = @"/xl/vbaProject.bin";
42        #region Classes & Enums
43        /// <summary>
44        /// Type of system where the VBA project was created.
45        /// </summary>
46        public enum eSyskind
47        {
48            Win16 = 0,
49            Win32 = 1,
50            Macintosh = 2,
51            Win64 = 3
52        }
53
54        #endregion
55        internal ExcelVbaProject(ExcelWorkbook wb)
56        {
57            _wb = wb;
58            _pck = _wb._package.Package;
59            References = new ExcelVbaReferenceCollection();
60            Modules = new ExcelVbaModuleCollection(this);
61            var rel = _wb.Part.GetRelationshipsByType(schemaRelVba).FirstOrDefault();
62            if (rel != null)
63            {
64                Uri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
65                Part = _pck.GetPart(Uri);
66#if !MONO
67                GetProject();               
68#endif
69            }
70            else
71            {
72                Lcid = 0;
73                Part = null;
74            }
75        }
76        internal ExcelWorkbook _wb;
77        internal Packaging.ZipPackage _pck;
78        #region Dir Stream Properties
79        /// <summary>
80        /// System kind. Default Win32.
81        /// </summary>
82        public eSyskind SystemKind { get; set; }
83        /// <summary>
84        /// Name of the project
85        /// </summary>
86        public string Name { get; set; }
87        /// <summary>
88        /// A description of the project
89        /// </summary>
90        public string Description { get; set; }
91        /// <summary>
92        /// A helpfile
93        /// </summary>
94        public string HelpFile1 { get; set; }
95        /// <summary>
96        /// Secondary helpfile
97        /// </summary>
98        public string HelpFile2 { get; set; }
99        /// <summary>
100        /// Context if refering the helpfile
101        /// </summary>
102        public int HelpContextID { get; set; }
103        /// <summary>
104        /// Conditional compilation constants
105        /// </summary>
106        public string Constants { get; set; }
107        /// <summary>
108        /// Codepage for encoding. Default is current regional setting.
109        /// </summary>
110        public int CodePage { get; internal set; }
111        internal int LibFlags { get; set; }
112        internal int MajorVersion { get; set; }
113        internal int MinorVersion { get; set; }
114        internal int Lcid { get; set; }
115        internal int LcidInvoke { get; set; }
116        internal string ProjectID { get; set; }
117        internal string ProjectStreamText { get; set; }
118        /// <summary>
119        /// Project references
120        /// </summary>       
121        public ExcelVbaReferenceCollection References { get; set; }
122        /// <summary>
123        /// Code Modules (Modules, classes, designer code)
124        /// </summary>
125        public ExcelVbaModuleCollection Modules { get; set; }
126        ExcelVbaSignature _signature = null;
127        /// <summary>
128        /// The digital signature
129        /// </summary>
130        public ExcelVbaSignature Signature
131        {
132            get
133            {
134                if (_signature == null)
135                {
136                    _signature=new ExcelVbaSignature(Part);
137                }
138                return _signature;
139            }
140        }
141        ExcelVbaProtection _protection=null;
142        /// <summary>
143        /// VBA protection
144        /// </summary>
145        public ExcelVbaProtection Protection
146        {
147            get
148            {
149                if (_protection == null)
150                {
151                    _protection = new ExcelVbaProtection(this);
152                }
153                return _protection;
154            }
155        }
156        #endregion
157#if !MONO
158        #region Read Project
159        private void GetProject()
160        {
161
162            var stream = Part.GetStream();
163            byte[] vba;
164            vba = new byte[stream.Length];
165            stream.Read(vba, 0, (int)stream.Length);
166            Document = new CompoundDocument(vba);
167
168            ReadDirStream();
169            ProjectStreamText = Encoding.GetEncoding(CodePage).GetString(Document.Storage.DataStreams["PROJECT"]);
170            ReadModules();
171            ReadProjectProperties();
172        }
173        private void ReadModules()
174        {
175            foreach (var modul in Modules)
176            {
177                var stream = Document.Storage.SubStorage["VBA"].DataStreams[modul.streamName];
178                var byCode = CompoundDocument.DecompressPart(stream, (int)modul.ModuleOffset);
179                string code = Encoding.GetEncoding(CodePage).GetString(byCode);
180                int pos=0;
181                while(pos+9<code.Length && code.Substring(pos,9)=="Attribute")
182                {
183                    int linePos=code.IndexOf("\r\n",pos);
184                    string[] lineSplit;
185                    if(linePos>0)
186                    {
187                        lineSplit = code.Substring(pos + 9, linePos - pos - 9).Split('=');
188                    }
189                    else
190                    {
191                        lineSplit=code.Substring(pos+9).Split(new char[]{'='},1);
192                    }
193                    if (lineSplit.Length > 1)
194                    {
195                        lineSplit[1] = lineSplit[1].Trim();
196                        var attr =
197                            new ExcelVbaModuleAttribute()
198                        {
199                            Name = lineSplit[0].Trim(),
200                            DataType = lineSplit[1].StartsWith("\"") ? eAttributeDataType.String : eAttributeDataType.NonString,
201                            Value = lineSplit[1].StartsWith("\"") ? lineSplit[1].Substring(1, lineSplit[1].Length - 2) : lineSplit[1]
202                        };
203                        modul.Attributes._list.Add(attr);
204                    }
205                    pos = linePos + 2;
206                }
207                modul.Code=code.Substring(pos);
208            }
209        }
210
211        private void ReadProjectProperties()
212        {
213            _protection = new ExcelVbaProtection(this);
214            string prevPackage = "";
215            var lines = Regex.Split(ProjectStreamText, "\r\n");
216            foreach (string line in lines)
217            {
218                if (line.StartsWith("["))
219                {
220
221                }
222                else
223                {
224                    var split = line.Split('=');
225                    if (split.Length > 1 && split[1].Length > 1 && split[1].StartsWith("\"")) //Remove any double qouates
226                    {
227                        split[1] = split[1].Substring(1, split[1].Length - 2);
228                    }
229                    switch (split[0])
230                    {
231                        case "ID":
232                            ProjectID = split[1];
233                            break;
234                        case "Document":
235                            string mn = split[1].Substring(0, split[1].IndexOf("/&H"));
236                            Modules[mn].Type = eModuleType.Document;
237                            break;
238                        case "Package":
239                            prevPackage = split[1];
240                            break;
241                        case "BaseClass":
242                            Modules[split[1]].Type = eModuleType.Designer;
243                            Modules[split[1]].ClassID = prevPackage;
244                            break;
245                        case "Module":
246                            Modules[split[1]].Type = eModuleType.Module;
247                            break;
248                        case "Class":
249                            Modules[split[1]].Type = eModuleType.Class;
250                            break;
251                        case "HelpFile":
252                        case "Name":
253                        case "HelpContextID":
254                        case "Description":
255                        case "VersionCompatible32":
256                            break;
257                        //393222000"
258                        case "CMG":
259                            byte[] cmg = Decrypt(split[1]);
260                            _protection.UserProtected = (cmg[0] & 1) != 0;
261                            _protection.HostProtected = (cmg[0] & 2) != 0;
262                            _protection.VbeProtected = (cmg[0] & 4) != 0;
263                            break;
264                        case "DPB":
265                            byte[] dpb = Decrypt(split[1]);
266                            if (dpb.Length >= 28)
267                            {
268                                byte reserved = dpb[0];
269                                var flags = new byte[3];
270                                Array.Copy(dpb, 1, flags, 0, 3);
271                                var keyNoNulls = new byte[4];
272                                _protection.PasswordKey = new byte[4];
273                                Array.Copy(dpb, 4, keyNoNulls, 0, 4);
274                                var hashNoNulls = new byte[20];
275                                _protection.PasswordHash = new byte[20];
276                                Array.Copy(dpb, 8, hashNoNulls, 0, 20);
277                                //Handle 0x00 bitwise 2.4.4.3
278                                for (int i = 0; i < 24; i++)
279                                {
280                                    int bit = 128 >> (int)((i % 8));
281                                    if (i < 4)
282                                    {
283                                        if ((int)(flags[0] & bit) == 0)
284                                        {
285                                            _protection.PasswordKey[i] = 0;
286                                        }
287                                        else
288                                        {
289                                            _protection.PasswordKey[i] = keyNoNulls[i];
290                                        }
291                                    }
292                                    else
293                                    {
294                                        int flagIndex = (i - i % 8) / 8;
295                                        if ((int)(flags[flagIndex] & bit) == 0)
296                                        {
297                                            _protection.PasswordHash[i - 4] = 0;
298                                        }
299                                        else
300                                        {
301                                            _protection.PasswordHash[i - 4] = hashNoNulls[i - 4];
302                                        }
303                                    }
304                                }
305                            }
306                            break;
307                        case "GC":
308                            _protection.VisibilityState = Decrypt(split[1])[0] == 0xFF;
309
310                            break;
311                    }
312                }
313            }
314        }
315
316        /// <summary>
317        /// 2.4.3.3 Decryption
318        /// </summary>
319        /// <param name="value">Byte hex string</param>
320        /// <returns>The decrypted value</returns>
321        private byte[] Decrypt(string value)
322        {
323            byte[] enc = GetByte(value);
324            byte[] dec = new byte[(value.Length - 1)];
325            byte seed, version, projKey, ignoredLength;
326            seed = enc[0];
327            dec[0] = (byte)(enc[1] ^ seed);
328            dec[1] = (byte)(enc[2] ^ seed);
329            for (int i = 2; i < enc.Length - 1; i++)
330            {
331                dec[i] = (byte)(enc[i + 1] ^ (enc[i - 1] + dec[i - 1]));
332            }
333            version = dec[0];
334            projKey = dec[1];
335            ignoredLength = (byte)((seed & 6) / 2);
336            int datalength = BitConverter.ToInt32(dec, ignoredLength + 2);
337            var data = new byte[datalength];
338            Array.Copy(dec, 6 + ignoredLength, data, 0, datalength);
339            return data;
340        }
341        /// <summary>
342        /// 2.4.3.2 Encryption
343        /// </summary>
344        /// <param name="value"></param>
345        /// <returns>Byte hex string</returns>
346        private string Encrypt(byte[] value)
347        {
348            byte[] seed = new byte[1];
349            var rn = RandomNumberGenerator.Create();
350            rn.GetBytes(seed);
351            BinaryWriter br = new BinaryWriter(new MemoryStream());
352            byte[] enc = new byte[value.Length + 10];
353            enc[0] = seed[0];
354            enc[1] = (byte)(2 ^ seed[0]);
355
356            byte projKey = 0;
357
358            foreach (var c in ProjectID)
359            {
360                projKey += (byte)c;
361            }
362            enc[2] = (byte)(projKey ^ seed[0]);
363            var ignoredLength = (seed[0] & 6) / 2;
364            for (int i = 0; i < ignoredLength; i++)
365            {
366                br.Write(seed[0]);
367            }
368            br.Write(value.Length);
369            br.Write(value);
370
371            int pos = 3;
372            byte pb = projKey;
373            foreach (var b in ((MemoryStream)br.BaseStream).ToArray())
374            {
375                enc[pos] = (byte)(b ^ (enc[pos - 2] + pb));
376                pos++;
377                pb = b;
378            }
379
380            return GetString(enc, pos - 1);
381        }
382        private string GetString(byte[] value, int max)
383        {
384            string ret = "";
385            for (int i = 0; i <= max; i++)
386            {
387                if (value[i] < 16)
388                {
389                    ret += "0" + value[i].ToString("x");
390                }
391                else
392                {
393                    ret += value[i].ToString("x");
394                }
395            }
396            return ret.ToUpper(CultureInfo.InvariantCulture);
397        }
398        private byte[] GetByte(string value)
399        {
400            byte[] ret = new byte[value.Length / 2];
401            for (int i = 0; i < ret.Length; i++)
402            {
403                ret[i] = byte.Parse(value.Substring(i * 2, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
404            }
405            return ret;
406        }
407        private void ReadDirStream()
408        {
409            byte[] dir = CompoundDocument.DecompressPart(Document.Storage.SubStorage["VBA"].DataStreams["dir"]);
410            MemoryStream ms = new MemoryStream(dir);
411            BinaryReader br = new BinaryReader(ms);
412            ExcelVbaReference currentRef = null;
413            string referenceName = "";
414            ExcelVBAModule currentModule = null;
415            bool terminate = false;
416            while (br.BaseStream.Position < br.BaseStream.Length && terminate == false)
417            {
418                ushort id = br.ReadUInt16();
419                uint size = br.ReadUInt32();
420                switch (id)
421                {
422                    case 0x01:
423                        SystemKind = (eSyskind)br.ReadUInt32();
424                        break;
425                    case 0x02:
426                        Lcid = (int)br.ReadUInt32();
427                        break;
428                    case 0x03:
429                        CodePage = (int)br.ReadUInt16();
430                        break;
431                    case 0x04:
432                        Name = GetString(br, size);
433                        break;
434                    case 0x05:
435                        Description = GetUnicodeString(br, size);
436                        break;
437                    case 0x06:
438                        HelpFile1 = GetString(br, size);
439                        break;
440                    case 0x3D:
441                        HelpFile2 = GetString(br, size);
442                        break;
443                    case 0x07:
444                        HelpContextID = (int)br.ReadUInt32();
445                        break;
446                    case 0x08:
447                        LibFlags = (int)br.ReadUInt32();
448                        break;
449                    case 0x09:
450                        MajorVersion = (int)br.ReadUInt32();
451                        MinorVersion = (int)br.ReadUInt16();
452                        break;
453                    case 0x0C:
454                        Constants = GetUnicodeString(br, size);
455                        break;
456                    case 0x0D:
457                        uint sizeLibID = br.ReadUInt32();
458                        var regRef = new ExcelVbaReference();
459                        regRef.Name = referenceName;
460                        regRef.ReferenceRecordID = id;
461                        regRef.Libid = GetString(br, sizeLibID);
462                        uint reserved1 = br.ReadUInt32();
463                        ushort reserved2 = br.ReadUInt16();
464                        References.Add(regRef);
465                        break;
466                    case 0x0E:
467                        var projRef = new ExcelVbaReferenceProject();
468                        projRef.ReferenceRecordID = id;
469                        projRef.Name = referenceName;
470                        sizeLibID = br.ReadUInt32();
471                        projRef.Libid = GetString(br, sizeLibID);
472                        sizeLibID = br.ReadUInt32();
473                        projRef.LibIdRelative = GetString(br, sizeLibID);
474                        projRef.MajorVersion = br.ReadUInt32();
475                        projRef.MinorVersion = br.ReadUInt16();
476                        References.Add(projRef);
477                        break;
478                    case 0x0F:
479                        ushort modualCount = br.ReadUInt16();
480                        break;
481                    case 0x13:
482                        ushort cookie = br.ReadUInt16();
483                        break;
484                    case 0x14:
485                        LcidInvoke = (int)br.ReadUInt32();
486                        break;
487                    case 0x16:
488                        referenceName = GetUnicodeString(br, size);
489                        break;
490                    case 0x19:
491                        currentModule = new ExcelVBAModule();
492                        currentModule.Name = GetUnicodeString(br, size);
493                        Modules.Add(currentModule);
494                        break;
495                    case 0x1A:
496                        currentModule.streamName = GetUnicodeString(br, size);
497                        break;
498                    case 0x1C:
499                        currentModule.Description = GetUnicodeString(br, size);
500                        break;
501                    case 0x1E:
502                        currentModule.HelpContext = (int)br.ReadUInt32();
503                        break;
504                    case 0x21:
505                    case 0x22:
506                        break;
507                    case 0x2B:      //Modul Terminator
508                        break;
509                    case 0x2C:
510                        currentModule.Cookie = br.ReadUInt16();
511                        break;
512                    case 0x31:
513                        currentModule.ModuleOffset = br.ReadUInt32();
514                        break;
515                    case 0x10:
516                        terminate = true;
517                        break;
518                    case 0x30:
519                        var extRef = (ExcelVbaReferenceControl)currentRef;
520                        var sizeExt = br.ReadUInt32();
521                        extRef.LibIdExternal = GetString(br, sizeExt);
522
523                        uint reserved4 = br.ReadUInt32();
524                        ushort reserved5 = br.ReadUInt16();
525                        extRef.OriginalTypeLib = new Guid(br.ReadBytes(16));
526                        extRef.Cookie = br.ReadUInt32();
527                        break;
528                    case 0x33:
529                        currentRef = new ExcelVbaReferenceControl();
530                        currentRef.ReferenceRecordID = id;
531                        currentRef.Name = referenceName;
532                        currentRef.Libid = GetString(br, size);
533                        References.Add(currentRef);
534                        break;
535                    case 0x2F:
536                        var contrRef = (ExcelVbaReferenceControl)currentRef;
537                        contrRef.ReferenceRecordID = id;
538
539                        var sizeTwiddled = br.ReadUInt32();
540                        contrRef.LibIdTwiddled = GetString(br, sizeTwiddled);
541                        var r1 = br.ReadUInt32();
542                        var r2 = br.ReadUInt16();
543
544                        break;
545                    case 0x25:
546                        currentModule.ReadOnly = true;
547                        break;
548                    case 0x28:
549                        currentModule.Private = true;
550                        break;
551                    default:
552                        break;
553                }
554            }
555        }
556        #endregion
557
558        #region Save Project
559        internal void Save()
560        {
561            if (Validate())
562            {
563                CompoundDocument doc = new CompoundDocument();
564                doc.Storage = new CompoundDocument.StoragePart();
565                var store = new CompoundDocument.StoragePart();
566                doc.Storage.SubStorage.Add("VBA", store);
567
568                store.DataStreams.Add("_VBA_PROJECT", CreateVBAProjectStream());
569                store.DataStreams.Add("dir", CreateDirStream());
570                foreach (var module in Modules)
571                {
572                    store.DataStreams.Add(module.Name, CompoundDocument.CompressPart(Encoding.GetEncoding(CodePage).GetBytes(module.Attributes.GetAttributeText() + module.Code)));
573                }
574
575                //Copy streams from the template, if used.
576                if (Document != null)
577                {
578                    foreach (var ss in Document.Storage.SubStorage)
579                    {
580                        if (ss.Key != "VBA")
581                        {
582                            doc.Storage.SubStorage.Add(ss.Key, ss.Value);
583                        }
584                    }
585                    foreach (var s in Document.Storage.DataStreams)
586                    {
587                        if (s.Key != "dir" && s.Key != "PROJECT" && s.Key != "PROJECTwm")
588                        {
589                            doc.Storage.DataStreams.Add(s.Key, s.Value);
590                        }
591                    }
592                }
593
594                doc.Storage.DataStreams.Add("PROJECT", CreateProjectStream());
595                doc.Storage.DataStreams.Add("PROJECTwm", CreateProjectwmStream());
596
597                if (Part == null)
598                {
599                    Uri = new Uri(PartUri, UriKind.Relative);
600                    Part = _pck.CreatePart(Uri, ExcelPackage.schemaVBA);
601                    var rel = _wb.Part.CreateRelationship(Uri, Packaging.TargetMode.Internal, schemaRelVba);
602                }
603                var vbaBuffer=doc.Save();
604                var st = Part.GetStream(FileMode.Create);
605                st.Write(vbaBuffer, 0, vbaBuffer.Length);
606                st.Flush();
607                //Save the digital signture
608                Signature.Save(this);
609            }
610        }
611
612        private bool Validate()
613        {
614            Description = Description ?? "";
615            HelpFile1 = HelpFile1 ?? "";
616            HelpFile2 = HelpFile2 ?? "";
617            Constants = Constants ?? "";
618            return true;
619        }
620
621        /// <summary>
622        /// MS-OVBA 2.3.4.1
623        /// </summary>
624        /// <returns></returns>
625        private byte[] CreateVBAProjectStream()
626        {
627            BinaryWriter bw = new BinaryWriter(new MemoryStream());
628            bw.Write((ushort)0x61CC); //Reserved1
629            bw.Write((ushort)0xFFFF); //Version
630            bw.Write((byte)0x0); //Reserved3
631            bw.Write((ushort)0x0); //Reserved4
632            return ((MemoryStream)bw.BaseStream).ToArray();
633        }
634        /// <summary>
635        /// MS-OVBA 2.3.4.1
636        /// </summary>
637        /// <returns></returns>
638        private byte[] CreateDirStream()
639        {
640            BinaryWriter bw = new BinaryWriter(new MemoryStream());
641
642            /****** PROJECTINFORMATION Record ******/
643            bw.Write((ushort)1);        //ID
644            bw.Write((uint)4);          //Size
645            bw.Write((uint)SystemKind); //SysKind
646
647            bw.Write((ushort)2);        //ID
648            bw.Write((uint)4);          //Size
649            bw.Write((uint)Lcid);       //Lcid
650
651            bw.Write((ushort)0x14);     //ID
652            bw.Write((uint)4);          //Size
653            bw.Write((uint)LcidInvoke); //Lcid Invoke
654
655            bw.Write((ushort)3);        //ID
656            bw.Write((uint)2);          //Size
657            bw.Write((ushort)CodePage);   //Codepage
658
659            //ProjectName
660            bw.Write((ushort)4);                                            //ID
661            bw.Write((uint)Name.Length);                             //Size
662            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(Name)); //Project Name
663
664            //Description
665            bw.Write((ushort)5);                                            //ID
666            bw.Write((uint)Description.Length);                             //Size
667            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(Description)); //Project Name
668            bw.Write((ushort)0x40);                                           //ID
669            bw.Write((uint)Description.Length*2);                           //Size
670            bw.Write(Encoding.Unicode.GetBytes(Description));               //Project Description
671
672            //Helpfiles
673            bw.Write((ushort)6);                                           //ID
674            bw.Write((uint)HelpFile1.Length);                              //Size
675            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(HelpFile1));  //HelpFile1           
676            bw.Write((ushort)0x3D);                                           //ID
677            bw.Write((uint)HelpFile2.Length);                              //Size
678            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(HelpFile2));  //HelpFile2
679
680            //Help context id
681            bw.Write((ushort)7);            //ID
682            bw.Write((uint)4);              //Size
683            bw.Write((uint)HelpContextID);  //Help context id
684
685            //Libflags
686            bw.Write((ushort)8);            //ID
687            bw.Write((uint)4);              //Size
688            bw.Write((uint)0);  //Help context id
689
690            //Vba Version
691            bw.Write((ushort)9);            //ID
692            bw.Write((uint)4);              //Reserved
693            bw.Write((uint)MajorVersion);   //Reserved
694            bw.Write((ushort)MinorVersion); //Help context id
695
696            //Constants
697            bw.Write((ushort)0x0C);           //ID
698            bw.Write((uint)Constants.Length);              //Size
699            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(Constants));              //Help context id
700            bw.Write((ushort)0x3C);                                           //ID
701            bw.Write((uint)Constants.Length/2);                              //Size
702            bw.Write(Encoding.Unicode.GetBytes(Constants));  //HelpFile2
703
704            /****** PROJECTREFERENCES Record ******/
705            foreach (var reference in References)
706            {
707                WriteNameReference(bw, reference);
708
709                if (reference.ReferenceRecordID == 0x2F)
710                {
711                    WriteControlReference(bw, reference);
712                }
713                else if (reference.ReferenceRecordID == 0x33)
714                {
715                    WriteOrginalReference(bw, reference);
716                }
717                else if (reference.ReferenceRecordID == 0x0D)
718                {
719                    WriteRegisteredReference(bw, reference);
720                }
721                else if (reference.ReferenceRecordID == 0x0E)
722                {
723                    WriteProjectReference(bw, reference);
724                }
725            }
726
727            bw.Write((ushort)0x0F);
728            bw.Write((uint)0x02);
729            bw.Write((ushort)Modules.Count);
730            bw.Write((ushort)0x13);
731            bw.Write((uint)0x02);
732            bw.Write((ushort)0xFFFF);
733
734            foreach (var module in Modules)
735            {
736                WriteModuleRecord(bw, module);
737            }
738            bw.Write((ushort)0x10);             //Terminator
739            bw.Write((uint)0);             
740
741            return CompoundDocument.CompressPart(((MemoryStream)bw.BaseStream).ToArray());
742        }
743
744        private void WriteModuleRecord(BinaryWriter bw, ExcelVBAModule module)
745        {
746            bw.Write((ushort)0x19);
747            bw.Write((uint)module.Name.Length);
748            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Name));     //Name
749
750            bw.Write((ushort)0x47);
751            bw.Write((uint)module.Name.Length*2);
752            bw.Write(Encoding.Unicode.GetBytes(module.Name));                   //Name
753
754            bw.Write((ushort)0x1A);
755            bw.Write((uint)module.Name.Length);
756            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Name));     //Stream Name 
757
758            bw.Write((ushort)0x32);
759            bw.Write((uint)module.Name.Length*2);
760            bw.Write(Encoding.Unicode.GetBytes(module.Name));                   //Stream Name
761
762            module.Description = module.Description ?? "";
763            bw.Write((ushort)0x1C);
764            bw.Write((uint)module.Description.Length);
765            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Description));     //Description
766
767            bw.Write((ushort)0x48);
768            bw.Write((uint)module.Description.Length*2);
769            bw.Write(Encoding.Unicode.GetBytes(module.Description));                   //Description
770
771            bw.Write((ushort)0x31);
772            bw.Write((uint)4);
773            bw.Write((uint)0);                              //Module Stream Offset (No PerformanceCache)
774
775            bw.Write((ushort)0x1E);
776            bw.Write((uint)4);
777            bw.Write((uint)module.HelpContext);            //Help context ID
778
779            bw.Write((ushort)0x2C);
780            bw.Write((uint)2);
781            bw.Write((ushort)0xFFFF);            //Help context ID
782
783            bw.Write((ushort)(module.Type == eModuleType.Module ? 0x21 : 0x22));
784            bw.Write((uint)0);
785
786            if (module.ReadOnly)
787            {
788                bw.Write((ushort)0x25);
789                bw.Write((uint)0);              //Readonly
790            }
791
792            if (module.Private)
793            {
794                bw.Write((ushort)0x28);
795                bw.Write((uint)0);              //Private
796            }
797
798            bw.Write((ushort)0x2B);             //Terminator
799            bw.Write((uint)0);             
800        }
801
802        private void WriteNameReference(BinaryWriter bw, ExcelVbaReference reference)
803        {
804            //Name record
805            bw.Write((ushort)0x16);                                             //ID
806            bw.Write((uint)reference.Name.Length);                              //Size
807            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(reference.Name));  //HelpFile1
808            bw.Write((ushort)0x3E);                                             //ID
809            bw.Write((uint)reference.Name.Length * 2);                            //Size
810            bw.Write(Encoding.Unicode.GetBytes(reference.Name));                //HelpFile2
811        }
812        private void WriteControlReference(BinaryWriter bw, ExcelVbaReference reference)
813        {
814            WriteOrginalReference(bw, reference);
815
816            bw.Write((ushort)0x2F);
817            var controlRef=(ExcelVbaReferenceControl)reference;
818            bw.Write((uint)(4 + controlRef.LibIdTwiddled.Length + 4 + 2));    // Size of SizeOfLibidTwiddled, LibidTwiddled, Reserved1, and Reserved2.
819            bw.Write((uint)controlRef.LibIdTwiddled.Length);                              //Size           
820            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(controlRef.LibIdTwiddled));  //LibID
821            bw.Write((uint)0);      //Reserved1
822            bw.Write((ushort)0);    //Reserved2
823            WriteNameReference(bw, reference);  //Name record again
824            bw.Write((ushort)0x30); //Reserved3
825            bw.Write((uint)(4 + controlRef.LibIdExternal.Length + 4 + 2 + 16 + 4));    //Size of SizeOfLibidExtended, LibidExtended, Reserved4, Reserved5, OriginalTypeLib, and Cookie
826            bw.Write((uint)controlRef.LibIdExternal.Length);                              //Size           
827            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(controlRef.LibIdExternal));  //LibID
828            bw.Write((uint)0);      //Reserved4
829            bw.Write((ushort)0);    //Reserved5
830            bw.Write(controlRef.OriginalTypeLib.ToByteArray());
831            bw.Write((uint)controlRef.Cookie);      //Cookie
832        }
833
834        private void WriteOrginalReference(BinaryWriter bw, ExcelVbaReference reference)
835        {
836            bw.Write((ushort)0x33);
837            bw.Write((uint)reference.Libid.Length);
838            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(reference.Libid));  //LibID
839        }
840        private void WriteProjectReference(BinaryWriter bw, ExcelVbaReference reference)
841        {
842            bw.Write((ushort)0x0E);
843            var projRef = (ExcelVbaReferenceProject)reference;
844            bw.Write((uint)(4 + projRef.Libid.Length + 4 + projRef.LibIdRelative.Length+4+2));
845            bw.Write((uint)projRef.Libid.Length);
846            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(projRef.Libid));  //LibAbsolute
847            bw.Write((uint)projRef.LibIdRelative.Length);
848            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(projRef.LibIdRelative));  //LibIdRelative
849            bw.Write(projRef.MajorVersion);
850            bw.Write(projRef.MinorVersion);
851        }
852
853        private void WriteRegisteredReference(BinaryWriter bw, ExcelVbaReference reference)
854        {
855            bw.Write((ushort)0x0D);
856            bw.Write((uint)(4+reference.Libid.Length+4+2));
857            bw.Write((uint)reference.Libid.Length);
858            bw.Write(Encoding.GetEncoding(CodePage).GetBytes(reference.Libid));  //LibID           
859            bw.Write((uint)0);      //Reserved1
860            bw.Write((ushort)0);    //Reserved2
861        }
862
863        private byte[] CreateProjectwmStream()
864        {
865            BinaryWriter bw = new BinaryWriter(new MemoryStream());
866
867            foreach (var module in Modules)
868            {
869                bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Name));     //Name
870                bw.Write((byte)0); //Null
871                bw.Write(Encoding.Unicode.GetBytes(module.Name));                   //Name
872                bw.Write((ushort)0); //Null
873            }
874            bw.Write((ushort)0); //Null
875            return CompoundDocument.CompressPart(((MemoryStream)bw.BaseStream).ToArray());
876        }       
877        private byte[] CreateProjectStream()
878        {
879            StringBuilder sb = new StringBuilder();
880            sb.AppendFormat("ID=\"{0}\"\r\n", ProjectID);
881            foreach(var module in Modules)
882            {
883                if (module.Type == eModuleType.Document)
884                {
885                    sb.AppendFormat("Document={0}/&H00000000\r\n", module.Name);
886                }
887                else if (module.Type == eModuleType.Module)
888                {
889                    sb.AppendFormat("Module={0}\r\n", module.Name);
890                }
891                else if (module.Type == eModuleType.Class)
892                {
893                    sb.AppendFormat("Class={0}\r\n", module.Name);
894                }
895                else
896                {
897                    //Designer
898                    sb.AppendFormat("Package={0}\r\n", module.ClassID);
899                    sb.AppendFormat("BaseClass={0}\r\n", module.Name);
900                }
901            }
902            if (HelpFile1 != "")
903            {
904                sb.AppendFormat("HelpFile={0}\r\n", HelpFile1);
905            }
906            sb.AppendFormat("Name=\"{0}\"\r\n", Name);
907            sb.AppendFormat("HelpContextID={0}\r\n", HelpContextID);
908
909            if (!string.IsNullOrEmpty(Description))
910            {
911                sb.AppendFormat("Description=\"{0}\"\r\n", Description);
912            }
913            sb.AppendFormat("VersionCompatible32=\"393222000\"\r\n");
914
915            sb.AppendFormat("CMG=\"{0}\"\r\n", WriteProtectionStat());
916            sb.AppendFormat("DPB=\"{0}\"\r\n", WritePassword());
917            sb.AppendFormat("GC=\"{0}\"\r\n\r\n", WriteVisibilityState());
918
919            sb.Append("[Host Extender Info]\r\n");
920            sb.Append("&H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000\r\n");
921            sb.Append("\r\n");
922            sb.Append("[Workspace]\r\n");
923            foreach(var module in Modules)
924            {
925                sb.AppendFormat("{0}=0, 0, 0, 0, C \r\n",module.Name);             
926            }
927            string s = sb.ToString();
928            return Encoding.GetEncoding(CodePage).GetBytes(s);
929        }
930        private string WriteProtectionStat()
931        {
932            int stat=(_protection.UserProtected ? 1:0) | 
933                     (_protection.HostProtected ? 2:0) |
934                     (_protection.VbeProtected ? 4:0);
935
936            return Encrypt(BitConverter.GetBytes(stat));   
937        }
938        private string WritePassword()
939        {
940            byte[] nullBits=new byte[3];
941            byte[] nullKey = new byte[4];
942            byte[] nullHash = new byte[20];
943            if (Protection.PasswordKey == null)
944            {
945                return Encrypt(new byte[] { 0 });
946            }
947            else
948            {
949                Array.Copy(Protection.PasswordKey, nullKey, 4);
950                Array.Copy(Protection.PasswordHash, nullHash, 20);
951
952                //Set Null bits
953                for (int i = 0; i < 24; i++)
954                {
955                    byte bit = (byte)(128 >> (int)((i % 8)));
956                    if (i < 4)
957                    {
958                        if (nullKey[i] == 0)
959                        {
960                            nullKey[i] = 1;
961                        }
962                        else
963                        {
964                            nullBits[0] |= bit;
965                        }
966                    }
967                    else
968                    {
969                        if (nullHash[i - 4] == 0)
970                        {
971                            nullHash[i - 4] = 1;
972                        }
973                        else
974                        {
975                            int byteIndex = (i - i % 8) / 8;
976                            nullBits[byteIndex] |= bit;
977                        }
978                    }
979                }
980                //Write the Password Hash Data Structure (2.4.4.1)
981                BinaryWriter bw = new BinaryWriter(new MemoryStream());
982                bw.Write((byte)0xFF);
983                bw.Write(nullBits);
984                bw.Write(nullKey);
985                bw.Write(nullHash);
986                bw.Write((byte)0);
987                return Encrypt(((MemoryStream)bw.BaseStream).ToArray());
988            }
989        }
990        private string WriteVisibilityState()
991        {
992            return Encrypt(new byte[] { (byte)(Protection.VisibilityState ? 0xFF : 0) });
993        }
994        #endregion
995        private string GetString(BinaryReader br, uint size)
996        {
997            return GetString(br, size, System.Text.Encoding.GetEncoding(CodePage));
998        }
999        private string GetString(BinaryReader br, uint size, Encoding enc)
1000        {
1001            if (size > 0)
1002            {
1003                byte[] byteTemp = new byte[size];
1004                byteTemp = br.ReadBytes((int)size);
1005                return enc.GetString(byteTemp);
1006            }
1007            else
1008            {
1009                return "";
1010            }
1011        }
1012        private string GetUnicodeString(BinaryReader br, uint size)
1013        {
1014            string s = GetString(br, size);
1015            int reserved = br.ReadUInt16();
1016            uint sizeUC = br.ReadUInt32();
1017            string sUC = GetString(br, sizeUC, System.Text.Encoding.Unicode);
1018            return sUC.Length == 0 ? s : sUC;
1019        }
1020        internal CompoundDocument Document { get; set; }
1021#endif
1022        internal Packaging.ZipPackagePart Part { get; set; }
1023        internal Uri Uri { get; private set; }
1024#if !MONO
1025        /// <summary>
1026        /// Create a new VBA Project
1027        /// </summary>
1028        internal void Create()
1029        {
1030            if(Lcid>0)
1031            {
1032                throw (new InvalidOperationException("Package already contains a VBAProject"));
1033            }
1034            ProjectID = "{5DD90D76-4904-47A2-AF0D-D69B4673604E}";
1035            Name = "VBAProject";
1036            SystemKind = eSyskind.Win32;            //Default
1037            Lcid = 1033;                            //English - United States
1038            LcidInvoke = 1033;                      //English - United States
1039            CodePage = Encoding.Default.CodePage;
1040            MajorVersion = 1361024421;
1041            MinorVersion = 6;
1042            HelpContextID = 0;
1043            Modules.Add(new ExcelVBAModule(_wb.CodeNameChange) { Name = "ThisWorkbook", Code = "", Attributes=GetDocumentAttributes("ThisWorkbook", "0{00020819-0000-0000-C000-000000000046}"), Type = eModuleType.Document, HelpContext = 0 });
1044            foreach (var sheet in _wb.Worksheets)
1045            {
1046                var name = GetModuleNameFromWorksheet(sheet);
1047                if (!Modules.Exists(name))
1048                {
1049                    Modules.Add(new ExcelVBAModule(sheet.CodeNameChange) { Name = name, Code = "", Attributes = GetDocumentAttributes(sheet.Name, "0{00020820-0000-0000-C000-000000000046}"), Type = eModuleType.Document, HelpContext = 0 });
1050                }
1051            }
1052            _protection = new ExcelVbaProtection(this) { UserProtected = false, HostProtected = false, VbeProtected = false, VisibilityState = true };
1053        }
1054
1055        internal string GetModuleNameFromWorksheet(ExcelWorksheet sheet)
1056        {
1057            var name = sheet.Name;
1058            if (name.Any(c => c > 255) || this.Modules[name] != null)
1059            {
1060                int i = sheet.PositionID;
1061                name = "Sheet" + i.ToString();
1062                while (this.Modules[name] != null)
1063                {
1064                    name = "Sheet" + (++i).ToString(); ;
1065                }
1066            }           
1067            return name;
1068        }
1069        internal ExcelVbaModuleAttributesCollection GetDocumentAttributes(string name, string clsid)
1070        {
1071            var attr = new ExcelVbaModuleAttributesCollection();
1072            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Name", Value = name, DataType = eAttributeDataType.String });
1073            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Base", Value = clsid, DataType = eAttributeDataType.String });
1074            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_GlobalNameSpace", Value = "False", DataType = eAttributeDataType.NonString });
1075            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Creatable", Value = "False", DataType = eAttributeDataType.NonString });
1076            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_PredeclaredId", Value = "True", DataType = eAttributeDataType.NonString });
1077            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Exposed", Value = "False", DataType = eAttributeDataType.NonString });
1078            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_TemplateDerived", Value = "False", DataType = eAttributeDataType.NonString });
1079            attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Customizable", Value = "True", DataType = eAttributeDataType.NonString });
1080
1081            return attr;
1082        }
1083
1084
1085        //internal string GetBlankDocumentModule(string name, string clsid)
1086        //{
1087        //    string ret=string.Format("Attribute VB_Name = \"{0}\"\r\n",name);
1088        //    ret += string.Format("Attribute VB_Base = \"{0}\"\r\n", clsid);  //Microsoft.Office.Interop.Excel.WorksheetClass
1089        //    ret += "Attribute VB_GlobalNameSpace = False\r\n";
1090        //    ret += "Attribute VB_Creatable = False\r\n";
1091        //    ret += "Attribute VB_PredeclaredId = True\r\n";
1092        //    ret += "Attribute VB_Exposed = True\r\n";
1093        //    ret += "Attribute VB_TemplateDerived = False\r\n";
1094        //    ret += "Attribute VB_Customizable = True";
1095        //    return ret;
1096        //}
1097        //internal string GetBlankModule(string name)
1098        //{
1099        //    return string.Format("Attribute VB_Name = \"{0}\"\r\n", name);
1100        //}
1101        //internal string GetBlankClassModule(string name, bool exposed)
1102        //{
1103        //    string ret=string.Format("Attribute VB_Name = \"{0}\"\r\n",name);
1104        //    ret += string.Format("Attribute VB_Base = \"{0}\"\r\n", "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"); 
1105        //    ret += "Attribute VB_GlobalNameSpace = False\r\n";
1106        //    ret += "Attribute VB_Creatable = False\r\n";
1107        //    ret += "Attribute VB_PredeclaredId = False\r\n";
1108        //    ret += string.Format("Attribute VB_Exposed = {0}\r\n", exposed ? "True" : "False");
1109        //    ret += "Attribute VB_TemplateDerived = False\r\n";
1110        //    ret += "Attribute VB_Customizable = False\r\n";
1111        //    return ret;
1112        //}
1113#endif
1114        /// <summary>
1115        /// Remove the project from the package
1116        /// </summary>
1117        public void Remove()
1118        {
1119            if (Part == null) return;
1120
1121            foreach (var rel in Part.GetRelationships())
1122            {
1123                _pck.DeleteRelationship(rel.Id);
1124            }
1125            if (_pck.PartExists(Uri))
1126            {
1127                _pck.DeletePart(Uri);
1128            }
1129            Part = null;
1130            Modules.Clear();
1131            References.Clear();
1132            Lcid = 0;
1133            LcidInvoke = 0;
1134            CodePage = 0;
1135            MajorVersion = 0;
1136            MinorVersion = 0;
1137            HelpContextID = 0;
1138        }
1139        public override string ToString()
1140        {
1141            return Name;
1142        }
1143    }
1144}
Note: See TracBrowser for help on using the repository browser.