diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/FluentBorder.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/FluentBorder.cs new file mode 100644 index 000000000..38dd344d8 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/FluentBorder.cs @@ -0,0 +1,35 @@ +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace QuickLook.Plugin.PEViewer; + +public class FluentBorder : Decorator +{ + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(FluentBorder), new FrameworkPropertyMetadata(new CornerRadius())); + + public CornerRadius CornerRadius + { + get => (CornerRadius)GetValue(CornerRadiusProperty); + set => SetValue(CornerRadiusProperty, value); + } + + public static readonly DependencyProperty BackgroundProperty = + DependencyProperty.Register("Background", typeof(Brush), typeof(FluentBorder), new FrameworkPropertyMetadata(Brushes.Transparent)); + + public Brush Background + { + get => (Brush)GetValue(BackgroundProperty); + set => SetValue(BackgroundProperty, value); + } + + protected override void OnRender(DrawingContext drawingContext) + { + if (Child != null) + { + Rect rect = new(new Point(0, 0), RenderSize); + drawingContext.DrawRoundedRectangle(Background, new Pen(Brushes.Transparent, 1), rect, CornerRadius.TopLeft, CornerRadius.TopRight); + } + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageCharacteristics.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageCharacteristics.cs new file mode 100644 index 000000000..2a0ce3d95 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageCharacteristics.cs @@ -0,0 +1,85 @@ +using System; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Specifies the values for the property of the COFF header of a PE image file. +/// +[Flags] +public enum ImageCharacteristics : ushort +{ + /// + /// Specifies that the file does not contain base relocations and must therefore be loaded at its preferred base address. If the base address is not available, the loader reports an error. The default behavior of the linker is to strip base relocations from executable (EXE) files. + /// + RelocationStripped = 0x1, + + /// + /// Specifies that the image file is valid and can be run. If this flag is not set, it indicates a linker error. + /// + Executable = 0x2, + + /// + /// Specifies that COFF line numbers have been removed. This flag is deprecated and should be zero. + /// + LineNumbersStripped = 0x4, + + /// + /// Specifies that COFF symbol table entries for local symbols have been removed. This flag is deprecated and should be zero. + /// + SymbolsStripped = 0x8, + + /// + /// Specifies to aggressively trim working set. This flag is deprecated for Windows 2000 and later and must be zero. + /// + AggressivelyTrimWorkingSet = 0x10, + + /// + /// Specifies that the application can handle > 2 GB addresses. + /// + LargeAddressAware = 0x20, + + /// + /// Specifies little endian: The least significant bit (LSB) precedes the most significant bit (MSB) in memory. This flag is deprecated and should be zero. + /// + BytesReversedLo = 0x80, + + /// + /// Specifies that the machine is based on a 32-bit-word architecture. + /// + Machine32 = 0x100, + + /// + /// Specifies that debugging information is removed from the image file. + /// + DebugStripped = 0x200, + + /// + /// Specifies that if the image is on removable media, to fully load it and copy it to the swap file. + /// + RemovableRunFromSwap = 0x400, + + /// + /// Specifies that if the image is on network media, to fully load it and copy it to the swap file. + /// + NetRunFromSwap = 0x800, + + /// + /// Specifies that the image file is a system file, not a user program. + /// + System = 0x1000, + + /// + /// Specifies that the image file is a dynamic-link library (DLL). Such files are considered executable files for almost all purposes, although they cannot be directly run. + /// + Dll = 0x2000, + + /// + /// Specifies that the file should be run only on a uniprocessor machine. + /// + UpSystem = 0x4000, + + /// + /// Specifies big endian: the MSB precedes the LSB in memory. This flag is deprecated and should be zero. + /// + BytesReversedHi = 0x8000 +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageCoffHeader.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageCoffHeader.cs new file mode 100644 index 000000000..9b6a5f595 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageCoffHeader.cs @@ -0,0 +1,46 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents the COFF header of a PE image file. +/// +public sealed class ImageCoffHeader +{ + /// + /// Gets the number that identifies the type of target machine. + /// + public ImageMachineType Machine { get; internal set; } + + /// + /// Gets the number of sections. This indicates the size of the section table, which immediately follows the headers. + /// + public ushort NumberOfSections { get; internal set; } + + /// + /// Gets the low 32 bits of the number of seconds since 01.01.1970 00:00:00, that indicates when the file was created. + /// + public uint TimeDateStamp { get; internal set; } + + /// + /// Gets the file offset of the COFF symbol table, or zero if no COFF symbol table is present. This value should be zero for an image because COFF debugging information is deprecated. + /// + public uint PointerToSymbolTable { get; internal set; } + + /// + /// Gets the number of entries in the symbol table. This data can be used to locate the string table, which immediately follows the symbol table. This value should be zero for an image because COFF debugging information is deprecated. + /// + public uint NumberOfSymbols { get; internal set; } + + /// + /// Gets the size of the optional header, which is required for executable files but not for object files. This value should be zero for an object file. + /// + public ushort SizeOfOptionalHeader { get; internal set; } + + /// + /// Gets the flags that indicate the attributes of the file. + /// + public ImageCharacteristics Characteristics { get; internal set; } + + internal ImageCoffHeader() + { + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDataDirectory.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDataDirectory.cs new file mode 100644 index 000000000..2b4e2fa8e --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDataDirectory.cs @@ -0,0 +1,32 @@ +using System.Diagnostics; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents a data directory of a PE image file. +/// +[DebuggerDisplay($"{nameof(ImageDataDirectory)}: Name = {{Name}}, VirtualAddress = {{VirtualAddress}}, Size = {{Size}}")] +public sealed class ImageDataDirectory +{ + /// + /// Gets the name of the data directory. This may not be a valid enum value of , if the image has more than 14 data directories. + /// + public ImageDataDirectoryName Name { get; private set; } + + /// + /// Gets the address of the first byte of a table or string that Windows uses. + /// + public uint VirtualAddress { get; private set; } + + /// + /// Gets size of a table or string that Windows uses. + /// + public uint Size { get; private set; } + + internal ImageDataDirectory(ImageDataDirectoryName name, uint virtualAddress, uint size) + { + Name = name; + VirtualAddress = virtualAddress; + Size = size; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDataDirectoryName.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDataDirectoryName.cs new file mode 100644 index 000000000..a7953439f --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDataDirectoryName.cs @@ -0,0 +1,82 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Specifies the name of a data directory entry of a PE image file. +/// +public enum ImageDataDirectoryName +{ + /// + /// Specifies the export table address and size. + /// + ExportTable = 0, + + /// + /// Specifies the import table address and size. + /// + ImportTable = 1, + + /// + /// Specifies the resource table address and size. + /// + ResourceTable = 2, + + /// + /// Specifies the exception table address and size. + /// + ExceptionTable = 3, + + /// + /// Specifies the attribute certificate table address and size. + /// + CertificateTable = 4, + + /// + /// Specifies the base relocation table address and size. + /// + BaseRelocationTable = 5, + + /// + /// Specifies the debug data starting address and size. + /// + DebugDirectory = 6, + + /// + /// Reserved, must be zero. + /// + Architecture = 7, + + /// + /// Specifies the RVA of the value to be stored in the global pointer register. The size member of this structure must be set to zero. + /// + GlobalPointer = 8, + + /// + /// Specifies the thread local storage (TLS) table address and size. + /// + TlsTable = 9, + + /// + /// Specifies the load configuration table address and size. + /// + LoadConfigurationTable = 10, + + /// + /// Specifies the bound import table address and size. + /// + BoundImportTable = 11, + + /// + /// Specifies the import address table address and size. + /// + ImportAddressTable = 12, + + /// + /// Specifies the delay import descriptor address and size. + /// + DelayImportDescriptors = 13, + + /// + /// Specifies the CLR runtime header address and size. + /// + ClrRuntimeHeader = 14, +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDllCharacteristics.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDllCharacteristics.cs new file mode 100644 index 000000000..88fa9a08e --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDllCharacteristics.cs @@ -0,0 +1,65 @@ +using System; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Specifies the values for the property of the optional header of a PE image file. +/// +[Flags] +public enum ImageDllCharacteristics : ushort +{ + /// + /// Specifies that the image can handle a high entropy 64-bit virtual address space. + /// + HighEntropyVA = 0x20, + + /// + /// Specifies that the DLL can be relocated at load time. + /// + DynamicBase = 0x40, + + /// + /// Specifies that Code Integrity checks are enforced. + /// + ForceIntegrity = 0x80, + + /// + /// Specifies that the image is NX compatible. + /// + NxCompatible = 0x100, + + /// + /// Specifies that the image is isolation aware, but the image is not isolated. + /// + IsolationAware = 0x200, + + /// + /// Specifies that the image does not use structured exception (SE) handling. No SE handler may be called in the image. + /// + NoSEH = 0x400, + + /// + /// Specifies that the image is not bound. + /// + DoNotBind = 0x800, + + /// + /// Specifies that the image must execute in an AppContainer. + /// + AppContainer = 0x1000, + + /// + /// Specifies that the image is a WDM driver. + /// + WdmDriver = 0x2000, + + /// + /// Specifies that the image supports Control Flow Guard. + /// + ControlFlowGuard = 0x4000, + + /// + /// Specifies that the image is terminal Server aware. + /// + TerminalServerAware = 0x8000, +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDosHeader.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDosHeader.cs new file mode 100644 index 000000000..8b4b3c7f7 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageDosHeader.cs @@ -0,0 +1,161 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents the DOS header of a PE image file. +/// +public sealed class ImageDosHeader +{ + /// + /// Specifies the amount of bytes on last page of the file. + /// + public ushort LastPageSize { get; internal set; } + + /// + /// Specifies the amount of pages in the file. + /// + public ushort PageCount { get; internal set; } + + /// + /// Specifies the amount of relocations in the file. + /// + public ushort RelocationCount { get; internal set; } + + /// + /// Specifies the size of header in paragraphs. + /// + public ushort HeaderSize { get; internal set; } + + /// + /// Specifies the minimum extra paragraphs needed. + /// + public ushort MinAlloc { get; internal set; } + + /// + /// Specifies the maximum extra paragraphs needed. + /// + public ushort MaxAlloc { get; internal set; } + + /// + /// Specifies the initial (relative) SS value. + /// + public ushort InitialSS { get; internal set; } + + /// + /// Specifies the initial SP value. + /// + public ushort InitialSP { get; internal set; } + + /// + /// Specifies the file checksum. + /// + public ushort Checksum { get; internal set; } + + /// + /// Specifies the initial IP value. + /// + public ushort InitialIP { get; internal set; } + + /// + /// Specifies the initial (relative) CS value. + /// + public ushort InitialCS { get; internal set; } + + /// + /// Specifies the file address of the relocation table. + /// + public ushort RelocationOffset { get; internal set; } + + /// + /// Specifies the overlay number. + /// + public ushort OverlayNumber { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved1 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved2 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved3 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved4 { get; internal set; } + + /// + /// Specifies the OEM Identifier. + /// + public ushort OemIdentifier { get; internal set; } + + /// + /// Specifies the OEM identifier. + /// + public ushort OemInformation { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved5 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved6 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved7 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved8 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved9 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved10 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved11 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved12 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved13 { get; internal set; } + + /// + /// Reserved. + /// + public ushort Reserved14 { get; internal set; } + + /// + /// Specifies the file address of new EXE header. + /// + public uint PEHeaderOffset { get; internal set; } + + internal ImageDosHeader() + { + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageMachineType.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageMachineType.cs new file mode 100644 index 000000000..0858eba8c --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageMachineType.cs @@ -0,0 +1,219 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Specifies the values for the property of the COFF header of a PE image file. +/// +public enum ImageMachineType : ushort +{ + /// + /// The machine is unspecified. It is assumed to be applicable to any machine type. + /// + Unknown = 0, + + /// + /// Useful for indicating we want to interact with the host and not a WoW guest. + /// + HOST = 0x001, + + /// + /// Specifies the Intel 386 or later processors and compatible processors. + /// + I386 = 0x14c, + + /// + /// Specifies the MIPS R3000 little endian processor. + /// + R3000 = 0x162, + + /// + /// Specifies the MIPS R4000 little endian processor. + /// + R4000 = 0x166, + + /// + /// Specifies the MIPS R10000 little endian processor. + /// + R10000 = 0x168, + + /// + /// Specifies the MIPS little endian WCE v2 processor. + /// + MIPSWCE2 = 0x169, + + /// + /// Specifies the Alpha AXP processor family. + /// + Alpha = 0x184, + + /// + /// Specifies the Hitachi SH-3 processor. + /// + SH3 = 0x1a2, + + /// + /// Specifies the Hitachi SH3-DSP processor. + /// + SH3DSP = 0x1a3, + + /// + /// Specifies the Hitachi SH-3E processor. + /// + SH3E = 0x1a4, + + /// + /// Specifies the Hitachi SH-4 processor. + /// + SH4 = 0x1a6, + + /// + /// Specifies the Hitachi SH-5 processor. + /// + SH5 = 0x1a8, + + /// + /// Specifies the ARM little endian processor. + /// + ARM = 0x1c0, + + /// + /// Specifies the ARM Thumb little endian processor. + /// + Thumb = 0x1c2, + + /// + /// Specifies the ARM Thumb-2 little endian processor. + /// + ARMNT = 0x1c4, + + /// + /// Specifies the Matsushita AM33 processor. + /// + AM33 = 0x1d3, + + /// + /// Specifies the Power PC little endian processor. + /// + PowerPC = 0x1f0, + + /// + /// Specifies the Power PC little endian with floating point support processor. + /// + PowerPCFP = 0x1f1, + + /// + /// Specifies the Intel Itanium processor family. + /// + IA64 = 0x200, + + /// + /// Specifies the MIPS16 processor. + /// + MIPS16 = 0x266, + + /// + /// Specifies the Alpha 64-bit processor family. + /// + Alpha64 = 0x284, + + /// + /// Specifies the MIPS with FPU processor. + /// + MIPSFPU = 0x366, + + /// + /// Specifies the MIPS16 with FPU processor. + /// + MIPSFPU16 = 0x466, + + /// + /// Specifies the Infinion TriCore processor family. + /// + Tricore = 0x520, + + /// + /// Specifies the IMAGE_FILE_MACHINE_CEF (0x0CEF) constant. + /// + CEF = 0xcef, + + /// + /// Specifies EFI byte code. + /// + EBC = 0xebc, + + /// + /// Specifies the RISCV32 processor. + /// + RISCV32 = 0x5032, + + /// + /// Specifies the RISCV64 processor. + /// + RISCV64 = 0x5064, + + /// + /// Specifies the RISCV128 processor. + /// + RISCV128 = 0x5128, + + /// + /// Specifies the AMD64 processor. + /// + AMD64 = 0x8664, + + /// + /// Specifies the Mitsubishi M32R little endian processor. + /// + M32R = 0x9041, + + /// + /// Specifies the ARM64 little endian processor. + /// + ARM64 = 0xaa64, + + /// + /// Specifies the IMAGE_FILE_MACHINE_CEE (0xC0EE) constant. + /// + CEE = 0xc0ee, +} + +public static class ImageMachineTypeExtension +{ + public static string ToImageMachineName(this ImageMachineType machine) + { + // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types + return machine switch + { + ImageMachineType.HOST => "HOST", + ImageMachineType.I386 => "x86", + ImageMachineType.R3000 => "R3000", + ImageMachineType.R4000 => "R4000", + ImageMachineType.R10000 => "R10000", + ImageMachineType.MIPSWCE2 => "MIPSWCE2", + ImageMachineType.Alpha => "ALPHA", + ImageMachineType.SH3 => "SH3", + ImageMachineType.SH3DSP => "SH3DSP", + ImageMachineType.SH3E => "SH3E", + ImageMachineType.SH4 => "SH4", + ImageMachineType.SH5 => "SH5", + ImageMachineType.ARM => "Arm", + ImageMachineType.Thumb => "THUMB", + ImageMachineType.ARMNT => "ArmNT", + ImageMachineType.AM33 => "AM33", + ImageMachineType.PowerPC => "PowerPC", + ImageMachineType.PowerPCFP => "PowerPCFP", + ImageMachineType.IA64 => "IA64", + ImageMachineType.MIPS16 => "MIPS16", + ImageMachineType.Alpha64 => "Alpha64", + ImageMachineType.MIPSFPU => "MIPSFPU", + ImageMachineType.MIPSFPU16 => "MIPSFPU16", + ImageMachineType.Tricore => "Tricore", + ImageMachineType.CEF => "CEF", + ImageMachineType.EBC => "EBC", + ImageMachineType.AMD64 => "x64", + ImageMachineType.M32R => "M32R", + ImageMachineType.ARM64 => "Arm64", + ImageMachineType.CEE => "CEE", + ImageMachineType.Unknown or _ => "UNKNOWN", + }; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader.cs new file mode 100644 index 000000000..5edf1a094 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader.cs @@ -0,0 +1,156 @@ +using System; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents the base class for PE image optional headers. This is an abstract class. +/// +public abstract class ImageOptionalHeader +{ + /// + /// Gets the linker major version number. + /// + public byte MajorLinkerVersion { get; internal set; } + + /// + /// Gets the linker minor version number. + /// + public byte MinorLinkerVersion { get; internal set; } + + /// + /// Gets the size of the code (text) section, or the sum of all code sections if there are multiple sections. + /// + public uint SizeOfCode { get; internal set; } + + /// + /// Gets the size of the initialized data section, or the sum of all such sections if there are multiple data sections. + /// + public uint SizeOfInitializedData { get; internal set; } + + /// + /// Gets the size of the uninitialized data section (BSS), or the sum of all such sections if there are multiple BSS sections. + /// + public uint SizeOfUninitializedData { get; internal set; } + + /// + /// Gets the address of the entry point relative to the image base when the executable file is loaded into memory. For program images, this is the starting address. For device drivers, this is the address of the initialization function. An entry point is optional for DLLs. When no entry point is present, this property must be zero. + /// + public uint AddressOfEntryPoint { get; internal set; } + + /// + /// Gets the address that is relative to the image base of the beginning-of-code section when it is loaded into memory. + /// + public uint BaseOfCode { get; internal set; } + + /// + /// Gets the alignment (in bytes) of sections when they are loaded into memory. It must be greater than or equal to . The default is the page size for the architecture. + /// + public uint SectionAlignment { get; internal set; } + + /// + /// Gets the alignment factor (in bytes) that is used to align the raw data of sections in the image file. The value should be a power of 2 between 512 and 64 K, inclusive. The default is 512. If the is less than the architecture's page size, then must match . + /// + public uint FileAlignment { get; internal set; } + + /// + /// Gets the major version number of the required operating system. + /// + public ushort MajorOperatingSystemVersion { get; internal set; } + + /// + /// Gets the minor version number of the required operating system. + /// + public ushort MinorOperatingSystemVersion { get; internal set; } + + /// + /// Gets the major version number of the image. + /// + public ushort MajorImageVersion { get; internal set; } + + /// + /// Gets the minor version number of the image. + /// + public ushort MinorImageVersion { get; internal set; } + + /// + /// Gets the major version number of the subsystem. + /// + public ushort MajorSubsystemVersion { get; internal set; } + + /// + /// Gets the minor version number of the subsystem. + /// + public ushort MinorSubsystemVersion { get; internal set; } + + /// + /// Reserved, must be zero. + /// + public uint Win32VersionValue { get; internal set; } + + /// + /// Gets the size (in bytes) of the image, including all headers, as the image is loaded in memory. It must be a multiple of . + /// + public uint SizeOfImage { get; internal set; } + + /// + /// Gets the combined size of an MS-DOS stub, PE header, and section headers rounded up to a multiple of . + /// + public uint SizeOfHeaders { get; internal set; } + + /// + /// Gets the image file checksum. The algorithm for computing the checksum is incorporated into IMAGHELP.DLL. The following are checked for validation at load time: all drivers, any DLL loaded at boot time, and any DLL that is loaded into a critical Windows process. + /// + public uint Checksum { get; internal set; } + + /// + /// Gets the subsystem that is required to run this image. + /// + public ImageSubsystem Subsystem { get; internal set; } + + /// + /// Gets the "DllCharacteristics" attribute of the PE image optional header. + /// + public ImageDllCharacteristics DllCharacteristics { get; internal set; } + + /// + /// Reserved, must be zero. + /// + public uint LoaderFlags { get; internal set; } + + /// + /// Gets the number of data-directory entries in the remainder of the optional header. Each describes a location and size. + /// + public uint NumberOfRvaAndSizes { get; internal set; } + + /// + /// Gets the collection data directories of the PE image optional header. + /// + public ImageDataDirectory[] DataDirectories { get; internal set; } + + internal ImageOptionalHeader() + { + DataDirectories = []; + } + + /// + /// Returns the or property of this instance. + /// + /// + /// The or property of this instance. + /// + public ulong GetImageBase() + { + if (this is ImageOptionalHeader32 optionalHeader32) + { + return optionalHeader32.ImageBase; + } + else if (this is ImageOptionalHeader64 optionalHeader64) + { + return optionalHeader64.ImageBase; + } + else + { + throw new NotSupportedException(nameof(optionalHeader32.ImageBase)); + } + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader32.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader32.cs new file mode 100644 index 000000000..001c7a766 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader32.cs @@ -0,0 +1,41 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents the optional header of a PE image file for x86 assemblies. +/// +public sealed class ImageOptionalHeader32 : ImageOptionalHeader +{ + /// + /// Gets the address that is relative to the image base of the beginning-of-data section when it is loaded into memory. + /// + public uint BaseOfData { get; internal set; } + + /// + /// Gets the preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K. The default for DLLs is 0x10000000. The default for Windows CE EXEs is 0x00010000. The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000. + /// + public uint ImageBase { get; internal set; } + + /// + /// Gets the size of the stack to reserve. Only is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public uint SizeOfStackReserve { get; internal set; } + + /// + /// Gets the size of the stack to commit. + /// + public uint SizeOfStackCommit { get; internal set; } + + /// + /// Gets the size of the local heap space to reserve. Only is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public uint SizeOfHeapReserve { get; internal set; } + + /// + /// Gets the size of the local heap space to commit. + /// + public uint SizeOfHeapCommit { get; internal set; } + + internal ImageOptionalHeader32() + { + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader64.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader64.cs new file mode 100644 index 000000000..227a49569 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageOptionalHeader64.cs @@ -0,0 +1,36 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents the optional header of a PE image file for x64 assemblies. +/// +public sealed class ImageOptionalHeader64 : ImageOptionalHeader +{ + /// + /// Gets the preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K. The default for DLLs is 0x10000000. The default for Windows CE EXEs is 0x00010000. The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000. + /// + public ulong ImageBase { get; internal set; } + + /// + /// Gets the size of the stack to reserve. Only is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public ulong SizeOfStackReserve { get; internal set; } + + /// + /// Gets the size of the stack to commit. + /// + public ulong SizeOfStackCommit { get; internal set; } + + /// + /// Gets the size of the local heap space to reserve. Only is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public ulong SizeOfHeapReserve { get; internal set; } + + /// + /// Gets the size of the local heap space to commit. + /// + public ulong SizeOfHeapCommit { get; internal set; } + + internal ImageOptionalHeader64() + { + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSection.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSection.cs new file mode 100644 index 000000000..ced9d7393 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSection.cs @@ -0,0 +1,104 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents a section of a PE image file, containing the header and a [] representing the contents of the section. +/// +[DebuggerDisplay($"{nameof(ImageSection)}: Header = {{Header.Name,nq}}, Size: {{Data.Length}}")] +public sealed class ImageSection +{ + /// + /// Gets the section header. + /// + public ImageSectionHeader Header { get; private set; } + + /// + /// Gets a [] representing the contents of the section. + /// + public byte[] Data { get; set; } = null!; + + internal ImageSection(ImageSectionHeader header) + { + Header = header; + } + + public void SetDataFromRsrc(byte[] originalImage) + { + if (Header.PointerToRawData + Header.SizeOfRawData <= originalImage.Length) + { + Data = originalImage.GetBytes((int)Header.PointerToRawData, (int)Header.SizeOfRawData); + } + else + { + throw new PEImageParseException(int.MinValue, "Section '" + Header.Name + "' incomplete."); + } + } + + public void SetDataFromRsrc(Stream originalImage, uint? length = null) + { + if (Header.PointerToRawData + Header.SizeOfRawData <= (length ?? originalImage.Length)) + { + Data = originalImage.GetBytes((int)Header.PointerToRawData, (int)Header.SizeOfRawData); + } + else + { + throw new PEImageParseException(int.MinValue, "Section '" + Header.Name + "' incomplete."); + } + } +} + +/// +/// Provides support for creation and generation of generic objects. +/// +file static class Create +{ + /// + /// Copies a specified number of bytes from this [] and returns a new array representing a fraction of the original []. + /// + /// The [] to take the subset of bytes from. + /// A value specifying the offset from which to start copying bytes. + /// A value specifying the number of bytes to copy. + /// + /// A new [] representing a fraction of the original []. + /// + public static byte[] GetBytes(this byte[] array, int index, int count) + { + byte[] result = new byte[count]; + Buffer.BlockCopy(array, index, result, 0, count); + return result; + } + + public static byte[] GetBytes(this Stream stream, long index, int count) + { + byte[] buffer = new byte[count]; + + // Ensure the stream is at the correct position + long currentPosition = stream.Position; + long bytesToSkip = index - currentPosition; + + if (bytesToSkip < 0) + { + throw new ArgumentException("Offset is before the current position in the stream."); + } + + // Skip bytes until reaching the target offset + while (bytesToSkip > 0) + { + int bytesSkipped = (int)Math.Min(bytesToSkip, int.MaxValue); + stream.Read(new byte[bytesSkipped], 0, bytesSkipped); + bytesToSkip -= bytesSkipped; + } + + // Read the required number of bytes + int bytesRead = stream.Read(buffer, 0, count); + if (bytesRead < count) + { + throw new EndOfStreamException("The stream has fewer bytes available than requested."); + } + + return buffer; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSectionFlags.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSectionFlags.cs new file mode 100644 index 000000000..cc0fbd318 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSectionFlags.cs @@ -0,0 +1,160 @@ +using System; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Specifies the values for the property of a section header of a PE image file. +/// +[Flags] +public enum ImageSectionFlags : uint +{ + /// + /// Specifies that the section should not be padded to the next boundary. This flag is obsolete and is replaced by . This is valid only for object files. + /// + NoPadding = 0x8, + + /// + /// Specifies that the section contains executable code. + /// + ContainsCode = 0x20, + + /// + /// Specifies that the section contains initialized data. + /// + ContainsInitializedData = 0x40, + + /// + /// Specifies that the section contains uninitialized data. + /// + ContainsUninitializedData = 0x80, + + /// + /// Specifies that the section contains comments or other information. The .drectve section has this type. This is valid for object files only. + /// + ContainsInformation = 0x200, + + /// + /// Specifies that the section will not become part of the image. This is valid only for object files. + /// + Remove = 0x800, + + /// + /// Specifies that the section contains COMDAT data. This is valid only for object files. + /// + ContainsComdat = 0x1000, + + /// + /// Specifies that the section contains data referenced through the global pointer (GP). + /// + ContainsGlobalPointerData = 0x8000, + + /// + /// Specifies to align data on a 1-byte boundary. Valid only for object files. + /// + Align1 = 0x100000, + + /// + /// Specifies to align data on a 2-byte boundary. Valid only for object files. + /// + Align2 = 0x200000, + + /// + /// Specifies to align data on a 4-byte boundary. Valid only for object files. + /// + Align4 = 0x300000, + + /// + /// Specifies to align data on a 8-byte boundary. Valid only for object files. + /// + Align8 = 0x400000, + + /// + /// Specifies to align data on a 16-byte boundary. Valid only for object files. + /// + Align16 = 0x500000, + + /// + /// Specifies to align data on a 32-byte boundary. Valid only for object files. + /// + Align32 = 0x600000, + + /// + /// Specifies to align data on a 64-byte boundary. Valid only for object files. + /// + Align64 = 0x700000, + + /// + /// Specifies to align data on a 128-byte boundary. Valid only for object files. + /// + Align128 = 0x800000, + + /// + /// Specifies to align data on a 256-byte boundary. Valid only for object files. + /// + Align256 = 0x900000, + + /// + /// Specifies to align data on a 512-byte boundary. Valid only for object files. + /// + Align512 = 0xa00000, + + /// + /// Specifies to align data on a 1024-byte boundary. Valid only for object files. + /// + Align1024 = 0xb00000, + + /// + /// Specifies to align data on a 2048-byte boundary. Valid only for object files. + /// + Align2048 = 0xc00000, + + /// + /// Specifies to align data on a 4096-byte boundary. Valid only for object files. + /// + Align4096 = 0xd00000, + + /// + /// Specifies to align data on a 8192-byte boundary. Valid only for object files. + /// + Align8192 = 0xe00000, + + /// + /// Specifies that the section contains extended relocations. + /// + ContainsExtendedRelocations = 0x1000000, + + /// + /// Specifies that the section can be discarded as needed. + /// + Discardable = 0x2000000, + + /// + /// Specifies that the section cannot be cached. + /// + NotCached = 0x4000000, + + /// + /// Specifies that the section is not pageable. + /// + NotPaged = 0x8000000, + + /// + /// Specifies that the section can be shared in memory. + /// + Shared = 0x10000000, + + /// + /// Specifies that the section can be executed as code. + /// + Execute = 0x20000000, + + /// + /// Specifies that the section can be read. + /// + Read = 0x40000000, + + /// + /// Specifies that the section can be written to. + /// + Write = 0x80000000, +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSectionHeader.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSectionHeader.cs new file mode 100644 index 000000000..3b385007c --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSectionHeader.cs @@ -0,0 +1,73 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents a section header of a PE image file. +/// +public sealed class ImageSectionHeader +{ + /// + /// Gets an 8-byte, null-padded UTF-8 encoded string. If the string is exactly 8 characters long, there is no terminating null. For longer names, this property contains a slash (/) that is followed by an ASCII representation of a decimal number that is an offset into the string table. Executable images do not use a string table and do not support section names longer than 8 characters. Long names in object files are truncated if they are emitted to an executable file. + /// + public string Name { get; internal set; } + + /// + /// Gets the total size of the section when loaded into memory. If this value is greater than , the section is zero-padded. This property is valid only for executable images and should be set to zero for object files. + /// + public uint VirtualSize { get; internal set; } + + /// + /// For executable images, Gets the address of the first byte of the section relative to the image base when the section is loaded into memory. For object files, this property is the address of the first byte before relocation is applied; for simplicity, compilers should set this to zero. Otherwise, it is an arbitrary value that is subtracted from offsets during relocation. + /// + public uint VirtualAddress { get; internal set; } + + /// + /// Gets the size of the section (for object files) or the size of the initialized data on disk (for image files). For executable images, this must be a multiple of from the optional header. If this is less than , the remainder of the section is zero-filled. Because the property is rounded but the property is not, it is possible for to be greater than as well. When a section contains only uninitialized data, this property should be zero. + /// + public uint SizeOfRawData { get; internal set; } + + /// + /// Gets the file pointer to the first page of the section within the COFF file. For executable images, this must be a multiple of from the optional header. For object files, the value should be aligned on a 4-byte boundary for best performance. When a section contains only uninitialized data, this property should be zero. + /// + public uint PointerToRawData { get; internal set; } + + /// + /// Gets the file pointer to the beginning of relocation entries for the section. This is set to zero for executable images or if there are no relocations. + /// + public uint PointerToRelocations { get; internal set; } + + /// + /// Gets the file pointer to the beginning of line-number entries for the section. This is set to zero if there are no COFF line numbers. This value should be zero for an image because COFF debugging information is deprecated. + /// + public uint PointerToLineNumbers { get; internal set; } + + /// + /// Gets the number of relocation entries for the section. This is set to zero for executable images. + /// + public ushort NumberOfRelocations { get; internal set; } + + /// + /// Gets the number of line-number entries for the section. This value should be zero for an image because COFF debugging information is deprecated. + /// + public ushort NumberOfLineNumbers { get; internal set; } + + /// + /// Gets the flags that describe the characteristics of the section. + /// + public ImageSectionFlags Characteristics { get; internal set; } + + internal ImageSectionHeader() + { + Name = string.Empty; + } + + /// + /// Returns the name of this . + /// + /// + /// The name of this . + /// + public override string ToString() + { + return Name; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSubsystem.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSubsystem.cs new file mode 100644 index 000000000..949b72bf5 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/ImageSubsystem.cs @@ -0,0 +1,77 @@ +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Specifies the values for the property of the optional header of a PE image file. +/// +public enum ImageSubsystem : ushort +{ + /// + /// Specifies an unknown subsystem. + /// + Unknown = 0, + + /// + /// Specifies device drivers and native Windows processes. + /// + Native = 1, + + /// + /// Specifies the Windows graphical user interface (GUI) subsystem. + /// + WindowsGui = 2, + + /// + /// Specifies the Windows character subsystem. + /// + WindowsCui = 3, + + /// + /// Specifies the OS/2 character subsystem. + /// + OS2Cui = 5, + + /// + /// Specifies the Posix character subsystem. + /// + PosixCui = 7, + + /// + /// Specifies a native Win9x driver. + /// + NativeWindows = 8, + + /// + /// Specifies Windows CE. + /// + WindowsCEGui = 9, + + /// + /// Specifies an Extensible Firmware Interface (EFI) application. + /// + EfiApplication = 10, + + /// + /// Specifies an EFI driver with boot services. + /// + EfiBootServiceDriver = 11, + + /// + /// Specifies an EFI driver with run-time services. + /// + EfiRuntimeDriver = 12, + + /// + /// Specifies an EFI ROM image. + /// + EfiRom = 13, + + /// + /// Specifies XBOX. + /// + XBox = 14, + + /// + /// Specifies a Windows boot application. + /// + WindowsBootApplication = 16, +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImage.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImage.cs new file mode 100644 index 000000000..59006fa53 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImage.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// Represents a PE (x86) or a PE+ (x64) image. This class parses binary files, typically EXE and DLL files. +/// +public class PEImage +{ + /// + /// Gets the original PE image file, if this file was loaded from an existing source; otherwise, . + /// + public byte[] OriginalImage { get; private set; } + + /// + /// Gets the DOS header of this PE image file. + /// + public ImageDosHeader DosHeader { get; private set; } + + /// + /// Gets the MS-DOS stub of this PE image file. + /// + public byte[] DosStub { get; private set; } + + /// + /// Gets the COFF header of this PE image file. + /// + public ImageCoffHeader CoffHeader { get; private set; } + + /// + /// Gets the optional header of this PE image file. + /// + public ImageOptionalHeader OptionalHeader { get; private set; } + + /// + /// Gets the collection of section headers and data of this PE image file. + /// + public ImageSection[] Sections { get; private set; } + + private PEImage(byte[] originalImage) + { + OriginalImage = originalImage; + + using BinaryReader reader = new(new MemoryStream(OriginalImage)); + + // MZ + if (reader.BaseStream.Length < 2) throw new PEImageParseException(0, "DOS signature not found."); + if (reader.ReadUInt16() != 0x5a4d) throw new PEImageParseException(0, "DOS header not found."); + + // DOS Header + if (reader.BaseStream.Length - reader.BaseStream.Position < 64) throw new PEImageParseException((int)reader.BaseStream.Position, "DOS header incomplete."); + + DosHeader = new() + { + LastPageSize = reader.ReadUInt16(), + PageCount = reader.ReadUInt16(), + RelocationCount = reader.ReadUInt16(), + HeaderSize = reader.ReadUInt16(), + MinAlloc = reader.ReadUInt16(), + MaxAlloc = reader.ReadUInt16(), + InitialSS = reader.ReadUInt16(), + InitialSP = reader.ReadUInt16(), + Checksum = reader.ReadUInt16(), + InitialIP = reader.ReadUInt16(), + InitialCS = reader.ReadUInt16(), + RelocationOffset = reader.ReadUInt16(), + OverlayNumber = reader.ReadUInt16(), + Reserved1 = reader.ReadUInt16(), + Reserved2 = reader.ReadUInt16(), + Reserved3 = reader.ReadUInt16(), + Reserved4 = reader.ReadUInt16(), + OemIdentifier = reader.ReadUInt16(), + OemInformation = reader.ReadUInt16(), + Reserved5 = reader.ReadUInt16(), + Reserved6 = reader.ReadUInt16(), + Reserved7 = reader.ReadUInt16(), + Reserved8 = reader.ReadUInt16(), + Reserved9 = reader.ReadUInt16(), + Reserved10 = reader.ReadUInt16(), + Reserved11 = reader.ReadUInt16(), + Reserved12 = reader.ReadUInt16(), + Reserved13 = reader.ReadUInt16(), + Reserved14 = reader.ReadUInt16(), + PEHeaderOffset = reader.ReadUInt32() + }; + + // DOS Stub + if (reader.BaseStream.Length < DosHeader.PEHeaderOffset) throw new PEImageParseException((int)reader.BaseStream.Position, "DOS stub incomplete."); + + DosStub = reader.ReadBytes((int)(DosHeader.PEHeaderOffset - reader.BaseStream.Position)); + + // COFF Header + if (reader.ReadUInt32() != 0x4550) throw new PEImageParseException((int)reader.BaseStream.Position - 4, "COFF header not found."); + if (reader.BaseStream.Length - reader.BaseStream.Position < 20) throw new PEImageParseException((int)reader.BaseStream.Position, "COFF header incomplete."); + + CoffHeader = new() + { + Machine = (ImageMachineType)reader.ReadUInt16(), + NumberOfSections = reader.ReadUInt16(), + TimeDateStamp = reader.ReadUInt32(), + PointerToSymbolTable = reader.ReadUInt32(), + NumberOfSymbols = reader.ReadUInt32(), + SizeOfOptionalHeader = reader.ReadUInt16(), + Characteristics = (ImageCharacteristics)reader.ReadUInt16() + }; + + // Optional Header + if (reader.BaseStream.Length - reader.BaseStream.Position < 2) throw new PEImageParseException((int)reader.BaseStream.Position, "Optional header not found."); + ushort magic = reader.ReadUInt16(); + + if (magic == 0x10b) + { + if (reader.BaseStream.Length - reader.BaseStream.Position < 94) throw new PEImageParseException((int)reader.BaseStream.Position, "Optional header incomplete."); + + OptionalHeader = new ImageOptionalHeader32 + { + MajorLinkerVersion = reader.ReadByte(), + MinorLinkerVersion = reader.ReadByte(), + SizeOfCode = reader.ReadUInt32(), + SizeOfInitializedData = reader.ReadUInt32(), + SizeOfUninitializedData = reader.ReadUInt32(), + AddressOfEntryPoint = reader.ReadUInt32(), + BaseOfCode = reader.ReadUInt32(), + BaseOfData = reader.ReadUInt32(), + ImageBase = reader.ReadUInt32(), + SectionAlignment = reader.ReadUInt32(), + FileAlignment = reader.ReadUInt32(), + MajorOperatingSystemVersion = reader.ReadUInt16(), + MinorOperatingSystemVersion = reader.ReadUInt16(), + MajorImageVersion = reader.ReadUInt16(), + MinorImageVersion = reader.ReadUInt16(), + MajorSubsystemVersion = reader.ReadUInt16(), + MinorSubsystemVersion = reader.ReadUInt16(), + Win32VersionValue = reader.ReadUInt32(), + SizeOfImage = reader.ReadUInt32(), + SizeOfHeaders = reader.ReadUInt32(), + Checksum = reader.ReadUInt32(), + Subsystem = (ImageSubsystem)reader.ReadUInt16(), + DllCharacteristics = (ImageDllCharacteristics)reader.ReadUInt16(), + SizeOfStackReserve = reader.ReadUInt32(), + SizeOfStackCommit = reader.ReadUInt32(), + SizeOfHeapReserve = reader.ReadUInt32(), + SizeOfHeapCommit = reader.ReadUInt32(), + LoaderFlags = reader.ReadUInt32(), + NumberOfRvaAndSizes = reader.ReadUInt32() + }; + } + else if (magic == 0x20b) + { + if (reader.BaseStream.Length - reader.BaseStream.Position < 110) throw new PEImageParseException((int)reader.BaseStream.Position, "Optional header incomplete."); + + OptionalHeader = new ImageOptionalHeader64 + { + MajorLinkerVersion = reader.ReadByte(), + MinorLinkerVersion = reader.ReadByte(), + SizeOfCode = reader.ReadUInt32(), + SizeOfInitializedData = reader.ReadUInt32(), + SizeOfUninitializedData = reader.ReadUInt32(), + AddressOfEntryPoint = reader.ReadUInt32(), + BaseOfCode = reader.ReadUInt32(), + ImageBase = reader.ReadUInt64(), + SectionAlignment = reader.ReadUInt32(), + FileAlignment = reader.ReadUInt32(), + MajorOperatingSystemVersion = reader.ReadUInt16(), + MinorOperatingSystemVersion = reader.ReadUInt16(), + MajorImageVersion = reader.ReadUInt16(), + MinorImageVersion = reader.ReadUInt16(), + MajorSubsystemVersion = reader.ReadUInt16(), + MinorSubsystemVersion = reader.ReadUInt16(), + Win32VersionValue = reader.ReadUInt32(), + SizeOfImage = reader.ReadUInt32(), + SizeOfHeaders = reader.ReadUInt32(), + Checksum = reader.ReadUInt32(), + Subsystem = (ImageSubsystem)reader.ReadUInt16(), + DllCharacteristics = (ImageDllCharacteristics)reader.ReadUInt16(), + SizeOfStackReserve = reader.ReadUInt64(), + SizeOfStackCommit = reader.ReadUInt64(), + SizeOfHeapReserve = reader.ReadUInt64(), + SizeOfHeapCommit = reader.ReadUInt64(), + LoaderFlags = reader.ReadUInt32(), + NumberOfRvaAndSizes = reader.ReadUInt32() + }; + } + else if (magic == 0x107) + { + throw new PEImageParseException((int)reader.BaseStream.Position - 2, "Optional header for ROM's is not supported."); + } + else + { + throw new PEImageParseException((int)reader.BaseStream.Position - 2, "Optional header magic value of '0x" + magic.ToString("x4") + "' unknown."); + } + + // Data Directories + if (reader.BaseStream.Length - reader.BaseStream.Position < OptionalHeader.NumberOfRvaAndSizes * 8) throw new PEImageParseException((int)reader.BaseStream.Position, "Data directories incomplete."); + + OptionalHeader.DataDirectories = Create.Array((int)OptionalHeader.NumberOfRvaAndSizes, i => new ImageDataDirectory((ImageDataDirectoryName)i, reader.ReadUInt32(), reader.ReadUInt32())); + + // Section Headers + if (reader.BaseStream.Length - reader.BaseStream.Position < CoffHeader.NumberOfSections * 40) throw new PEImageParseException((int)reader.BaseStream.Position, "Section headers incomplete."); + + Sections = Create + .Enumerable(CoffHeader.NumberOfSections, i => new ImageSectionHeader + { + Name = reader.ReadBytes(8).TakeWhile(c => c != 0).ToArray().ToUTF8String(), + VirtualSize = reader.ReadUInt32(), + VirtualAddress = reader.ReadUInt32(), + SizeOfRawData = reader.ReadUInt32(), + PointerToRawData = reader.ReadUInt32(), + PointerToRelocations = reader.ReadUInt32(), + PointerToLineNumbers = reader.ReadUInt32(), + NumberOfRelocations = reader.ReadUInt16(), + NumberOfLineNumbers = reader.ReadUInt16(), + Characteristics = (ImageSectionFlags)reader.ReadUInt32() + }) + .Select(header => + { + return new ImageSection(header); + + //if (header.PointerToRawData + header.SizeOfRawData <= reader.BaseStream.Length) + //{ + // return new ImageSection(header, OriginalImage.GetBytes((int)header.PointerToRawData, (int)header.SizeOfRawData)); + //} + //else + //{ + // throw new PEImageParseException((int)reader.BaseStream.Position, "Section '" + header.Name + "' incomplete."); + //} + }) + .ToArray(); + } + + /// + /// Creates a from the specified file with the specified form name. + /// + /// A specifying the path of a file from which to create the . + /// + /// The this method creates. + /// + public static PEImage FromFile(string path) + { + _ = path ?? throw new ArgumentNullException(nameof(path)); + _ = File.Exists(path) ? default(bool) : throw new FileNotFoundException(path); + + return new(File.ReadAllBytes(path)); + } + + /// + /// Creates a from the specified [] that represents a PE image file. + /// + /// The [] that represents a to parse. + /// + /// The this method creates. + /// + public static PEImage FromBinary(byte[] file) + { + _ = file ?? throw new ArgumentNullException(nameof(file)); + + return new([.. file]); + } +} + +/// +/// Provides support for creation and generation of generic objects. +/// +file static class Create +{ + /// + /// Creates an of the specified type and initialized each element with a value that is retrieved from . + /// + /// The type of the created . + /// The number of elements of the . + /// A to retrieve new values for the based on the given index. + /// + /// A new with the specified length, where each element is initialized with a value that is retrieved from . + /// + public static T[] Array(int length, Func valueSelector) + { + T[] array = new T[length]; + for (int i = 0; i < length; i++) + { + array[i] = valueSelector(i); + } + return array; + } + + /// + /// Creates an of the specified type and returns values that are retrieved from . + /// + /// The type of the created . + /// The number of elements to return. + /// A to retrieve new values for the based on the given index. + /// + /// A new with the specified number of elements, where each element is initialized with a value that is retrieved from . + /// + public static IEnumerable Enumerable(int count, Func valueSelector) + { + for (int i = 0; i < count; i++) + { + yield return valueSelector(i); + } + } + + /// + /// Decodes all the bytes in this [] into a using the encoding. + /// + /// The [] containing the sequence of bytes to decode. + /// + /// A that contains the results of decoding this sequence of bytes. + /// + public static string ToUTF8String(this byte[] array) + { + return Encoding.UTF8.GetString(array); + } + + /// + /// Copies a specified number of bytes from this [] and returns a new array representing a fraction of the original []. + /// + /// The [] to take the subset of bytes from. + /// A value specifying the offset from which to start copying bytes. + /// A value specifying the number of bytes to copy. + /// + /// A new [] representing a fraction of the original []. + /// + public static byte[] GetBytes(this byte[] array, int index, int count) + { + byte[] result = new byte[count]; + Buffer.BlockCopy(array, index, result, 0, count); + return result; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImageParseException.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImageParseException.cs new file mode 100644 index 000000000..88b4e3323 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImageParseException.cs @@ -0,0 +1,26 @@ +using System; + +namespace QuickLook.Plugin.PEViewer.PEImageParser; + +/// +/// The exception that is thrown when parsing of a fails. +/// +public sealed class PEImageParseException : Exception +{ + /// + /// Gets the offset within the image file at which parsing failed. + /// + public int Offset { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The offset within the image file at which parsing failed. + /// The message that describes the error. + public PEImageParseException(int offset, string message) : base(message) + { + _ = message ?? throw new ArgumentNullException(nameof(message)); + + Offset = offset; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEInfoPanel.xaml b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEInfoPanel.xaml new file mode 100644 index 000000000..a6c4aedaf --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEInfoPanel.xaml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEInfoPanel.xaml.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEInfoPanel.xaml.cs new file mode 100644 index 000000000..151d54fe9 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEInfoPanel.xaml.cs @@ -0,0 +1,110 @@ +// Copyright © 2017 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using QuickLook.Common.ExtensionMethods; +using QuickLook.Common.Helpers; +using QuickLook.Plugin.PEViewer.PEImageParser; +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using System.Windows.Controls; + +namespace QuickLook.Plugin.PEViewer; + +public partial class PEInfoPanel : UserControl +{ + private bool _stop; + + public PEInfoPanel() + { + InitializeComponent(); + + // apply global theme + Resources.MergedDictionaries[0].Clear(); + + string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config"); + totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile); + fileVersionTitle.Text = TranslationHelper.Get("FILE_VERSION", translationFile); + productVersionTitle.Text = TranslationHelper.Get("PRODUCT_VERSION", translationFile); + } + + public bool Stop + { + set => _stop = value; + get => _stop; + } + + public void DisplayInfo(string path) + { + Task.Run(() => + { + var scale = DisplayDeviceHelper.GetCurrentScaleFactor(); + + var icon = + WindowsThumbnailProvider.GetThumbnail(path, + (int)(128 * scale.Horizontal), + (int)(128 * scale.Vertical), + ThumbnailOptions.ScaleUp); + + var source = icon?.ToBitmapSource(); + icon?.Dispose(); + + Dispatcher.BeginInvoke(new Action(() => image.Source = source)); + }); + + var name = Path.GetFileName(path); + filename.Text = string.IsNullOrEmpty(name) ? path : name; + + Stop = false; + + _ = Task.Run(() => + { + if (File.Exists(path)) + { + var info = FileVersionInfo.GetVersionInfo(path); + var size = new FileInfo(path).Length; + var arch = default(string); + + try + { + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); + using var binaryReader = new BinaryReader(stream); + var byteArray = binaryReader.ReadBytes(1024); // Fast reading + var peImage = PEImage.FromBinary(byteArray); + var machine = peImage.CoffHeader.Machine; + + arch = machine.ToImageMachineName(); + } + catch + { + // Usually because DOS Header not found + } + + Dispatcher.Invoke(() => + { + architectureContainer.Visibility = string.IsNullOrEmpty(arch) ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible; + architecture.Text = arch; + fileVersion.Text = info.FileVersion; + productVersion.Text = info.ProductVersion; + totalSize.Text = size.ToPrettySize(2); + }); + } + }); + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Plugin.cs new file mode 100644 index 000000000..d9350c837 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Plugin.cs @@ -0,0 +1,76 @@ +// Copyright © 2024 ema +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using QuickLook.Common.Plugin; +using System; +using System.IO; +using System.Linq; +using System.Windows; + +namespace QuickLook.Plugin.PEViewer; + +public class Plugin : IViewer +{ + private static readonly string[] Extensions = + [ + ".exe", ".sys", ".scr", ".ocx", ".cpl", ".bpl", + ".dll", ".ax", ".drv", ".vxd", + ".mui", ".mun", + ".tlb", + ".msi", + ".efi", ".mz", + ]; + + private PEInfoPanel _ip; + private string _path; + + public int Priority => 0; + + public void Init() + { + } + + public bool CanHandle(string path) + { + return !Directory.Exists(path) && Extensions.Any(path.ToLower().EndsWith); + } + + public void Prepare(string path, ContextObject context) + { + context.PreferredSize = new Size { Width = 520, Height = 192 }; + } + + public void View(string path, ContextObject context) + { + _path = path; + _ip = new PEInfoPanel(); + + _ip.DisplayInfo(_path); + _ip.Tag = context; + + context.ViewerContent = _ip; + context.Title = $"{Path.GetFileName(path)}"; + context.IsBusy = false; + } + + public void Cleanup() + { + GC.SuppressFinalize(this); + + _ip = null; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Properties/AssemblyInfo.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..8b33ea146 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Properties/AssemblyInfo.cs @@ -0,0 +1,50 @@ +// Copyright © 2024 ema +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("QuickLook.Plugin.PEViewer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("pooi.moe")] +[assembly: AssemblyProduct("QuickLook.Plugin.PEViewer")] +[assembly: AssemblyCopyright("Copyright © ema 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("eb7d1f10-6e04-4f7e-96a0-4e18359fbd30")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/QuickLook.Plugin.PEViewer.csproj b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/QuickLook.Plugin.PEViewer.csproj new file mode 100644 index 000000000..4b0535da3 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/QuickLook.Plugin.PEViewer.csproj @@ -0,0 +1,78 @@ + + + + Library + net462 + QuickLook.Plugin.PEViewer + QuickLook.Plugin.PEViewer + 512 + false + true + latest + false + false + false + MinimumRecommendedRules.ruleset + {EB7D1F10-6E04-4F7E-96A0-4E18359FBD30} + + + + true + full + false + ..\..\Build\Debug\QuickLook.Plugin\QuickLook.Plugin.PEViewer\ + DEBUG;TRACE + prompt + + + + pdbonly + true + ..\..\Build\Release\QuickLook.Plugin\QuickLook.Plugin.PEViewer\ + TRACE + prompt + + + + true + full + ..\..\Build\Debug\QuickLook.Plugin\QuickLook.Plugin.PEViewer\ + DEBUG;TRACE + x86 + prompt + + + + ..\..\Build\Release\QuickLook.Plugin\QuickLook.Plugin.PEViewer\ + TRACE + true + pdbonly + x86 + prompt + + + + + {85FDD6BA-871D-46C8-BD64-F6BB0CB5EA95} + QuickLook.Common + False + + + + + + Properties\GitVersion.cs + + + + + + + + + + PreserveNewest + + + + diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Translations.config b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Translations.config new file mode 100644 index 000000000..9780779a8 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/Translations.config @@ -0,0 +1,24 @@ + + + + + Total Size + File Version + Product Version + + + 总大小 + 文件版本 + 产品版本 + + + 縂大小 + 檔案版本 + 產品版本 + + + 合計サイズ + ファイルバージョン + 製品バージョン + + diff --git a/QuickLook.Plugin/QuickLook.Plugin.PEViewer/WindowsThumbnailProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/WindowsThumbnailProvider.cs new file mode 100644 index 000000000..62291bc33 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PEViewer/WindowsThumbnailProvider.cs @@ -0,0 +1,225 @@ +// Copyright © 2017 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; + +namespace QuickLook.Plugin.PEViewer; + +[Flags] +internal enum ThumbnailOptions +{ + None = 0x00, + BiggerSizeOk = 0x01, + InMemoryOnly = 0x02, + IconOnly = 0x04, + ThumbnailOnly = 0x08, + InCacheOnly = 0x10, + IconBackground = 0x80, + ScaleUp = 0x100 +} + +internal static class WindowsThumbnailProvider +{ + private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93"; + + [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int SHCreateItemFromParsingName( + [MarshalAs(UnmanagedType.LPWStr)] string path, + // The following parameter is not used - binding context. + IntPtr pbc, + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); + + [DllImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DeleteObject(IntPtr hObject); + + public static Bitmap GetThumbnail(string fileName, int width, int height, ThumbnailOptions options) + { + var hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options); + + if (hBitmap == IntPtr.Zero) + return null!; + + try + { + // return a System.Drawing.Bitmap from the hBitmap + return GetBitmapFromHBitmap(hBitmap); + } + finally + { + // delete HBitmap to avoid memory leaks + DeleteObject(hBitmap); + } + } + + internal static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap) + { + var bmp = Image.FromHbitmap(nativeHBitmap); + + if (Image.GetPixelFormatSize(bmp.PixelFormat) < 32) + return bmp; + + return CreateAlphaBitmap(bmp, PixelFormat.Format32bppArgb); + } + + internal static Bitmap CreateAlphaBitmap(Bitmap srcBitmap, PixelFormat targetPixelFormat) + { + var result = new Bitmap(srcBitmap.Width, srcBitmap.Height, targetPixelFormat); + + var bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height); + + var srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat); + + var isAlplaBitmap = false; + + try + { + for (var y = 0; y <= srcData.Height - 1; y++) + for (var x = 0; x <= srcData.Width - 1; x++) + { + var pixelColor = Color.FromArgb( + Marshal.ReadInt32(srcData.Scan0, srcData.Stride * y + 4 * x)); + + if ((pixelColor.A > 0) & (pixelColor.A < 255)) + isAlplaBitmap = true; + + result.SetPixel(x, y, pixelColor); + } + } + finally + { + srcBitmap.UnlockBits(srcData); + } + + if (isAlplaBitmap) + return result; + return srcBitmap; + } + + private static IntPtr GetHBitmap(string fileName, int width, int height, ThumbnailOptions options) + { + var shellItem2Guid = new Guid(IShellItem2Guid); + var retCode = + SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out var nativeShellItem); + + if (retCode != 0) + return IntPtr.Zero; + + var nativeSize = new NativeSize + { + Width = width, + Height = height + }; + + var hr = ((IShellItemImageFactory)nativeShellItem).GetImage(nativeSize, options, out var hBitmap); + + Marshal.ReleaseComObject(nativeShellItem); + + return hr == HResult.Ok ? hBitmap : IntPtr.Zero; + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] + internal interface IShellItem + { + void BindToHandler(IntPtr pbc, + [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, + [MarshalAs(UnmanagedType.LPStruct)] Guid riid, + out IntPtr ppv); + + void GetParent(out IShellItem ppsi); + + void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName); + + void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); + + void Compare(IShellItem psi, uint hint, out int piOrder); + } + + internal enum SIGDN : uint + { + NORMALDISPLAY = 0, + PARENTRELATIVEPARSING = 0x80018001, + PARENTRELATIVEFORADDRESSBAR = 0x8001c001, + DESKTOPABSOLUTEPARSING = 0x80028000, + PARENTRELATIVEEDITING = 0x80031001, + DESKTOPABSOLUTEEDITING = 0x8004c000, + FILESYSPATH = 0x80058000, + URL = 0x80068000 + } + + internal enum HResult + { + Ok = 0x0000, + False = 0x0001, + InvalidArguments = unchecked((int)0x80070057), + OutOfMemory = unchecked((int)0x8007000E), + NoInterface = unchecked((int)0x80004002), + Fail = unchecked((int)0x80004005), + ElementNotFound = unchecked((int)0x80070490), + TypeElementNotFound = unchecked((int)0x8002802B), + NoObject = unchecked((int)0x800401E5), + Win32ErrorCanceled = 1223, + Canceled = unchecked((int)0x800704C7), + ResourceInUse = unchecked((int)0x800700AA), + AccessDenied = unchecked((int)0x80030005) + } + + [ComImport] + [Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IShellItemImageFactory + { + [PreserveSig] + HResult GetImage( + [In][MarshalAs(UnmanagedType.Struct)] NativeSize size, + [In] ThumbnailOptions flags, + [Out] out IntPtr phbm); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NativeSize + { + private int width; + private int height; + + public int Width + { + set => width = value; + } + + public int Height + { + set => height = value; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct RGBQUAD + { + public byte rgbBlue; + public byte rgbGreen; + public byte rgbRed; + public byte rgbReserved; + } +} diff --git a/QuickLook.sln b/QuickLook.sln index dc746fe5d..e370fd5c7 100644 --- a/QuickLook.sln +++ b/QuickLook.sln @@ -49,6 +49,7 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "QuickLook.Installer", "Quic {8B4A9CE5-67B5-4A94-81CB-3771F688FDEB} = {8B4A9CE5-67B5-4A94-81CB-3771F688FDEB} {CE22A1F3-7F2C-4EC8-BFDE-B58D0EB625FC} = {CE22A1F3-7F2C-4EC8-BFDE-B58D0EB625FC} {BD58F3FC-7601-47BA-AAA1-D8A9D54A33DA} = {BD58F3FC-7601-47BA-AAA1-D8A9D54A33DA} + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A} = {8D50A1DA-601C-4C26-9A7E-7D477EA8430A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QuickLook.Native64", "QuickLook.Native\QuickLook.Native64\QuickLook.Native64.vcxproj", "{794E4DCF-F715-4836-9D30-ABD296586D23}" @@ -71,6 +72,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Common", "QuickLo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.PluginInstaller", "QuickLook.Plugin\QuickLook.Plugin.PluginInstaller\QuickLook.Plugin.PluginInstaller.csproj", "{BD58F3FC-7601-47BA-AAA1-D8A9D54A33DA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.PEViewer", "QuickLook.Plugin\QuickLook.Plugin.PEViewer\QuickLook.Plugin.PEViewer.csproj", "{8D50A1DA-601C-4C26-9A7E-7D477EA8430A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -201,6 +204,14 @@ Global {BD58F3FC-7601-47BA-AAA1-D8A9D54A33DA}.Release|Any CPU.Build.0 = Release|Any CPU {BD58F3FC-7601-47BA-AAA1-D8A9D54A33DA}.Release|x86.ActiveCfg = Release|x86 {BD58F3FC-7601-47BA-AAA1-D8A9D54A33DA}.Release|x86.Build.0 = Release|x86 + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Debug|x86.ActiveCfg = Debug|Any CPU + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Debug|x86.Build.0 = Debug|Any CPU + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Release|Any CPU.Build.0 = Release|Any CPU + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Release|x86.ActiveCfg = Release|Any CPU + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -219,6 +230,7 @@ Global {863ECAAC-18D9-4256-A27D-0F308089FB47} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93} {45E94893-3076-4A8E-8969-6955B6340739} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93} {BD58F3FC-7601-47BA-AAA1-D8A9D54A33DA} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93} + {8D50A1DA-601C-4C26-9A7E-7D477EA8430A} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D3761C32-8C5F-498A-892B-3B0882994B62}