Skip to content

Commit

Permalink
Merge pull request #16 from emoacht/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
emoacht authored Sep 8, 2024
2 parents b505ef4 + c857388 commit b4ba5c2
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 126 deletions.
1 change: 1 addition & 0 deletions Source/HelloSwitcher.Core/HelloSwitcher.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<Compile Include="Models\DeviceUsbWindowWatcher.cs" />
<Compile Include="Models\Logger.cs" />
<Compile Include="Models\PnpUtility.cs" />
<Compile Include="Models\PowerSuspendResumeWatcher.cs" />
<Compile Include="Models\Settings.cs" />
<Compile Include="Models\VidPid.cs" />
<Compile Include="Models\WmiUsbHelper.cs" />
Expand Down
67 changes: 45 additions & 22 deletions Source/HelloSwitcher.Core/Models/DeviceSwitcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,47 +19,70 @@ public DeviceSwitcher(Settings settings, Logger logger)
public bool RemovableCameraExists => _removableCameraExists.GetValueOrDefault();
private bool? _removableCameraExists = null;

private readonly object _lock = new();
private int _checkCount = 0;
private readonly TimeSpan _checkInterval = TimeSpan.FromSeconds(1);

public Task CheckAsync(string actionName, CancellationToken cancellationToken = default) => CheckAsync(actionName, null, false, cancellationToken);

public async Task CheckAsync(string actionName, string deviceName, bool exists, CancellationToken cancellationToken = default)
{
var result = new List<string> { actionName };
void RecordResult() => _logger?.RecordOperation(string.Join(Environment.NewLine, result));

result.Add($"deviceName: [{deviceName}], exists: {exists}");

lock (_lock)
bool isEntered = false;
try
{
if ((deviceName is not null) && (_settings.RemovableCameraVidPid?.IsValid is true))
isEntered = (Interlocked.Increment(ref _checkCount) == 1);
if (isEntered)
{
result.Add($"RemovableCameraVidPid: [{_settings.RemovableCameraVidPid}]");

if (!_settings.RemovableCameraVidPid.Equals(new VidPid(deviceName)))
{
RecordResult();
return;
}
var checkTask = CheckBaseAsync(actionName, deviceName, exists, cancellationToken);
var intervalTask = Task.Delay(_checkInterval, cancellationToken);
await Task.WhenAll(checkTask, intervalTask);
}
else
}
catch (TaskCanceledException)
{
}
finally
{
if (isEntered)
{
result.Add($"RemovableCameraClassGuid: {_settings.RemovableCameraClassGuid}, RemovableCameraDeviceInstanceId: [{_settings.RemovableCameraDeviceInstanceId}]");

exists = DeviceUsbHelper.UsbCameraExists(_settings.RemovableCameraClassGuid, _settings.RemovableCameraDeviceInstanceId);
Interlocked.Exchange(ref _checkCount, 0);
}
}
}

