Add settings for fallback behavior
update action icon with padding
This commit is contained in:
parent
1dc8ab8a2d
commit
90c014e932
@ -1,73 +0,0 @@
|
|||||||
using CoreAudio;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
|
||||||
{
|
|
||||||
public class ActiveAudioSessionWrapper
|
|
||||||
{
|
|
||||||
public string DisplayName { get; set; }
|
|
||||||
public string ExecutablePath { get; set; }
|
|
||||||
private List<SimpleAudioVolume> Volume { get; } = new List<SimpleAudioVolume>();
|
|
||||||
|
|
||||||
public string Icon { get; set; }
|
|
||||||
|
|
||||||
public bool Any()
|
|
||||||
{
|
|
||||||
return Volume.Any();
|
|
||||||
}
|
|
||||||
public int Count => Volume.Count;
|
|
||||||
|
|
||||||
public void AddVolume(SimpleAudioVolume volume)
|
|
||||||
{
|
|
||||||
Volume.Add(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleMute()
|
|
||||||
{
|
|
||||||
//when all volumes are muted, Volume.All will return true
|
|
||||||
//so we swap from muted to false (opposite of Volume.All)
|
|
||||||
|
|
||||||
//when any volumes are unmuted, Volume.All will return false
|
|
||||||
//so we set muted to true (opposite of Volume.All)
|
|
||||||
|
|
||||||
var muted = Volume.All(x => x.Mute);
|
|
||||||
|
|
||||||
Volume.ForEach(x => x.Mute = !muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? GetMuted()
|
|
||||||
{
|
|
||||||
var muted = Volume.All(x => x.Mute);
|
|
||||||
var unmuted = Volume.All(x => !x.Mute);
|
|
||||||
|
|
||||||
if(muted == !unmuted)
|
|
||||||
{
|
|
||||||
return muted;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IncrementVolumeLevel(int step, int ticks)
|
|
||||||
{
|
|
||||||
//if you have more than one volume. they will all get set based on the first volume control
|
|
||||||
var level = Volume.FirstOrDefault()?.MasterVolume ?? 0;
|
|
||||||
|
|
||||||
level += (0.01f * step) * ticks;
|
|
||||||
level = Math.Max(level, 0);
|
|
||||||
|
|
||||||
Volume.ForEach(x => x.MasterVolume = level);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetVolumeLevel()
|
|
||||||
{
|
|
||||||
var level = Volume.FirstOrDefault()?.MasterVolume ?? 0;
|
|
||||||
return (int)(level * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,18 @@
|
|||||||
using CoreAudio;
|
using CoreAudio;
|
||||||
|
using FocusVolumeControl.AudioSessions;
|
||||||
using System;
|
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;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
namespace FocusVolumeControl;
|
||||||
|
|
||||||
|
public class AudioHelper
|
||||||
{
|
{
|
||||||
public class AudioHelper
|
IAudioSession _current;
|
||||||
{
|
|
||||||
ActiveAudioSessionWrapper _current;
|
|
||||||
List<Process> _currentProcesses;
|
List<Process> _currentProcesses;
|
||||||
|
|
||||||
public ActiveAudioSessionWrapper FindSession(List<Process> processes)
|
public IAudioSession FindSession(List<Process> processes)
|
||||||
{
|
{
|
||||||
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
|
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
|
||||||
|
|
||||||
@ -34,7 +32,7 @@ namespace FocusVolumeControl
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var displayName = audioProcess.MainModule.FileVersionInfo.FileDescription;
|
var displayName = audioProcess.MainModule.FileVersionInfo.FileDescription;
|
||||||
if(string.IsNullOrEmpty(displayName))
|
if (string.IsNullOrEmpty(displayName))
|
||||||
{
|
{
|
||||||
displayName = audioProcess.ProcessName;
|
displayName = audioProcess.ProcessName;
|
||||||
}
|
}
|
||||||
@ -54,7 +52,11 @@ namespace FocusVolumeControl
|
|||||||
return matchingSession.Any() ? matchingSession : null;
|
return matchingSession.Any() ? matchingSession : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActiveAudioSessionWrapper GetActiveSession()
|
static object _lock = new object();
|
||||||
|
|
||||||
|
public IAudioSession GetActiveSession(FallbackBehavior fallbackBehavior)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
{
|
{
|
||||||
var processes = GetPossibleProcesses();
|
var processes = GetPossibleProcesses();
|
||||||
|
|
||||||
@ -63,9 +65,22 @@ namespace FocusVolumeControl
|
|||||||
_current = FindSession(processes);
|
_current = FindSession(processes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_current == null)
|
||||||
|
{
|
||||||
|
if(fallbackBehavior == FallbackBehavior.SystemSounds)
|
||||||
|
{
|
||||||
|
_current = GetSystemSounds();
|
||||||
|
}
|
||||||
|
else if(fallbackBehavior == FallbackBehavior.SystemVolume)
|
||||||
|
{
|
||||||
|
_current = GetSystemVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_currentProcesses = processes;
|
_currentProcesses = processes;
|
||||||
return _current;
|
return _current;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the list of processes that might be currently selected
|
/// Get the list of processes that might be currently selected
|
||||||
@ -117,5 +132,49 @@ namespace FocusVolumeControl
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResetAll()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
|
||||||
|
|
||||||
|
using var device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
|
||||||
|
using var manager = device.AudioSessionManager2;
|
||||||
|
|
||||||
|
foreach (var session in manager.Sessions)
|
||||||
|
{
|
||||||
|
session.SimpleAudioVolume.MasterVolume = 1;
|
||||||
|
session.SimpleAudioVolume.Mute = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAudioSession GetSystemSounds()
|
||||||
|
{
|
||||||
|
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
|
||||||
|
|
||||||
|
using var device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
|
||||||
|
using var manager = device.AudioSessionManager2;
|
||||||
|
|
||||||
|
var sessions = manager.Sessions;
|
||||||
|
|
||||||
|
foreach (var session in sessions)
|
||||||
|
{
|
||||||
|
if (session.IsSystemSoundsSession)
|
||||||
|
{
|
||||||
|
return new SystemSoundsAudioSession(session.SimpleAudioVolume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public IAudioSession GetSystemVolume()
|
||||||
|
{
|
||||||
|
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
|
||||||
|
|
||||||
|
using var device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
|
||||||
|
return new SystemVolumeAudioSession(device.AudioEndpointVolume);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
using CoreAudio;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using BarRaider.SdTools;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace FocusVolumeControl.AudioSessions;
|
||||||
|
|
||||||
|
public class ActiveAudioSessionWrapper : IAudioSession
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
public string ExecutablePath { get; set; }
|
||||||
|
private List<SimpleAudioVolume> Volume { get; } = new List<SimpleAudioVolume>();
|
||||||
|
|
||||||
|
string _icon;
|
||||||
|
|
||||||
|
public string GetIcon()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_icon))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tmp = Icon.ExtractAssociatedIcon(ExecutablePath);
|
||||||
|
_icon = Tools.ImageToBase64(tmp.ToBitmap(), true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_icon = "Image/pluginIcon.png";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Any()
|
||||||
|
{
|
||||||
|
return Volume.Any();
|
||||||
|
}
|
||||||
|
public int Count => Volume.Count;
|
||||||
|
|
||||||
|
public void AddVolume(SimpleAudioVolume volume)
|
||||||
|
{
|
||||||
|
Volume.Add(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleMute()
|
||||||
|
{
|
||||||
|
//when all volumes are muted, Volume.All will return true
|
||||||
|
//so we swap from muted to false (opposite of Volume.All)
|
||||||
|
|
||||||
|
//when any volumes are unmuted, Volume.All will return false
|
||||||
|
//so we set muted to true (opposite of Volume.All)
|
||||||
|
|
||||||
|
var muted = Volume.All(x => x.Mute);
|
||||||
|
|
||||||
|
Volume.ForEach(x => x.Mute = !muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMuted()
|
||||||
|
{
|
||||||
|
return Volume.All(x => x.Mute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementVolumeLevel(int step, int ticks)
|
||||||
|
{
|
||||||
|
//if you have more than one volume. they will all get set based on the first volume control
|
||||||
|
var level = Volume.FirstOrDefault()?.MasterVolume ?? 0;
|
||||||
|
|
||||||
|
level += (0.01f * step) * ticks;
|
||||||
|
level = Math.Max(level, 0);
|
||||||
|
level = Math.Min(level, 1);
|
||||||
|
|
||||||
|
Volume.ForEach(x => x.MasterVolume = level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetVolumeLevel()
|
||||||
|
{
|
||||||
|
var level = Volume.FirstOrDefault()?.MasterVolume ?? 0;
|
||||||
|
return (int)(level * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/FocusVolumeControl/AudioSessions/IAudioSession.cs
Normal file
16
src/FocusVolumeControl/AudioSessions/IAudioSession.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace FocusVolumeControl.AudioSessions;
|
||||||
|
|
||||||
|
public interface IAudioSession
|
||||||
|
{
|
||||||
|
public string DisplayName { get; }
|
||||||
|
|
||||||
|
public string GetIcon();
|
||||||
|
|
||||||
|
public void ToggleMute();
|
||||||
|
|
||||||
|
public bool IsMuted();
|
||||||
|
|
||||||
|
public void IncrementVolumeLevel(int step, int ticks);
|
||||||
|
|
||||||
|
public int GetVolumeLevel();
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
using CoreAudio;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FocusVolumeControl.AudioSessions;
|
||||||
|
|
||||||
|
internal class SystemSoundsAudioSession : IAudioSession
|
||||||
|
{
|
||||||
|
public SystemSoundsAudioSession(SimpleAudioVolume volumeControl)
|
||||||
|
{
|
||||||
|
_volumeControl = volumeControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleAudioVolume _volumeControl;
|
||||||
|
|
||||||
|
public string DisplayName => "System sounds";
|
||||||
|
public string GetIcon() => "Images/systemSounds";
|
||||||
|
|
||||||
|
public void ToggleMute()
|
||||||
|
{
|
||||||
|
_volumeControl.Mute = !_volumeControl.Mute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMuted() => _volumeControl.Mute;
|
||||||
|
|
||||||
|
public void IncrementVolumeLevel(int step, int ticks)
|
||||||
|
{
|
||||||
|
var level = _volumeControl.MasterVolume;
|
||||||
|
|
||||||
|
level += (0.01f * step) * ticks;
|
||||||
|
level = Math.Max(level, 0);
|
||||||
|
level = Math.Min(level, 1);
|
||||||
|
|
||||||
|
_volumeControl.MasterVolume = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetVolumeLevel() => (int)(_volumeControl.MasterVolume * 100);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
using CoreAudio;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FocusVolumeControl.AudioSessions;
|
||||||
|
|
||||||
|
internal class SystemVolumeAudioSession : IAudioSession
|
||||||
|
{
|
||||||
|
public SystemVolumeAudioSession(AudioEndpointVolume volumeControl)
|
||||||
|
{
|
||||||
|
_volumeControl = volumeControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioEndpointVolume _volumeControl;
|
||||||
|
|
||||||
|
public string DisplayName => "System Volume";
|
||||||
|
public string GetIcon() => "Images/actionIcon";
|
||||||
|
|
||||||
|
public void ToggleMute()
|
||||||
|
{
|
||||||
|
_volumeControl.Mute = !_volumeControl.Mute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMuted() => _volumeControl.Mute;
|
||||||
|
|
||||||
|
public void IncrementVolumeLevel(int step, int ticks)
|
||||||
|
{
|
||||||
|
var level = _volumeControl.MasterVolumeLevelScalar;
|
||||||
|
|
||||||
|
level += (0.01f * step) * ticks;
|
||||||
|
level = Math.Max(level, 0);
|
||||||
|
level = Math.Min(level, 1);
|
||||||
|
|
||||||
|
_volumeControl.MasterVolumeLevelScalar = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetVolumeLevel() => (int)(_volumeControl.MasterVolumeLevelScalar * 100);
|
||||||
|
|
||||||
|
}
|
@ -1,46 +1,29 @@
|
|||||||
using BarRaider.SdTools;
|
using BarRaider.SdTools;
|
||||||
using BarRaider.SdTools.Payloads;
|
using BarRaider.SdTools.Payloads;
|
||||||
using CoreAudio;
|
using FocusVolumeControl.AudioSessions;
|
||||||
using FocusVolumeControl.UI;
|
using FocusVolumeControl.UI;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.ServiceModel.Description;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
namespace FocusVolumeControl;
|
||||||
|
|
||||||
|
[PluginActionId("com.dlprows.focusvolumecontrol.dialaction")]
|
||||||
|
public class DialAction : EncoderBase
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
|
||||||
todo:
|
|
||||||
link both discord processes
|
|
||||||
steam not detecting
|
|
||||||
|
|
||||||
long press reset
|
|
||||||
|
|
||||||
option for what to do when on app without sound
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
[PluginActionId("com.dlprows.focusvolumecontrol.dialaction")]
|
|
||||||
public class DialAction : EncoderBase
|
|
||||||
{
|
|
||||||
private class PluginSettings
|
private class PluginSettings
|
||||||
{
|
{
|
||||||
|
|
||||||
|
[JsonProperty("onVolumeNotFound")]
|
||||||
|
public FallbackBehavior FallbackBehavior { get; set; }
|
||||||
|
|
||||||
public static PluginSettings CreateDefaultSettings()
|
public static PluginSettings CreateDefaultSettings()
|
||||||
{
|
{
|
||||||
PluginSettings instance = new PluginSettings();
|
PluginSettings instance = new PluginSettings();
|
||||||
|
instance.FallbackBehavior = FallbackBehavior.PreviousApp;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +33,7 @@ namespace FocusVolumeControl
|
|||||||
IntPtr _foregroundWindowChangedEvent;
|
IntPtr _foregroundWindowChangedEvent;
|
||||||
Native.WinEventDelegate _delegate;
|
Native.WinEventDelegate _delegate;
|
||||||
|
|
||||||
ActiveAudioSessionWrapper _currentAudioSession;
|
IAudioSession _currentAudioSession;
|
||||||
AudioHelper _audioHelper = new AudioHelper();
|
AudioHelper _audioHelper = new AudioHelper();
|
||||||
|
|
||||||
Thread _thread;
|
Thread _thread;
|
||||||
@ -84,6 +67,8 @@ namespace FocusVolumeControl
|
|||||||
_thread.SetApartmentState(ApartmentState.STA);
|
_thread.SetApartmentState(ApartmentState.STA);
|
||||||
_thread.Start();
|
_thread.Start();
|
||||||
|
|
||||||
|
|
||||||
|
_currentAudioSession = settings.FallbackBehavior == FallbackBehavior.SystemSounds ? _audioHelper.GetSystemSounds() : _audioHelper.GetSystemVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void DialDown(DialPayload payload)
|
public override async void DialDown(DialPayload payload)
|
||||||
@ -98,7 +83,7 @@ namespace FocusVolumeControl
|
|||||||
Logger.Instance.LogMessage(TracingLevel.INFO, "Touch Press");
|
Logger.Instance.LogMessage(TracingLevel.INFO, "Touch Press");
|
||||||
if (payload.IsLongPress)
|
if (payload.IsLongPress)
|
||||||
{
|
{
|
||||||
//todo: iterate through all sessions setting them back to 100 except the master volume
|
_audioHelper.ResetAll();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -108,6 +93,9 @@ namespace FocusVolumeControl
|
|||||||
|
|
||||||
async Task ToggleMuteAsync()
|
async Task ToggleMuteAsync()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
if (_currentAudioSession != null)
|
if (_currentAudioSession != null)
|
||||||
{
|
{
|
||||||
_currentAudioSession.ToggleMute();
|
_currentAudioSession.ToggleMute();
|
||||||
@ -118,11 +106,19 @@ namespace FocusVolumeControl
|
|||||||
await Connection.ShowAlert();
|
await Connection.ShowAlert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await Connection.ShowAlert();
|
||||||
|
Logger.Instance.LogMessage(TracingLevel.ERROR, $"Unable to toggle mute: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override async void DialRotate(DialRotatePayload payload)
|
public override async void DialRotate(DialRotatePayload payload)
|
||||||
{
|
{
|
||||||
Logger.Instance.LogMessage(TracingLevel.INFO, "Dial Rotate");
|
Logger.Instance.LogMessage(TracingLevel.INFO, "Dial Rotate");
|
||||||
//dial rotated. ticks positive for right, negative for left
|
//dial rotated. ticks positive for right, negative for left
|
||||||
|
try
|
||||||
|
{
|
||||||
if (_currentAudioSession != null)
|
if (_currentAudioSession != null)
|
||||||
{
|
{
|
||||||
_currentAudioSession.IncrementVolumeLevel(1, payload.Ticks);
|
_currentAudioSession.IncrementVolumeLevel(1, payload.Ticks);
|
||||||
@ -133,6 +129,12 @@ namespace FocusVolumeControl
|
|||||||
await Connection.ShowAlert();
|
await Connection.ShowAlert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await Connection.ShowAlert();
|
||||||
|
Logger.Instance.LogMessage(TracingLevel.ERROR, $"Unable to toggle mute: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void DialUp(DialPayload payload)
|
public override void DialUp(DialPayload payload)
|
||||||
{
|
{
|
||||||
@ -152,14 +154,10 @@ namespace FocusVolumeControl
|
|||||||
|
|
||||||
public override async void OnTick()
|
public override async void OnTick()
|
||||||
{
|
{
|
||||||
//called once every 1000ms and can be used for updating the title/image fo the key
|
//called once every 1000ms and can be used for updating the title/image of the key
|
||||||
var activeSession = _audioHelper.GetActiveSession();
|
var activeSession = _audioHelper.GetActiveSession(settings.FallbackBehavior);
|
||||||
|
|
||||||
if (activeSession == null)
|
if(activeSession != null)
|
||||||
{
|
|
||||||
//todo: something?
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
_currentAudioSession = activeSession;
|
_currentAudioSession = activeSession;
|
||||||
}
|
}
|
||||||
@ -172,7 +170,7 @@ namespace FocusVolumeControl
|
|||||||
if (_currentAudioSession != null)
|
if (_currentAudioSession != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
var uiState = UIState.Build(_currentAudioSession);
|
var uiState = new UIState(_currentAudioSession);
|
||||||
|
|
||||||
if ( _previousState != null && uiState != null &&
|
if ( _previousState != null && uiState != null &&
|
||||||
uiState.Title == _previousState.Title &&
|
uiState.Title == _previousState.Title &&
|
||||||
@ -216,6 +214,4 @@ namespace FocusVolumeControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
8
src/FocusVolumeControl/FallbackBehavior.cs
Normal file
8
src/FocusVolumeControl/FallbackBehavior.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace FocusVolumeControl;
|
||||||
|
|
||||||
|
public enum FallbackBehavior
|
||||||
|
{
|
||||||
|
PreviousApp,
|
||||||
|
SystemSounds,
|
||||||
|
SystemVolume
|
||||||
|
}
|
@ -54,13 +54,16 @@
|
|||||||
<Reference Include="WindowsBase" />
|
<Reference Include="WindowsBase" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ActiveAudioSessionWrapper.cs" />
|
<Compile Include="AudioSessions\ActiveAudioSessionWrapper.cs" />
|
||||||
<Compile Include="AudioHelper.cs" />
|
<Compile Include="AudioHelper.cs" />
|
||||||
|
<Compile Include="AudioSessions\SystemSoundsAudioSession.cs" />
|
||||||
|
<Compile Include="AudioSessions\SystemVolumeAudioSession.cs" />
|
||||||
<Compile Include="DialAction.cs" />
|
<Compile Include="DialAction.cs" />
|
||||||
<Compile Include="ISDConnectionExtensions.cs" />
|
<Compile Include="AudioSessions\IAudioSession.cs" />
|
||||||
|
<Compile Include="FallbackBehavior.cs" />
|
||||||
|
<Compile Include="UI\ISDConnectionExtensions.cs" />
|
||||||
<Compile Include="Native.cs" />
|
<Compile Include="Native.cs" />
|
||||||
<Compile Include="ParentProcessUtilities.cs" />
|
<Compile Include="ParentProcessUtilities.cs" />
|
||||||
<Compile Include="PluginAction.cs" />
|
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="UI\UIState.cs" />
|
<Compile Include="UI\UIState.cs" />
|
||||||
@ -104,6 +107,12 @@
|
|||||||
<Content Include="Images\pluginIcon.png">
|
<Content Include="Images\pluginIcon.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="Images\systemSounds%402x.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Images\systemSounds.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="PropertyInspector\PluginActionPI.html">
|
<Content Include="PropertyInspector\PluginActionPI.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
using BarRaider.SdTools;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
|
||||||
{
|
|
||||||
internal static class ISDConnectionExtensions
|
|
||||||
{
|
|
||||||
public static async Task SetFeedbackAsync(this ISDConnection _this, object feedbackPayload)
|
|
||||||
{
|
|
||||||
await _this.SetFeedbackAsync(JObject.FromObject(feedbackPayload));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.8 KiB |
BIN
src/FocusVolumeControl/Images/systemSounds.png
Normal file
BIN
src/FocusVolumeControl/Images/systemSounds.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
src/FocusVolumeControl/Images/systemSounds@2x.png
Normal file
BIN
src/FocusVolumeControl/Images/systemSounds@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -1,14 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
namespace FocusVolumeControl;
|
||||||
|
|
||||||
|
public class Native
|
||||||
{
|
{
|
||||||
public class Native
|
|
||||||
{
|
|
||||||
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
@ -59,6 +56,7 @@ namespace FocusVolumeControl
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DllImport("ntdll.dll")]
|
||||||
|
public static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
namespace FocusVolumeControl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A utility class to determine a process parent.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct ParentProcessUtilities
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A utility class to determine a process parent.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct ParentProcessUtilities
|
|
||||||
{
|
|
||||||
// These members must match PROCESS_BASIC_INFORMATION
|
// These members must match PROCESS_BASIC_INFORMATION
|
||||||
internal IntPtr Reserved1;
|
internal IntPtr Reserved1;
|
||||||
internal IntPtr PebBaseAddress;
|
internal IntPtr PebBaseAddress;
|
||||||
@ -22,8 +18,6 @@ namespace FocusVolumeControl
|
|||||||
internal IntPtr UniqueProcessId;
|
internal IntPtr UniqueProcessId;
|
||||||
internal IntPtr InheritedFromUniqueProcessId;
|
internal IntPtr InheritedFromUniqueProcessId;
|
||||||
|
|
||||||
[DllImport("ntdll.dll")]
|
|
||||||
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the parent process of specified process.
|
/// Gets the parent process of specified process.
|
||||||
@ -44,7 +38,7 @@ namespace FocusVolumeControl
|
|||||||
public static Process GetParentProcess(Process process)
|
public static Process GetParentProcess(Process process)
|
||||||
{
|
{
|
||||||
var data = new ParentProcessUtilities();
|
var data = new ParentProcessUtilities();
|
||||||
int status = NtQueryInformationProcess(process.Handle, 0, ref data, Marshal.SizeOf(data), out var returnLength);
|
int status = Native.NtQueryInformationProcess(process.Handle, 0, ref data, Marshal.SizeOf(data), out var returnLength);
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@ -60,6 +54,4 @@ namespace FocusVolumeControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
using BarRaider.SdTools;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
|
||||||
{
|
|
||||||
[PluginActionId("FocusVolumeControl.pluginaction")]
|
|
||||||
public class PluginAction : KeypadBase
|
|
||||||
{
|
|
||||||
private class PluginSettings
|
|
||||||
{
|
|
||||||
public static PluginSettings CreateDefaultSettings()
|
|
||||||
{
|
|
||||||
PluginSettings instance = new PluginSettings();
|
|
||||||
instance.OutputFileName = String.Empty;
|
|
||||||
instance.InputString = String.Empty;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
[FilenameProperty]
|
|
||||||
[JsonProperty(PropertyName = "outputFileName")]
|
|
||||||
public string OutputFileName { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "inputString")]
|
|
||||||
public string InputString { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Private Members
|
|
||||||
|
|
||||||
private PluginSettings settings;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
public PluginAction(SDConnection connection, InitialPayload payload) : base(connection, payload)
|
|
||||||
{
|
|
||||||
if (payload.Settings == null || payload.Settings.Count == 0)
|
|
||||||
{
|
|
||||||
this.settings = PluginSettings.CreateDefaultSettings();
|
|
||||||
SaveSettings();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.settings = payload.Settings.ToObject<PluginSettings>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Dispose()
|
|
||||||
{
|
|
||||||
Logger.Instance.LogMessage(TracingLevel.INFO, $"Destructor called");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void KeyPressed(KeyPayload payload)
|
|
||||||
{
|
|
||||||
Logger.Instance.LogMessage(TracingLevel.INFO, "Key Pressed");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void KeyReleased(KeyPayload payload) { }
|
|
||||||
|
|
||||||
public override void OnTick() { }
|
|
||||||
|
|
||||||
public override void ReceivedSettings(ReceivedSettingsPayload payload)
|
|
||||||
{
|
|
||||||
Tools.AutoPopulateSettings(settings, payload.Settings);
|
|
||||||
SaveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { }
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
private Task SaveSettings()
|
|
||||||
{
|
|
||||||
return Connection.SetSettingsAsync(JObject.FromObject(settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,9 @@
|
|||||||
using BarRaider.SdTools;
|
using BarRaider.SdTools;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl
|
namespace FocusVolumeControl;
|
||||||
|
|
||||||
|
internal class Program
|
||||||
{
|
{
|
||||||
internal class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
// Uncomment this line of code to allow for debugging
|
// Uncomment this line of code to allow for debugging
|
||||||
@ -16,5 +11,4 @@ namespace FocusVolumeControl
|
|||||||
|
|
||||||
SDWrapper.Run(args);
|
SDWrapper.Run(args);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,24 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="sdpi-wrapper">
|
<div class="sdpi-wrapper">
|
||||||
|
<div class="sdpi-item">
|
||||||
|
<div class="sdpi-item-label">Fallback Behavior</div>
|
||||||
|
<select class="sdpi-item-value sdProperty" id="fallbackBehavior" oninput="setSettings()">
|
||||||
|
<option value="0">Previous App</option>
|
||||||
|
<option value="1">System Sounds</option>
|
||||||
|
<option value="2">Main System Volume</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="sdpi-item">
|
||||||
|
<details class="message">
|
||||||
|
<p>If the focused app does not have a volume control you can pick your desired behavior</p>
|
||||||
|
<ul>
|
||||||
|
<li>Previous App - the focused app will be the last app that had a volume control</li>
|
||||||
|
<li>System Sounds - switch to control system sounds. For example, if you're using an app that makes a windows "error" sound, its volume is controlled by system sounds. So you can adjust the error sound without impacting other apps</li>
|
||||||
|
<li>Main System Volume - switch to the main volume control for the system. This will change the volume of all apps</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
13
src/FocusVolumeControl/UI/ISDConnectionExtensions.cs
Normal file
13
src/FocusVolumeControl/UI/ISDConnectionExtensions.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using BarRaider.SdTools;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FocusVolumeControl.UI;
|
||||||
|
|
||||||
|
internal static class ISDConnectionExtensions
|
||||||
|
{
|
||||||
|
public static async Task SetFeedbackAsync(this ISDConnection _this, object feedbackPayload)
|
||||||
|
{
|
||||||
|
await _this.SetFeedbackAsync(JObject.FromObject(feedbackPayload));
|
||||||
|
}
|
||||||
|
}
|
@ -8,11 +8,12 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BarRaider.SdTools;
|
using BarRaider.SdTools;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using FocusVolumeControl.AudioSessions;
|
||||||
|
|
||||||
namespace FocusVolumeControl.UI
|
namespace FocusVolumeControl.UI;
|
||||||
|
|
||||||
|
internal class UIState
|
||||||
{
|
{
|
||||||
internal class UIState
|
|
||||||
{
|
|
||||||
[JsonProperty("title")]
|
[JsonProperty("title")]
|
||||||
public string Title { get; private init; }
|
public string Title { get; private init; }
|
||||||
|
|
||||||
@ -25,41 +26,16 @@ namespace FocusVolumeControl.UI
|
|||||||
[JsonProperty("icon")]
|
[JsonProperty("icon")]
|
||||||
public ValueWithOpacity<string> icon { get; private init; }
|
public ValueWithOpacity<string> icon { get; private init; }
|
||||||
|
|
||||||
public static UIState Build(ActiveAudioSessionWrapper session)
|
public UIState(IAudioSession session)
|
||||||
{
|
{
|
||||||
var volume = session.GetVolumeLevel();
|
var volume = session.GetVolumeLevel();
|
||||||
|
var opacity = session.IsMuted() ? 0.5f : 1;
|
||||||
|
var iconData = session.GetIcon();
|
||||||
|
|
||||||
var opacity = session.GetMuted() != true ? 1 : 0.5f;
|
Title = session.DisplayName;
|
||||||
|
Value = new() { Value = $"{volume}%", Opacity = opacity };
|
||||||
var iconData = "";
|
Indicator = new() { Value = volume, Opacity = opacity };
|
||||||
|
icon = new() { Value = iconData, Opacity = opacity };
|
||||||
if (session.Icon != null)
|
|
||||||
{
|
|
||||||
iconData = session.Icon;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var icon = Icon.ExtractAssociatedIcon(session.ExecutablePath);
|
|
||||||
iconData = Tools.ImageToBase64(icon.ToBitmap(), true);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
iconData = "Image/pluginIcon.png";
|
|
||||||
}
|
|
||||||
session.Icon = iconData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return new UIState()
|
|
||||||
{
|
|
||||||
Title = session.DisplayName,
|
|
||||||
Value = new() { Value = $"{volume}%", Opacity = opacity },
|
|
||||||
Indicator = new() { Value = volume, Opacity = opacity },
|
|
||||||
icon = new() { Value = iconData, Opacity = opacity },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,13 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data.SqlTypes;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FocusVolumeControl.UI
|
namespace FocusVolumeControl.UI;
|
||||||
|
|
||||||
|
internal class ValueWithOpacity<T>
|
||||||
{
|
{
|
||||||
internal class ValueWithOpacity<T>
|
|
||||||
{
|
|
||||||
[JsonProperty("value")]
|
[JsonProperty("value")]
|
||||||
public required T Value { get; init; }
|
public required T Value { get; init; }
|
||||||
|
|
||||||
[JsonProperty("opacity")]
|
[JsonProperty("opacity")]
|
||||||
public required float Opacity { get; init; }
|
public required float Opacity { get; init; }
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,17 @@
|
|||||||
using CoreAudio;
|
using CoreAudio;
|
||||||
using FocusVolumeControl;
|
using FocusVolumeControl;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Management;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Data;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
using System.Windows.Navigation;
|
|
||||||
using System.Windows.Shapes;
|
|
||||||
|
|
||||||
namespace SoundBrowser
|
namespace SoundBrowser;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for MainWindow.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class MainWindow : Window
|
|
||||||
{
|
|
||||||
|
|
||||||
AudioHelper _audioHelper;
|
AudioHelper _audioHelper;
|
||||||
Native.WinEventDelegate _delegate;
|
Native.WinEventDelegate _delegate;
|
||||||
@ -91,7 +77,6 @@ namespace SoundBrowser
|
|||||||
sb.AppendLine("picked the following best match");
|
sb.AppendLine("picked the following best match");
|
||||||
sb.AppendLine($"\tsession: {session.DisplayName}");
|
sb.AppendLine($"\tsession: {session.DisplayName}");
|
||||||
sb.AppendLine($"\tvolume: {session.GetVolumeLevel()}");
|
sb.AppendLine($"\tvolume: {session.GetVolumeLevel()}");
|
||||||
sb.AppendLine($"\tcount: {session.Count}");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -130,6 +115,4 @@ namespace SoundBrowser
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SoundBrowser
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A utility class to determine a process parent.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct ParentProcessUtilities
|
|
||||||
{
|
|
||||||
// These members must match PROCESS_BASIC_INFORMATION
|
|
||||||
internal IntPtr Reserved1;
|
|
||||||
internal IntPtr PebBaseAddress;
|
|
||||||
internal IntPtr Reserved2_0;
|
|
||||||
internal IntPtr Reserved2_1;
|
|
||||||
internal IntPtr UniqueProcessId;
|
|
||||||
internal IntPtr InheritedFromUniqueProcessId;
|
|
||||||
|
|
||||||
[DllImport("ntdll.dll")]
|
|
||||||
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the parent process of specified process.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The process id.</param>
|
|
||||||
/// <returns>An instance of the Process class.</returns>
|
|
||||||
public static Process? GetParentProcess(int id)
|
|
||||||
{
|
|
||||||
Process process = Process.GetProcessById(id);
|
|
||||||
return GetParentProcess(process.Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the parent process of a specified process.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="handle">The process handle.</param>
|
|
||||||
/// <returns>An instance of the Process class.</returns>
|
|
||||||
public static Process? GetParentProcess(IntPtr handle)
|
|
||||||
{
|
|
||||||
ParentProcessUtilities pbi = new ParentProcessUtilities();
|
|
||||||
int returnLength;
|
|
||||||
int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
|
|
||||||
if (status != 0)
|
|
||||||
throw new Win32Exception(status);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
|
|
||||||
}
|
|
||||||
catch (ArgumentException)
|
|
||||||
{
|
|
||||||
// not found
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user