Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HLScript/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/3.1.3/EPPlus-3.1.3/VBA/ExcelVBAProject.cs @ 10331

Last change on this file since 10331 was 9580, checked in by sforsten, 12 years ago

#1730:

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