private async Task CheckBaseAsync(string actionName, string deviceName, bool exists, CancellationToken cancellationToken = default)
{
var result = new List<string> { actionName };
void RecordResult() => _logger?.RecordOperation(string.Join(Environment.NewLine, result));

result.Add($"deviceName: [{deviceName}], exists: {exists}");

result.Add($"removableCameraExists: [{_removableCameraExists}], exists: {exists}");
if ((deviceName is not null) && (_settings.RemovableCameraVidPid?.IsValid is true))
{
result.Add($"RemovableCameraVidPid: [{_settings.RemovableCameraVidPid}]");

if (_removableCameraExists == exists)
if (!_settings.RemovableCameraVidPid.Equals(new VidPid(deviceName)))
{
RecordResult();
return;
}
}
else
{
result.Add($"RemovableCameraClassGuid: {_settings.RemovableCameraClassGuid}, RemovableCameraDeviceInstanceId: [{_settings.RemovableCameraDeviceInstanceId}]");

_removableCameraExists = exists;
exists = DeviceUsbHelper.UsbCameraExists(_settings.RemovableCameraClassGuid, _settings.RemovableCameraDeviceInstanceId);
}

result.Add($"removableCameraExists: [{_removableCameraExists}], exists: {exists}");

if (_removableCameraExists == exists)
{
RecordResult();
return;
}

_removableCameraExists = exists;

if (cancellationToken.IsCancellationRequested)
{
RecordResult();
Expand Down
158 changes: 158 additions & 0 deletions Source/HelloSwitcher.Core/Models/PowerSuspendResumeWatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace HelloSwitcher.Models;

public class PowerSuspendResumeWatcher : IDisposable
{
#region Win32

[DllImport("Powrprof.dll")]
private static extern uint PowerRegisterSuspendResumeNotification(
uint flags,
in DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS recipient,
out IntPtr registrationHandle);

[DllImport("Powrprof.dll")]
private static extern uint PowerUnregisterSuspendResumeNotification(
IntPtr registrationHandle);

private const uint DEVICE_NOTIFY_CALLBACK = 2;

[StructLayout(LayoutKind.Sequential)]
private struct DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
{
public DeviceNotifyCallbackRoutine callback;
public IntPtr context;
}

private delegate uint DeviceNotifyCallbackRoutine(
IntPtr context,
int type,
IntPtr setting);

private const uint ERROR_SUCCESS = 0;

#endregion

private DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS _recipient;
private IntPtr _registrationHandle;

public PowerSuspendResumeWatcher()
{
_recipient = new DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
{
callback = new DeviceNotifyCallbackRoutine(DeviceNotifyCallback),
context = IntPtr.Zero
};
uint result = PowerRegisterSuspendResumeNotification(
DEVICE_NOTIFY_CALLBACK,
in _recipient,
out _registrationHandle);
if (result != ERROR_SUCCESS)
{
Debug.WriteLine($"Failed to register suspend resume notification. ({result})");
}
}

private uint DeviceNotifyCallback(IntPtr context, int type, IntPtr setting)
{
if (Enum.IsDefined(typeof(PowerStatus), type))
{
PowerStatusChanged?.Invoke(this, (PowerStatus)type);
}
return 0;
}

public event EventHandler<PowerStatus> PowerStatusChanged;

#region IDisposable

private bool _isDisposed = false;

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
return;

if (disposing)
{
// Free any other managed objects here.
PowerStatusChanged = null;

if (_registrationHandle != IntPtr.Zero)
{
uint result = PowerUnregisterSuspendResumeNotification(_registrationHandle);
if (result != ERROR_SUCCESS)
{
Debug.WriteLine($"Failed to unregister suspend resume notification. ({result})");
}
}
}

// Free any unmanaged objects here.
_isDisposed = true;
}

#endregion
}

public enum PowerStatus
{
/// <summary>
/// PBT_APMQUERYSUSPEND
/// </summary>
QuerySuspend = 0x0000,

/// <summary>
/// PBT_APMQUERYSUSPENDFAILED
/// </summary>
QuerySuspendFailed = 0x0002,

/// <summary>
/// PBT_APMSUSPEND
/// </summary>
Suspend = 0x0004,

/// <summary>
/// PBT_APMRESUMECRITICAL
/// </summary>
ResumeCritical = 0x0006,

/// <summary>
/// PBT_APMRESUMESUSPEND
/// </summary>
ResumeSuspend = 0x0007,

/// <summary>
/// PBT_APMBATTERYLOW
/// </summary>
BatteryLow = 0x0009,

/// <summary>
/// PBT_APMPOWERSTATUSCHANGE
/// </summary>
PowerStatusChange = 0x000A,

/// <summary>
/// PBT_APMOEMEVENT
/// </summary>
OemEvent = 0x000B,

/// <summary>
/// PBT_APMRESUMEAUTOMATIC
/// </summary>
ResumeAutomatic = 0x0012,

/// <summary>
/// PBT_POWERSETTINGCHANGE
/// </summary>
PowerSettingChange = 0x8013
}
4 changes: 2 additions & 2 deletions Source/HelloSwitcher.Core/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.6.0.0")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: AssemblyVersion("1.6.1.0")]
[assembly: AssemblyFileVersion("1.6.1.0")]
15 changes: 7 additions & 8 deletions Source/HelloSwitcher.Service/HelloSwitcherService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Threading;
using System.Threading.Tasks;

using HelloSwitcher.Helper;
using HelloSwitcher.Models;

namespace HelloSwitcher.Service;
Expand All @@ -17,7 +16,6 @@ public partial class HelloSwitcherService : ServiceBase

internal bool IsPaused { get; private set; }

private readonly Sample _check;
private CancellationTokenSource _checkTokenSource;
private IntPtr _notificationHandle;

Expand All @@ -30,10 +28,6 @@ public HelloSwitcherService()

Settings = new Settings();
Logger = new Logger("operation.service.log", "error.service.log");

_check = new Sample(
TimeSpan.FromSeconds(1),
(actionName, cancellationToken) => _switcher?.CheckAsync(actionName, cancellationToken));
}

