private BinaryXmlTemplate ReadBinaryXmlTemplateRecord(FragmentHeader fragmentHeader, EventRecord record, ref int offset) { BinaryXmlTemplate template = new BinaryXmlTemplate(); ////first 4 bytes appear to be null offset += 4; this.guidBuffer.Initialize(); Buffer.BlockCopy(this.buffer, offset, this.guidBuffer, 0, this.guidBuffer.Length); offset += this.guidBuffer.Length; template = new BinaryXmlTemplate(); template.TemplateGuid = new Guid(this.guidBuffer); template.ChunkOffset = fragmentHeader.TemplateDefinitionOffset; template.Size = BitConverter.ToInt32(this.buffer, offset); this.templates.Add(fragmentHeader.TemplateDefinitionOffset, template); ////62-66 should be a new static fragment header offset += 4; int fragmentSignature = BitConverter.ToInt32(this.buffer, offset); if (fragmentSignature != FragmentHeader.Signature) { throw new FragmentHeaderNotFoundException( string.Format("Fragment Header for BinaryXmlTemplate not found at offset {0} in ElfChnk offset {1}", offset, this.fileOffset), this.fileOffset, record.GetNextRecordOffset()); } offset += 4; return(template); }
public IEnumerable <EventRecord> ReadEventRecords(int startingOffset) { int offset = startingOffset; while (offset < this.buffer.Length) { ////verify header int signature = BitConverter.ToInt32(this.buffer, offset); if (signature != recordSignature) { break; } EventRecord record = new EventRecord(); record.ChunkOffset = offset; offset += 4; record.BlockSize = BitConverter.ToInt32(this.buffer, offset); offset += 4; record.RecordId = BitConverter.ToUInt64(this.buffer, offset); offset += 8; record.RecordTime = DateTime.FromFileTime(BitConverter.ToInt64(this.buffer, offset)); offset += 8; FragmentHeader parentFragmentHeader = this.ReadFragmentHeader(record, ref offset); BinaryXmlTemplate template = null; if (parentFragmentHeader.ContainsTemplateDefinition) { template = this.ReadBinaryXmlTemplateRecord(parentFragmentHeader, record, ref offset); int recordOffset = offset; try { template.RootElement = this.ReadBinaryXmlElement(ref recordOffset); offset = recordOffset; } catch (Exception ex) { throw new BinaryXmlReadException(string.Format("Problem reading BinaryXml for record at offset {0} in ElfChunk offset {1}: {2}", record.ChunkOffset, this.fileOffset, ex.Message), this.fileOffset, record.GetNextRecordOffset()); } } template = this.templates[parentFragmentHeader.TemplateDefinitionOffset]; ////whether we found a template or not, we should be in position to read value descriptors int valuesOffset = offset; ValueDescriptor[] descriptors = this.ReadValueDescriptors(ref valuesOffset); ////before enumerating the template elements check if the template found the event data yet. if (template.RootElement.ValueType == BinaryValueType.BinXmlType) { if (!template.RootElement.Children.Any(e => e.Name == "EventData")) { valuesOffset = descriptors[template.RootElement.ValueIndex].Offset; FragmentHeader childFragmentHeader = this.ReadFragmentHeader(record, ref valuesOffset); ////Revisit to see if we really need to store the eventDataTemplate in the Dictionary BinaryXmlTemplate eventDataTemplate = null; if (childFragmentHeader.ContainsTemplateDefinition) { eventDataTemplate = this.ReadBinaryXmlTemplateRecord(childFragmentHeader, record, ref valuesOffset); eventDataTemplate.RootElement = this.ReadBinaryXmlElement(ref valuesOffset); } eventDataTemplate = this.templates[childFragmentHeader.TemplateDefinitionOffset]; template.RootElement.Children.Add(eventDataTemplate.RootElement); } else { ////move the offset forward to the inner value descriptors for event data valuesOffset = descriptors[template.RootElement.ValueIndex].Offset; FragmentHeader instanceHeader = this.ReadFragmentHeader(record, ref valuesOffset); } } foreach (BinaryXmlElement element in template.RootElement.EnumerateAllChildren().Where(e => e.Name != "Data")) { switch (element.Name) { case "Level": byte eventLevel = this.buffer[descriptors[element.ValueIndex].Offset]; record.SetEventLevel(eventLevel); break; case "Provider": if (element.Attributes.Count > 0) { if (!string.IsNullOrEmpty(element.Attributes[0].Value)) { record.Provider = element.Attributes[0].Value; } else { ////might be optional substitution record.Provider = Encoding.Unicode.GetString( this.buffer, descriptors[element.Attributes[0].ValueIndex].Offset, descriptors[element.Attributes[0].ValueIndex].Size); } } else { record.Provider = "Unknown"; } break; case "TimeCreated": BinaryXmlAttribute systemTimeAttribute = element.Attributes.FirstOrDefault(e => e.Name == "SystemTime"); if (systemTimeAttribute != null && systemTimeAttribute.ValueType == BinaryValueType.FileTime) { long fileTime = BitConverter.ToInt64(this.buffer, descriptors[systemTimeAttribute.ValueIndex].Offset); record.TimeCreated = DateTime.FromFileTime(fileTime); } break; case "EventData": ValueDescriptor[] eventDataDescriptors = this.ReadValueDescriptors(ref valuesOffset); StringBuilder message = new StringBuilder(); foreach (BinaryXmlElement dataElement in element.EnumerateAllChildren().Where(e => e.Name == "Data" && e.ValueType == BinaryValueType.Utf16StringArray)) { ////There is a null terminator at the end of these int dataOffset = eventDataDescriptors[dataElement.ValueIndex].Offset; int dataSize = eventDataDescriptors[dataElement.ValueIndex].Size; if (dataOffset + dataSize > this.buffer.Length) { ////I think the buffer is recycled by the writer, and not cleaned up afterwards. I was seeing garbage data after the LastRecordId. ////I believe it just so happened that the garbage started with 0x2A2A0000 which made the logic believe there was a next record in the chunk. ////Even so, I will leave this here just in case this scenario really does occur. throw new EventDataOutsideOfBufferException( string.Format("Event data is refrenced outside of buffer. Record Offset: {0} BinaryXmlElement.Offset: {1}", this.FileOffset + record.ChunkOffset, dataElement.Offset), this.fileOffset + 65536, (int)ElfChunk.HeaderSize); } message.Append(Encoding.Unicode.GetString(this.buffer, dataOffset, dataSize).TrimEnd('\0')); } record.Message = message.ToString(); break; } } offset = record.GetNextRecordOffset(); yield return(record); if (record.RecordId == this.lastRecordId) { yield break; } } }