/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Core/Portable/CvtRes.cs
861 строка
40 KB
Julien Couvreur
Warn for multiple blank lines in source (#65451)
17 ноя 2022, 22:55
Не верифицирован
17 ноя 2022, 22:55
a8824e8
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Microsoft.CodeAnalysis.Text; using System.Diagnostics; using BYTE = System.Byte; using DWORD = System.UInt32; using WCHAR = System.Char; using WORD = System.UInt16; using System.Reflection.PortableExecutable; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { internal class RESOURCE { internal RESOURCE_STRING? pstringType; internal RESOURCE_STRING? pstringName; internal DWORD DataSize; // size of data without header internal DWORD HeaderSize; // Length of the header // [Ordinal or Name TYPE] // [Ordinal or Name NAME] internal DWORD DataVersion; // version of data struct internal WORD MemoryFlags; // state of the resource internal WORD LanguageId; // Unicode support for NLS internal DWORD Version; // Version of the resource data internal DWORD Characteristics; // Characteristics of the data internal byte[]? data; //data }; internal class RESOURCE_STRING { internal WORD Ordinal; internal string? theString; }; /// <summary> /// Parses .RES a file into its constituent resource elements. /// Mostly translated from cvtres.cpp. /// </summary> internal class CvtResFile { private const WORD RT_DLGINCLUDE = 17; internal static List<RESOURCE> ReadResFile(Stream stream) { var reader = new BinaryReader(stream, Encoding.Unicode); var resourceNames = new List<RESOURCE>(); var startPos = stream.Position; var initial32Bits = reader.ReadUInt32(); //RC.EXE output starts with a resource that contains no data. if (initial32Bits != 0) throw new ResourceException("Stream does not begin with a null resource and is not in .RES format."); stream.Position = startPos; // Build up Type and Name directories while (stream.Position < stream.Length) { // Get the sizes from the file var cbData = reader.ReadUInt32(); var cbHdr = reader.ReadUInt32(); if (cbHdr < 2 * sizeof(DWORD)) { throw new ResourceException(String.Format("Resource header beginning at offset 0x{0:x} is malformed.", stream.Position - 8)); //ErrorPrint(ERR_FILECORRUPT, szFilename); } // Discard null resource if (cbData == 0) { stream.Position += cbHdr - 2 * sizeof(DWORD); continue; } var pAdditional = new RESOURCE() { HeaderSize = cbHdr, DataSize = cbData }; // Read the TYPE and NAME pAdditional.pstringType = ReadStringOrID(reader); pAdditional.pstringName = ReadStringOrID(reader); //round up to dword boundary. stream.Position = (stream.Position + 3) & ~3; // Read the rest of the header pAdditional.DataVersion = reader.ReadUInt32(); pAdditional.MemoryFlags = reader.ReadUInt16(); pAdditional.LanguageId = reader.ReadUInt16(); pAdditional.Version = reader.ReadUInt32(); pAdditional.Characteristics = reader.ReadUInt32(); pAdditional.data = new byte[pAdditional.DataSize]; reader.Read(pAdditional.data, 0, pAdditional.data.Length); stream.Position = (stream.Position + 3) & ~3; if (pAdditional.pstringType.theString == null && (pAdditional.pstringType.Ordinal == (WORD)RT_DLGINCLUDE)) { // Ignore DLGINCLUDE resources continue; } resourceNames.Add(pAdditional); } return resourceNames; } private static RESOURCE_STRING ReadStringOrID(BinaryReader fhIn) { // Reads a String structure from fhIn // If the first word is 0xFFFF then this is an ID // return the ID instead RESOURCE_STRING pstring = new RESOURCE_STRING(); WCHAR firstWord = fhIn.ReadChar(); if (firstWord == 0xFFFF) { // An ID pstring.Ordinal = fhIn.ReadUInt16(); } else { // A string pstring.Ordinal = 0xFFFF; //keep reading until null reached. StringBuilder sb = new StringBuilder(); WCHAR curChar = firstWord; do { sb.Append(curChar); curChar = fhIn.ReadChar(); } while (curChar != 0); pstring.theString = sb.ToString(); } return (pstring); } } internal static class COFFResourceReader { private static void ConfirmSectionValues(SectionHeader hdr, long fileSize) { if ((long)hdr.PointerToRawData + hdr.SizeOfRawData > fileSize) throw new ResourceException(CodeAnalysisResources.CoffResourceInvalidSectionSize); } internal static Microsoft.Cci.ResourceSection ReadWin32ResourcesFromCOFF(Stream stream) { var peHeaders = new PEHeaders(stream); var rsrc1 = new SectionHeader(); var rsrc2 = new SectionHeader(); int foundCount = 0; foreach (var sectionHeader in peHeaders.SectionHeaders) { if (sectionHeader.Name == ".rsrc$01") { rsrc1 = sectionHeader; foundCount++; } else if (sectionHeader.Name == ".rsrc$02") { rsrc2 = sectionHeader; foundCount++; } } if (foundCount != 2) throw new ResourceException(CodeAnalysisResources.CoffResourceMissingSection); ConfirmSectionValues(rsrc1, stream.Length); ConfirmSectionValues(rsrc2, stream.Length); //This will be the final resource section bytes without a header. It contains the concatenation //of .rsrc$02 on to the end of .rsrc$01. var imageResourceSectionBytes = new byte[checked(rsrc1.SizeOfRawData + rsrc2.SizeOfRawData)]; stream.Seek(rsrc1.PointerToRawData, SeekOrigin.Begin); stream.TryReadAll(imageResourceSectionBytes, 0, rsrc1.SizeOfRawData); // ConfirmSectionValues ensured that data are available stream.Seek(rsrc2.PointerToRawData, SeekOrigin.Begin); stream.TryReadAll(imageResourceSectionBytes, rsrc1.SizeOfRawData, rsrc2.SizeOfRawData); // ConfirmSectionValues ensured that data are available const int SizeOfRelocationEntry = 10; try { var relocLastAddress = checked(rsrc1.PointerToRelocations + (rsrc1.NumberOfRelocations * SizeOfRelocationEntry)); if (relocLastAddress > stream.Length) throw new ResourceException(CodeAnalysisResources.CoffResourceInvalidRelocation); } catch (OverflowException) { throw new ResourceException(CodeAnalysisResources.CoffResourceInvalidRelocation); } //.rsrc$01 contains the directory tree. .rsrc$02 contains the raw resource data. //.rsrc$01 has references to spots in .rsrc$02. Those spots are expressed as relocations. //These will need to be fixed up when the RVA of the .rsrc section in the final image is known. var relocationOffsets = new uint[rsrc1.NumberOfRelocations]; //offsets into .rsrc$01 var relocationSymbolIndices = new uint[rsrc1.NumberOfRelocations]; var reader = new BinaryReader(stream, Encoding.Unicode); stream.Position = rsrc1.PointerToRelocations; for (int i = 0; i < rsrc1.NumberOfRelocations; i++) { relocationOffsets[i] = reader.ReadUInt32(); //What is being read and stored is the reloc's "Value" //This is the symbol's index. relocationSymbolIndices[i] = reader.ReadUInt32(); reader.ReadUInt16(); //we do nothing with the "Type" } //now that symbol indices are gathered, begin indexing the symbols stream.Position = peHeaders.CoffHeader.PointerToSymbolTable; const uint ImageSizeOfSymbol = 18; try { var lastSymAddress = checked(peHeaders.CoffHeader.PointerToSymbolTable + peHeaders.CoffHeader.NumberOfSymbols * ImageSizeOfSymbol); if (lastSymAddress > stream.Length) throw new ResourceException(CodeAnalysisResources.CoffResourceInvalidSymbol); } catch (OverflowException) { throw new ResourceException(CodeAnalysisResources.CoffResourceInvalidSymbol); } var outputStream = new MemoryStream(imageResourceSectionBytes); var writer = new BinaryWriter(outputStream); //encoding shouldn't matter. There are no strings being written. for (int i = 0; i < relocationSymbolIndices.Length; i++) { if (relocationSymbolIndices[i] > peHeaders.CoffHeader.NumberOfSymbols) throw new ResourceException(CodeAnalysisResources.CoffResourceInvalidRelocation); var offsetOfSymbol = peHeaders.CoffHeader.PointerToSymbolTable + relocationSymbolIndices[i] * ImageSizeOfSymbol; stream.Position = offsetOfSymbol; stream.Position += 8; //skip over symbol name var symValue = reader.ReadUInt32(); var symSection = reader.ReadInt16(); var symType = reader.ReadUInt16(); //ignore the rest of the fields. const ushort IMAGE_SYM_TYPE_NULL = 0x0000; if (symType != IMAGE_SYM_TYPE_NULL || symSection != 3) //3rd section is .rsrc$02 throw new ResourceException(CodeAnalysisResources.CoffResourceInvalidSymbol); //perform relocation. We are concatenating the contents of .rsrc$02 (the raw resource data) //on to the end of .rsrc$01 (the directory tree) to yield the final resource section for the image. //The directory tree has references into the raw resource data. These references are expressed //in the final image as file positions, not positions relative to the beginning of the section. //First make the resources be relative to the beginning of the section by adding the size //of .rsrc$01 to them. They will ultimately need the RVA of the final image resource section added //to them. We don't know that yet. That is why the array of offsets is preserved. outputStream.Position = relocationOffsets[i]; writer.Write((uint)(symValue + rsrc1.SizeOfRawData)); } return new Cci.ResourceSection(imageResourceSectionBytes, relocationOffsets); } } internal static class Win32ResourceConversions { private struct ICONDIRENTRY { internal BYTE bWidth; internal BYTE bHeight; internal BYTE bColorCount; internal BYTE bReserved; internal WORD wPlanes; internal WORD wBitCount; internal DWORD dwBytesInRes; internal DWORD dwImageOffset; }; internal static void AppendIconToResourceStream(Stream resStream, Stream iconStream) { var iconReader = new BinaryReader(iconStream); //read magic reserved WORD var reserved = iconReader.ReadUInt16(); if (reserved != 0) throw new ResourceException(CodeAnalysisResources.IconStreamUnexpectedFormat); var type = iconReader.ReadUInt16(); if (type != 1) throw new ResourceException(CodeAnalysisResources.IconStreamUnexpectedFormat); var count = iconReader.ReadUInt16(); if (count == 0) throw new ResourceException(CodeAnalysisResources.IconStreamUnexpectedFormat); var iconDirEntries = new ICONDIRENTRY[count]; for (ushort i = 0; i < count; i++) { // Read the Icon header iconDirEntries[i].bWidth = iconReader.ReadByte(); iconDirEntries[i].bHeight = iconReader.ReadByte(); iconDirEntries[i].bColorCount = iconReader.ReadByte(); iconDirEntries[i].bReserved = iconReader.ReadByte(); iconDirEntries[i].wPlanes = iconReader.ReadUInt16(); iconDirEntries[i].wBitCount = iconReader.ReadUInt16(); iconDirEntries[i].dwBytesInRes = iconReader.ReadUInt32(); iconDirEntries[i].dwImageOffset = iconReader.ReadUInt32(); } // Because Icon files don't seem to record the actual w and BitCount in // the ICONDIRENTRY, get the info from the BITMAPINFOHEADER at the beginning // of the data here: //EDMAURER: PNG compressed icons must be treated differently. Do what has always //been done for uncompressed icons. Assume modern, compressed icons set the //ICONDIRENTRY fields correctly. //if (*(DWORD*)icoBuffer == sizeof(BITMAPINFOHEADER)) //{ // grp[i].Planes = ((BITMAPINFOHEADER*)icoBuffer)->biPlanes; // grp[i].BitCount = ((BITMAPINFOHEADER*)icoBuffer)->biBitCount; //} for (ushort i = 0; i < count; i++) { iconStream.Position = iconDirEntries[i].dwImageOffset; if (iconReader.ReadUInt32() == 40) { iconStream.Position += 8; iconDirEntries[i].wPlanes = iconReader.ReadUInt16(); iconDirEntries[i].wBitCount = iconReader.ReadUInt16(); } } //read everything and no exceptions. time to write. var resWriter = new BinaryWriter(resStream); //write all of the icon images as individual resources, then follow up with //a resource that groups them. const WORD RT_ICON = 3; for (ushort i = 0; i < count; i++) { /* write resource header. struct RESOURCEHEADER { DWORD DataSize; DWORD HeaderSize; WORD Magic1; WORD Type; WORD Magic2; WORD Name; DWORD DataVersion; WORD MemoryFlags; WORD LanguageId; DWORD Version; DWORD Characteristics; }; */ resStream.Position = (resStream.Position + 3) & ~3; //headers begin on 4-byte boundaries. resWriter.Write((DWORD)iconDirEntries[i].dwBytesInRes); resWriter.Write((DWORD)0x00000020); resWriter.Write((WORD)0xFFFF); resWriter.Write((WORD)RT_ICON); resWriter.Write((WORD)0xFFFF); resWriter.Write((WORD)(i + 1)); //EDMAURER this is not general. Implies you can only append one icon to the resources. //This icon ID would seem to be global among all of the icons not just this group. //Zero appears to not be an acceptable ID. Note that this ID is referred to below. resWriter.Write((DWORD)0x00000000); resWriter.Write((WORD)0x1010); resWriter.Write((WORD)0x0000); resWriter.Write((DWORD)0x00000000); resWriter.Write((DWORD)0x00000000); //write the data. iconStream.Position = iconDirEntries[i].dwImageOffset; resWriter.Write(iconReader.ReadBytes(checked((int)iconDirEntries[i].dwBytesInRes))); } /* struct ICONDIR { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource Type (1 for icons) WORD idCount; // How many images? ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) }/ struct ICONRESDIR { BYTE Width; // = ICONDIRENTRY.bWidth; BYTE Height; // = ICONDIRENTRY.bHeight; BYTE ColorCount; // = ICONDIRENTRY.bColorCount; BYTE reserved; // = ICONDIRENTRY.bReserved; WORD Planes; // = ICONDIRENTRY.wPlanes; WORD BitCount; // = ICONDIRENTRY.wBitCount; DWORD BytesInRes; // = ICONDIRENTRY.dwBytesInRes; WORD IconId; // = RESOURCEHEADER.Name }; */ const WORD RT_GROUP_ICON = RT_ICON + 11; resStream.Position = (resStream.Position + 3) & ~3; //align 4-byte boundary //write the icon group. first a RESOURCEHEADER. the data is the ICONDIR resWriter.Write((DWORD)(3 * sizeof(WORD) + count * /*sizeof(ICONRESDIR)*/ 14)); resWriter.Write((DWORD)0x00000020); resWriter.Write((WORD)0xFFFF); resWriter.Write((WORD)RT_GROUP_ICON); resWriter.Write((WORD)0xFFFF); resWriter.Write((WORD)0x7F00); //IDI_APPLICATION resWriter.Write((DWORD)0x00000000); resWriter.Write((WORD)0x1030); resWriter.Write((WORD)0x0000); resWriter.Write((DWORD)0x00000000); resWriter.Write((DWORD)0x00000000); //the ICONDIR resWriter.Write((WORD)0x0000); resWriter.Write((WORD)0x0001); resWriter.Write((WORD)count); for (ushort i = 0; i < count; i++) { resWriter.Write((BYTE)iconDirEntries[i].bWidth); resWriter.Write((BYTE)iconDirEntries[i].bHeight); resWriter.Write((BYTE)iconDirEntries[i].bColorCount); resWriter.Write((BYTE)iconDirEntries[i].bReserved); resWriter.Write((WORD)iconDirEntries[i].wPlanes); resWriter.Write((WORD)iconDirEntries[i].wBitCount); resWriter.Write((DWORD)iconDirEntries[i].dwBytesInRes); resWriter.Write((WORD)(i + 1)); //ID } } /* * Dev10 alink had the following fallback behavior. private uint[] FileVersion { get { if (fileVersionContents != null) return fileVersionContents; else { System.Diagnostics.Debug.Assert(assemblyVersionContents != null); return assemblyVersionContents; } } } private uint[] ProductVersion { get { if (productVersionContents != null) return productVersionContents; else return this.FileVersion; } } */ internal static void AppendVersionToResourceStream(Stream resStream, bool isDll, string fileVersion, //should be [major.minor.build.rev] but doesn't have to be string originalFileName, string internalName, string productVersion, //4 ints Version assemblyVersion, //individual values must be smaller than 65535 string fileDescription = " ", //the old compiler put blank here if nothing was user-supplied string legalCopyright = " ", //the old compiler put blank here if nothing was user-supplied string? legalTrademarks = null, string? productName = null, string? comments = null, string? companyName = null) { var resWriter = new BinaryWriter(resStream, Encoding.Unicode); resStream.Position = (resStream.Position + 3) & ~3; const DWORD RT_VERSION = 16; var ver = new VersionResourceSerializer(isDll, comments, companyName, fileDescription, fileVersion, internalName, legalCopyright, legalTrademarks, originalFileName, productName, productVersion, assemblyVersion); var startPos = resStream.Position; var dataSize = ver.GetDataSize(); const int headerSize = 0x20; resWriter.Write((DWORD)dataSize); //data size resWriter.Write((DWORD)headerSize); //header size resWriter.Write((WORD)0xFFFF); //identifies type as ordinal. resWriter.Write((WORD)RT_VERSION); //type resWriter.Write((WORD)0xFFFF); //identifies name as ordinal. resWriter.Write((WORD)0x0001); //only ever 1 ver resource (what Dev10 does) resWriter.Write((DWORD)0x00000000); //data version resWriter.Write((WORD)0x0030); //memory flags (this is what the Dev10 compiler uses) resWriter.Write((WORD)0x0000); //languageId resWriter.Write((DWORD)0x00000000); //version resWriter.Write((DWORD)0x00000000); //characteristics ver.WriteVerResource(resWriter); System.Diagnostics.Debug.Assert(resStream.Position - startPos == dataSize + headerSize); } internal static void AppendManifestToResourceStream(Stream resStream, Stream manifestStream, bool isDll) { resStream.Position = (resStream.Position + 3) & ~3; const WORD RT_MANIFEST = 24; var resWriter = new BinaryWriter(resStream); resWriter.Write((DWORD)(manifestStream.Length)); //data size resWriter.Write((DWORD)0x00000020); //header size resWriter.Write((WORD)0xFFFF); //identifies type as ordinal. resWriter.Write((WORD)RT_MANIFEST); //type resWriter.Write((WORD)0xFFFF); //identifies name as ordinal. resWriter.Write((WORD)((isDll) ? 0x0002 : 0x0001)); //EDMAURER executables are named "1", DLLs "2" resWriter.Write((DWORD)0x00000000); //data version resWriter.Write((WORD)0x1030); //memory flags resWriter.Write((WORD)0x0000); //languageId resWriter.Write((DWORD)0x00000000); //version resWriter.Write((DWORD)0x00000000); //characteristics manifestStream.CopyTo(resStream); } private class VersionResourceSerializer { private readonly string? _commentsContents; private readonly string? _companyNameContents; private readonly string _fileDescriptionContents; private readonly string _fileVersionContents; private readonly string _internalNameContents; private readonly string _legalCopyrightContents; private readonly string? _legalTrademarksContents; private readonly string _originalFileNameContents; private readonly string? _productNameContents; private readonly string _productVersionContents; private readonly Version _assemblyVersionContents; private const string vsVersionInfoKey = "VS_VERSION_INFO"; private const string varFileInfoKey = "VarFileInfo"; private const string translationKey = "Translation"; private const string stringFileInfoKey = "StringFileInfo"; private readonly string _langIdAndCodePageKey; //should be 8 characters private const DWORD CP_WINUNICODE = 1200; private const ushort sizeVS_FIXEDFILEINFO = sizeof(DWORD) * 13; private readonly bool _isDll; internal VersionResourceSerializer(bool isDll, string? comments, string? companyName, string fileDescription, string fileVersion, string internalName, string legalCopyright, string? legalTrademark, string originalFileName, string? productName, string productVersion, Version assemblyVersion) { _isDll = isDll; _commentsContents = comments; _companyNameContents = companyName; _fileDescriptionContents = fileDescription; _fileVersionContents = fileVersion; _internalNameContents = internalName; _legalCopyrightContents = legalCopyright; _legalTrademarksContents = legalTrademark; _originalFileNameContents = originalFileName; _productNameContents = productName; _productVersionContents = productVersion; _assemblyVersionContents = assemblyVersion; _langIdAndCodePageKey = System.String.Format("{0:x4}{1:x4}", 0 /*langId*/, CP_WINUNICODE /*codepage*/); } private const uint VFT_APP = 0x00000001; private const uint VFT_DLL = 0x00000002; private IEnumerable<KeyValuePair<string, string>> GetVerStrings() { if (_commentsContents != null) yield return new KeyValuePair<string, string>("Comments", _commentsContents); if (_companyNameContents != null) yield return new KeyValuePair<string, string>("CompanyName", _companyNameContents); if (_fileDescriptionContents != null) yield return new KeyValuePair<string, string>("FileDescription", _fileDescriptionContents); yield return new KeyValuePair<string, string>("FileVersion", _fileVersionContents); if (_internalNameContents != null) yield return new KeyValuePair<string, string>("InternalName", _internalNameContents); if (_legalCopyrightContents != null) yield return new KeyValuePair<string, string>("LegalCopyright", _legalCopyrightContents); if (_legalTrademarksContents != null) yield return new KeyValuePair<string, string>("LegalTrademarks", _legalTrademarksContents); if (_originalFileNameContents != null) yield return new KeyValuePair<string, string>("OriginalFilename", _originalFileNameContents); if (_productNameContents != null) yield return new KeyValuePair<string, string>("ProductName", _productNameContents); yield return new KeyValuePair<string, string>("ProductVersion", _productVersionContents); if (_assemblyVersionContents != null) yield return new KeyValuePair<string, string>("Assembly Version", _assemblyVersionContents.ToString()); } private uint FileType { get { return (_isDll) ? VFT_DLL : VFT_APP; } } private void WriteVSFixedFileInfo(BinaryWriter writer) { //There's nothing guaranteeing that these are n.n.n.n format. //The documentation says that if they're not that format the behavior is undefined. Version fileVersion; VersionHelper.TryParse(_fileVersionContents, version: out fileVersion); Version productVersion; VersionHelper.TryParse(_productVersionContents, version: out productVersion); writer.Write((DWORD)0xFEEF04BD); writer.Write((DWORD)0x00010000); writer.Write((DWORD)((uint)fileVersion.Major << 16) | (uint)fileVersion.Minor); writer.Write((DWORD)((uint)fileVersion.Build << 16) | (uint)fileVersion.Revision); writer.Write((DWORD)((uint)productVersion.Major << 16) | (uint)productVersion.Minor); writer.Write((DWORD)((uint)productVersion.Build << 16) | (uint)productVersion.Revision); writer.Write((DWORD)0x0000003F); //VS_FFI_FILEFLAGSMASK (EDMAURER) really? all these bits are valid? writer.Write((DWORD)0); //file flags writer.Write((DWORD)0x00000004); //VOS__WINDOWS32 writer.Write((DWORD)this.FileType); writer.Write((DWORD)0); //file subtype writer.Write((DWORD)0); //date most sig writer.Write((DWORD)0); //date least sig } /// <summary> /// Assume that 3 WORDs preceded this string and that they began 32-bit aligned. /// Given the string length compute the number of bytes that should be written to end /// the buffer on a 32-bit boundary</summary> /// <param name="cb"></param> /// <returns></returns> private static int PadKeyLen(int cb) { //add previously written 3 WORDS, round up, then subtract the 3 WORDS. return PadToDword(cb + 3 * sizeof(WORD)) - 3 * sizeof(WORD); } /// <summary> /// assuming the length of bytes submitted began on a 32-bit boundary, /// round up this length as necessary so that it ends at a 32-bit boundary. /// </summary> /// <param name="cb"></param> /// <returns></returns> private static int PadToDword(int cb) { return (cb + 3) & ~3; } private const int HDRSIZE = 3 * sizeof(ushort); private static ushort SizeofVerString(string lpszKey, string lpszValue) { int cbKey, cbValue; cbKey = (lpszKey.Length + 1) * 2; // Make room for the NULL cbValue = (lpszValue.Length + 1) * 2; return checked((ushort)(PadKeyLen(cbKey) + // key, 0 padded to DWORD boundary cbValue + // value HDRSIZE)); // block header. } private static void WriteVersionString(KeyValuePair<string, string> keyValuePair, BinaryWriter writer) { RoslynDebug.Assert(keyValuePair.Value != null); ushort cbBlock = SizeofVerString(keyValuePair.Key, keyValuePair.Value); int cbKey = (keyValuePair.Key.Length + 1) * 2; // includes terminating NUL int cbVal = (keyValuePair.Value.Length + 1) * 2; // includes terminating NUL var startPos = writer.BaseStream.Position; Debug.Assert((startPos & 3) == 0); writer.Write((WORD)cbBlock); writer.Write((WORD)(keyValuePair.Value.Length + 1)); //add 1 for nul writer.Write((WORD)1); writer.Write(keyValuePair.Key.ToCharArray()); writer.Write((WORD)'\0'); writer.Write(new byte[PadKeyLen(cbKey) - cbKey]); Debug.Assert((writer.BaseStream.Position & 3) == 0); writer.Write(keyValuePair.Value.ToCharArray()); writer.Write((WORD)'\0'); //writer.Write(new byte[PadToDword(cbVal) - cbVal]); System.Diagnostics.Debug.Assert(cbBlock == writer.BaseStream.Position - startPos); } /// <summary> /// compute number of chars needed to end up on a 32-bit boundary assuming that three /// WORDS preceded this string. /// </summary> /// <param name="sz"></param> /// <returns></returns> private static int KEYSIZE(string sz) { return PadKeyLen((sz.Length + 1) * sizeof(WCHAR)) / sizeof(WCHAR); } private static int KEYBYTES(string sz) { return KEYSIZE(sz) * sizeof(WCHAR); } private int GetStringsSize() { int sum = 0; foreach (var verString in GetVerStrings()) { sum = (sum + 3) & ~3; //ensure that each String data structure starts on a 32bit boundary. sum += SizeofVerString(verString.Key, verString.Value); } return sum; } internal int GetDataSize() { int sizeEXEVERRESOURCE = sizeof(WORD) * 3 * 5 + 2 * sizeof(WORD) + //five headers + two words for CP and lang KEYBYTES(vsVersionInfoKey) + KEYBYTES(varFileInfoKey) + KEYBYTES(translationKey) + KEYBYTES(stringFileInfoKey) + KEYBYTES(_langIdAndCodePageKey) + sizeVS_FIXEDFILEINFO; return GetStringsSize() + sizeEXEVERRESOURCE; } internal void WriteVerResource(BinaryWriter writer) { /* must be assumed to start on a 32-bit boundary. * * the sub-elements of the VS_VERSIONINFO consist of a header (3 WORDS) a string * and then beginning on the next 32-bit boundary, the elements children struct VS_VERSIONINFO { WORD cbRootBlock; // size of whole resource WORD cbRootValue; // size of VS_FIXEDFILEINFO structure WORD fRootText; // root is text? WCHAR szRootKey[KEYSIZE("VS_VERSION_INFO")]; // Holds "VS_VERSION_INFO" VS_FIXEDFILEINFO vsFixed; // fixed information. WORD cbVarBlock; // size of VarFileInfo block WORD cbVarValue; // always 0 WORD fVarText; // VarFileInfo is text? WCHAR szVarKey[KEYSIZE("VarFileInfo")]; // Holds "VarFileInfo" WORD cbTransBlock; // size of Translation block WORD cbTransValue; // size of Translation value WORD fTransText; // Translation is text? WCHAR szTransKey[KEYSIZE("Translation")]; // Holds "Translation" WORD langid; // language id WORD codepage; // codepage id WORD cbStringBlock; // size of StringFileInfo block WORD cbStringValue; // always 0 WORD fStringText; // StringFileInfo is text? WCHAR szStringKey[KEYSIZE("StringFileInfo")]; // Holds "StringFileInfo" WORD cbLangCpBlock; // size of language/codepage block WORD cbLangCpValue; // always 0 WORD fLangCpText; // LangCp is text? WCHAR szLangCpKey[KEYSIZE("12345678")]; // Holds hex version of language/codepage // followed by strings }; */ var debugPos = writer.BaseStream.Position; var dataSize = GetDataSize(); writer.Write((WORD)dataSize); writer.Write((WORD)sizeVS_FIXEDFILEINFO); writer.Write((WORD)0); writer.Write(vsVersionInfoKey.ToCharArray()); writer.Write(new byte[KEYBYTES(vsVersionInfoKey) - vsVersionInfoKey.Length * 2]); System.Diagnostics.Debug.Assert((writer.BaseStream.Position & 3) == 0); WriteVSFixedFileInfo(writer); writer.Write((WORD)(sizeof(WORD) * 2 + 2 * HDRSIZE + KEYBYTES(varFileInfoKey) + KEYBYTES(translationKey))); writer.Write((WORD)0); writer.Write((WORD)1); writer.Write(varFileInfoKey.ToCharArray()); writer.Write(new byte[KEYBYTES(varFileInfoKey) - varFileInfoKey.Length * 2]); //padding System.Diagnostics.Debug.Assert((writer.BaseStream.Position & 3) == 0); writer.Write((WORD)(sizeof(WORD) * 2 + HDRSIZE + KEYBYTES(translationKey))); writer.Write((WORD)(sizeof(WORD) * 2)); writer.Write((WORD)0); writer.Write(translationKey.ToCharArray()); writer.Write(new byte[KEYBYTES(translationKey) - translationKey.Length * 2]); //padding System.Diagnostics.Debug.Assert((writer.BaseStream.Position & 3) == 0); writer.Write((WORD)0); //langId; MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)) = 0 writer.Write((WORD)CP_WINUNICODE); //codepage; 1200 = CP_WINUNICODE System.Diagnostics.Debug.Assert((writer.BaseStream.Position & 3) == 0); writer.Write((WORD)(2 * HDRSIZE + KEYBYTES(stringFileInfoKey) + KEYBYTES(_langIdAndCodePageKey) + GetStringsSize())); writer.Write((WORD)0); writer.Write((WORD)1); writer.Write(stringFileInfoKey.ToCharArray()); //actually preceded by 5 WORDS so not consistent with the //assumptions of KEYBYTES, but equivalent. writer.Write(new byte[KEYBYTES(stringFileInfoKey) - stringFileInfoKey.Length * 2]); //padding. System.Diagnostics.Debug.Assert((writer.BaseStream.Position & 3) == 0); writer.Write((WORD)(HDRSIZE + KEYBYTES(_langIdAndCodePageKey) + GetStringsSize())); writer.Write((WORD)0); writer.Write((WORD)1); writer.Write(_langIdAndCodePageKey.ToCharArray()); writer.Write(new byte[KEYBYTES(_langIdAndCodePageKey) - _langIdAndCodePageKey.Length * 2]); //padding System.Diagnostics.Debug.Assert((writer.BaseStream.Position & 3) == 0); System.Diagnostics.Debug.Assert(writer.BaseStream.Position - debugPos == dataSize - GetStringsSize()); debugPos = writer.BaseStream.Position; foreach (var entry in GetVerStrings()) { var writerPos = writer.BaseStream.Position; //write any padding necessary to align the String struct on a 32 bit boundary. writer.Write(new byte[((writerPos + 3) & ~3) - writerPos]); System.Diagnostics.Debug.Assert(entry.Value != null); WriteVersionString(entry, writer); } System.Diagnostics.Debug.Assert(writer.BaseStream.Position - debugPos == GetStringsSize()); } } } }