protected override async void OnStart(string[] args)
Expand Down Expand Up @@ -91,7 +85,7 @@ protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
{
case PowerBroadcastStatus.ResumeAutomatic:
case PowerBroadcastStatus.ResumeSuspend:
_check.Push($"Power Changed Check", _checkTokenSource?.Token ?? default);
OnChanged($"Power Changed Check", _checkTokenSource?.Token ?? default);
break;
}
}
Expand All @@ -118,9 +112,14 @@ protected override void OnCustomCommand(int command)

if (!IsPaused)
{
_check.Push($"Device Changed Check", _checkTokenSource?.Token ?? default);
OnChanged($"Device Changed Check", _checkTokenSource?.Token ?? default);
}
break;
}
}

private async void OnChanged(string actionName, CancellationToken cancellationToken)
{
await _switcher?.CheckAsync(actionName, cancellationToken);
}
}
4 changes: 2 additions & 2 deletions Source/HelloSwitcher.Service/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.6.0.0")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: AssemblyVersion("1.6.1.0")]
[assembly: AssemblyFileVersion("1.6.1.0")]
22 changes: 18 additions & 4 deletions Source/HelloSwitcher/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public partial class App : Application
internal Logger Logger { get; }

private DeviceSwitcher _switcher;
private DeviceUsbWindowWatcher _watcher;
private DeviceUsbWindowWatcher _deviceWatcher;
private PowerSuspendResumeWatcher _powerWatcher;
private NotifyIconHolder _holder;

internal static bool IsInteractive { get; } = Environment.UserInteractive;
Expand Down Expand Up @@ -50,8 +51,8 @@ protected override async void OnStartup(StartupEventArgs e)
_switcher = new DeviceSwitcher(Settings, Logger);
await _switcher.CheckAsync("Initial Check");

_watcher = new DeviceUsbWindowWatcher();
_watcher.UsbDeviceChanged += async (_, e) =>
_deviceWatcher = new DeviceUsbWindowWatcher();
_deviceWatcher.UsbDeviceChanged += async (_, e) =>
{
await _switcher.CheckAsync("Device Changed Check", e.deviceName, e.exists);
Expand All @@ -62,6 +63,18 @@ protected override async void OnStartup(StartupEventArgs e)
}
};

_powerWatcher = new PowerSuspendResumeWatcher();
_powerWatcher.PowerStatusChanged += async (_, status) =>
{
switch (status)
{
case PowerStatus.ResumeAutomatic:
case PowerStatus.ResumeSuspend:
await _switcher.CheckAsync($"Resumed Check ({status})");
break;
}
};

if (IsInteractive)
{
_holder = new NotifyIconHolder(
Expand All @@ -81,7 +94,8 @@ protected override async void OnStartup(StartupEventArgs e)

protected override void OnExit(ExitEventArgs e)
{
_watcher?.Dispose();
_deviceWatcher?.Dispose();
_powerWatcher?.Dispose();
_holder?.Dispose();
End();

Expand Down
4 changes: 2 additions & 2 deletions Source/HelloSwitcher/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.6.0.0")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: AssemblyVersion("1.6.1.0")]
[assembly: AssemblyFileVersion("1.6.1.0")]
[assembly: Guid("24aba232-090c-4c0e-8568-27e13b281fab")]
Loading

0 comments on commit b4ba5c2

Please sign in to comment.