diff --git a/Core/AppWindow.cs b/Core/AppWindow.cs index 056f01d3..e59fd4aa 100644 --- a/Core/AppWindow.cs +++ b/Core/AppWindow.cs @@ -21,24 +21,21 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Drawing; -using System.Drawing.Imaging; -using System.IO; using System.Linq; using System.Runtime.Caching; using System.Runtime.InteropServices; using System.Text; using System.Windows.Media.Imaging; using ManagedWinapi.Windows; +using Microsoft.Win32; namespace Switcheroo.Core { - /// /// This class is a wrapper around the Win32 api window handles /// - public class AppWindow : ManagedWinapi.Windows.SystemWindow + public class AppWindow : SystemWindow { public string FormattedTitle { get; set; } @@ -59,82 +56,21 @@ public string ProcessTitle public string FormattedProcessTitle { get; set; } - public BitmapImage IconImage - { - get - { - var key = "IconImage-" + HWnd; - var iconImage = MemoryCache.Default.Get(key) as BitmapImage; - if (iconImage == null) - { - iconImage = ExtractIcon() ?? new BitmapImage(); - MemoryCache.Default.Add(key, iconImage, DateTimeOffset.Now.AddHours(1)); - } - return iconImage; - } - } - private BitmapImage ExtractIcon() - { - Icon extractAssociatedIcon = null; - try - { - extractAssociatedIcon = Icon.ExtractAssociatedIcon(GetExecutablePath(Process)); - } - catch (Win32Exception) - { - // Could not extract icon - } - if (extractAssociatedIcon == null) - { - return null; - } - using (var memory = new MemoryStream()) - { - var bitmap = extractAssociatedIcon.ToBitmap(); - bitmap.Save(memory, ImageFormat.Png); - memory.Position = 0; - var bitmapImage = new BitmapImage(); - bitmapImage.BeginInit(); - bitmapImage.StreamSource = memory; - bitmapImage.CacheOption = BitmapCacheOption.OnLoad; - bitmapImage.EndInit(); - return bitmapImage; - } + public Icon LargeWindowIcon + { + get { return new WindowIconFinder().Find(this, WindowIconSize.Large); } } - private static string GetExecutablePath(Process process) + public Icon SmallWindowIcon { - // If Vista or later - if (Environment.OSVersion.Version.Major >= 6) - { - return GetExecutablePathAboveVista(process.Id); - } - - return process.MainModule.FileName; + get { return new WindowIconFinder().Find(this, WindowIconSize.Small); } } - private static string GetExecutablePathAboveVista(int processId) + public string ExecutablePath { - var buffer = new StringBuilder(1024); - var hprocess = WinApi.OpenProcess(WinApi.ProcessAccess.QueryLimitedInformation, false, processId); - if (hprocess == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); - - try - { - // ReSharper disable once RedundantAssignment - var size = buffer.Capacity; - if (WinApi.QueryFullProcessImageName(hprocess, 0, buffer, out size)) - { - return buffer.ToString(); - } - } - finally - { - WinApi.CloseHandle(hprocess); - } - throw new Win32Exception(Marshal.GetLastWin32Error()); + get { return GetExecutablePath(Process.Id); } } public AppWindow(IntPtr HWnd) : base(HWnd) { } @@ -182,7 +118,7 @@ public bool IsAltTabWindow() private bool HasWindowTitle() { - return Title.Length > 0; + return !string.IsNullOrEmpty(Title); } private bool IsToolWindow() @@ -223,5 +159,28 @@ private bool IsOwnerOrOwnerNotVisible() { return Owner == null || !Owner.Visible; } + + // This method only works on Windows >= Windows Vista + private static string GetExecutablePath(int processId) + { + var buffer = new StringBuilder(1024); + var hprocess = WinApi.OpenProcess(WinApi.ProcessAccess.QueryLimitedInformation, false, processId); + if (hprocess == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); + + try + { + // ReSharper disable once RedundantAssignment + var size = buffer.Capacity; + if (WinApi.QueryFullProcessImageName(hprocess, 0, buffer, out size)) + { + return buffer.ToString(); + } + } + finally + { + WinApi.CloseHandle(hprocess); + } + throw new Win32Exception(Marshal.GetLastWin32Error()); + } } } diff --git a/Core/Core.csproj b/Core/Core.csproj index 7928c716..c5b82740 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -59,6 +59,7 @@ + diff --git a/Core/WinApi.cs b/Core/WinApi.cs index dc2f9992..a58e4f07 100644 --- a/Core/WinApi.cs +++ b/Core/WinApi.cs @@ -246,5 +246,59 @@ public enum MapVirtualKeyMapTypes : uint /// MAPVK_VK_TO_VSC_EX = 0x04 } + + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hwnd, int message, int wParam, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern IntPtr DefWindowProc(IntPtr hWnd, int message, int wParam, IntPtr lParam); + + public enum ClassLongFlags + { + GCLP_MENUNAME = -8, + GCLP_HBRBACKGROUND = -10, + GCLP_HCURSOR = -12, + GCLP_HICON = -14, + GCLP_HMODULE = -16, + GCL_CBWNDEXTRA = -18, + GCL_CBCLSEXTRA = -20, + GCLP_WNDPROC = -24, + GCL_STYLE = -26, + GCLP_HICONSM = -34, + GCW_ATOM = -32 + } + + public static IntPtr GetClassLongPtr(IntPtr hWnd, ClassLongFlags flags) + { + return IntPtr.Size > 4 ? GetClassLongPtr64(hWnd, flags) : new IntPtr(GetClassLongPtr32(hWnd, flags)); + } + + [DllImport("user32.dll", EntryPoint = "GetClassLong")] + public static extern uint GetClassLongPtr32(IntPtr hWnd, ClassLongFlags flags); + + [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] + public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, ClassLongFlags flags); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern uint RegisterWindowMessage(string lpString); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SendMessageTimeout( + IntPtr hWnd, + uint Msg, + IntPtr wParam, + IntPtr lParam, + SendMessageTimeoutFlags fuFlags, + uint uTimeout, + out IntPtr lpdwResult); + + [Flags] + public enum SendMessageTimeoutFlags : uint + { + SMTO_NORMAL = 0x0, + SMTO_BLOCK = 0x1, + SMTO_ABORTIFHUNG = 0x2, + SMTO_NOTIMEOUTIFNOTHUNG = 0x8 + } } } \ No newline at end of file diff --git a/Core/WindowIconFinder.cs b/Core/WindowIconFinder.cs new file mode 100644 index 00000000..c9f4be63 --- /dev/null +++ b/Core/WindowIconFinder.cs @@ -0,0 +1,68 @@ +/* + * Switcheroo - The incremental-search task switcher for Windows. + * http://www.switcheroo.io/ + * Copyright 2009, 2010 James Sulak + * Copyright 2014 Regin Larsen + * + * Switcheroo 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. + * + * Switcheroo 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 Switcheroo. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Drawing; + +namespace Switcheroo.Core +{ + public enum WindowIconSize + { + Small, + Large + } + + public class WindowIconFinder + { + public Icon Find(AppWindow window, WindowIconSize size) + { + Icon icon = null; + try + { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms632625(v=vs.85).aspx + IntPtr response; + var outvalue = WinApi.SendMessageTimeout(window.HWnd, 0x007F, size == WindowIconSize.Small ? new IntPtr(2) : new IntPtr(1), + IntPtr.Zero, WinApi.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 100, out response); + + if (outvalue == IntPtr.Zero || response == IntPtr.Zero) + { + response = WinApi.GetClassLongPtr(window.HWnd, + size == WindowIconSize.Small ? WinApi.ClassLongFlags.GCLP_HICONSM : WinApi.ClassLongFlags.GCLP_HICON); + } + + if (response != IntPtr.Zero) + { + icon = Icon.FromHandle(response); + } + else + { + var executablePath = window.ExecutablePath; + icon = Icon.ExtractAssociatedIcon(executablePath); + } + } + catch (Win32Exception) + { + // Could not extract icon + } + return icon; + } + } +} \ No newline at end of file diff --git a/Switcheroo.sln b/Switcheroo.sln index 03030f39..655f80be 100644 --- a/Switcheroo.sln +++ b/Switcheroo.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30110.0 +VisualStudioVersion = 12.0.30723.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Switcheroo", "Switcheroo\Switcheroo.csproj", "{B28E183B-0E38-48A8-8A4E-B4AB7B23CE93}" ProjectSection(ProjectDependencies) = postProject diff --git a/Switcheroo/IconToBitmapConverter.cs b/Switcheroo/IconToBitmapConverter.cs new file mode 100644 index 00000000..6e4d3781 --- /dev/null +++ b/Switcheroo/IconToBitmapConverter.cs @@ -0,0 +1,51 @@ +/* + * Switcheroo - The incremental-search task switcher for Windows. + * http://www.switcheroo.io/ + * Copyright 2009, 2010 James Sulak + * Copyright 2014 Regin Larsen + * + * Switcheroo 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. + * + * Switcheroo 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 Switcheroo. If not, see . + */ + +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Windows.Media.Imaging; + +namespace Switcheroo +{ + public class IconToBitmapImageConverter + { + public BitmapImage Convert(Icon icon) + { + if (icon == null) + { + return null; + } + + using (var memory = new MemoryStream()) + { + var bitmap = icon.ToBitmap(); + bitmap.Save(memory, ImageFormat.Png); + memory.Position = 0; + var bitmapImage = new BitmapImage(); + bitmapImage.BeginInit(); + bitmapImage.StreamSource = memory; + bitmapImage.CacheOption = BitmapCacheOption.OnLoad; + bitmapImage.EndInit(); + return bitmapImage; + } + } + } +} diff --git a/Switcheroo/MainWindow.xaml b/Switcheroo/MainWindow.xaml index 636ab302..a040055f 100644 --- a/Switcheroo/MainWindow.xaml +++ b/Switcheroo/MainWindow.xaml @@ -24,9 +24,14 @@ - - - + + + + + + + + @@ -51,7 +56,14 @@ - + + + + + + + + MSBuild:Compile Designer + + + Designer MSBuild:Compile diff --git a/Switcheroo/WindowHandleToCachedIconConverter.cs b/Switcheroo/WindowHandleToCachedIconConverter.cs new file mode 100644 index 00000000..6bfd2b4b --- /dev/null +++ b/Switcheroo/WindowHandleToCachedIconConverter.cs @@ -0,0 +1,42 @@ +/* + * Switcheroo - The incremental-search task switcher for Windows. + * http://www.switcheroo.io/ + * Copyright 2009, 2010 James Sulak + * Copyright 2014 Regin Larsen + * + * Switcheroo 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. + * + * Switcheroo 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 Switcheroo. If not, see . + */ + +using System; +using System.Globalization; +using System.Runtime.Caching; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace Switcheroo +{ + public class WindowHandleToCachedIconConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var key = "IconImage-" + value + "-longCache"; + return MemoryCache.Default.Get(key) as BitmapImage; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Switcheroo/WindowHandleToIconConverter.cs b/Switcheroo/WindowHandleToIconConverter.cs new file mode 100644 index 00000000..4dd24f46 --- /dev/null +++ b/Switcheroo/WindowHandleToIconConverter.cs @@ -0,0 +1,94 @@ +/* + * Switcheroo - The incremental-search task switcher for Windows. + * http://www.switcheroo.io/ + * Copyright 2009, 2010 James Sulak + * Copyright 2014 Regin Larsen + * + * Switcheroo 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. + * + * Switcheroo 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 Switcheroo. If not, see . + */ + +using System; +using System.Globalization; +using System.Runtime.Caching; +using System.Windows.Data; +using System.Windows.Media.Imaging; +using Microsoft.Win32; +using Switcheroo.Core; + +namespace Switcheroo +{ + public class WindowHandleToIconConverter : IValueConverter + { + private readonly IconToBitmapImageConverter _iconToBitmapConverter; + + public WindowHandleToIconConverter() + { + _iconToBitmapConverter = new IconToBitmapImageConverter(); + } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var handle = (IntPtr)value; + var key = "IconImage-" + handle; + var shortCacheKey = key + "-shortCache"; + var longCacheKey = key + "-longCache"; + var iconImage = MemoryCache.Default.Get(shortCacheKey) as BitmapImage; + if (iconImage == null) + { + var window = new AppWindow(handle); + var icon = ShouldUseSmallTaskbarIcons() ? window.SmallWindowIcon : window.LargeWindowIcon; + iconImage = _iconToBitmapConverter.Convert(icon) ?? new BitmapImage(); + MemoryCache.Default.Add(shortCacheKey, iconImage, DateTimeOffset.Now.AddSeconds(5)); + MemoryCache.Default.Add(longCacheKey, iconImage, DateTimeOffset.Now.AddMinutes(120)); + } + return iconImage; + } + + private static bool ShouldUseSmallTaskbarIcons() + { + var cacheKey = "SmallTaskbarIcons"; + + var cachedSetting = MemoryCache.Default.Get(cacheKey) as bool?; + if (cachedSetting != null) + { + return cachedSetting.Value; + } + + using (var registryKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced")) + { + if (registryKey == null) + { + return false; + } + + var value = registryKey.GetValue("TaskbarSmallIcons"); + if (value == null) + { + return false; + } + + int intValue; + int.TryParse(value.ToString(), out intValue); + var smallTaskbarIcons = intValue == 1; + MemoryCache.Default.Set(cacheKey, smallTaskbarIcons, DateTimeOffset.Now.AddMinutes(120)); + return smallTaskbarIcons; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file