Преглед изворни кода

Fix persistence of unmanaged resources.
Correct ordering issue for emission of classes and their supertypes.
Essentially all insertion of TypeSpec metatdata is done conditionally.

k_john_gough_cp пре 15 година
родитељ
комит
f9a680544b

+ 78 - 88
PERWAPI/MDClassDefElems.cs

@@ -260,15 +260,6 @@ namespace QUT.PERWAPI
             }
         }
 
-        /*    /// <summary>
-            /// Make this class inherit from ValueType
-            /// </summary>
-            public override void MakeValueClass() {
-              superType = MSCorLib.mscorlib.ValueType();
-              typeIndex = (byte)ElementType.ValueType;
-            }
-            */
-
         /// <summary>
         /// Add an attribute to the attributes of this class
         /// </summary>
@@ -976,85 +967,84 @@ namespace QUT.PERWAPI
             }
         }
 
-        internal sealed override void BuildTables(MetaDataOut md)
-        {
-            md.AddToTable(MDTable.TypeDef, this);
-            //if ((flags & (uint)TypeAttr.Interface) != 0) { superType = null; }
-            if (superType != null)
-            {
-                superType.BuildMDTables(md);
-                if (superType is ClassSpec) md.AddToTable(MDTable.TypeSpec, superType);
-            }
-            for (int i = 0; i < genericParams.Count; i++)
-            {
-                ((GenericParam)genericParams[i]).BuildMDTables(md);
-            }
-            nameIx = md.AddToStringsHeap(name);
-            nameSpaceIx = md.AddToStringsHeap(nameSpace);
-            if (security != null)
-            {
-                for (int i = 0; i < security.Count; i++)
-                {
-                    ((DeclSecurity)security[i]).BuildMDTables(md);
-                }
-            }
-            // Console.WriteLine("Building tables for " + name);
-            if (layout != null) layout.BuildMDTables(md);
-            // Console.WriteLine("adding methods " + methods.Count);
-            methodIx = md.TableIndex(MDTable.Method);
-            for (int i = 0; i < methods.Count; i++)
-            {
-                ((MethodDef)methods[i]).BuildMDTables(md);
-            }
-            // Console.WriteLine("adding fields");
-            fieldIx = md.TableIndex(MDTable.Field);
-            for (int i = 0; i < fields.Count; i++)
-            {
-                ((FieldDef)fields[i]).BuildMDTables(md);
-            }
-            // Console.WriteLine("adding interfaceimpls and methodimpls");
-            if (interfaces.Count > 0)
-            {
-                for (int i = 0; i < interfaces.Count; i++)
-                {
-                    ((InterfaceImpl)interfaces[i]).BuildMDTables(md);
-                }
-            }
-            if (methodImpls.Count > 0)
-            {
-                for (int i = 0; i < methodImpls.Count; i++)
-                {
-                    ((MethodImpl)methodImpls[i]).BuildMDTables(md);
-                }
-            }
-            // Console.WriteLine("adding events and properties");
-            if (events.Count > 0)
-            {
-                new MapElem(this, md.TableIndex(MDTable.Event), MDTable.EventMap).BuildMDTables(md);
-                for (int i = 0; i < events.Count; i++)
-                {
-                    ((Event)events[i]).BuildMDTables(md);
-                }
-            }
-            if (properties.Count > 0)
-            {
-                new MapElem(this, md.TableIndex(MDTable.Property), MDTable.PropertyMap).BuildMDTables(md);
-                for (int i = 0; i < properties.Count; i++)
-                {
-                    ((Property)properties[i]).BuildMDTables(md);
-                }
-            }
-            // Console.WriteLine("Adding nested classes");
-            if (nestedClasses.Count > 0)
-            {
-                for (int i = 0; i < nestedClasses.Count; i++)
-                {
-                    ClassDef nClass = (ClassDef)nestedClasses[i];
-                    nClass.BuildMDTables(md);
-                    new MapElem(nClass, this, MDTable.NestedClass).BuildTables(md);
-                }
-            }
-            // Console.WriteLine("End of building tables");
+        internal sealed override void BuildTables(MetaDataOut md) {
+          // The processing of superTypes must be done either entirely
+          // before or entirely after the processing of this TypeDef.
+          // Otherwise the method indices will not be monotonic in table-2.
+
+          if (superType != null) {
+            superType.BuildMDTables(md);
+            if (superType is ClassSpec) md.ConditionalAddTypeSpec(superType);
+          }
+
+          //uint startT = md.TableIndex(MDTable.TypeDef);
+          md.AddToTable(MDTable.TypeDef, this);
+
+          for (int i = 0; i < genericParams.Count; i++) {
+            ((GenericParam)genericParams[i]).BuildMDTables(md);
+          }
+          nameIx = md.AddToStringsHeap(name);
+          nameSpaceIx = md.AddToStringsHeap(nameSpace);
+          if (security != null) {
+            for (int i = 0; i < security.Count; i++) {
+              ((DeclSecurity)security[i]).BuildMDTables(md);
+            }
+          }
+
+          if (layout != null) layout.BuildMDTables(md);
+
+          //uint startM = md.TableIndex(MDTable.Method);
+
+          // Console.WriteLine("adding methods " + methods.Count);
+          methodIx = md.TableIndex(MDTable.Method);
+          for (int i = 0; i < methods.Count; i++) {
+            ((MethodDef)methods[i]).BuildMDTables(md);
+          }
+
+          //Console.WriteLine("Building tables for " + this.TypeName());
+          //Console.WriteLine("tIx {0}, methods {1} - {2}",
+          //  Hex.Short((short)startT),
+          //  Hex.Short((short)startM),
+          //  Hex.Short((short)md.TableIndex(MDTable.Method)));
+
+          // Console.WriteLine("adding fields");
+          fieldIx = md.TableIndex(MDTable.Field);
+          for (int i = 0; i < fields.Count; i++) {
+            ((FieldDef)fields[i]).BuildMDTables(md);
+          }
+          // Console.WriteLine("adding interfaceimpls and methodimpls");
+          if (interfaces.Count > 0) {
+            for (int i = 0; i < interfaces.Count; i++) {
+              ((InterfaceImpl)interfaces[i]).BuildMDTables(md);
+            }
+          }
+          if (methodImpls.Count > 0) {
+            for (int i = 0; i < methodImpls.Count; i++) {
+              ((MethodImpl)methodImpls[i]).BuildMDTables(md);
+            }
+          }
+          // Console.WriteLine("adding events and properties");
+          if (events.Count > 0) {
+            new MapElem(this, md.TableIndex(MDTable.Event), MDTable.EventMap).BuildMDTables(md);
+            for (int i = 0; i < events.Count; i++) {
+              ((Event)events[i]).BuildMDTables(md);
+            }
+          }
+          if (properties.Count > 0) {
+            new MapElem(this, md.TableIndex(MDTable.Property), MDTable.PropertyMap).BuildMDTables(md);
+            for (int i = 0; i < properties.Count; i++) {
+              ((Property)properties[i]).BuildMDTables(md);
+            }
+          }
+          // Console.WriteLine("Adding nested classes");
+          if (nestedClasses.Count > 0) {
+            for (int i = 0; i < nestedClasses.Count; i++) {
+              ClassDef nClass = (ClassDef)nestedClasses[i];
+              nClass.BuildMDTables(md);
+              new MapElem(nClass, this, MDTable.NestedClass).BuildTables(md);
+            }
+          }
+          // Console.WriteLine("End of building tables");
         }
 
         internal override void BuildCILInfo(CILWriter output)

+ 1 - 2
PERWAPI/MDClassElems.cs

@@ -475,14 +475,13 @@ namespace QUT.PERWAPI
         }
 
         internal override sealed Type AddTypeSpec(MetaDataOut md) {
-          md.AddToTable(MDTable.TypeSpec, this);
+          md.ConditionalAddTypeSpec(this);
           BuildMDTables(md);
           return this;
         }
 
         internal override void BuildTables(MetaDataOut md)
         {
-            //md.AddToTable(MDTable.TypeSpec,this);
             if (!genClass.isDef())
                 genClass.BuildMDTables(md);
             for (int i = 0; i < genericParams.Count; i++)

+ 1 - 1
PERWAPI/MDElements.cs

@@ -573,7 +573,7 @@ namespace QUT.PERWAPI
         {
             md.AddToTable(MDTable.InterfaceImpl, this);
             if (!theInterface.isDef()) theInterface.BuildMDTables(md);
-            if (theInterface is ClassSpec) md.AddToTable(MDTable.TypeSpec, theInterface);
+            if (theInterface is ClassSpec) md.ConditionalAddTypeSpec(theInterface);
         }
 
         internal static uint Size(MetaData md)

+ 2 - 2
PERWAPI/MDFieldElems.cs

@@ -504,12 +504,12 @@ namespace QUT.PERWAPI
         {
             md.AddToTable(tabIx, this);
             nameIx = md.AddToStringsHeap(name);
-            if (type is ClassSpec) md.AddToTable(MDTable.TypeSpec, type);
+            if (type is ClassSpec) md.ConditionalAddTypeSpec(type);
             if (!type.isDef())
                 type.BuildMDTables(md);
             if (parent != null)
             {
-              if (parent is ClassSpec) md.AddToTable(MDTable.TypeSpec, parent);
+              if (parent is ClassSpec) md.ConditionalAddTypeSpec(parent);
               parent.BuildMDTables(md);
             }
         }

+ 2 - 2
PERWAPI/MDMethodElems.cs

@@ -532,9 +532,9 @@ namespace QUT.PERWAPI
             nameIx = md.AddToStringsHeap(name);
             if (parent != null)
             {
-                if (parent is ClassSpec) md.ConditionalAddToTable(MDTable.TypeSpec, parent);
+                if (parent is ClassSpec) md.ConditionalAddTypeSpec(parent);
                 if (parent is ConstructedTypeSpec) 
-                    md.AddToTable(MDTable.TypeSpec, ((ConstructedTypeSpec)parent).Spec);
+                    md.ConditionalAddTypeSpec(((ConstructedTypeSpec)parent).Spec);
                 parent.BuildMDTables(md);
             }
             sig.BuildTables(md);

+ 2 - 2
PERWAPI/MDTypeElems.cs

@@ -184,7 +184,7 @@ namespace QUT.PERWAPI
         internal override sealed Type AddTypeSpec(MetaDataOut md)
         {
             if (typeSpecAdded) return this;
-            md.AddToTable(MDTable.TypeSpec, this);
+            md.ConditionalAddTypeSpec(this);
             BuildMDTables(md);
             typeSpecAdded = true;
             return this;
@@ -553,7 +553,7 @@ namespace QUT.PERWAPI
                 constraints[i] = new GenericParamConstraint(this, cClass);
                 if (cClass is ClassRef) cClass.BuildMDTables(md);
                 // Fix by CK - should be BuildTables too??
-                if (cClass is ClassSpec) md.AddToTable(MDTable.TypeSpec, cClass);
+                if (cClass is ClassSpec) md.ConditionalAddTypeSpec(cClass);
             }
         }
 

