diff --git a/Wox.Infrastructure/PeHeaderReader.cs b/Wox.Infrastructure/PeHeaderReader.cs new file mode 100644 index 0000000000..ebae5c6a6a --- /dev/null +++ b/Wox.Infrastructure/PeHeaderReader.cs @@ -0,0 +1,565 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Wox.Infrastructure +{ + // Source: http://code.cheesydesign.com/?p=572 + /// + /// Reads in the header information of the Portable Executable format. + /// Provides information such as the date the assembly was compiled. + /// + public class PeHeaderReader + { + #region File Header Structures + + public struct IMAGE_DOS_HEADER + { // DOS .EXE header + public UInt16 e_magic; // Magic number + public UInt16 e_cblp; // Bytes on last page of file + public UInt16 e_cp; // Pages in file + public UInt16 e_crlc; // Relocations + public UInt16 e_cparhdr; // Size of header in paragraphs + public UInt16 e_minalloc; // Minimum extra paragraphs needed + public UInt16 e_maxalloc; // Maximum extra paragraphs needed + public UInt16 e_ss; // Initial (relative) SS value + public UInt16 e_sp; // Initial SP value + public UInt16 e_csum; // Checksum + public UInt16 e_ip; // Initial IP value + public UInt16 e_cs; // Initial (relative) CS value + public UInt16 e_lfarlc; // File address of relocation table + public UInt16 e_ovno; // Overlay number + public UInt16 e_res_0; // Reserved words + public UInt16 e_res_1; // Reserved words + public UInt16 e_res_2; // Reserved words + public UInt16 e_res_3; // Reserved words + public UInt16 e_oemid; // OEM identifier (for e_oeminfo) + public UInt16 e_oeminfo; // OEM information; e_oemid specific + public UInt16 e_res2_0; // Reserved words + public UInt16 e_res2_1; // Reserved words + public UInt16 e_res2_2; // Reserved words + public UInt16 e_res2_3; // Reserved words + public UInt16 e_res2_4; // Reserved words + public UInt16 e_res2_5; // Reserved words + public UInt16 e_res2_6; // Reserved words + public UInt16 e_res2_7; // Reserved words + public UInt16 e_res2_8; // Reserved words + public UInt16 e_res2_9; // Reserved words + public UInt32 e_lfanew; // File address of new exe header + } + + [StructLayout(LayoutKind.Sequential)] + public struct IMAGE_DATA_DIRECTORY + { + public UInt32 VirtualAddress; + public UInt32 Size; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_OPTIONAL_HEADER32 + { + public UInt16 Magic; + public Byte MajorLinkerVersion; + public Byte MinorLinkerVersion; + public UInt32 SizeOfCode; + public UInt32 SizeOfInitializedData; + public UInt32 SizeOfUninitializedData; + public UInt32 AddressOfEntryPoint; + public UInt32 BaseOfCode; + public UInt32 BaseOfData; + public UInt32 ImageBase; + public UInt32 SectionAlignment; + public UInt32 FileAlignment; + public UInt16 MajorOperatingSystemVersion; + public UInt16 MinorOperatingSystemVersion; + public UInt16 MajorImageVersion; + public UInt16 MinorImageVersion; + public UInt16 MajorSubsystemVersion; + public UInt16 MinorSubsystemVersion; + public UInt32 Win32VersionValue; + public UInt32 SizeOfImage; + public UInt32 SizeOfHeaders; + public UInt32 CheckSum; + public UInt16 Subsystem; + public UInt16 DllCharacteristics; + public UInt32 SizeOfStackReserve; + public UInt32 SizeOfStackCommit; + public UInt32 SizeOfHeapReserve; + public UInt32 SizeOfHeapCommit; + public UInt32 LoaderFlags; + public UInt32 NumberOfRvaAndSizes; + + public IMAGE_DATA_DIRECTORY ExportTable; + public IMAGE_DATA_DIRECTORY ImportTable; + public IMAGE_DATA_DIRECTORY ResourceTable; + public IMAGE_DATA_DIRECTORY ExceptionTable; + public IMAGE_DATA_DIRECTORY CertificateTable; + public IMAGE_DATA_DIRECTORY BaseRelocationTable; + public IMAGE_DATA_DIRECTORY Debug; + public IMAGE_DATA_DIRECTORY Architecture; + public IMAGE_DATA_DIRECTORY GlobalPtr; + public IMAGE_DATA_DIRECTORY TLSTable; + public IMAGE_DATA_DIRECTORY LoadConfigTable; + public IMAGE_DATA_DIRECTORY BoundImport; + public IMAGE_DATA_DIRECTORY IAT; + public IMAGE_DATA_DIRECTORY DelayImportDescriptor; + public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; + public IMAGE_DATA_DIRECTORY Reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_OPTIONAL_HEADER64 + { + public UInt16 Magic; + public Byte MajorLinkerVersion; + public Byte MinorLinkerVersion; + public UInt32 SizeOfCode; + public UInt32 SizeOfInitializedData; + public UInt32 SizeOfUninitializedData; + public UInt32 AddressOfEntryPoint; + public UInt32 BaseOfCode; + public UInt64 ImageBase; + public UInt32 SectionAlignment; + public UInt32 FileAlignment; + public UInt16 MajorOperatingSystemVersion; + public UInt16 MinorOperatingSystemVersion; + public UInt16 MajorImageVersion; + public UInt16 MinorImageVersion; + public UInt16 MajorSubsystemVersion; + public UInt16 MinorSubsystemVersion; + public UInt32 Win32VersionValue; + public UInt32 SizeOfImage; + public UInt32 SizeOfHeaders; + public UInt32 CheckSum; + public UInt16 Subsystem; + public UInt16 DllCharacteristics; + public UInt64 SizeOfStackReserve; + public UInt64 SizeOfStackCommit; + public UInt64 SizeOfHeapReserve; + public UInt64 SizeOfHeapCommit; + public UInt32 LoaderFlags; + public UInt32 NumberOfRvaAndSizes; + + public IMAGE_DATA_DIRECTORY ExportTable; + public IMAGE_DATA_DIRECTORY ImportTable; + public IMAGE_DATA_DIRECTORY ResourceTable; + public IMAGE_DATA_DIRECTORY ExceptionTable; + public IMAGE_DATA_DIRECTORY CertificateTable; + public IMAGE_DATA_DIRECTORY BaseRelocationTable; + public IMAGE_DATA_DIRECTORY Debug; + public IMAGE_DATA_DIRECTORY Architecture; + public IMAGE_DATA_DIRECTORY GlobalPtr; + public IMAGE_DATA_DIRECTORY TLSTable; + public IMAGE_DATA_DIRECTORY LoadConfigTable; + public IMAGE_DATA_DIRECTORY BoundImport; + public IMAGE_DATA_DIRECTORY IAT; + public IMAGE_DATA_DIRECTORY DelayImportDescriptor; + public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; + public IMAGE_DATA_DIRECTORY Reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_FILE_HEADER + { + public UInt16 Machine; + public UInt16 NumberOfSections; + public UInt32 TimeDateStamp; + public UInt32 PointerToSymbolTable; + public UInt32 NumberOfSymbols; + public UInt16 SizeOfOptionalHeader; + public UInt16 Characteristics; + } + + // Grabbed the following 2 definitions from http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html + + [StructLayout(LayoutKind.Explicit)] + public struct IMAGE_SECTION_HEADER + { + [FieldOffset(0)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public char[] Name; + [FieldOffset(8)] + public UInt32 VirtualSize; + [FieldOffset(12)] + public UInt32 VirtualAddress; + [FieldOffset(16)] + public UInt32 SizeOfRawData; + [FieldOffset(20)] + public UInt32 PointerToRawData; + [FieldOffset(24)] + public UInt32 PointerToRelocations; + [FieldOffset(28)] + public UInt32 PointerToLinenumbers; + [FieldOffset(32)] + public UInt16 NumberOfRelocations; + [FieldOffset(34)] + public UInt16 NumberOfLinenumbers; + [FieldOffset(36)] + public DataSectionFlags Characteristics; + + public string Section + { + get { return new string(Name); } + } + } + + [Flags] + public enum DataSectionFlags : uint + { + /// + /// Reserved for future use. + /// + TypeReg = 0x00000000, + /// + /// Reserved for future use. + /// + TypeDsect = 0x00000001, + /// + /// Reserved for future use. + /// + TypeNoLoad = 0x00000002, + /// + /// Reserved for future use. + /// + TypeGroup = 0x00000004, + /// + /// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. + /// + TypeNoPadded = 0x00000008, + /// + /// Reserved for future use. + /// + TypeCopy = 0x00000010, + /// + /// The section contains executable code. + /// + ContentCode = 0x00000020, + /// + /// The section contains initialized data. + /// + ContentInitializedData = 0x00000040, + /// + /// The section contains uninitialized data. + /// + ContentUninitializedData = 0x00000080, + /// + /// Reserved for future use. + /// + LinkOther = 0x00000100, + /// + /// The section contains comments or other information. The .drectve section has this type. This is valid for object files only. + /// + LinkInfo = 0x00000200, + /// + /// Reserved for future use. + /// + TypeOver = 0x00000400, + /// + /// The section will not become part of the image. This is valid only for object files. + /// + LinkRemove = 0x00000800, + /// + /// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files. + /// + LinkComDat = 0x00001000, + /// + /// Reset speculative exceptions handling bits in the TLB entries for this section. + /// + NoDeferSpecExceptions = 0x00004000, + /// + /// The section contains data referenced through the global pointer (GP). + /// + RelativeGP = 0x00008000, + /// + /// Reserved for future use. + /// + MemPurgeable = 0x00020000, + /// + /// Reserved for future use. + /// + Memory16Bit = 0x00020000, + /// + /// Reserved for future use. + /// + MemoryLocked = 0x00040000, + /// + /// Reserved for future use. + /// + MemoryPreload = 0x00080000, + /// + /// Align data on a 1-byte boundary. Valid only for object files. + /// + Align1Bytes = 0x00100000, + /// + /// Align data on a 2-byte boundary. Valid only for object files. + /// + Align2Bytes = 0x00200000, + /// + /// Align data on a 4-byte boundary. Valid only for object files. + /// + Align4Bytes = 0x00300000, + /// + /// Align data on an 8-byte boundary. Valid only for object files. + /// + Align8Bytes = 0x00400000, + /// + /// Align data on a 16-byte boundary. Valid only for object files. + /// + Align16Bytes = 0x00500000, + /// + /// Align data on a 32-byte boundary. Valid only for object files. + /// + Align32Bytes = 0x00600000, + /// + /// Align data on a 64-byte boundary. Valid only for object files. + /// + Align64Bytes = 0x00700000, + /// + /// Align data on a 128-byte boundary. Valid only for object files. + /// + Align128Bytes = 0x00800000, + /// + /// Align data on a 256-byte boundary. Valid only for object files. + /// + Align256Bytes = 0x00900000, + /// + /// Align data on a 512-byte boundary. Valid only for object files. + /// + Align512Bytes = 0x00A00000, + /// + /// Align data on a 1024-byte boundary. Valid only for object files. + /// + Align1024Bytes = 0x00B00000, + /// + /// Align data on a 2048-byte boundary. Valid only for object files. + /// + Align2048Bytes = 0x00C00000, + /// + /// Align data on a 4096-byte boundary. Valid only for object files. + /// + Align4096Bytes = 0x00D00000, + /// + /// Align data on an 8192-byte boundary. Valid only for object files. + /// + Align8192Bytes = 0x00E00000, + /// + /// The section contains extended relocations. + /// + LinkExtendedRelocationOverflow = 0x01000000, + /// + /// The section can be discarded as needed. + /// + MemoryDiscardable = 0x02000000, + /// + /// The section cannot be cached. + /// + MemoryNotCached = 0x04000000, + /// + /// The section is not pageable. + /// + MemoryNotPaged = 0x08000000, + /// + /// The section can be shared in memory. + /// + MemoryShared = 0x10000000, + /// + /// The section can be executed as code. + /// + MemoryExecute = 0x20000000, + /// + /// The section can be read. + /// + MemoryRead = 0x40000000, + /// + /// The section can be written to. + /// + MemoryWrite = 0x80000000 + } + + #endregion File Header Structures + + #region Private Fields + + /// + /// The DOS header + /// + private IMAGE_DOS_HEADER dosHeader; + /// + /// The file header + /// + private IMAGE_FILE_HEADER fileHeader; + /// + /// Optional 32 bit file header + /// + private IMAGE_OPTIONAL_HEADER32 optionalHeader32; + /// + /// Optional 64 bit file header + /// + private IMAGE_OPTIONAL_HEADER64 optionalHeader64; + /// + /// Image Section headers. Number of sections is in the file header. + /// + private IMAGE_SECTION_HEADER[] imageSectionHeaders; + + #endregion Private Fields + + #region Public Methods + + public PeHeaderReader(string filePath) + { + // Read in the DLL or EXE and get the timestamp + using (FileStream stream = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + { + BinaryReader reader = new BinaryReader(stream); + dosHeader = FromBinaryReader(reader); + + // Add 4 bytes to the offset + stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); + + UInt32 ntHeadersSignature = reader.ReadUInt32(); + fileHeader = FromBinaryReader(reader); + if (this.Is32BitHeader) + { + optionalHeader32 = FromBinaryReader(reader); + } + else + { + optionalHeader64 = FromBinaryReader(reader); + } + + imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections]; + for (int headerNo = 0; headerNo < imageSectionHeaders.Length; ++headerNo) + { + imageSectionHeaders[headerNo] = FromBinaryReader(reader); + } + + } + } + + /// + /// Gets the header of the .NET assembly that called this function + /// + /// + public static PeHeaderReader GetCallingAssemblyHeader() + { + // Get the path to the calling assembly, which is the path to the + // DLL or EXE that we want the time of + string filePath = System.Reflection.Assembly.GetCallingAssembly().Location; + + // Get and return the timestamp + return new PeHeaderReader(filePath); + } + + /// + /// Gets the header of the .NET assembly that called this function + /// + /// + public static PeHeaderReader GetAssemblyHeader() + { + // Get the path to the calling assembly, which is the path to the + // DLL or EXE that we want the time of + string filePath = System.Reflection.Assembly.GetAssembly(typeof(PeHeaderReader)).Location; + + // Get and return the timestamp + return new PeHeaderReader(filePath); + } + + /// + /// Reads in a block from a file and converts it to the struct + /// type specified by the template parameter + /// + /// + /// + /// + public static T FromBinaryReader(BinaryReader reader) + { + // Read in a byte array + byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T))); + + // Pin the managed memory while, copy it out the data, then unpin it + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); + handle.Free(); + + return theStructure; + } + + #endregion Public Methods + + #region Properties + + /// + /// Gets if the file header is 32 bit or not + /// + public bool Is32BitHeader + { + get + { + UInt16 IMAGE_FILE_32BIT_MACHINE = 0x0100; + return (IMAGE_FILE_32BIT_MACHINE & FileHeader.Characteristics) == IMAGE_FILE_32BIT_MACHINE; + } + } + + /// + /// Gets the file header + /// + public IMAGE_FILE_HEADER FileHeader + { + get + { + return fileHeader; + } + } + + /// + /// Gets the optional header + /// + public IMAGE_OPTIONAL_HEADER32 OptionalHeader32 + { + get + { + return optionalHeader32; + } + } + + /// + /// Gets the optional header + /// + public IMAGE_OPTIONAL_HEADER64 OptionalHeader64 + { + get + { + return optionalHeader64; + } + } + + public IMAGE_SECTION_HEADER[] ImageSectionHeaders + { + get + { + return imageSectionHeaders; + } + } + + /// + /// Gets the timestamp from the file header + /// + public DateTime TimeStamp + { + get + { + // Timestamp is a date offset from 1970 + DateTime returnValue = new DateTime(1970, 1, 1, 0, 0, 0); + + // Add in the number of seconds since 1970/1/1 + returnValue = returnValue.AddSeconds(fileHeader.TimeDateStamp); + // Adjust to local timezone + returnValue += TimeZone.CurrentTimeZone.GetUtcOffset(returnValue); + + return returnValue; + } + } + + #endregion Properties + } +} \ No newline at end of file diff --git a/Wox.Infrastructure/WindowsShellRun.cs b/Wox.Infrastructure/WindowsShellRun.cs index d3f78fd905..73627d1a24 100644 --- a/Wox.Infrastructure/WindowsShellRun.cs +++ b/Wox.Infrastructure/WindowsShellRun.cs @@ -80,7 +80,7 @@ namespace Wox.Infrastructure static void ShellExecCmdLine(IntPtr hInstance, IntPtr hwnd, string command, string startDir, global::System.Diagnostics.ProcessWindowStyle nShow, ShellExecCmdLineFlags dwSeclFlags) { - string cmd = command; + string cmd = command; string args = null; if (UrlIs(command, URLIS_URL)) cmd = command; @@ -103,17 +103,39 @@ namespace Wox.Infrastructure startDir = dir; } - if (UserSettingStorage.Instance.LeaveCmdOpen) + if (UserSettingStorage.Instance.LeaveCmdOpen && File.Exists(cmd)) { - string cmdExe; - string dummy; - EvaluateSystemAndUserCommandLine("cmd.exe", startDir, out cmdExe, out dummy, dwSeclFlags); + bool needsCommandLine; - // check whether user typed >cmd, because we don't want to create 2 nested shells - if (cmdExe != cmd) + try { - args = string.Format("/k {0} {1}", cmd, args); - cmd = cmdExe; + + var peHeaderReader = new PeHeaderReader(cmd); + + if (peHeaderReader.Is32BitHeader) + needsCommandLine = peHeaderReader.OptionalHeader32.Subsystem == 3; // IMAGE_SUBSYSTEM_WINDOWS_CUI == 3 + else + needsCommandLine = peHeaderReader.OptionalHeader64.Subsystem == 3; + } + + catch (Exception) + { + // Error reading the headers. We will try to run the command the standard way. + needsCommandLine = false; + } + + if (needsCommandLine) + { + string cmdExe; + string dummy; + EvaluateSystemAndUserCommandLine("cmd.exe", startDir, out cmdExe, out dummy, dwSeclFlags); + + // check whether user typed >cmd, because we don't want to create 2 nested shells + if (cmdExe != cmd) + { + args = string.Format("/k {0} {1}", cmd, args); + cmd = cmdExe; + } } } diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index d1dda9db37..9eb0ebbd0d 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -49,6 +49,7 @@ + diff --git a/Wox/Helper/WindowOpener.cs b/Wox/Helper/WindowOpener.cs index c7804adb55..0bc37d7ee6 100644 --- a/Wox/Helper/WindowOpener.cs +++ b/Wox/Helper/WindowOpener.cs @@ -12,9 +12,9 @@ namespace Wox.Helper { var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T)) ?? (T)Activator.CreateInstance(typeof(T), args); + Application.Current.MainWindow.Hide(); window.Show(); window.Focus(); - Application.Current.MainWindow.Hide(); return (T)window; }