2023-11-22 12:22:01 -05:00
|
|
|
#if NET20 || NET35 || NET40
|
2023-11-14 16:10:10 -05:00
|
|
|
|
|
|
|
|
// Licensed to the .NET Foundation under one or more agreements.
|
|
|
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
|
|
2025-07-31 14:21:34 -04:00
|
|
|
using System;
|
2023-11-14 16:10:10 -05:00
|
|
|
using System.Threading;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
2025-09-22 21:04:17 -04:00
|
|
|
namespace ProtectionScan
|
2023-11-14 16:10:10 -05:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides an IProgress{T} that invokes callbacks for each reported progress value.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T">Specifies the type of the progress report value.</typeparam>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Any handler provided to the constructor or event handlers registered with
|
|
|
|
|
/// the <see cref="ProgressChanged"/> event are invoked through a
|
|
|
|
|
/// <see cref="SynchronizationContext"/> instance captured
|
|
|
|
|
/// when the instance is constructed. If there is no current SynchronizationContext
|
|
|
|
|
/// at the time of construction, the callbacks will be invoked on the ThreadPool.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <see href="https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Progress.cs"/>
|
2025-09-22 21:04:17 -04:00
|
|
|
internal class Progress<T> : IProgress<T> where T : EventArgs
|
2023-11-14 16:10:10 -05:00
|
|
|
{
|
|
|
|
|
/// <summary>The synchronization context captured upon construction. This will never be null.</summary>
|
2024-04-24 11:35:09 -04:00
|
|
|
private readonly SynchronizationContext? _synchronizationContext;
|
2023-11-14 16:10:10 -05:00
|
|
|
/// <summary>The handler specified to the constructor. This may be null.</summary>
|
|
|
|
|
private readonly Action<T>? _handler;
|
|
|
|
|
/// <summary>A cached delegate used to post invocation to the synchronization context.</summary>
|
|
|
|
|
private readonly SendOrPostCallback _invokeHandlers;
|
|
|
|
|
|
|
|
|
|
/// <summary>Initializes the <see cref="Progress{T}"/>.</summary>
|
|
|
|
|
public Progress()
|
|
|
|
|
{
|
|
|
|
|
// Capture the current synchronization context.
|
|
|
|
|
// If there is no current context, we use a default instance targeting the ThreadPool.
|
|
|
|
|
_synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext;
|
2026-01-25 17:27:42 -05:00
|
|
|
Debug.Assert(_synchronizationContext is not null);
|
2023-11-14 16:10:10 -05:00
|
|
|
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary>
|
|
|
|
|
/// <param name="handler">
|
|
|
|
|
/// A handler to invoke for each reported progress value. This handler will be invoked
|
|
|
|
|
/// in addition to any delegates registered with the <see cref="ProgressChanged"/> event.
|
|
|
|
|
/// Depending on the <see cref="SynchronizationContext"/> instance captured by
|
|
|
|
|
/// the <see cref="Progress{T}"/> at construction, it's possible that this handler instance
|
|
|
|
|
/// could be invoked concurrently with itself.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <exception cref="ArgumentNullException">The <paramref name="handler"/> is null (<see langword="Nothing" /> in Visual Basic).</exception>
|
|
|
|
|
public Progress(Action<T> handler) : this()
|
|
|
|
|
{
|
2023-11-21 10:17:25 -05:00
|
|
|
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
2023-11-14 16:10:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Raised for each reported progress value.</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// Handlers registered with this event will be invoked on the
|
|
|
|
|
/// <see cref="SynchronizationContext"/> captured when the instance was constructed.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public event EventHandler<T>? ProgressChanged;
|
|
|
|
|
|
|
|
|
|
/// <summary>Reports a progress change.</summary>
|
|
|
|
|
/// <param name="value">The value of the updated progress.</param>
|
|
|
|
|
protected virtual void OnReport(T value)
|
|
|
|
|
{
|
|
|
|
|
// If there's no handler, don't bother going through the sync context.
|
|
|
|
|
// Inside the callback, we'll need to check again, in case
|
|
|
|
|
// an event handler is removed between now and then.
|
|
|
|
|
Action<T>? handler = _handler;
|
|
|
|
|
EventHandler<T>? changedEvent = ProgressChanged;
|
2026-01-25 17:27:42 -05:00
|
|
|
if (handler is not null || changedEvent is not null)
|
2023-11-14 16:10:10 -05:00
|
|
|
{
|
|
|
|
|
// Post the processing to the sync context.
|
|
|
|
|
// (If T is a value type, it will get boxed here.)
|
2024-04-24 11:35:09 -04:00
|
|
|
_synchronizationContext?.Post(_invokeHandlers, value);
|
2023-11-14 16:10:10 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Reports a progress change.</summary>
|
|
|
|
|
/// <param name="value">The value of the updated progress.</param>
|
|
|
|
|
void IProgress<T>.Report(T value) { OnReport(value); }
|
|
|
|
|
|
|
|
|
|
/// <summary>Invokes the action and event callbacks.</summary>
|
|
|
|
|
/// <param name="state">The progress value.</param>
|
|
|
|
|
private void InvokeHandlers(object? state)
|
|
|
|
|
{
|
|
|
|
|
T value = (T)state!;
|
|
|
|
|
|
|
|
|
|
Action<T>? handler = _handler;
|
|
|
|
|
EventHandler<T>? changedEvent = ProgressChanged;
|
|
|
|
|
|
|
|
|
|
handler?.Invoke(value);
|
|
|
|
|
changedEvent?.Invoke(this, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Holds static values for <see cref="Progress{T}"/>.</summary>
|
|
|
|
|
/// <remarks>This avoids one static instance per type T.</remarks>
|
|
|
|
|
internal static class ProgressStatics
|
|
|
|
|
{
|
|
|
|
|
/// <summary>A default synchronization context that targets the ThreadPool.</summary>
|
2023-11-21 10:17:25 -05:00
|
|
|
internal static readonly SynchronizationContext DefaultContext = new();
|
2023-11-14 16:10:10 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-21 18:55:55 -04:00
|
|
|
#endif
|