Compare commits
	
		
			5 Commits
		
	
	
		
			v1.2.0-pre
			...
			6de76da8ad
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6de76da8ad | |||
| 48161b5c2e | |||
| d1df235af0 | |||
| 6aaa32cf92 | |||
| 4ca0ad021f | 
@ -1,12 +1,8 @@
 | 
			
		||||
using BarRaider.SdTools;
 | 
			
		||||
using FocusVolumeControl.AudioSessions;
 | 
			
		||||
using FocusVolumeControl.AudioSessions;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace FocusVolumeControl.AudioHelpers;
 | 
			
		||||
 | 
			
		||||
@ -27,6 +23,18 @@ public class AudioHelper
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Process GetProcessById(int id)
 | 
			
		||||
	{
 | 
			
		||||
		try
 | 
			
		||||
		{
 | 
			
		||||
			return Process.GetProcessById(id);
 | 
			
		||||
		}
 | 
			
		||||
		catch
 | 
			
		||||
		{
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public IAudioSession FindSession(List<Process> processes)
 | 
			
		||||
	{
 | 
			
		||||
		var results = new ActiveAudioSessionWrapper();
 | 
			
		||||
@ -55,7 +63,12 @@ public class AudioHelper
 | 
			
		||||
				sessionEnumerator.GetSession(i, out var session);
 | 
			
		||||
 | 
			
		||||
				session.GetProcessId(out var sessionProcessId);
 | 
			
		||||
				var audioProcess = Process.GetProcessById(sessionProcessId);
 | 
			
		||||
				var audioProcess = GetProcessById(sessionProcessId);
 | 
			
		||||
 | 
			
		||||
				if(audioProcess == null)
 | 
			
		||||
				{
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				var index = processes.FindIndex(x => x.Id == sessionProcessId || x.ProcessName == audioProcess?.ProcessName);
 | 
			
		||||
 | 
			
		||||
@ -68,7 +81,14 @@ public class AudioHelper
 | 
			
		||||
					{
 | 
			
		||||
						bestProcessMatch = audioProcess;
 | 
			
		||||
						currentIndex = index;
 | 
			
		||||
 | 
			
		||||
						if(string.IsNullOrEmpty(results.DisplayName))
 | 
			
		||||
						{
 | 
			
		||||
							session.GetDisplayName(out var displayName);
 | 
			
		||||
							results.DisplayName = displayName;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
					//some apps like discord have multiple volume processes.
 | 
			
		||||
					//and some apps will be on multiple devices
 | 
			
		||||
@ -92,7 +112,7 @@ public class AudioHelper
 | 
			
		||||
		lock (_lock)
 | 
			
		||||
		{
 | 
			
		||||
			var processes = GetPossibleProcesses();
 | 
			
		||||
			var processIds = processes.Select(x => x.Id).ToArray();
 | 
			
		||||
			var processIds = processes?.Select(x => x.Id).ToArray();
 | 
			
		||||
 | 
			
		||||
			if (_currentProcesses == null || !_currentProcesses.SequenceEqual(processIds))
 | 
			
		||||
			{
 | 
			
		||||
@ -138,7 +158,7 @@ public class AudioHelper
 | 
			
		||||
 | 
			
		||||
		if (handle == IntPtr.Zero)
 | 
			
		||||
		{
 | 
			
		||||
			return null;
 | 
			
		||||
			return new List<Process>();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var ids = Native.GetProcessesOfChildWindows(handle);
 | 
			
		||||
@ -150,6 +170,11 @@ public class AudioHelper
 | 
			
		||||
						   .Select(x => Process.GetProcessById(x))
 | 
			
		||||
						   .ToList();
 | 
			
		||||
 | 
			
		||||
		if(processes.FirstOrDefault()?.ProcessName == "explorer")
 | 
			
		||||
		{
 | 
			
		||||
			return new List<Process>();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try
 | 
			
		||||
		{
 | 
			
		||||
			//note. in instances where you launch a game from steam. this ends up mapping the process to both steam and to the game. which is unfortunate
 | 
			
		||||
 | 
			
		||||
@ -26,10 +26,13 @@ public class NameAndIconHelper
 | 
			
		||||
			var appx = AppxPackage.FromProcess(process);
 | 
			
		||||
			if (appx == null)
 | 
			
		||||
			{
 | 
			
		||||
				//usingg process.MainModule.FileVersionInfo sometimes throws permission exceptions
 | 
			
		||||
				//using process.MainModule.FileVersionInfo sometimes throws permission exceptions
 | 
			
		||||
				//we get the file version info with a limited query flag to avoid that
 | 
			
		||||
				var fileVersionInfo = GetFileVersionInfo(process);
 | 
			
		||||
 | 
			
		||||
				//if the display name is already set, then it came from the display name of the audio session
 | 
			
		||||
				if (string.IsNullOrEmpty(results.DisplayName))
 | 
			
		||||
				{
 | 
			
		||||
					results.DisplayName = process.MainWindowTitle;
 | 
			
		||||
 | 
			
		||||
					if (string.IsNullOrEmpty(results.DisplayName))
 | 
			
		||||
@ -40,6 +43,7 @@ public class NameAndIconHelper
 | 
			
		||||
							results.DisplayName = process.ProcessName;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				results.ExecutablePath = fileVersionInfo?.FileName;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -4,44 +4,59 @@ using System.Linq;
 | 
			
		||||
using BarRaider.SdTools;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using FocusVolumeControl.UI;
 | 
			
		||||
using BitFaster.Caching.Lru;
 | 
			
		||||
 | 
			
		||||
namespace FocusVolumeControl.AudioSessions;
 | 
			
		||||
 | 
			
		||||
public sealed class ActiveAudioSessionWrapper : IAudioSession
 | 
			
		||||
{
 | 
			
		||||
	static ConcurrentLru<string, string> _iconCache = new ConcurrentLru<string, string>(10);
 | 
			
		||||
 | 
			
		||||
	public string DisplayName { get; set; }
 | 
			
		||||
	public string ExecutablePath { get; set; }
 | 
			
		||||
	public string IconPath { get; set; }
 | 
			
		||||
	private List<IAudioSessionControl2> Sessions { get; } = new List<IAudioSessionControl2>();
 | 
			
		||||
	private IEnumerable<ISimpleAudioVolume> Volume => Sessions.Cast<ISimpleAudioVolume>();
 | 
			
		||||
 | 
			
		||||
	string _icon;
 | 
			
		||||
 | 
			
		||||
	public string GetIcon()
 | 
			
		||||
	string GetIconFromIconPath()
 | 
			
		||||
	{
 | 
			
		||||
		if (string.IsNullOrEmpty(_icon))
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				if(!string.IsNullOrEmpty(IconPath))
 | 
			
		||||
		return _iconCache.GetOrAdd(IconPath, (key) =>
 | 
			
		||||
		{
 | 
			
		||||
			var tmp = (Bitmap)Bitmap.FromFile(IconPath);
 | 
			
		||||
			tmp.MakeTransparent();
 | 
			
		||||
					_icon = Tools.ImageToBase64(tmp, true);
 | 
			
		||||
			return Tools.ImageToBase64(tmp, true);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	string GetIconFromExecutablePath()
 | 
			
		||||
	{
 | 
			
		||||
		return _iconCache.GetOrAdd(ExecutablePath, (key) =>
 | 
			
		||||
		{
 | 
			
		||||
			var tmp = IconExtraction.GetIcon(ExecutablePath);
 | 
			
		||||
			//var tmp = Icon.ExtractAssociatedIcon(ExecutablePath);
 | 
			
		||||
			return Tools.ImageToBase64(tmp, true);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public string GetIcon()
 | 
			
		||||
	{
 | 
			
		||||
		try
 | 
			
		||||
		{
 | 
			
		||||
			if (!string.IsNullOrEmpty(IconPath))
 | 
			
		||||
			{
 | 
			
		||||
				return GetIconFromIconPath();
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
					var tmp = Icon.ExtractAssociatedIcon(ExecutablePath);
 | 
			
		||||
					_icon = Tools.ImageToBase64(tmp.ToBitmap(), true);
 | 
			
		||||
				return GetIconFromExecutablePath();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		catch
 | 
			
		||||
		{
 | 
			
		||||
				_icon = "Images/encoderIcon";
 | 
			
		||||
			return "Images/encoderIcon";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
		return _icon;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public bool Any()
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
@ -65,6 +65,7 @@
 | 
			
		||||
    <Compile Include="AudioSessions\IAudioSession.cs" />
 | 
			
		||||
    <Compile Include="FallbackBehavior.cs" />
 | 
			
		||||
    <Compile Include="AudioHelpers\NameAndIconHelper.cs" />
 | 
			
		||||
    <Compile Include="UI\IconExtraction.cs" />
 | 
			
		||||
    <Compile Include="UI\ISDConnectionExtensions.cs" />
 | 
			
		||||
    <Compile Include="Native.cs" />
 | 
			
		||||
    <Compile Include="AudioHelpers\ParentProcessUtilities.cs" />
 | 
			
		||||
@ -96,6 +97,9 @@
 | 
			
		||||
    </Content>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="BitFaster.Caching">
 | 
			
		||||
      <Version>2.2.1</Version>
 | 
			
		||||
    </PackageReference>
 | 
			
		||||
    <PackageReference Include="IsExternalInit">
 | 
			
		||||
      <Version>1.0.3</Version>
 | 
			
		||||
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										267
									
								
								src/FocusVolumeControl/UI/IconExtraction.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								src/FocusVolumeControl/UI/IconExtraction.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,267 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace FocusVolumeControl.UI
 | 
			
		||||
{
 | 
			
		||||
	internal class IconExtraction
 | 
			
		||||
	{
 | 
			
		||||
		public static Bitmap GetIcon(string path)
 | 
			
		||||
		{
 | 
			
		||||
			var index = GetIconIndex(path);
 | 
			
		||||
			var handle = GetIconHandle(index);
 | 
			
		||||
 | 
			
		||||
			using var icon = (Icon)Icon.FromHandle(handle).Clone();
 | 
			
		||||
 | 
			
		||||
			Shell32.DestroyIcon(handle);
 | 
			
		||||
			return icon.ToBitmap();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		static int GetIconIndex(string pszFile)
 | 
			
		||||
		{
 | 
			
		||||
			SHFILEINFO sfi = new SHFILEINFO();
 | 
			
		||||
			Shell32.SHGetFileInfo(pszFile, 0, ref sfi, (uint)Marshal.SizeOf(sfi), (uint)(SHGFI.SysIconIndex | SHGFI.LargeIcon | SHGFI.UseFileAttributes));
 | 
			
		||||
			return sfi.iIcon;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 256*256
 | 
			
		||||
		static IntPtr GetIconHandle(int iImage)
 | 
			
		||||
		{
 | 
			
		||||
			IImageList spiml = null;
 | 
			
		||||
			Guid guil = new Guid(IID_IImageList2);//or IID_IImageList
 | 
			
		||||
 | 
			
		||||
			Shell32.SHGetImageList(Shell32.SHIL_EXTRALARGE, ref guil, ref spiml);
 | 
			
		||||
			IntPtr hIcon = IntPtr.Zero;
 | 
			
		||||
			spiml.GetIcon(iImage, Shell32.ILD_TRANSPARENT | Shell32.ILD_IMAGE, ref hIcon);
 | 
			
		||||
 | 
			
		||||
			return hIcon;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const string IID_IImageList = "46EB5926-582E-4017-9FDF-E8998DAA0950";
 | 
			
		||||
		const string IID_IImageList2 = "192B9D83-50FC-457B-90A0-2B82A8B5DAE1";
 | 
			
		||||
 | 
			
		||||
		public static class Shell32
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
			public const int SHIL_LARGE = 0x0;
 | 
			
		||||
			public const int SHIL_SMALL = 0x1;
 | 
			
		||||
			public const int SHIL_EXTRALARGE = 0x2;
 | 
			
		||||
			public const int SHIL_SYSSMALL = 0x3;
 | 
			
		||||
			public const int SHIL_JUMBO = 0x4;
 | 
			
		||||
			public const int SHIL_LAST = 0x4;
 | 
			
		||||
 | 
			
		||||
			public const int ILD_TRANSPARENT = 0x00000001;
 | 
			
		||||
			public const int ILD_IMAGE = 0x00000020;
 | 
			
		||||
 | 
			
		||||
			[DllImport("shell32.dll", EntryPoint = "#727")]
 | 
			
		||||
			public extern static int SHGetImageList(int iImageList, ref Guid riid, ref IImageList ppv);
 | 
			
		||||
 | 
			
		||||
			[DllImport("user32.dll", EntryPoint = "DestroyIcon", SetLastError = true)]
 | 
			
		||||
			public static extern int DestroyIcon(IntPtr hIcon);
 | 
			
		||||
 | 
			
		||||
			//[DllImport("shell32.dll")]
 | 
			
		||||
			//public static extern uint SHGetIDListFromObject([MarshalAs(UnmanagedType.IUnknown)] object iUnknown, out IntPtr ppidl);
 | 
			
		||||
 | 
			
		||||
			[DllImport("Shell32.dll")]
 | 
			
		||||
			public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Flags]
 | 
			
		||||
		enum SHGFI : uint
 | 
			
		||||
		{
 | 
			
		||||
			/// <summary>get icon</summary>
 | 
			
		||||
			Icon = 0x000000100,
 | 
			
		||||
			/// <summary>get display name</summary>
 | 
			
		||||
			DisplayName = 0x000000200,
 | 
			
		||||
			/// <summary>get type name</summary>
 | 
			
		||||
			TypeName = 0x000000400,
 | 
			
		||||
			/// <summary>get attributes</summary>
 | 
			
		||||
			Attributes = 0x000000800,
 | 
			
		||||
			/// <summary>get icon location</summary>
 | 
			
		||||
			IconLocation = 0x000001000,
 | 
			
		||||
			/// <summary>return exe type</summary>
 | 
			
		||||
			ExeType = 0x000002000,
 | 
			
		||||
			/// <summary>get system icon index</summary>
 | 
			
		||||
			SysIconIndex = 0x000004000,
 | 
			
		||||
			/// <summary>put a link overlay on icon</summary>
 | 
			
		||||
			LinkOverlay = 0x000008000,
 | 
			
		||||
			/// <summary>show icon in selected state</summary>
 | 
			
		||||
			Selected = 0x000010000,
 | 
			
		||||
			/// <summary>get only specified attributes</summary>
 | 
			
		||||
			Attr_Specified = 0x000020000,
 | 
			
		||||
			/// <summary>get large icon</summary>
 | 
			
		||||
			LargeIcon = 0x000000000,
 | 
			
		||||
			/// <summary>get small icon</summary>
 | 
			
		||||
			SmallIcon = 0x000000001,
 | 
			
		||||
			/// <summary>get open icon</summary>
 | 
			
		||||
			OpenIcon = 0x000000002,
 | 
			
		||||
			/// <summary>get shell size icon</summary>
 | 
			
		||||
			ShellIconSize = 0x000000004,
 | 
			
		||||
			/// <summary>pszPath is a pidl</summary>
 | 
			
		||||
			PIDL = 0x000000008,
 | 
			
		||||
			/// <summary>use passed dwFileAttribute</summary>
 | 
			
		||||
			UseFileAttributes = 0x000000010,
 | 
			
		||||
			/// <summary>apply the appropriate overlays</summary>
 | 
			
		||||
			AddOverlays = 0x000000020,
 | 
			
		||||
			/// <summary>Get the index of the overlay in the upper 8 bits of the iIcon</summary>
 | 
			
		||||
			OverlayIndex = 0x000000040,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[StructLayout(LayoutKind.Sequential)]
 | 
			
		||||
		public struct SHFILEINFO
 | 
			
		||||
		{
 | 
			
		||||
			public const int NAMESIZE = 80;
 | 
			
		||||
			public IntPtr hIcon;
 | 
			
		||||
			public int iIcon;
 | 
			
		||||
			public uint dwAttributes;
 | 
			
		||||
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
 | 
			
		||||
			public string szDisplayName;
 | 
			
		||||
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
 | 
			
		||||
			public string szTypeName;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		[StructLayout(LayoutKind.Sequential)]
 | 
			
		||||
		public struct RECT
 | 
			
		||||
		{
 | 
			
		||||
			public int left, top, right, bottom;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[StructLayout(LayoutKind.Sequential)]
 | 
			
		||||
		public struct POINT
 | 
			
		||||
		{
 | 
			
		||||
			int x;
 | 
			
		||||
			int y;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[StructLayout(LayoutKind.Sequential)]
 | 
			
		||||
		public struct IMAGELISTDRAWPARAMS
 | 
			
		||||
		{
 | 
			
		||||
			public int cbSize;
 | 
			
		||||
			public IntPtr himl;
 | 
			
		||||
			public int i;
 | 
			
		||||
			public IntPtr hdcDst;
 | 
			
		||||
			public int x;
 | 
			
		||||
			public int y;
 | 
			
		||||
			public int cx;
 | 
			
		||||
			public int cy;
 | 
			
		||||
			public int xBitmap;    // x offest from the upperleft of bitmap
 | 
			
		||||
			public int yBitmap;    // y offset from the upperleft of bitmap
 | 
			
		||||
			public int rgbBk;
 | 
			
		||||
			public int rgbFg;
 | 
			
		||||
			public int fStyle;
 | 
			
		||||
			public int dwRop;
 | 
			
		||||
			public int fState;
 | 
			
		||||
			public int Frame;
 | 
			
		||||
			public int crEffect;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[StructLayout(LayoutKind.Sequential)]
 | 
			
		||||
		public struct IMAGEINFO
 | 
			
		||||
		{
 | 
			
		||||
			public IntPtr hbmImage;
 | 
			
		||||
			public IntPtr hbmMask;
 | 
			
		||||
			public int Unused1;
 | 
			
		||||
			public int Unused2;
 | 
			
		||||
			public RECT rcImage;
 | 
			
		||||
		}
 | 
			
		||||
		[ComImportAttribute()]
 | 
			
		||||
		[GuidAttribute("46EB5926-582E-4017-9FDF-E8998DAA0950")]
 | 
			
		||||
		[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
		public interface IImageList
 | 
			
		||||
		{
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int Add(IntPtr hbmImage, IntPtr hbmMask, ref int pi);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int ReplaceIcon(int i, IntPtr hicon, ref int pi);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int SetOverlayImage(int iImage, int iOverlay);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int Replace(int i, IntPtr hbmImage, IntPtr hbmMask);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int AddMasked(IntPtr hbmImage, int crMask, ref int pi);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int Draw(ref IMAGELISTDRAWPARAMS pimldp);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int Remove(int i);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetIcon(int i, int flags, ref IntPtr picon);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetImageInfo(int i, ref IMAGEINFO pImageInfo);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int Copy(int iDst, IImageList punkSrc, int iSrc, int uFlags);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int Merge(int i1, IImageList punk2, int i2, int dx, int dy, ref Guid riid, ref IntPtr ppv);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int Clone(ref Guid riid, ref IntPtr ppv);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetImageRect(int i, ref RECT prc);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetIconSize(ref int cx, ref int cy);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int SetIconSize(int cx, int cy);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetImageCount(ref int pi);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int SetImageCount(int uNewCount);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int SetBkColor(int clrBk, ref int pclr);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetBkColor(ref int pclr);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int BeginDrag(int iTrack, int dxHotspot, int dyHotspot);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int EndDrag();
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int DragEnter(IntPtr hwndLock, int x, int y);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int DragLeave(IntPtr hwndLock);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int DragMove(int x, int y);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int SetDragCursorImage(ref IImageList punk, int iDrag, int dxHotspot, int dyHotspot);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int DragShowNolock(int fShow);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetDragImage(ref POINT ppt, ref POINT pptHotspot, ref Guid riid, ref IntPtr ppv);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetItemFlags(int i, ref int dwFlags);
 | 
			
		||||
 | 
			
		||||
			[PreserveSig]
 | 
			
		||||
			int GetOverlayImage(int iOverlay, ref int piIndex);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -39,12 +39,24 @@ namespace FocusVolumeControl
 | 
			
		||||
 | 
			
		||||
		public event Action WindowChanged;
 | 
			
		||||
 | 
			
		||||
		private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
 | 
			
		||||
		CancellationTokenSource? _cancellationTokenSource = null;
 | 
			
		||||
 | 
			
		||||
		private async void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				//debounce the window changed events by 100 ms because if you click mouse over an application on the start bar
 | 
			
		||||
				//and then click on the preview window, it will quickly go from current -> fallback -> new app
 | 
			
		||||
				//which can often result in it getting stuck on the fallback app
 | 
			
		||||
				_cancellationTokenSource?.Cancel();
 | 
			
		||||
				_cancellationTokenSource = new CancellationTokenSource();
 | 
			
		||||
				await Task.Delay(100, _cancellationTokenSource.Token);
 | 
			
		||||
				WindowChanged?.Invoke();
 | 
			
		||||
			}
 | 
			
		||||
			catch (TaskCanceledException)
 | 
			
		||||
			{
 | 
			
		||||
				//ignored
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception ex)
 | 
			
		||||
			{
 | 
			
		||||
				Logger.Instance.LogMessage(TracingLevel.ERROR, $"Unexpected Error in EventHandler:\n {ex}");
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user