Skip to content

Commit

Permalink
CleanUp leftover of previous lib
Browse files Browse the repository at this point in the history
Use a ComThread for all Com Actions
  • Loading branch information
Antoine Aflalo committed Feb 26, 2019
1 parent 4c7fc4f commit 4fbc88c
Show file tree
Hide file tree
Showing 22 changed files with 230 additions and 124 deletions.
25 changes: 10 additions & 15 deletions SoundSwitch.Audio.Manager/AudioSwitcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using SoundSwitch.Audio.Manager.Interop;
using SoundSwitch.Audio.Manager.Interop.Client;
using SoundSwitch.Audio.Manager.Interop.Enum;
using SoundSwitch.Audio.Manager.Interop.Threading;

namespace SoundSwitch.Audio.Manager
{
Expand All @@ -24,33 +25,27 @@ public static AudioSwitcher Instance
return _instance;
}

return _instance = new AudioSwitcher();
return _instance = ComThread.Invoke(() => new AudioSwitcher());
}
}

public bool SwitchTo(string deviceId, ERole role)
public void SwitchTo(string deviceId, ERole role)
{
if (role != ERole.ERole_enum_count)
{
return InternalSwitchTo(deviceId, role);
ComThread.Invoke((() => _policyClient.SetDefaultEndpoint(deviceId, role)));

return;
}

var result = true;
result &= InternalSwitchTo(deviceId, ERole.eConsole);
result &= InternalSwitchTo(deviceId, ERole.eMultimedia);
result &= InternalSwitchTo(deviceId, ERole.eCommunications);
return result;
SwitchTo(deviceId, ERole.eConsole);
SwitchTo(deviceId, ERole.eMultimedia);
SwitchTo(deviceId, ERole.eCommunications);
}

public bool IsDefault(string deviceId, EDataFlow flow, ERole role)
{
return _enumerator.IsDefault(deviceId, flow, role);
}