+ 2 - 4
PERWAPI/MetaDataOut.cs

@@ -201,10 +201,8 @@ namespace QUT.PERWAPI
           table.Add(elem);
         }
 
-        internal void ConditionalAddToTable(MDTable tableIx, MetaDataElement elem) {
-          // updates Row field of the element
-          // Console.WriteLine("Adding element to table " + (uint)tableIx);
-          ArrayList table = GetTable(tableIx);
+        internal void ConditionalAddTypeSpec(MetaDataElement elem) {
+          ArrayList table = GetTable(MDTable.TypeSpec);
           if (!table.Contains(elem)) {
             elem.Row = (uint)table.Count + 1;
             table.Add(elem);

+ 14 - 6
PERWAPI/PEFile.cs

@@ -38,7 +38,10 @@ namespace QUT.PERWAPI
         private Assembly thisAssembly;
         PEWriter output;
         MetaDataOut metaData;
-        System.IO.FileStream unmanagedResources;
+
+        System.IO.FileStream unmanagedResources;   // Unmanaged resources read from a file.
+
+        internal PEResourceDirectory unmanagedResourceRoot; // Unmanaged resources added programmatically.
         internal MetaDataTables metaDataTables;
         internal PEFileVersionInfo versionInfo;
 
@@ -252,14 +255,14 @@ namespace QUT.PERWAPI
         }
 
         /// <summary>
-        /// Add a manifest resource to this PEFile 
+        /// Add an unmanaged resource to this PEFile 
         /// </summary>
-        public void AddUnmanagedResources(string resFilename)
+        public void AddUnmanagedResourceFile(string resFilename)
         {
             if (!System.IO.File.Exists(resFilename))
                 throw (new FileNotFoundException("Unmanaged Resource File Not Found", resFilename));
-            unmanagedResources = System.IO.File.OpenRead(resFilename);
-            throw new NotYetImplementedException("Unmanaged Resources are not yet implemented");
+            // unmanagedResources = System.IO.File.OpenRead(resFilename);
+            throw new NotYetImplementedException("Unmanaged Resources from input files are not yet implemented");
         }
 
         /// <summary>
@@ -374,7 +377,6 @@ namespace QUT.PERWAPI
         /// <param name="debug">include debug information</param>
         public void WritePEFile(bool writePDB)
         {
-
             if (outStream == null)
             {
                 if (outputDir != null)
@@ -395,6 +397,12 @@ namespace QUT.PERWAPI
             }
 
             BuildMetaData();
+
+            // If the application is roundtripping an input PE-file with
+            // unmanaged resources, then this.unmanagedResourceRoot != null.
+            if (this.unmanagedResourceRoot != null)
+              output.AddUnmanagedResourceDirectory(this.unmanagedResourceRoot);
+
             output.MakeFile(versionInfo);
         }
 

+ 28 - 2
PERWAPI/PEReader.cs

@@ -84,11 +84,12 @@ namespace QUT.PERWAPI
             ReadCLIHeader();
             ReadMetaData();
             if (refsOnly)
-                ReadMetaDataTableRefs();
+                this.ReadMetaDataTableRefs();
             else
             {
-                ReadMetaDataTables();
+                this.ReadMetaDataTables();
                 pefile.metaDataTables = new MetaDataTables(tables);
+                this.SaveUnmanagedResources();
             }
             file.Close();
 
@@ -129,6 +130,8 @@ namespace QUT.PERWAPI
             return pefile;
         }
 
+
+
         internal static ReferenceScope GetExportedInterface(string filename)
         {
             System.IO.FileStream file = GetFile(filename);
@@ -292,6 +295,7 @@ namespace QUT.PERWAPI
             /* Data Directories */
             DataDirectoryRVA = new uint[FileImage.NumDataDirectories];
             DataDirectorySize = new uint[FileImage.NumDataDirectories];
+            // (Index 2 is resource table address and size)
             for (int i = 0; i < FileImage.NumDataDirectories; i++)
             {
                 DataDirectoryRVA[i] = ReadUInt32();
@@ -835,6 +839,28 @@ namespace QUT.PERWAPI
             }
         }
 
+      /// <summary>
+      /// This method saves any *unmanaged* resources in the input PE-file 
+      /// to the PEResourcesDirectory field PEFile.unmanagedResourceRoot.
+      /// These should be written out to the .rscr section in the PE-file.
+      /// Managed resources appear as ManifestResouces in metadata, and are
+      /// handled completely differently.
+      /// </summary>
+        private void SaveUnmanagedResources() {
+          if (this.DataDirectorySize[2] != 0) {
+            uint resourceRVA = this.DataDirectoryRVA[2];
+            uint fileOffset = this.GetOffset(resourceRVA);
+            long savedPos = this.BaseStream.Position;
+            this.BaseStream.Seek(fileOffset, SeekOrigin.Begin);
+            PEFile client = thisScope as PEFile;
+            if (client != null) {
+              client.unmanagedResourceRoot = new PEResourceDirectory();
+              client.unmanagedResourceRoot.PopulateResourceDirectory(this, fileOffset);
+            }
+            this.BaseStream.Seek(savedPos, SeekOrigin.Begin);
+          }
+        }
+
         internal uint GetIndex(MDTable tabIx)
         {
             if (md.largeIx[(int)tabIx]) return ReadUInt32();

+ 222 - 98
PERWAPI/PEResourceClasses.cs

@@ -18,10 +18,16 @@
 using System;
 using System.IO;
 using System.Collections;
+using System.Collections.Generic;
 
 
 namespace QUT.PERWAPI
 {
+  /// <summary>
+  /// (Unmanaged) Resource Elements consist of PEResourceDirectories
+  /// or PEResourceData elements.  Resource directories may be nested up
+  /// to three deep sorted on Type, Name and Language in that order.
+  /// </summary>
     public abstract class PEResourceElement
     {
 
@@ -44,116 +50,221 @@ namespace QUT.PERWAPI
 
         protected internal abstract uint Size();
 
-        protected internal abstract void Write(BinaryWriter dest, uint RVA);
+      /// <summary>
+      /// Write out the unmanaged resource data.
+      /// </summary>
+      /// <param name="dest">The Binary Writer</param>
+      /// <param name="baseOffset">File position at start of .rsrc section</param>
+      /// <param name="RVA">RVA of .rsrc section when loaded</param>
+        protected internal abstract void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA);
+
+        protected internal long offset;
+
+        protected const uint HeaderSize = 16;
+        protected const uint EntrySize = 8;
     }
 
-    public class PEResourceDirectory : PEResourceElement
-    {
-        public PEResourceDirectory() { }
+    /// <summary>
+    /// ResourceDirectory entries, as defined in Winnt.h
+    /// as type struct _IMAGE_RESOURCE_DIRECTORY.
+    /// </summary>
+    public class PEResourceDirectory : PEResourceElement {
 
-        private uint date = 0;
-        private ushort majver = 1;
-        private ushort minver = 0;
+      private uint date = 0;
+      private ushort majver = 1;
+      private ushort minver = 0;
 
-        public uint Date { get { return date; } set { date = value; } }
-        public ushort MajVer { get { return majver; } set { majver = value; } }
-        public ushort MinVer { get { return minver; } set { minver = value; } }
+      public uint Date { get { return date; } set { date = value; } }
+      public ushort MajVer { get { return majver; } set { majver = value; } }
+      public ushort MinVer { get { return minver; } set { minver = value; } }
 
-        private ArrayList subitems = new ArrayList();
+      //private ArrayList subItems = new ArrayList();
+      private List<PEResourceElement> elements = new List<PEResourceElement>();
 
-        public PEResourceElement this[int i] { get { return (PEResourceElement)subitems[i]; } }
+      public int Count() { return elements.Count; }
 
-        public int Count() { return subitems.Count; }
+      /// <summary>
+      /// Programmatically create unmanaged resource.
+      /// </summary>
+      public PEResourceDirectory() { }
 
-        public bool HasData()
-        {
-            return subitems.Count > 0;
+
+      /// <summary>
+      /// Read unmanged resource directory structure from PE-file.
+      /// </summary>
+      /// <param name="reader"></param>
+      internal void PopulateResourceDirectory(PEReader reader, long baseOffset) {
+        PEResourceElement resElement = null;
+        PEResourceDirectory resDirectory;
+        PEResourceData resData;
+
+        int junk = reader.ReadInt32(); // Must be zero.
+        this.date = reader.ReadUInt32();    // Time stamp.
+        this.majver = reader.ReadUInt16();
+        this.minver = reader.ReadUInt16();
+
+        int numNameEntries = reader.ReadUInt16(); // Number of named entries.
+        int numIdntEntries = reader.ReadUInt16(); // Number of ID entries.
+        for (int i = 0; i < numNameEntries; i++) {
+          uint nameOrId = reader.ReadUInt32();
+          uint elemOfst = reader.ReadUInt32();
+          if ((elemOfst & 0x80000000) != 0) // Element is subdirectory.
+            resElement = new PEResourceDirectory();
+          else
+            resElement = new PEResourceData();
+          resElement.Name = ReadName(reader, baseOffset + nameOrId & 0x7fffffff);
+          resElement.offset = baseOffset + (long)(elemOfst & 0x7fffffff);
+          this.AddElement(resElement);
+        }
+        // Read the Ident entries.
+        for (int i = 0; i < numIdntEntries; i++) {
+          uint nameOrId = reader.ReadUInt32();
+          uint elemOfst = reader.ReadUInt32();
+          if ((elemOfst & 0x80000000) != 0) // Element is subdirectory.
+            resElement = new PEResourceDirectory();
+          else
+            resElement = new PEResourceData();
+          resElement.Id = (ushort)nameOrId;
+          resElement.offset = baseOffset + (long)(elemOfst & 0x7fffffff);
+          this.AddElement(resElement);
         }
+        // Now recurse to get subdirectories/the real data.
+        foreach (PEResourceElement elem in this.elements) {
+          if ((resDirectory = elem as PEResourceDirectory) != null) {
+            reader.BaseStream.Seek(resDirectory.offset, SeekOrigin.Begin);
+            resDirectory.PopulateResourceDirectory(reader, baseOffset);
+          }
+          else if ((resData = elem as PEResourceData) != null) {
+            reader.BaseStream.Seek(resData.offset, SeekOrigin.Begin);
+            resData.PopulateResourceData(reader, baseOffset);
+          }
+        }
+      }
 
-        public void AddElement(PEResourceElement el)
-        {
-            subitems.Add(el);
+      private string ReadName(BinaryReader rdr, long offset) {
+        long savedPos = rdr.BaseStream.Position;
+        rdr.BaseStream.Seek(offset, SeekOrigin.Begin);
+        ushort nLength = rdr.ReadUInt16();
+        char[] name = new char[nLength];
+        for (int i = 0; i < nLength; i++)
+          name[i] = (char)rdr.ReadUInt16();
+        return new string(name);
+      }
+
+      public bool HasData() {
+        return elements.Count > 0;
+      }
+
+      public void AddElement(PEResourceElement el) {
+        //subItems.Add(el);
+        elements.Add(el);
+      }
+
+      /// <summary>
+      /// Total file-space size of all child elements
+      /// </summary>
+      private uint subSize;
+
+      /// <summary>
+      /// File-space needed for all names of this directory
+      /// </summary>
+      private uint nameSize;
+
+      /// <summary>
+      /// File-space taken up by this directory
+      /// </summary>
+      private uint dirSize;
+
+      /// <summary>
+      /// Number of named elements.  These come first in list.
+      /// </summary>
+      private uint numNamed;
+
+      private uint numIds { get { return (uint)elements.Count - numNamed; } }
+
+      protected internal override uint Size() {
+        nameSize = 0;
+        numNamed = 0;
+        subSize = 0;
+        //for (int i = 0; i < subItems.Count; i++) 
+        foreach (PEResourceElement elem in this.elements) {
+          subSize += elem.Size();
+          if (elem.Name != null) {
+            nameSize += 2 + (uint)elem.Name.Length * 2;
+            numNamed++;
+          }
         }
-        private uint subsize, namesize, dirsize, numnamed;
-        protected internal override uint Size()
-        {
-            namesize = 0;
-            numnamed = 0;
-            subsize = 0;
-            for (int i = 0; i < subitems.Count; i++)
-            {
-                PEResourceElement el = (PEResourceElement)subitems[i];
-                subsize += el.Size();
-                if (el.Name != null)
-                {
-                    namesize += 2 + (uint)el.Name.Length * 2;
-                    numnamed++;
-                }
-            }
-            dirsize = (uint)subitems.Count * 8 + 16;
-            return dirsize + namesize + subsize;
+        dirSize = (uint)elements.Count * EntrySize + HeaderSize;
+        return dirSize + nameSize + subSize;
+      }
+
+      /// <summary>
+      /// Write out the unmanaged resource rooted at this directory.
+      /// </summary>
+      /// <param name="dest">The Binary Writer</param>
+      /// <param name="RVA">RVA of this .rsrc section</param>
+      internal void Write(BinaryWriter dest, uint RVA) {
+        Size();
+        dest.Flush();
+        uint baseOffset = (uint)dest.BaseStream.Position;
+        this.Write(dest, baseOffset, 0, RVA);
+      }
+
+      protected internal override void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA) { 
+        uint nameOffset = currentOffset + this.dirSize;
+        uint targetOffset = currentOffset + this.dirSize;
+        dest.Write((uint)0); // characteristics
+        dest.Write(date);    // datetime
+        dest.Write(majver);
+        dest.Write(minver);
+        dest.Write((ushort)numNamed);
+        dest.Write((ushort)numIds);
+        currentOffset += HeaderSize;
+
+        // Write out the named items.
+        foreach (PEResourceElement elem in elements) { 
+          if (elem.Name != null) {
+            dest.Write((uint)(nameOffset | 0x80000000));
+            if (elem is PEResourceDirectory)
+              dest.Write((uint)(targetOffset | 0x80000000));
+            else
+              dest.Write((uint)targetOffset);
+            nameOffset += 2 + (uint)elem.Name.Length * 2;
+            targetOffset += (uint)elem.Size();
+            currentOffset += EntrySize;
+          }
         }
 
-        protected internal override void Write(BinaryWriter dest, uint RVA)
-        {
-            Size();
-            dest.Flush();
-            uint startnameoffset = (uint)dest.BaseStream.Position + (uint)dirsize;
-            uint curritemoffset = startnameoffset + (uint)namesize;
-            dest.Write((uint)0); // characteristics
-            dest.Write(date); // datetime
-            dest.Write(majver);
-            dest.Write(minver);
-            dest.Write((ushort)numnamed);
-            dest.Write((ushort)(subitems.Count - numnamed));
-
-            uint currnameoffset = startnameoffset;
-            for (int i = 0; i < subitems.Count; i++)
-            {
-                PEResourceElement el = (PEResourceElement)subitems[i];
-                if (el.Name != null)
-                {
-                    dest.Write((uint)(currnameoffset | 0x80000000));
-                    if (el is PEResourceDirectory)
-                        dest.Write((uint)(curritemoffset | 0x80000000));
-                    else
-                        dest.Write((uint)curritemoffset);
-                    currnameoffset += 2 + (uint)el.Name.Length * 2;
-                }
-                curritemoffset += el.Size();
-            }
-            curritemoffset = startnameoffset + namesize;
-            for (int i = 0; i < subitems.Count; i++)
-            {
-                PEResourceElement el = (PEResourceElement)subitems[i];
-                if (el.Name == null)
-                {
-                    dest.Write(el.Id);
-                    if (el is PEResourceDirectory)
-                        dest.Write((uint)(curritemoffset | 0x80000000));
-                    else
-                        dest.Write((uint)curritemoffset);
-                }
-                curritemoffset += el.Size();
-            }
-            for (int i = 0; i < subitems.Count; i++)
-            {
-                PEResourceElement el = (PEResourceElement)subitems[i];
-                string s = el.Name;
-                if (s != null)
-                {
-                    dest.Write((ushort)s.Length);
-                    byte[] b = System.Text.Encoding.Unicode.GetBytes(s);
-                    dest.Write(b);
-                }
-            }
-            for (int i = 0; i < subitems.Count; i++)
-            {
-                PEResourceElement el = (PEResourceElement)subitems[i];
-                el.Write(dest, RVA);
-            }
+        // Write out the items with ID.
+        foreach (PEResourceElement elem in elements) { 
+          if (elem.Name == null) {
+            dest.Write(elem.Id);
+            if (elem is PEResourceDirectory)
+              dest.Write((uint)(targetOffset | 0x80000000));
+            else
+              dest.Write((uint)targetOffset);
+            currentOffset += EntrySize;
+            targetOffset += elem.Size();
+          }
+        }
 
+        // Write out the name strings.
+        foreach (PEResourceElement elem in elements) {
+          string s = elem.Name;
+          if (s != null) {
+            dest.Write((ushort)s.Length);
+            byte[] b = System.Text.Encoding.Unicode.GetBytes(s);
+            dest.Write(b);
+          }
         }
+        currentOffset += this.nameSize;
+
+        // Now recurse to the children.
+        foreach (PEResourceElement elem in elements) {
+          elem.Write(dest, baseOffset, currentOffset, RVA);
+          currentOffset += elem.Size();
+        }
+      }
     }
 
     public class PEResourceData : PEResourceElement
@@ -171,10 +282,23 @@ namespace QUT.PERWAPI
             return 16 + (uint)Data.Length;
         }
 
-        protected internal override void Write(BinaryWriter dest, uint RVA)
+      /// <summary>
+      /// Read the binary data from the PE file.
+      /// </summary>
+      /// <param name="reader"></param>
+        internal void PopulateResourceData(PEReader reader, long baseOffset) {
+          uint dataRVA = reader.ReadUInt32();
+          int dataLength = reader.ReadInt32();
+          this.codepage = reader.ReadInt32();
+          uint junk = reader.ReadUInt32(); // Must be zero.
+          reader.BaseStream.Seek(reader.GetOffset(dataRVA), SeekOrigin.Begin);
+          data = new byte[dataLength];
+          int numberRead = reader.BaseStream.Read(data, 0, dataLength);
+        }
+
+        protected internal override void Write(BinaryWriter dest, uint baseOffset, uint currentOffset, uint RVA)
         {
-            dest.Flush();
-            dest.Write((uint)(dest.BaseStream.Position + 16) + RVA);
+            dest.Write((uint)(currentOffset + HeaderSize) + RVA);
             dest.Write((uint)data.Length);
             dest.Write((uint)codepage);
             dest.Write((uint)0);

+ 23 - 7
PERWAPI/PEWriter.cs

@@ -30,7 +30,8 @@ namespace QUT.PERWAPI
     internal class PEWriter : BinaryWriter
     {
         private Section text, sdata, rsrc = null;
-        ArrayList data;
+        ArrayList data;  // Used for accumulating data for the .sdata Section.
+        PEResourceDirectory unmanagedResourceRoot;
         BinaryWriter reloc = new BinaryWriter(new MemoryStream());
         uint dateStamp = 0, codeStart = 0;
         uint numSections = 2; // always have .text  and .reloc sections
@@ -88,7 +89,6 @@ namespace QUT.PERWAPI
                 if (!verInfo.fromExisting) verInfo.characteristics = FileImage.exeCharacteristics;
             }
             text = new Section(FileImage.textName, 0x60000020);     // IMAGE_SCN_CNT  CODE, EXECUTE, READ
-            //			rsrc = new Section(rsrcName,0x40000040);     // IMAGE_SCN_CNT  INITIALIZED_DATA, READ
             metaData = md;
             metaData.InitMetaDataOut(this);
 
@@ -425,7 +425,11 @@ namespace QUT.PERWAPI
 
         private void WriteRsrcSection()
         {
-            Console.WriteLine("Trying to write rsrc section !!!");
+            //Console.WriteLine("Trying to write rsrc section !!!");
+            long pos = BaseStream.Position;
+            this.unmanagedResourceRoot.Write(this, rsrc.RVA());
+            pos = BaseStream.Position - pos;
+            WriteZeros(NumToAlign((uint)pos, verInfo.fileAlign));
         }
 
         private void WriteRelocSection()
@@ -449,8 +453,13 @@ namespace QUT.PERWAPI
                 data = new ArrayList();
             }
             data.Add(cVal);
-            //cVal.DataOffset = sdata.Tide();
-            //sdata.IncTide(cVal.GetSize());
+        }
+
+        internal void AddUnmanagedResourceDirectory(PEResourceDirectory directory) {
+          if (rsrc == null)
+            rsrc = new Section(FileImage.rsrcName, 0x40000040);
+          rsrc.IncTide(directory.Size());
+          unmanagedResourceRoot = directory;
         }
 
         internal void WriteZeros(uint numZeros)
@@ -504,10 +513,17 @@ namespace QUT.PERWAPI
             Write(FileImage.HeapCommitSize);
             Write(FileImage.LoaderFlags);
             Write(FileImage.NumDataDirectories);  // Data Directories
-            WriteZeros(8);                  // Export Table
+            WriteZeros(8);                        // Export Table
             Write(importTableOffset + text.RVA());
             Write(totalImportTableSize);
-            WriteZeros(24);            // Resource, Exception and Certificate Tables
+
+            if (rsrc != null) {
+              Write(rsrc.RVA());
+              Write(rsrc.Tide()); // Tide() is loadedSize, Size() is sizeOnDisk.
+            }
+            else
+              WriteZeros(8);
+            WriteZeros(16);            // Exception and Certificate Tables
             Write(relocRVA);
             Write(relocTide);
             Write(debugRVA);

+ 6 - 6
PERWAPI/Utils.cs

@@ -115,7 +115,7 @@ namespace QUT.PERWAPI
         readonly static uint nibble1Mask = 0x000000F0;
 
         /// <summary>
-        /// Derives a hexademical string for a byte value
+        /// Derives a hexadecimal string for a byte value
         /// </summary>
         /// <param name="b">the byte value</param>
         /// <returns>hex string for the byte value</returns>
@@ -131,7 +131,7 @@ namespace QUT.PERWAPI
         }
 
         /// <summary>
-        /// Derives a hexademical string for a short value
+        /// Derives a hexadecimal string for a short value
         /// </summary>
         /// <param name="b">the short value</param>
         /// <returns>hex string for the short value</returns>
@@ -152,7 +152,7 @@ namespace QUT.PERWAPI
         }
 
         /// <summary>
-        /// Derives a hexademical string for an int value
+        /// Derives a hexadecimal string for an int value
         /// </summary>
         /// <param name="val">the int value</param>
         /// <returns>hex string for the int value</returns>
@@ -174,7 +174,7 @@ namespace QUT.PERWAPI
         }
 
         /// <summary>
-        /// Derives a hexademical string for an unsigned int value
+        /// Derives a hexadecimal string for an unsigned int value
         /// </summary>
         /// <param name="num">the unsigned int value</param>
         /// <returns>hex string for the unsigned int value</returns>
@@ -195,7 +195,7 @@ namespace QUT.PERWAPI
         }
 
         /// <summary>
-        /// Derives a hexademical string for a long value
+        /// Derives a hexadecimal string for a long value
         /// </summary>
         /// <param name="lnum">the long value</param>
         /// <returns>hex string for the long value</returns>
@@ -206,7 +206,7 @@ namespace QUT.PERWAPI
         }
 
         /// <summary>
-        /// Derives a hexademical string for an unsigned long value
+        /// Derives a hexadecimal string for an unsigned long value
         /// </summary>
         /// <param name="num">the unsigned long value</param>
         /// <returns>hex string for the unsigned long value</returns>