Compare commits
	
		
			6 Commits
		
	
	
		
			6de76da8ad
			...
			playWithAp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f1d7aeed5d | |||
| 0f7f1fffcd | |||
| aa905fe443 | |||
| 160bedd461 | |||
| 68d5154756 | |||
| 8eebf1af47 | 
@ -109,6 +109,13 @@ public sealed class AppxPackage
 | 
				
			|||||||
							var properties = reader.GetProperties();
 | 
												var properties = reader.GetProperties();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							properties.GetStringValue("DisplayName", out var displayName);
 | 
												properties.GetStringValue("DisplayName", out var displayName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												if(displayName.StartsWith("ms-resource:"))
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													var packageFullName = Marshal.PtrToStringUni(info.packageFullName);
 | 
				
			||||||
 | 
													displayName = LoadResourceString(fullName, displayName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
							package.DisplayName = displayName;
 | 
												package.DisplayName = displayName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							properties.GetStringValue("Logo", out var logo);
 | 
												properties.GetStringValue("Logo", out var logo);
 | 
				
			||||||
@ -158,6 +165,40 @@ public sealed class AppxPackage
 | 
				
			|||||||
				ClosePackageInfo(infoRef);
 | 
									ClosePackageInfo(infoRef);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static string LoadResourceString(string packageFullName, string resource)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (packageFullName == null)
 | 
				
			||||||
 | 
								throw new ArgumentNullException("packageFullName");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (string.IsNullOrWhiteSpace(resource))
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const string resourceScheme = "ms-resource:";
 | 
				
			||||||
 | 
							if (!resource.StartsWith(resourceScheme))
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							string part = resource.Substring(resourceScheme.Length);
 | 
				
			||||||
 | 
							string url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (part.StartsWith("/"))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								url = resourceScheme + "//" + part;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								url = resourceScheme + "///resources/" + part;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							string source = string.Format("@{{{0}? {1}}}", packageFullName, url);
 | 
				
			||||||
 | 
							var sb = new StringBuilder(1024);
 | 
				
			||||||
 | 
							int i = SHLoadIndirectString(source, sb, sb.Capacity, IntPtr.Zero);
 | 
				
			||||||
 | 
							if (i != 0)
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return sb.ToString();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ using System;
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Diagnostics;
 | 
					using System.Diagnostics;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FocusVolumeControl.AudioHelpers;
 | 
					namespace FocusVolumeControl.AudioHelpers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,6 +13,7 @@ public class AudioHelper
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	static object _lock = new object();
 | 
						static object _lock = new object();
 | 
				
			||||||
	int[] _currentProcesses;
 | 
						int[] _currentProcesses;
 | 
				
			||||||
 | 
						int _retryFallbackCount = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public IAudioSession Current { get; private set; }
 | 
						public IAudioSession Current { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,6 +39,7 @@ public class AudioHelper
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public IAudioSession FindSession(List<Process> processes)
 | 
						public IAudioSession FindSession(List<Process> processes)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							//var blah = new AudioPolicyConfigFactoryImplFor21H2();
 | 
				
			||||||
		var results = new ActiveAudioSessionWrapper();
 | 
							var results = new ActiveAudioSessionWrapper();
 | 
				
			||||||
		Process bestProcessMatch = null;
 | 
							Process bestProcessMatch = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,9 +52,10 @@ public class AudioHelper
 | 
				
			|||||||
			deviceCollection.Item(d, out var device);
 | 
								deviceCollection.Item(d, out var device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
								Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
				
			||||||
			device.Activate(ref iid, 0, IntPtr.Zero, out var m);
 | 
								device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
 | 
				
			||||||
			var manager = (IAudioSessionManager2)m;
 | 
								var manager = (IAudioSessionManager2)m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								device.GetId(out var currentDeviceId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			manager.GetSessionEnumerator(out var sessionEnumerator);
 | 
								manager.GetSessionEnumerator(out var sessionEnumerator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -70,6 +74,16 @@ public class AudioHelper
 | 
				
			|||||||
					continue;
 | 
										continue;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/*
 | 
				
			||||||
 | 
									blah.GetPersistedDefaultAudioEndpoint(sessionProcessId, DataFlow.Render, Role.Multimedia, out var persistedDeviceId);
 | 
				
			||||||
 | 
									persistedDeviceId = UnpackDeviceId(persistedDeviceId);
 | 
				
			||||||
 | 
									if(!string.IsNullOrEmpty(persistedDeviceId) && persistedDeviceId != currentDeviceId)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				var index = processes.FindIndex(x => x.Id == sessionProcessId || x.ProcessName == audioProcess?.ProcessName);
 | 
									var index = processes.FindIndex(x => x.Id == sessionProcessId || x.ProcessName == audioProcess?.ProcessName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (index > -1)
 | 
									if (index > -1)
 | 
				
			||||||
@ -114,10 +128,18 @@ public class AudioHelper
 | 
				
			|||||||
			var processes = GetPossibleProcesses();
 | 
								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))
 | 
								//_currentProcesses null - first time getting sessions
 | 
				
			||||||
 | 
								//_currentProcesses not equal to processIds - changed the active process
 | 
				
			||||||
 | 
								//_retryFallbackCount - some processes like chrome or minecraft will start their audio process when they first try to do some sound stuff
 | 
				
			||||||
 | 
								if (_currentProcesses == null || !_currentProcesses.SequenceEqual(processIds) || _retryFallbackCount == 5)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
 | 
									_retryFallbackCount = 0;
 | 
				
			||||||
				Current = FindSession(processes);
 | 
									Current = FindSession(processes);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								else if(Current is SystemSoundsAudioSession || Current is SystemVolumeAudioSession)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									_retryFallbackCount++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (Current == null)
 | 
								if (Current == null)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
@ -214,7 +236,7 @@ public class AudioHelper
 | 
				
			|||||||
			deviceCollection.Item(d, out var device);
 | 
								deviceCollection.Item(d, out var device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
								Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
				
			||||||
			device.Activate(ref iid, 0, IntPtr.Zero, out var m);
 | 
								device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
 | 
				
			||||||
			var manager = (IAudioSessionManager2)m;
 | 
								var manager = (IAudioSessionManager2)m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -245,7 +267,7 @@ public class AudioHelper
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
								Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
				
			||||||
			device.Activate(ref iid, 0, IntPtr.Zero, out var m);
 | 
								device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
 | 
				
			||||||
			var manager = (IAudioSessionManager2)m;
 | 
								var manager = (IAudioSessionManager2)m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -271,10 +293,130 @@ public class AudioHelper
 | 
				
			|||||||
		deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia, out var device);
 | 
							deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia, out var device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Guid iid = typeof(IAudioEndpointVolume).GUID;
 | 
							Guid iid = typeof(IAudioEndpointVolume).GUID;
 | 
				
			||||||
		device.Activate(ref iid, 0, IntPtr.Zero, out var o);
 | 
							device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var o);
 | 
				
			||||||
		var endpointVolume = (IAudioEndpointVolume)o;
 | 
							var endpointVolume = (IAudioEndpointVolume)o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return new SystemVolumeAudioSession(endpointVolume);
 | 
							return new SystemVolumeAudioSession(endpointVolume);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						class AudioPolicyConfigFactoryImplFor21H2
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							private readonly IAudioPolicyConfigFactoryVariantFor21H2 _factory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							internal AudioPolicyConfigFactoryImplFor21H2()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								var iid = typeof(IAudioPolicyConfigFactoryVariantFor21H2).GUID;
 | 
				
			||||||
 | 
								Combase.RoGetActivationFactory("Windows.Media.Internal.AudioPolicyConfig", ref iid, out object factory);
 | 
				
			||||||
 | 
								_factory = (IAudioPolicyConfigFactoryVariantFor21H2)factory;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public uint ClearAllPersistedApplicationDefaultEndpoints()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return _factory.ClearAllPersistedApplicationDefaultEndpoints();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public uint GetPersistedDefaultAudioEndpoint(int processId, DataFlow flow, Role role, out string deviceId)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return _factory.GetPersistedDefaultAudioEndpoint(processId, flow, role, out deviceId);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public uint SetPersistedDefaultAudioEndpoint(int processId, DataFlow flow, Role role, IntPtr deviceId)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return _factory.SetPersistedDefaultAudioEndpoint(processId, flow, role, deviceId);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[Guid("ab3d4648-e242-459f-b02f-541c70306324")]
 | 
				
			||||||
 | 
						[InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
 | 
				
			||||||
 | 
						public interface IAudioPolicyConfigFactoryVariantFor21H2
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int __incomplete__add_CtxVolumeChange();
 | 
				
			||||||
 | 
							int __incomplete__remove_CtxVolumeChanged();
 | 
				
			||||||
 | 
							int __incomplete__add_RingerVibrateStateChanged();
 | 
				
			||||||
 | 
							int __incomplete__remove_RingerVibrateStateChange();
 | 
				
			||||||
 | 
							int __incomplete__SetVolumeGroupGainForId();
 | 
				
			||||||
 | 
							int __incomplete__GetVolumeGroupGainForId();
 | 
				
			||||||
 | 
							int __incomplete__GetActiveVolumeGroupForEndpointId();
 | 
				
			||||||
 | 
							int __incomplete__GetVolumeGroupsForEndpoint();
 | 
				
			||||||
 | 
							int __incomplete__GetCurrentVolumeContext();
 | 
				
			||||||
 | 
							int __incomplete__SetVolumeGroupMuteForId();
 | 
				
			||||||
 | 
							int __incomplete__GetVolumeGroupMuteForId();
 | 
				
			||||||
 | 
							int __incomplete__SetRingerVibrateState();
 | 
				
			||||||
 | 
							int __incomplete__GetRingerVibrateState();
 | 
				
			||||||
 | 
							int __incomplete__SetPreferredChatApplication();
 | 
				
			||||||
 | 
							int __incomplete__ResetPreferredChatApplication();
 | 
				
			||||||
 | 
							int __incomplete__GetPreferredChatApplication();
 | 
				
			||||||
 | 
							int __incomplete__GetCurrentChatApplications();
 | 
				
			||||||
 | 
							int __incomplete__add_ChatContextChanged();
 | 
				
			||||||
 | 
							int __incomplete__remove_ChatContextChanged();
 | 
				
			||||||
 | 
							[PreserveSig]
 | 
				
			||||||
 | 
							uint SetPersistedDefaultAudioEndpoint(int processId, DataFlow flow, Role role, IntPtr deviceId);
 | 
				
			||||||
 | 
							[PreserveSig]
 | 
				
			||||||
 | 
							uint GetPersistedDefaultAudioEndpoint(int processId, DataFlow flow, Role role, [Out, MarshalAs(UnmanagedType.HString)] out string deviceId);
 | 
				
			||||||
 | 
							[PreserveSig]
 | 
				
			||||||
 | 
							uint ClearAllPersistedApplicationDefaultEndpoints();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[Guid("2a59116d-6c4f-45e0-a74f-707e3fef9258")]
 | 
				
			||||||
 | 
						[InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
 | 
				
			||||||
 | 
						public interface IAudioPolicyConfigFactoryVariantForDownlevel
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int __incomplete__add_CtxVolumeChange();
 | 
				
			||||||
 | 
							int __incomplete__remove_CtxVolumeChanged();
 | 
				
			||||||
 | 
							int __incomplete__add_RingerVibrateStateChanged();
 | 
				
			||||||
 | 
							int __incomplete__remove_RingerVibrateStateChange();
 | 
				
			||||||
 | 
							int __incomplete__SetVolumeGroupGainForId();
 | 
				
			||||||
 | 
							int __incomplete__GetVolumeGroupGainForId();
 | 
				
			||||||
 | 
							int __incomplete__GetActiveVolumeGroupForEndpointId();
 | 
				
			||||||
 | 
							int __incomplete__GetVolumeGroupsForEndpoint();
 | 
				
			||||||
 | 
							int __incomplete__GetCurrentVolumeContext();
 | 
				
			||||||
 | 
							int __incomplete__SetVolumeGroupMuteForId();
 | 
				
			||||||
 | 
							int __incomplete__GetVolumeGroupMuteForId();
 | 
				
			||||||
 | 
							int __incomplete__SetRingerVibrateState();
 | 
				
			||||||
 | 
							int __incomplete__GetRingerVibrateState();
 | 
				
			||||||
 | 
							int __incomplete__SetPreferredChatApplication();
 | 
				
			||||||
 | 
							int __incomplete__ResetPreferredChatApplication();
 | 
				
			||||||
 | 
							int __incomplete__GetPreferredChatApplication();
 | 
				
			||||||
 | 
							int __incomplete__GetCurrentChatApplications();
 | 
				
			||||||
 | 
							int __incomplete__add_ChatContextChanged();
 | 
				
			||||||
 | 
							int __incomplete__remove_ChatContextChanged();
 | 
				
			||||||
 | 
							[PreserveSig]
 | 
				
			||||||
 | 
							uint SetPersistedDefaultAudioEndpoint(int processId, DataFlow flow, Role role, IntPtr deviceId);
 | 
				
			||||||
 | 
							[PreserveSig]
 | 
				
			||||||
 | 
							uint GetPersistedDefaultAudioEndpoint(int processId, DataFlow flow, Role role, [Out, MarshalAs(UnmanagedType.HString)] out string deviceId);
 | 
				
			||||||
 | 
							[PreserveSig]
 | 
				
			||||||
 | 
							uint ClearAllPersistedApplicationDefaultEndpoints();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static class Combase
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							[DllImport("combase.dll", PreserveSig = false)]
 | 
				
			||||||
 | 
							public static extern void RoGetActivationFactory(
 | 
				
			||||||
 | 
								[MarshalAs(UnmanagedType.HString)] string activatableClassId,
 | 
				
			||||||
 | 
								[In] ref Guid iid,
 | 
				
			||||||
 | 
								[Out, MarshalAs(UnmanagedType.IInspectable)] out Object factory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[DllImport("combase.dll", PreserveSig = false)]
 | 
				
			||||||
 | 
							public static extern void WindowsCreateString(
 | 
				
			||||||
 | 
								[MarshalAs(UnmanagedType.LPWStr)] string src,
 | 
				
			||||||
 | 
								[In] uint length,
 | 
				
			||||||
 | 
								[Out] out IntPtr hstring);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private const string DEVINTERFACE_AUDIO_RENDER = "#{e6327cad-dcec-4949-ae8a-991e976a79d2}";
 | 
				
			||||||
 | 
						private const string DEVINTERFACE_AUDIO_CAPTURE = "#{2eef81be-33fa-4800-9670-1cd474972c3f}";
 | 
				
			||||||
 | 
						private const string MMDEVAPI_TOKEN = @"\\?\SWD#MMDEVAPI#";
 | 
				
			||||||
 | 
						private string UnpackDeviceId(string deviceId)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (deviceId.StartsWith(MMDEVAPI_TOKEN)) deviceId = deviceId.Remove(0, MMDEVAPI_TOKEN.Length);
 | 
				
			||||||
 | 
							if (deviceId.EndsWith(DEVINTERFACE_AUDIO_RENDER)) deviceId = deviceId.Remove(deviceId.Length - DEVINTERFACE_AUDIO_RENDER.Length);
 | 
				
			||||||
 | 
							if (deviceId.EndsWith(DEVINTERFACE_AUDIO_CAPTURE)) deviceId = deviceId.Remove(deviceId.Length - DEVINTERFACE_AUDIO_CAPTURE.Length);
 | 
				
			||||||
 | 
							return deviceId;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,10 @@
 | 
				
			|||||||
using BarRaider.SdTools;
 | 
					using BarRaider.SdTools;
 | 
				
			||||||
 | 
					using FocusVolumeControl.AudioSession;
 | 
				
			||||||
using FocusVolumeControl.AudioSessions;
 | 
					using FocusVolumeControl.AudioSessions;
 | 
				
			||||||
 | 
					using FocusVolumeControl.UI;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Diagnostics;
 | 
					using System.Diagnostics;
 | 
				
			||||||
 | 
					using System.Drawing;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Runtime.InteropServices;
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
@ -10,12 +13,12 @@ namespace FocusVolumeControl.AudioHelpers;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class NameAndIconHelper
 | 
					public class NameAndIconHelper
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	public (string name, string icon) GetProcessInfo(Process process)
 | 
						public string GetProcessInfo(Process process)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		//i know this is dumb, but its only used by the sound browser, not real prod code
 | 
							//i know this is dumb, but its only used by the sound browser, not real prod code
 | 
				
			||||||
		var blah = new ActiveAudioSessionWrapper();
 | 
							var blah = new ActiveAudioSessionWrapper();
 | 
				
			||||||
		SetProcessInfo(process, blah);
 | 
							SetProcessInfo(process, blah);
 | 
				
			||||||
		return (blah.DisplayName, blah.IconPath ?? blah.ExecutablePath);
 | 
							return blah.DisplayName;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void SetProcessInfo(Process process, ActiveAudioSessionWrapper results)
 | 
						public void SetProcessInfo(Process process, ActiveAudioSessionWrapper results)
 | 
				
			||||||
@ -45,12 +48,27 @@ public class NameAndIconHelper
 | 
				
			|||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				results.ExecutablePath = fileVersionInfo?.FileName;
 | 
									//for java apps (minecraft), the process will just have a java icon
 | 
				
			||||||
 | 
									//and there's not just a file that you can get the real icon from
 | 
				
			||||||
 | 
									//so you have to send some messages to the apps to get the icons.
 | 
				
			||||||
 | 
									//but they will only be 32x32 (or smaller) so we only want to use this logic for java
 | 
				
			||||||
 | 
									//because these will be lower resolution than the normal way of getting icons
 | 
				
			||||||
 | 
									if (process.ProcessName == "javaw" || process.ProcessName == "java" || process.ProcessName == "dotnet")
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										var windowHandle = process.MainWindowHandle;
 | 
				
			||||||
 | 
										var lazyIcon = () => JavaIconExtractor.GetWindowBigIconWithRetry(windowHandle);
 | 
				
			||||||
 | 
										results.IconWrapper = new RawIcon(windowHandle.ToString(), lazyIcon);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										results.IconWrapper = new NormalIcon(fileVersionInfo?.FileName);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				results.DisplayName = appx.DisplayName;
 | 
									results.DisplayName = appx.DisplayName;
 | 
				
			||||||
				results.IconPath = Path.Combine(appx.Path, appx.Logo);
 | 
									results.IconWrapper = new AppxIcon(Path.Combine(appx.Path, appx.Logo));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		catch { }
 | 
							catch { }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,55 +6,27 @@ using System.Drawing;
 | 
				
			|||||||
using System.Runtime.InteropServices;
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
using FocusVolumeControl.UI;
 | 
					using FocusVolumeControl.UI;
 | 
				
			||||||
using BitFaster.Caching.Lru;
 | 
					using BitFaster.Caching.Lru;
 | 
				
			||||||
 | 
					using FocusVolumeControl.AudioSession;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FocusVolumeControl.AudioSessions;
 | 
					namespace FocusVolumeControl.AudioSessions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public sealed class ActiveAudioSessionWrapper : IAudioSession
 | 
					public sealed class ActiveAudioSessionWrapper : IAudioSession
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static ConcurrentLru<string, string> _iconCache = new ConcurrentLru<string, string>(10);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public string DisplayName { get; set; }
 | 
						public string DisplayName { get; set; }
 | 
				
			||||||
	public string ExecutablePath { get; set; }
 | 
					 | 
				
			||||||
	public string IconPath { get; set; }
 | 
					 | 
				
			||||||
	private List<IAudioSessionControl2> Sessions { get; } = new List<IAudioSessionControl2>();
 | 
						private List<IAudioSessionControl2> Sessions { get; } = new List<IAudioSessionControl2>();
 | 
				
			||||||
	private IEnumerable<ISimpleAudioVolume> Volume => Sessions.Cast<ISimpleAudioVolume>();
 | 
						private IEnumerable<ISimpleAudioVolume> Volume => Sessions.Cast<ISimpleAudioVolume>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	string GetIconFromIconPath()
 | 
						public IconWrapper? IconWrapper { get; set; }
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return _iconCache.GetOrAdd(IconPath, (key) =>
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			var tmp = (Bitmap)Bitmap.FromFile(IconPath);
 | 
					 | 
				
			||||||
			tmp.MakeTransparent();
 | 
					 | 
				
			||||||
			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()
 | 
						public string GetIcon()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		try
 | 
							try
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (!string.IsNullOrEmpty(IconPath))
 | 
								return IconWrapper?.GetIconData() ?? IconWrapper.FallbackIconData;
 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				return GetIconFromIconPath();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				return GetIconFromExecutablePath();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		catch
 | 
							catch
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return "Images/encoderIcon";
 | 
								return IconWrapper.FallbackIconData;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,32 @@ public class MMDeviceEnumerator
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Flags]
 | 
				
			||||||
 | 
					public enum CLSCTX : uint
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						INPROC_SERVER = 0x1,
 | 
				
			||||||
 | 
						INPROC_HANDLER = 0x2,
 | 
				
			||||||
 | 
						LOCAL_SERVER = 0x4,
 | 
				
			||||||
 | 
						INPROC_SERVER16 = 0x8,
 | 
				
			||||||
 | 
						REMOTE_SERVER = 0x10,
 | 
				
			||||||
 | 
						INPROC_HANDLER16 = 0x20,
 | 
				
			||||||
 | 
						RESERVED1 = 0x40,
 | 
				
			||||||
 | 
						RESERVED2 = 0x80,
 | 
				
			||||||
 | 
						RESERVED3 = 0x100,
 | 
				
			||||||
 | 
						RESERVED4 = 0x200,
 | 
				
			||||||
 | 
						NO_CODE_DOWNLOAD = 0x400,
 | 
				
			||||||
 | 
						RESERVED5 = 0x800,
 | 
				
			||||||
 | 
						NO_CUSTOM_MARSHAL = 0x1000,
 | 
				
			||||||
 | 
						ENABLE_CODE_DOWNLOAD = 0x2000,
 | 
				
			||||||
 | 
						NO_FAILURE_LOG = 0x4000,
 | 
				
			||||||
 | 
						DISABLE_AAA = 0x8000,
 | 
				
			||||||
 | 
						ENABLE_AAA = 0x10000,
 | 
				
			||||||
 | 
						FROM_DEFAULT_CONTEXT = 0x20000,
 | 
				
			||||||
 | 
						INPROC = INPROC_SERVER | INPROC_HANDLER,
 | 
				
			||||||
 | 
						SERVER = INPROC_SERVER | LOCAL_SERVER | REMOTE_SERVER,
 | 
				
			||||||
 | 
						ALL = SERVER | INPROC_HANDLER
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public enum DataFlow
 | 
					public enum DataFlow
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Render,
 | 
						Render,
 | 
				
			||||||
@ -35,6 +61,13 @@ public enum DeviceState : uint
 | 
				
			|||||||
	MaskAll = 0xFu
 | 
						MaskAll = 0xFu
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum AudioSessionState
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						AudioSessionStateInactive = 0,
 | 
				
			||||||
 | 
						AudioSessionStateActive = 1,
 | 
				
			||||||
 | 
						AudioSessionStateExpired = 2
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E")]
 | 
					[Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E")]
 | 
				
			||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
					[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
				
			||||||
@ -62,7 +95,13 @@ public interface IMMDeviceEnumerator
 | 
				
			|||||||
public interface IMMDevice
 | 
					public interface IMMDevice
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	[PreserveSig]
 | 
						[PreserveSig]
 | 
				
			||||||
	int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
 | 
						int Activate(ref Guid iid, CLSCTX dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[PreserveSig]
 | 
				
			||||||
 | 
						int NotImpl1();
 | 
				
			||||||
 | 
						[PreserveSig]
 | 
				
			||||||
 | 
						int GetId([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppstrId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
					[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
				
			||||||
@ -104,9 +143,9 @@ public interface ISimpleAudioVolume
 | 
				
			|||||||
[Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
					[Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
				
			||||||
public interface IAudioSessionControl2
 | 
					public interface IAudioSessionControl2
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// IAudioSessionControl
 | 
						//elgato seems to use this to determine whether the icon should be black and white
 | 
				
			||||||
	[PreserveSig]
 | 
						[PreserveSig]
 | 
				
			||||||
	int NotImpl0();
 | 
						int GetState(out AudioSessionState audioSessionState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[PreserveSig]
 | 
						[PreserveSig]
 | 
				
			||||||
	int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
 | 
						int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
 | 
				
			||||||
@ -134,7 +173,7 @@ public interface IAudioSessionControl2
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// IAudioSessionControl2
 | 
						// IAudioSessionControl2
 | 
				
			||||||
	[PreserveSig]
 | 
						[PreserveSig]
 | 
				
			||||||
	int GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
 | 
						uint GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[PreserveSig]
 | 
						[PreserveSig]
 | 
				
			||||||
	int GetSessionInstanceIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
 | 
						int GetSessionInstanceIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										103
									
								
								src/FocusVolumeControl/AudioSessions/IconWrapper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/FocusVolumeControl/AudioSessions/IconWrapper.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					using BarRaider.SdTools;
 | 
				
			||||||
 | 
					using BitFaster.Caching.Lru;
 | 
				
			||||||
 | 
					using FocusVolumeControl.UI;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Drawing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FocusVolumeControl.AudioSession
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public abstract class IconWrapper
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							protected static ConcurrentLru<string, string> _iconCache = new ConcurrentLru<string, string>(10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public abstract string GetIconData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							internal const string FallbackIconData = "Images/encoderIcon";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						internal class AppxIcon : IconWrapper
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							private readonly string _iconPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public AppxIcon(string iconPath)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								_iconPath = iconPath;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override string GetIconData()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if(string.IsNullOrEmpty(_iconPath))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									return FallbackIconData;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return _iconCache.GetOrAdd(_iconPath, (key) =>
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									var tmp = (Bitmap)Bitmap.FromFile(_iconPath);
 | 
				
			||||||
 | 
									tmp.MakeTransparent();
 | 
				
			||||||
 | 
									return Tools.ImageToBase64(tmp, true);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						internal class NormalIcon : IconWrapper
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							private readonly string _iconPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public NormalIcon(string iconPath)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								_iconPath = iconPath;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override string GetIconData()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if(string.IsNullOrEmpty(_iconPath))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									return FallbackIconData;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return _iconCache.GetOrAdd(_iconPath, (key) =>
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									var tmp = IconExtraction.GetIcon(_iconPath);
 | 
				
			||||||
 | 
									return Tools.ImageToBase64(tmp, true);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						internal class RawIcon : IconWrapper
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							private readonly string _data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public RawIcon(string name, Func<Bitmap?> getIcon)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								_data = _iconCache.GetOrAdd(name, (key) =>
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									var icon = getIcon();
 | 
				
			||||||
 | 
									if (icon == null)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										return FallbackIconData;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (icon.Height < 48 && icon.Width < 48)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										using var newImage = new Bitmap(48, 48);
 | 
				
			||||||
 | 
										newImage.MakeTransparent();
 | 
				
			||||||
 | 
										using var graphics = Graphics.FromImage(newImage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										graphics.DrawImage(icon, 4, 4, 40, 40);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return Tools.ImageToBase64(newImage, true);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										return Tools.ImageToBase64(icon, true);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override string GetIconData() => _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -33,7 +33,7 @@ public class DialAction : EncoderBase
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PluginSettings settings;
 | 
						PluginSettings settings;
 | 
				
			||||||
	AudioHelper _audioHelper = new AudioHelper();
 | 
						static AudioHelper _audioHelper = new AudioHelper();
 | 
				
			||||||
	UIState _previousState;
 | 
						UIState _previousState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public DialAction(ISDConnection connection, InitialPayload payload) : base(connection, payload)
 | 
						public DialAction(ISDConnection connection, InitialPayload payload) : base(connection, payload)
 | 
				
			||||||
 | 
				
			|||||||
@ -55,6 +55,7 @@
 | 
				
			|||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <Compile Include="AudioHelpers\AppxPackage.cs" />
 | 
					    <Compile Include="AudioHelpers\AppxPackage.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="AudioSessions\IconWrapper.cs" />
 | 
				
			||||||
    <Compile Include="AudioSessions\ActiveAudioSessionWrapper.cs" />
 | 
					    <Compile Include="AudioSessions\ActiveAudioSessionWrapper.cs" />
 | 
				
			||||||
    <Compile Include="AudioHelpers\AudioHelper.cs" />
 | 
					    <Compile Include="AudioHelpers\AudioHelper.cs" />
 | 
				
			||||||
    <Compile Include="AudioSessions\CoreAudio.cs" />
 | 
					    <Compile Include="AudioSessions\CoreAudio.cs" />
 | 
				
			||||||
@ -71,6 +72,7 @@
 | 
				
			|||||||
    <Compile Include="AudioHelpers\ParentProcessUtilities.cs" />
 | 
					    <Compile Include="AudioHelpers\ParentProcessUtilities.cs" />
 | 
				
			||||||
    <Compile Include="Program.cs" />
 | 
					    <Compile Include="Program.cs" />
 | 
				
			||||||
    <Compile Include="Properties\AssemblyInfo.cs" />
 | 
					    <Compile Include="Properties\AssemblyInfo.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="UI\JavaIconExtractor.cs" />
 | 
				
			||||||
    <Compile Include="UI\UIState.cs" />
 | 
					    <Compile Include="UI\UIState.cs" />
 | 
				
			||||||
    <Compile Include="UI\ValueWithOpacity.cs" />
 | 
					    <Compile Include="UI\ValueWithOpacity.cs" />
 | 
				
			||||||
    <Compile Include="WindowChangedEventLoop.cs" />
 | 
					    <Compile Include="WindowChangedEventLoop.cs" />
 | 
				
			||||||
 | 
				
			|||||||
@ -6,8 +6,10 @@ internal class Program
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	static void Main(string[] args)
 | 
						static void Main(string[] args)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
					#if DEBUG
 | 
				
			||||||
		// Uncomment this line of code to allow for debugging
 | 
							// Uncomment this line of code to allow for debugging
 | 
				
			||||||
		//while (!System.Diagnostics.Debugger.IsAttached) { System.Threading.Thread.Sleep(100); }
 | 
							//while (!System.Diagnostics.Debugger.IsAttached) { System.Threading.Thread.Sleep(100); }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		SDWrapper.Run(args);
 | 
							SDWrapper.Run(args);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										96
									
								
								src/FocusVolumeControl/UI/JavaIconExtractor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/FocusVolumeControl/UI/JavaIconExtractor.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Drawing;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FocusVolumeControl.UI
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						internal class JavaIconExtractor
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const int WM_GETICON = 0x7F;
 | 
				
			||||||
 | 
							const int WM_QUERYDRAGICON = 0x0037;
 | 
				
			||||||
 | 
							//const int ICON_SMALL = 0; //(16x16)
 | 
				
			||||||
 | 
							const int ICON_BIG = 1; //(32x32)
 | 
				
			||||||
 | 
							const int SMTO_ABORTIFHUNG = 0x3;
 | 
				
			||||||
 | 
							const int GCL_HICON = -14;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[DllImport("User32.dll")]
 | 
				
			||||||
 | 
							static extern int SendMessageTimeout(IntPtr hWnd, int uMsg, int wParam, int lParam, int fuFlags, int uTimeout, out int lpdwResult);
 | 
				
			||||||
 | 
							[DllImport("User32.dll")]
 | 
				
			||||||
 | 
							static extern int GetClassLong(IntPtr hWnd, int index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[DllImport("user32.dll", EntryPoint = "DestroyIcon", SetLastError = true)]
 | 
				
			||||||
 | 
							public static extern int DestroyIcon(IntPtr hIcon);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public static Bitmap? GetWindowBigIconWithRetry(IntPtr hWnd)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								var retry = 5;
 | 
				
			||||||
 | 
								var icon = GetWindowBigIcon(hWnd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while(icon == null || retry > 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Thread.Sleep(100);
 | 
				
			||||||
 | 
									icon = GetWindowBigIcon(hWnd);
 | 
				
			||||||
 | 
									retry--;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return icon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Retrieves a big icon (32*32) of a window
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="hWnd"></param>
 | 
				
			||||||
 | 
							/// <returns></returns>
 | 
				
			||||||
 | 
							public static Bitmap? GetWindowBigIcon(IntPtr hWnd)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								IntPtr hIcon = IntPtr.Zero;
 | 
				
			||||||
 | 
								try
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									int result;
 | 
				
			||||||
 | 
									SendMessageTimeout(hWnd, WM_GETICON, ICON_BIG, //big icon size
 | 
				
			||||||
 | 
										0, SMTO_ABORTIFHUNG, 1000, out result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									hIcon = new IntPtr(result);
 | 
				
			||||||
 | 
									if (hIcon == IntPtr.Zero) //some applications don't respond to sendmessage, we have to use GetClassLong in that case
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										result = GetClassLong(hWnd, GCL_HICON); //big icon size
 | 
				
			||||||
 | 
										hIcon = new IntPtr(result);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (hIcon == IntPtr.Zero)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										SendMessageTimeout(hWnd, WM_QUERYDRAGICON, 0, 0, SMTO_ABORTIFHUNG, 1000, out result);
 | 
				
			||||||
 | 
										hIcon = new IntPtr(result);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (hIcon == IntPtr.Zero)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										return null;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										using var tmp = (Icon)Icon.FromHandle(hIcon).Clone();
 | 
				
			||||||
 | 
										return tmp.ToBitmap();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								catch (Exception)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								finally
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if(hIcon != IntPtr.Zero)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										DestroyIcon(hIcon);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -57,7 +57,7 @@ public partial class MainWindow : Window
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			foreach (var p in processes)
 | 
								foreach (var p in processes)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				var (displayName, _) = (new NameAndIconHelper()).GetProcessInfo(p);
 | 
									var displayName = (new NameAndIconHelper()).GetProcessInfo(p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				sb.AppendLine($"pid: {p.Id}");
 | 
									sb.AppendLine($"pid: {p.Id}");
 | 
				
			||||||
				sb.AppendLine($"\tprocessName: {p.ProcessName}");
 | 
									sb.AppendLine($"\tprocessName: {p.ProcessName}");
 | 
				
			||||||
@ -92,14 +92,16 @@ public partial class MainWindow : Window
 | 
				
			|||||||
		deviceEnumerator.EnumAudioEndpoints(DataFlow.Render, DeviceState.Active, out var deviceCollection);
 | 
							deviceEnumerator.EnumAudioEndpoints(DataFlow.Render, DeviceState.Active, out var deviceCollection);
 | 
				
			||||||
		deviceCollection.GetCount(out var num);
 | 
							deviceCollection.GetCount(out var num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (int i = 0; i < num; i++)
 | 
							for (int i = 0; i < num; i++)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			deviceCollection.Item(i, out var device);
 | 
								deviceCollection.Item(i, out var device);
 | 
				
			||||||
			//todo: put the device name in the output
 | 
								//todo: put the device name in the output
 | 
				
			||||||
			sb.AppendLine("----");
 | 
								sb.AppendLine($"----");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
								Guid iid = typeof(IAudioSessionManager2).GUID;
 | 
				
			||||||
			device.Activate(ref iid, 0, IntPtr.Zero, out var m);
 | 
								device.Activate(ref iid, CLSCTX.ALL, IntPtr.Zero, out var m);
 | 
				
			||||||
			var manager = (IAudioSessionManager2)m;
 | 
								var manager = (IAudioSessionManager2)m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -112,9 +114,10 @@ public partial class MainWindow : Window
 | 
				
			|||||||
				sessionEnumerator.GetSession(s, out var session);
 | 
									sessionEnumerator.GetSession(s, out var session);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				session.GetProcessId(out var processId);
 | 
									session.GetProcessId(out var processId);
 | 
				
			||||||
 | 
									session.GetIconPath(out var path);
 | 
				
			||||||
				var audioProcess = Process.GetProcessById(processId);
 | 
									var audioProcess = Process.GetProcessById(processId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				var (displayName, _) = (new NameAndIconHelper()).GetProcessInfo(audioProcess);
 | 
									var displayName = (new NameAndIconHelper()).GetProcessInfo(audioProcess);
 | 
				
			||||||
				sb.AppendLine($"pid: {audioProcess.Id}\t\t processName: {displayName}");
 | 
									sb.AppendLine($"pid: {audioProcess.Id}\t\t processName: {displayName}");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user