private bool InternalSwitchTo(string deviceId, ERole role)
{
_policyClient.SetDefaultEndpoint(deviceId, role);
return true;
return ComThread.Invoke(() => _enumerator.IsDefault(deviceId, flow, role));
}
}
}
3 changes: 2 additions & 1 deletion SoundSwitch.Audio.Manager/Interop/Client/PolicyClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
using SoundSwitch.Audio.Manager.Interop.Enum;
using SoundSwitch.Audio.Manager.Interop.Interface;
using SoundSwitch.Audio.Manager.Interop.Interface.Policy;
Expand Down
98 changes: 98 additions & 0 deletions SoundSwitch.Audio.Manager/Interop/Threading/ComTaskScheduler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace SoundSwitch.Audio.Manager.Interop.Threading
{
internal sealed class ComTaskScheduler : TaskScheduler, IDisposable
{
/// <summary>The STA threads used by the scheduler.</summary>
private readonly Thread _thread;

/// <summary>Stores the queued tasks to be executed by our pool of STA threads.</summary>
private BlockingCollection<Task> _tasks;

/// <summary>Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.</summary>
public ComTaskScheduler()
{
// Initialize the tasks collection
_tasks = new BlockingCollection<Task>();

_thread = new Thread(() =>
{
// Continually get the next task and try to execute it.
// This will continue until the scheduler is disposed and no more tasks remain.
foreach (var t in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(t);
}

//lightweight pump of the thread
Thread.CurrentThread.Join(1);
});

_thread.IsBackground = true;
_thread.SetApartmentState(ApartmentState.STA);

// Start all of the threads
_thread.Start();
}

public int ThreadId
{
get { return _thread == null ? -1 : _thread.ManagedThreadId; }
}

/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
public override int MaximumConcurrencyLevel
{
get { return 1; }
}

/// <summary>
/// Cleans up the scheduler by indicating that no more tasks will be queued.
/// This method blocks until all threads successfully shutdown.
/// </summary>
public void Dispose()
{
if (_tasks == null) return;

// Indicate that no new tasks will be coming in
_tasks.CompleteAdding();

_thread.Join();

// Cleanup
_tasks.Dispose();
_tasks = null;
}

/// <summary>Queues a Task to be executed by this scheduler.</summary>
/// <param name="task">The task to be executed.</param>
protected override void QueueTask(Task task)
{
// Push it into the blocking collection of tasks
_tasks.Add(task);
}

/// <summary>Provides a list of the scheduled tasks for the debugger to consume.</summary>
/// <returns>An enumerable of all tasks currently scheduled.</returns>
protected override IEnumerable<Task> GetScheduledTasks()
{
// Serialize the contents of the blocking collection of tasks for the debugger
return _tasks.ToArray();
}

/// <summary>Determines whether a Task may be inlined.</summary>
/// <param name="task">The task to be executed.</param>
/// <param name="taskWasPreviouslyQueued">Whether the task was previously queued.</param>
/// <returns>true if the task was successfully inlined; otherwise, false.</returns>
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
//Never run inline, it HAS to be run on the COM thread
return false;
}
}
}
60 changes: 60 additions & 0 deletions SoundSwitch.Audio.Manager/Interop/Threading/ComThread.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace SoundSwitch.Audio.Manager.Interop.Threading
{
internal static class ComThread
{
private static readonly ComTaskScheduler COM_SCHEDULER = new ComTaskScheduler();

private static bool InvokeRequired
{
get { return Thread.CurrentThread.ManagedThreadId != Scheduler.ThreadId; }
}

private static ComTaskScheduler Scheduler
{
get { return COM_SCHEDULER; }
}

/// <summary>
/// Asserts that the execution following this statement is running on the ComThreads
/// <exception cref="InvalidThreadException">Thrown if the assertion fails</exception>
/// </summary>
public static void Assert()
{
if (InvokeRequired)
throw new InvalidThreadException(String.Format("This operation must be run on the ComThread ThreadId: {0}", Scheduler.ThreadId));
}

public static void Invoke(Action action)
{
if (!InvokeRequired)
{
action();
return;
}

BeginInvoke(action).Wait();
}

public static Task BeginInvoke(Action action)
{
return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, COM_SCHEDULER);
}

public static T Invoke<T>(Func<T> func)
{
if (!InvokeRequired)
return func();

return BeginInvoke(func).Result;
}

public static Task<T> BeginInvoke<T>(Func<T> func)
{
return Task<T>.Factory.StartNew(func, CancellationToken.None, TaskCreationOptions.None, COM_SCHEDULER);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace SoundSwitch.Audio.Manager.Interop.Threading
{
public sealed class InvalidThreadException : Exception
{

public InvalidThreadException()
{

}

public InvalidThreadException(string message)
: base(message)
{
}
}
}
3 changes: 3 additions & 0 deletions SoundSwitch.Audio.Manager/SoundSwitch.Audio.Manager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
<Compile Include="AudioSwitcher.cs" />
<Compile Include="Interop\Client\EnumeratorClient.cs" />
<Compile Include="Interop\Client\PolicyClient.cs" />
<Compile Include="Interop\Threading\ComTaskScheduler.cs" />
<Compile Include="Interop\Threading\ComThread.cs" />
<Compile Include="Interop\Enum\DeviceState.cs" />
<Compile Include="Interop\Enum\EDataFlow.cs" />
<Compile Include="Interop\Enum\ERole.cs" />
Expand All @@ -55,6 +57,7 @@
<Compile Include="Interop\Interface\Policy\IPolicyConfig.cs" />
<Compile Include="Interop\Interface\Policy\IPolicyConfigVista.cs" />
<Compile Include="Interop\Interface\Policy\IPolicyConfigX.cs" />
<Compile Include="Interop\Threading\InvalidThreadException.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
52 changes: 0 additions & 52 deletions SoundSwitch.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,11 @@ VisualStudioVersion = 15.0.26403.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoundSwitch", "SoundSwitch\SoundSwitch.csproj", "{A17ECFE0-C4E2-4410-839A-14DF8C11428D}"
ProjectSection(ProjectDependencies) = postProject
{115BF2B4-B3BE-446D-BA76-348E274258F7} = {115BF2B4-B3BE-446D-BA76-348E274258F7}
{082AB6E3-EB26-4791-8025-5BA9682460EA} = {082AB6E3-EB26-4791-8025-5BA9682460EA}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoundSwitch.UI.UserControls", "SoundSwitch.UI.UserControls\SoundSwitch.UI.UserControls.csproj", "{082AB6E3-EB26-4791-8025-5BA9682460EA}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AudioDefaultSwitcher", "AudioDefaultSwitcher\AudioDefaultSwitcher\AudioDefaultSwitcherLibrary.vcxproj", "{866E7A49-8977-4CE2-A2A0-A484DBCF2138}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Audio.Default.Switcher.Wrapper", "AudioDefaultSwitcher\Audio.EndPoint.Controller.Wrapper\Audio.EndPoint.Controller.Wrapper.vcxproj", "{115BF2B4-B3BE-446D-BA76-348E274258F7}"
ProjectSection(ProjectDependencies) = postProject
{866E7A49-8977-4CE2-A2A0-A484DBCF2138} = {866E7A49-8977-4CE2-A2A0-A484DBCF2138}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoundSwitch.Audio.Manager", "SoundSwitch.Audio.Manager\SoundSwitch.Audio.Manager.csproj", "{B4F7987E-A5FA-45B1-9AE4-5E40144FFC54}"
EndProject
Global
Expand Down Expand Up @@ -84,50 +76,6 @@ Global
{082AB6E3-EB26-4791-8025-5BA9682460EA}.Release|x64.Build.0 = Release|Any CPU
{082AB6E3-EB26-4791-8025-5BA9682460EA}.Release|x86.ActiveCfg = Release|Any CPU
{082AB6E3-EB26-4791-8025-5BA9682460EA}.Release|x86.Build.0 = Release|Any CPU
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.AppVeyor|Any CPU.ActiveCfg = AppVeyor|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.AppVeyor|x64.ActiveCfg = AppVeyor|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.AppVeyor|x64.Build.0 = AppVeyor|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.AppVeyor|x86.ActiveCfg = AppVeyor|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.AppVeyor|x86.Build.0 = AppVeyor|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Beta|Any CPU.ActiveCfg = Release|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Beta|Any CPU.Build.0 = Release|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Beta|x64.ActiveCfg = Release|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Beta|x64.Build.0 = Release|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Beta|x86.ActiveCfg = Release|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Beta|x86.Build.0 = Release|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Debug|Any CPU.ActiveCfg = Debug|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Debug|Any CPU.Build.0 = Debug|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Debug|x64.ActiveCfg = Debug|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Debug|x64.Build.0 = Debug|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Debug|x86.ActiveCfg = Debug|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Debug|x86.Build.0 = Debug|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Release|Any CPU.ActiveCfg = Release|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Release|x64.ActiveCfg = Release|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Release|x64.Build.0 = Release|x64
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Release|x86.ActiveCfg = Release|Win32
{866E7A49-8977-4CE2-A2A0-A484DBCF2138}.Release|x86.Build.0 = Release|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.AppVeyor|Any CPU.ActiveCfg = AppVeyor|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.AppVeyor|x64.ActiveCfg = AppVeyor|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.AppVeyor|x64.Build.0 = AppVeyor|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.AppVeyor|x86.ActiveCfg = AppVeyor|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.AppVeyor|x86.Build.0 = AppVeyor|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Beta|Any CPU.ActiveCfg = Release|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Beta|Any CPU.Build.0 = Release|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Beta|x64.ActiveCfg = Release|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Beta|x64.Build.0 = Release|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Beta|x86.ActiveCfg = Release|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Beta|x86.Build.0 = Release|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Debug|Any CPU.ActiveCfg = Debug|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Debug|Any CPU.Build.0 = Debug|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Debug|x64.ActiveCfg = Debug|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Debug|x64.Build.0 = Debug|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Debug|x86.ActiveCfg = Debug|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Debug|x86.Build.0 = Debug|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Release|Any CPU.ActiveCfg = Release|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Release|x64.ActiveCfg = Release|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Release|x64.Build.0 = Release|x64
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Release|x86.ActiveCfg = Release|Win32
{115BF2B4-B3BE-446D-BA76-348E274258F7}.Release|x86.Build.0 = Release|Win32
{B4F7987E-A5FA-45B1-9AE4-5E40144FFC54}.AppVeyor|Any CPU.ActiveCfg = Release|Any CPU
{B4F7987E-A5FA-45B1-9AE4-5E40144FFC54}.AppVeyor|Any CPU.Build.0 = Release|Any CPU
{B4F7987E-A5FA-45B1-9AE4-5E40144FFC54}.AppVeyor|x64.ActiveCfg = Release|Any CPU
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AudioDefaultSwitcherWrapper;
using NAudio.CoreAudioApi;
using Serilog;
using SoundSwitch.Audio.Manager;
Expand All @@ -28,7 +27,7 @@ namespace SoundSwitch.Framework.DeviceCyclerManager.DeviceCycler
{
public abstract class ADeviceCycler : IDeviceCycler
{
private readonly IDictionary<DeviceType, DeviceFullInfo> _lastDevices = new Dictionary<DeviceType, DeviceFullInfo>();
private readonly IDictionary<DataFlow, DeviceFullInfo> _lastDevices = new Dictionary<DataFlow, DeviceFullInfo>();

public abstract DeviceCyclerTypeEnum TypeEnum { get; }
public abstract string Label { get; }
Expand All @@ -37,15 +36,15 @@ public abstract class ADeviceCycler : IDeviceCycler
/// Cycle the audio device for the given type
/// </summary>
/// <param name="type"></param>
public abstract bool CycleAudioDevice(DeviceType type);
public abstract bool CycleAudioDevice(DataFlow type);

/// <summary>
/// Get the next device that need to be set as Default
/// </summary>
/// <param name="audioDevices"></param>
/// <param name="type"></param>
/// <returns></returns>
protected DeviceFullInfo GetNextDevice(ICollection<DeviceFullInfo> audioDevices, DeviceType type)
protected DeviceFullInfo GetNextDevice(ICollection<DeviceFullInfo> audioDevices, DataFlow type)
{
_lastDevices.TryGetValue(type, out var lastDevice);

Expand Down Expand Up @@ -75,7 +74,7 @@ public bool SetActiveDevice(DeviceFullInfo device)
Log.Information("Set Default Communication device: {Device}", device);
AudioSwitcher.Instance.SwitchTo(device.Id, ERole.ERole_enum_count);
}
_lastDevices[(DeviceType)device.Type] = device;
_lastDevices[device.Type] = device;
return true;

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AudioDefaultSwitcherWrapper;
using NAudio.CoreAudioApi;
using SoundSwitch.Framework.Configuration.Device;
using SoundSwitch.Model;
Expand All @@ -32,15 +31,15 @@ public class DeviceCyclerAll : ADeviceCycler
/// Cycle the audio device for the given type
/// </summary>
/// <param name="type"></param>
public override bool CycleAudioDevice(DeviceType type)
public override bool CycleAudioDevice(DataFlow type)
{
ICollection<DeviceFullInfo> audioDevices;
switch (type)
{
case DeviceType.Playback:
case DataFlow.Render:
audioDevices = AppModel.Instance.ActiveAudioDeviceLister.PlaybackDevices;
break;
case DeviceType.Recording:
case DataFlow.Capture:
audioDevices = AppModel.Instance.ActiveAudioDeviceLister.RecordingDevices;
break;
default:
Expand Down
Loading

0 comments on commit 4fbc88c

Please sign in to comment.