Fix various warnings

This commit is contained in:
Ben Olden-Cooligan 2022-12-20 21:24:16 -08:00
parent 2fdc5fbbf9
commit 3cc922ecdd
39 changed files with 127 additions and 364 deletions

View File

@ -6,11 +6,12 @@ using Xunit;
namespace NAPS2.App.Tests.Appium;
#pragma warning disable CS0162
[Collection("appium")]
public class ScanAndSaveTests : AppiumTests
{
private const string WIA_DEVICE_NAME = "";
private const string TWAIN_DEVICE_NAME = "Canon MP495 ser";
private const string TWAIN_DEVICE_NAME = "";
[VerifyTheory(AllowDebug = true)]
[ClassData(typeof(AppiumTestData))]

View File

@ -69,7 +69,7 @@ public class EsclClient
response.EnsureSuccessStatusCode();
return new EsclJob
{
Uri = response.Headers.Location
Uri = response.Headers.Location!
};
}

View File

@ -1,6 +1,7 @@
// ReSharper disable once CheckNamespace
namespace NAPS2.Util;
#pragma warning disable CS0162
public class LeakTracer
{
private const bool ENABLE_TRACING = false;

View File

@ -52,7 +52,7 @@ public class ProfileManagerTests : ContextualTests
Assert.Single(profiles);
Assert.Equal("test_driver", profiles[0].DriverName);
Assert.Equal("test_id", profiles[0].Device?.ID);
Assert.Equal("test_id", profiles[0].Device?.Id);
Assert.Equal(2, profiles[0].Version);
Assert.Null(profiles[0].UpgradedFrom);
}
@ -67,7 +67,7 @@ public class ProfileManagerTests : ContextualTests
Assert.Single(profiles);
Assert.Equal("wia", profiles[0].DriverName);
Assert.Equal("test_id", profiles[0].Device?.ID);
Assert.Equal("test_id", profiles[0].Device?.Id);
Assert.Equal(ScanDpi.Dpi200, profiles[0].Resolution);
Assert.Equal(2, profiles[0].Version);
Assert.Equal(0, profiles[0].UpgradedFrom);
@ -83,7 +83,7 @@ public class ProfileManagerTests : ContextualTests
Assert.Single(profiles);
Assert.Equal("wia", profiles[0].DriverName);
Assert.Equal("test_id", profiles[0].Device?.ID);
Assert.Equal("test_id", profiles[0].Device?.Id);
Assert.Equal(ScanDpi.Dpi200, profiles[0].Resolution);
Assert.Equal(2, profiles[0].Version);
Assert.Equal(1, profiles[0].UpgradedFrom);
@ -99,7 +99,7 @@ public class ProfileManagerTests : ContextualTests
Assert.Single(profiles);
Assert.Equal("twain", profiles[0].DriverName);
Assert.Equal("test_id", profiles[0].Device?.ID);
Assert.Equal("test_id", profiles[0].Device?.Id);
Assert.True(profiles[0].UseNativeUI);
Assert.Equal(2, profiles[0].Version);
Assert.Equal(1, profiles[0].UpgradedFrom);

View File

@ -50,7 +50,7 @@ public static class WinFormsEntryPoint
application.MainForm = desktop;
desktop.Show();
var appContext = new wf.ApplicationContext(desktop.ToNative());
Invoker.Current = new WinFormsInvoker(appContext);
Invoker.Current = new WinFormsInvoker(() => appContext.MainForm!);
WinFormsDesktopForm.ApplicationContext = appContext;
var setOptionsMethod =
typeof(ApplicationHandler).GetMethod("SetOptions", BindingFlags.Instance | BindingFlags.NonPublic);

View File

@ -1,5 +1,6 @@
using System.Threading;
using System.Windows.Forms;
using NAPS2.EtoForms.WinForms;
using NAPS2.Modules;
using NAPS2.Scan.Internal.Twain;
using NAPS2.WinForms;
@ -22,7 +23,7 @@ public static class WindowsWorkerEntryPoint
// Set up a form for the worker process
// A parent form is needed for some operations, namely 64-bit TWAIN scanning
var form = new BackgroundForm();
Invoker.Current = form;
Invoker.Current = new WinFormsInvoker(() => form);
TwainHandleManager.Factory = () => new WinFormsTwainHandleManager(form);
return WorkerEntryPoint.Run(args, new GdiModule(), () => Application.Run(form), () => form.Close());

View File

@ -13,13 +13,12 @@ namespace NAPS2.EtoForms.Ui;
public class WinFormsDesktopForm : DesktopForm
{
public static wf.ApplicationContext ApplicationContext { get; set; }
public static wf.ApplicationContext? ApplicationContext { get; set; }
private readonly ToolbarFormatter _toolbarFormatter = new(new StringWrapper());
private readonly wf.Form _form;
private wf.ToolStrip _toolStrip = null!;
private wf.ToolStripContainer _container = null!;
private wf.Button btnZoomIn, btnZoomOut, btnZoomMouseCatcher;
public WinFormsDesktopForm(
Naps2Config config,
@ -67,14 +66,14 @@ public class WinFormsDesktopForm : DesktopForm
// Disabled buttons don't prevent click events from being sent to the listview below the button, so without this
// "mouse catcher" control you could e.g. spam click zoom out until it's maxed and then accidentally keep
// clicking and change the listview selection.
btnZoomMouseCatcher = new wf.Button
var mouseCatcher = new wf.Button
{
BackColor = Color.White,
Size = new Size(45, 23),
FlatStyle = wf.FlatStyle.Flat
};
return L.Overlay(
btnZoomMouseCatcher.ToEto(),
mouseCatcher.ToEto(),
base.GetZoomButtons()
);
}
@ -84,6 +83,11 @@ public class WinFormsDesktopForm : DesktopForm
protected override void SetMainForm(Form newMainForm)
{
base.SetMainForm(newMainForm);
if (ApplicationContext == null)
{
Log.Error("ApplicationContext should not be null");
return;
}
ApplicationContext.MainForm = newMainForm.ToSWF();
}

View File

@ -4,22 +4,22 @@ namespace NAPS2.EtoForms.WinForms;
public class WinFormsInvoker : IInvoker
{
private readonly ApplicationContext _appContext;
private readonly Func<Form> _formFunc;
public WinFormsInvoker(ApplicationContext appContext)
public WinFormsInvoker(Func<Form> formFunc)
{
_appContext = appContext;
_formFunc = formFunc;
}
public void Invoke(Action action)
{
_appContext.MainForm.Invoke(action);
_formFunc().Invoke(action);
}
// TODO: Maybe these can be extension methods?
public T InvokeGet<T>(Func<T> func)
{
T value = default;
T value = default!;
Invoke(() => value = func());
return value;
}

View File

@ -69,7 +69,7 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
private bool UseCustomRendering => !_behavior.ShowLabels && !_behavior.Checkboxes;
private void CustomRenderItem(object sender, DrawListViewItemEventArgs e)
private void CustomRenderItem(object? sender, DrawListViewItemEventArgs e)
{
int width, height;
var image = ImageList.Get(e.Item);

View File

@ -39,7 +39,8 @@ public class PrintDocumentPrinter : IScannedImagePrinter
return false;
}
public async Task<bool> Print(PrinterSettings printerSettings, IList<ProcessedImage> images, IList<ProcessedImage> selectedImages)
public async Task<bool> Print(PrinterSettings printerSettings, IList<ProcessedImage> images,
IList<ProcessedImage> selectedImages)
{
IList<ProcessedImage> imagesToPrint;
switch (printerSettings.PrintRange)
@ -87,7 +88,7 @@ public class PrintDocumentPrinter : IScannedImagePrinter
? new Rectangle(pb.Left, pb.Top, image.Width * pb.Height / image.Height, pb.Height)
: new Rectangle(pb.Left, pb.Top, pb.Width, image.Height * pb.Width / image.Width);
e.Graphics.DrawImage(image.AsBitmap(), rect);
e.Graphics!.DrawImage(image.AsBitmap(), rect);
}
finally
{
@ -98,7 +99,7 @@ public class PrintDocumentPrinter : IScannedImagePrinter
};
printDocument.PrinterSettings = printerSettings;
printDocument.Print();
Log.Event(EventType.Print, new EventParams
{
Name = MiscResources.Print,

View File

@ -44,7 +44,10 @@ internal static class TwainApi
do
{
string? name = tw.GetCurrentName();
result.Add(new ScanDevice(name, name));
if (name != null)
{
result.Add(new ScanDevice(name, name));
}
} while (tw.GetNext());
return result;
}
@ -56,7 +59,7 @@ internal static class TwainApi
{
throw new DeviceNotFoundException();
}
if (!tw.SelectByName(device.ID!))
if (!tw.SelectByName(device.Id!))
{
throw new DeviceNotFoundException();
}

View File

@ -238,7 +238,7 @@ internal class TwImageInfo
public int ImageLength;
public short SamplesPerPixel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public short[] BitsPerSample;
public short[]? BitsPerSample;
public short BitsPerPixel;
public short Planar;
public short PixelType;

View File

@ -5,7 +5,7 @@ namespace NAPS2.WinForms;
/// <summary>
/// A basic implementation of an invisible form.
/// </summary>
public class BackgroundForm : FormBase
public class BackgroundForm : Form
{
public BackgroundForm()
{

View File

@ -28,7 +28,7 @@ public class DragScrollListView : ListView
private int EdgeSize => Font.Height;
private void ListViewBase_DragOver(object sender, DragEventArgs e)
private void ListViewBase_DragOver(object? sender, DragEventArgs e)
{
Point position = PointToClient(new Point(e.X, e.Y));

View File

@ -1,14 +1,14 @@
using System.Windows.Forms;
namespace NAPS2.WinForms;
// TODO: Do we want to migrate this to Eto? Ideally we can figure out why Twain Legacy is needed for some circumstances
// and remove this form entirely...
internal partial class FTwainGui : FormBase
internal partial class FTwainGui : Form
{
public FTwainGui()
{
InitializeComponent();
SaveFormState = false;
RestoreFormState = false;
label1.Text = UiStrings.WaitingForTwain;
}
}

View File

@ -1,208 +0,0 @@
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;
using NAPS2.EtoForms;
using NAPS2.Scan;
namespace NAPS2.WinForms;
// TODO: Remove ConfigScopes.User dependency from reusable forms
public class FormBase : Form, IInvoker, IFormBase
{
private bool _loaded;
private FormState _formState;
public FormBase()
{
UpdateRTL();
RestoreFormState = true;
SaveFormState = true;
Load += OnLoadInternal;
Closed += OnClosed;
Resize += OnResize;
Move += OnMove;
}
public FormStateController FormStateController => throw new NotSupportedException();
public IFormFactory FormFactory { get; set; }
public Naps2Config Config { get; set; }
protected bool RestoreFormState { get; set; }
protected bool SaveFormState { get; set; }
#region Helper Methods
protected void AddEnumItems<T>(ComboBox combo)
{
AddEnumItems<T>(combo, Combo_Format);
}
protected void AddEnumItems<T>(ComboBox combo, Func<T, string> format)
{
AddEnumItems<T>(combo, (sender, e) => e.Value = format((T) e.ListItem));
}
protected void AddEnumItems<T>(ComboBox combo, ListControlConvertEventHandler format)
{
foreach (object item in Enum.GetValues(typeof(T)))
{
combo.Items.Add(item);
}
combo.Format += format;
}
void Combo_Format(object sender, ListControlConvertEventArgs e)
{
e.Value = ((Enum)e.ListItem).Description();
}
public void Invoke(Action action)
{
((Control) this).Invoke(action);
}
public T InvokeGet<T>(Func<T> func)
{
T value = default;
Invoke(() => value = func());
return value;
}
public void SafeInvoke(Action action)
{
try
{
Invoke(action);
}
catch (ObjectDisposedException)
{
}
catch (InvalidOperationException)
{
}
}
public void SafeInvokeAsync(Action action)
{
try
{
BeginInvoke(action);
}
catch (ObjectDisposedException)
{
}
catch (InvalidOperationException)
{
}
}
#endregion
protected void UpdateRTL()
{
bool isRTL = CultureInfo.CurrentCulture.TextInfo.IsRightToLeft;
RightToLeft = isRTL ? RightToLeft.Yes : RightToLeft.No;
RightToLeftLayout = isRTL;
}
/// <summary>
/// Descendant forms should override this instead of subscribing to the Load event when logic needs
/// to be performed before the form is resized (e.g. setting up LayoutManager).
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
protected virtual void OnLoad(object sender, EventArgs eventArgs)
{
}
protected virtual void AfterLoad(object sender, EventArgs eventArgs)
{
}
#region Event Handlers
private void OnLoadInternal(object sender, EventArgs eventArgs)
{
OnLoad(this, EventArgs.Empty);
if (DesignMode)
{
RestoreFormState = SaveFormState = false;
}
if (RestoreFormState || SaveFormState)
{
var formStates = Config.Get(c => c.FormStates);
_formState = formStates.SingleOrDefault(x => x.Name == Name) ?? new FormState {Name = Name};
}
if (RestoreFormState)
{
DoRestoreFormState();
}
_loaded = true;
AfterLoad(this, EventArgs.Empty);
}
protected void DoRestoreFormState()
{
var location = new Point(_formState.Location.X, _formState.Location.Y);
var size = new Size(_formState.Size.Width, _formState.Size.Height);
if (!location.IsEmpty)
{
if (Screen.AllScreens.Any(x => x.WorkingArea.Contains(location)))
{
// Only move to the specified location if it's onscreen
// It might be offscreen if the user has disconnected a monitor
Location = location;
}
}
if (!size.IsEmpty)
{
Size = size;
}
if (_formState.Maximized)
{
WindowState = FormWindowState.Maximized;
}
}
private void OnResize(object sender, EventArgs eventArgs)
{
if (_loaded && SaveFormState)
{
_formState.Maximized = (WindowState == FormWindowState.Maximized);
if (WindowState == FormWindowState.Normal)
{
_formState.Size = new FormState.FormSize(Size.Width, Size.Height);
}
}
}
private void OnMove(object sender, EventArgs eventArgs)
{
if (_loaded && SaveFormState)
{
if (WindowState == FormWindowState.Normal)
{
_formState.Location = new FormState.FormLocation(Location.X, Location.Y);
}
}
}
private void OnClosed(object sender, EventArgs eventArgs)
{
if (SaveFormState && _formState != null)
{
var formStates = Config.Get(c => c.FormStates);
formStates = formStates.RemoveAll(fs => fs.Name == Name).Add(_formState);
Config.User.Set(c => c.FormStates, formStates);
}
}
#endregion
}

View File

@ -13,7 +13,7 @@ public class NotificationManager : INotificationManager
private const int SPACING_Y = 20;
private readonly Naps2Config _config;
private readonly List<NotifyWidgetBase> _slots = new();
private readonly List<NotifyWidgetBase?> _slots = new();
private wf.Form? _parentForm;
public NotificationManager(Naps2Config config, DesktopFormProvider desktopFormProvider)
@ -68,9 +68,10 @@ public class NotificationManager : INotificationManager
_slots.Clear();
for (int i = 0; i < old.Count; i++)
{
if (old[i] != null)
var slot = old[i];
if (slot != null)
{
Show(old[i].Clone());
Show(slot.Clone());
}
}
}
@ -93,13 +94,14 @@ public class NotificationManager : INotificationManager
});
}
private void parentForm_Resize(object sender, EventArgs e)
private void parentForm_Resize(object? sender, EventArgs e)
{
for (int i = 0; i < _slots.Count; i++)
{
if (_slots[i] != null)
var slot = _slots[i];
if (slot != null)
{
_slots[i].Location = GetPosition(_slots[i], i);
slot.Location = GetPosition(slot, i);
}
}
}

View File

@ -4,10 +4,10 @@ namespace NAPS2.WinForms;
public partial class NotifyWidget : NotifyWidgetBase
{
private readonly string _linkTarget;
private readonly string? _linkTarget;
private readonly string? _folderTarget;
public NotifyWidget(string title, string linkLabel, string linkTarget, string? folderTarget)
public NotifyWidget(string title, string linkLabel, string? linkTarget, string? folderTarget)
{
_linkTarget = linkTarget;
_folderTarget = folderTarget;
@ -66,6 +66,11 @@ public partial class NotifyWidget : NotifyWidgetBase
protected virtual void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (_linkTarget == null)
{
Log.Error("Link target should not be null");
return;
}
if (e.Button == MouseButtons.Right)
{
contextMenuStrip1.Show(linkLabel1, linkLabel1.Location);

View File

@ -4,35 +4,16 @@ namespace NAPS2.WinForms;
public class NotifyWidgetBase : UserControl
{
public event EventHandler HideNotify;
public event EventHandler? HideNotify;
protected void InvokeHideNotify()
{
SafeInvoke(() => HideNotify?.Invoke(this, new EventArgs()));
Invoker.Current.SafeInvoke(() => HideNotify?.Invoke(this, EventArgs.Empty));
}
public virtual void ShowNotify()
{
}
public void Invoke(Action action)
{
((Control)this).Invoke(action);
}
public void SafeInvoke(Action action)
{
try
{
Invoke(action);
}
catch (ObjectDisposedException)
{
}
catch (InvalidOperationException)
{
}
}
public virtual NotifyWidgetBase Clone() => throw new NotImplementedException();
}

View File

@ -47,23 +47,23 @@ public partial class OperationProgressNotifyWidget : NotifyWidgetBase
InvokeHideNotify();
}
private void Op_StatusChanged(object sender, EventArgs e)
private void Op_StatusChanged(object? sender, EventArgs e)
{
SafeInvoke(DisplayProgress);
Invoker.Current.SafeInvoke(DisplayProgress);
}
private void Op_Finished(object sender, EventArgs e)
private void Op_Finished(object? sender, EventArgs e)
{
DoHideNotify();
}
private void cancelToolStripMenuItem_Click(object sender, EventArgs e)
private void cancelToolStripMenuItem_Click(object? sender, EventArgs e)
{
_op.Cancel();
cancelToolStripMenuItem.Enabled = false;
}
private void OperationProgressNotifyWidget_Click(object sender, MouseEventArgs e)
private void OperationProgressNotifyWidget_Click(object? sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{

View File

@ -16,7 +16,7 @@ public class SelectableListView<T> : ISelectable<T> where T : notnull
public event EventHandler? SelectionChanged;
private void ListViewOnSelectedIndexChanged(object sender, EventArgs e)
private void ListViewOnSelectedIndexChanged(object? sender, EventArgs e)
{
if (!_refreshing)
{

View File

@ -8,29 +8,27 @@ public class ToolStripDoubleButton : ToolStripButton
{
private int _currentButton = -1;
public ToolStripDoubleButton()
{
}
public event EventHandler? FirstClick;
public event EventHandler? SecondClick;
public event EventHandler FirstClick;
public event EventHandler SecondClick;
public Image FirstImage { get; set; }
public Image SecondImage { get; set; }
public required Image FirstImage { get; init; }
public required Image SecondImage { get; init; }
[Localizable(true)]
public string FirstText { get; set; }
[Localizable(true)]
public string SecondText { get; set; }
public required string FirstText { get; init; }
public int MaxTextWidth { get; set; }
[Localizable(true)]
public required string SecondText { get; init; }
public int MaxTextWidth { get; init; }
public override Size GetPreferredSize(Size constrainingSize)
{
bool wrap = false;
var sumWidth = Padding.Left + Padding.Right
+ Math.Max(FirstImage?.Width ?? 0, SecondImage?.Width ?? 0)
+ Math.Max(MeasureTextWidth(FirstText, ref wrap), MeasureTextWidth(SecondText, ref wrap));
+ Math.Max(MeasureTextWidth(FirstText, ref wrap),
MeasureTextWidth(SecondText, ref wrap));
var sumHeight = Padding.Top + Padding.Bottom
+ (FirstImage?.Height ?? 0) + (SecondImage?.Height ?? 0)
+ 16 + (wrap ? 12 : 0);
@ -40,7 +38,7 @@ public class ToolStripDoubleButton : ToolStripButton
private int MeasureTextWidth(string text, ref bool wrap)
{
using var g = Graphics.FromImage(new Bitmap(1, 1));
var width = (int)Math.Ceiling(g.MeasureString(text, Font).Width);
var width = (int) Math.Ceiling(g.MeasureString(text, Font).Width);
if (MaxTextWidth > 0 && width > MaxTextWidth)
{
var words = text.Split(' ');
@ -48,7 +46,7 @@ public class ToolStripDoubleButton : ToolStripButton
{
var left = string.Join(" ", words.Take(words.Length - i));
var right = string.Join(" ", words.Skip(words.Length - i));
var wrappedWidth = (int)Math.Ceiling(g.MeasureString(left + "\n" + right, Font).Width);
var wrappedWidth = (int) Math.Ceiling(g.MeasureString(left + "\n" + right, Font).Width);
if (wrappedWidth < width)
{
width = wrappedWidth;
@ -83,35 +81,31 @@ public class ToolStripDoubleButton : ToolStripButton
flags |= TextFormatFlags.WordBreak;
}
if (FirstImage != null && FirstText != null)
if (Enabled)
{
if (Enabled)
{
e.Graphics.DrawImage(FirstImage, new Point(Padding.Left, Height / 4 - FirstImage.Height / 2 + 1));
}
else
{
ControlPaint.DrawImageDisabled(e.Graphics, FirstImage, Padding.Left, Height / 4 - FirstImage.Height / 2 + 1, Color.Transparent);
}
var textRectangle = new Rectangle(Padding.Left + FirstImage.Width, 0, textWidth, Height / 2);
renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(e.Graphics, this, FirstText, textRectangle, ForeColor, Font, flags));
e.Graphics.DrawImage(FirstImage, new Point(Padding.Left, Height / 4 - FirstImage.Height / 2 + 1));
}
if (SecondImage != null && SecondText != null)
else
{
if (Enabled)
{
e.Graphics.DrawImage(SecondImage, new Point(Padding.Left, Height * 3 / 4 - SecondImage.Height / 2));
}
else
{
ControlPaint.DrawImageDisabled(e.Graphics, SecondImage, Padding.Left, Height * 3 / 4 - SecondImage.Height / 2, Color.Transparent);
}
var textRectangle = new Rectangle(Padding.Left + SecondImage.Width, Height / 2, textWidth, Height / 2);
renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(e.Graphics, this, SecondText, textRectangle, ForeColor, Font, flags));
ControlPaint.DrawImageDisabled(e.Graphics, FirstImage, Padding.Left, Height / 4 - FirstImage.Height / 2 + 1,
Color.Transparent);
}
var textRectangle1 = new Rectangle(Padding.Left + FirstImage.Width, 0, textWidth, Height / 2);
renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(e.Graphics, this, FirstText, textRectangle1,
ForeColor, Font, flags));
if (Enabled)
{
e.Graphics.DrawImage(SecondImage, new Point(Padding.Left, Height * 3 / 4 - SecondImage.Height / 2));
}
else
{
ControlPaint.DrawImageDisabled(e.Graphics, SecondImage, Padding.Left,
Height * 3 / 4 - SecondImage.Height / 2, Color.Transparent);
}
var textRectangle2 = new Rectangle(Padding.Left + SecondImage.Width, Height / 2, textWidth, Height / 2);
renderer.DrawItemText(new ToolStripItemTextRenderEventArgs(e.Graphics, this, SecondText, textRectangle2,
ForeColor, Font, flags));
Image = null;
}

View File

@ -44,7 +44,7 @@ public class DesktopScanController : IDesktopScanController
{
_desktopFormProvider.DesktopForm.BringToFront();
ScanProfile? profile;
if (_profileManager.DefaultProfile?.Device?.ID == deviceID)
if (_profileManager.DefaultProfile?.Device?.Id == deviceID)
{
// Try to use the default profile if it has the right device
profile = _profileManager.DefaultProfile;
@ -53,7 +53,7 @@ public class DesktopScanController : IDesktopScanController
{
// Otherwise just pick any old profile with the right device
// Not sure if this is the best way to do it, but it's hard to prioritize profiles
profile = _profileManager.Profiles.FirstOrDefault(x => x.Device != null && x.Device.ID == deviceID);
profile = _profileManager.Profiles.FirstOrDefault(x => x.Device != null && x.Device.Id == deviceID);
}
if (profile == null)
{

View File

@ -19,7 +19,7 @@ public class SelectDeviceForm : EtoDialogBase
{
_devices.Items.Add(new ListItem
{
Key = device.ID,
Key = device.Id,
Text = device.Name
});
}
@ -54,6 +54,6 @@ public class SelectDeviceForm : EtoDialogBase
_devices.Focus();
return;
}
SelectedDevice = DeviceList.FirstOrDefault(x => x.ID == _devices.SelectedKey);
SelectedDevice = DeviceList.FirstOrDefault(x => x.Id == _devices.SelectedKey);
}
}

View File

@ -251,7 +251,7 @@ internal class ScanPerformer : IScanPerformer
private async Task<bool> PopulateDevice(ScanProfile scanProfile, ScanOptions options)
{
// If a device wasn't specified, prompt the user to pick one
if (string.IsNullOrEmpty(scanProfile.Device?.ID))
if (string.IsNullOrEmpty(scanProfile.Device?.Id))
{
options.Device = await PromptForDevice(options);
if (options.Device == null)

View File

@ -2,6 +2,8 @@
<PropertyGroup>
<TargetFrameworks>net6;net462</TargetFrameworks>
<!-- As these are just samples, no need for "windows only api" warnings for GdiImageContext -->
<NoWarn>CA1416</NoWarn>
</PropertyGroup>
<Import Project="..\NAPS2.Setup\targets\CommonTargets.targets" />

View File

@ -23,12 +23,12 @@ public class RemoteScanControllerTests : ContextualTests
var deviceList = await controller.GetDeviceList(new ScanOptions { Driver = Driver.Wia });
Assert.Equal(2, deviceList.Count);
Assert.Equal("test_id1", deviceList[0].ID);
Assert.Equal("WIA-test_id2", deviceList[1].ID);
Assert.Equal("test_id1", deviceList[0].Id);
Assert.Equal("WIA-test_id2", deviceList[1].Id);
deviceList = await controller.GetDeviceList(new ScanOptions { Driver = Driver.Twain });
Assert.Single(deviceList);
Assert.Equal("test_id1", deviceList[0].ID);
Assert.Equal("test_id1", deviceList[0].Id);
deviceList = await controller.GetDeviceList(new ScanOptions
{ Driver = Driver.Twain, TwainOptions = { IncludeWiaDevices = true } });

View File

@ -45,7 +45,7 @@ public class ScanErrorHandling : ContextualTests
controller.PropagateErrors = true;
bridgeFactory.Setup(factory => factory.Create(It.IsAny<ScanOptions>())).Throws<InvalidOperationException>();
var source = controller.Scan(new ScanOptions { Device = new ScanDevice { ID = "blah" } });
var source = controller.Scan(new ScanOptions { Device = new ScanDevice("foo", "bar") });
await Assert.ThrowsAsync<InvalidOperationException>(async () => await source.ToListAsync());
}
@ -91,7 +91,7 @@ public class ScanErrorHandling : ContextualTests
localPostProcessor.Setup(pp =>
pp.PostProcess(It.IsAny<ProcessedImage>(), It.IsAny<ScanOptions>(), It.IsAny<PostProcessingContext>()))
.Throws<InvalidOperationException>();
var source = controller.Scan(new ScanOptions { Device = new ScanDevice { ID = "blah" } });
var source = controller.Scan(new ScanOptions { Device = new ScanDevice("foo", "bar") });
await Assert.ThrowsAsync<InvalidOperationException>(async () => await source.ToListAsync());
}
@ -107,7 +107,7 @@ public class ScanErrorHandling : ContextualTests
controller.PropagateErrors = true;
bridgeFactory.Setup(factory => factory.Create(It.IsAny<ScanOptions>())).Returns(bridge);
var source = controller.Scan(new ScanOptions { Device = new ScanDevice { ID = "blah" } });
var source = controller.Scan(new ScanOptions { Device = new ScanDevice("foo", "bar") });
await Assert.ThrowsAsync<InvalidOperationException>(async () => await source.ToListAsync());
}

View File

@ -71,7 +71,7 @@ public class WorkerChannelTests : ContextualTests
var deviceList = await channel.Client.GetDeviceList(new ScanOptions());
Assert.Single(deviceList);
Assert.Equal("test_id", deviceList[0].ID);
Assert.Equal("test_id", deviceList[0].Id);
Assert.Equal("test_name", deviceList[0].Name);
remoteScanController.Verify(rsc => rsc.GetDeviceList(It.IsAny<ScanOptions>()));
remoteScanController.VerifyNoOtherCalls();

View File

@ -19,7 +19,10 @@ internal class AppleScanDriver : IScanDriver
using var reader = new DeviceReader();
reader.Start();
await Task.Delay(2000);
return reader.Devices.Select(x => new ScanDevice(x.Uuid, x.Name)).ToList();
return reader.Devices
.Where(x => x.Uuid != null && x.Name != null)
.Select(x => new ScanDevice(x.Uuid!, x.Name!))
.ToList();
}
public async Task Scan(ScanOptions options, CancellationToken cancelToken, IScanEvents scanEvents,
@ -37,7 +40,7 @@ internal class AppleScanDriver : IScanDriver
var tcs = new TaskCompletionSource<ICScannerDevice>();
reader.DeviceFound += (_, args) =>
{
if (args.Device.Uuid == scanDevice.ID)
if (args.Device.Uuid == scanDevice.Id)
{
tcs.TrySetResult(args.Device);
}

View File

@ -69,7 +69,7 @@ internal class DeviceOperator : ICScannerDeviceDelegate
public override void DidEncounterError(ICDevice device, NSError? error)
{
var ex = new DeviceException(error.Description);
var ex = error != null ? new DeviceException(error.Description) : new DeviceException();
// TODO: Put these in a list or something
_openSessionTcs.TrySetException(ex);
_readyTcs.TrySetException(ex);

View File

@ -23,7 +23,7 @@ internal class RemoteScanController : IRemoteScanController
var deviceList = await _scanDriverFactory.Create(options).GetDeviceList(options);
if (options.Driver == Driver.Twain && !options.TwainOptions.IncludeWiaDevices)
{
deviceList = deviceList.Where(x => !x.ID.StartsWith("WIA-", StringComparison.InvariantCulture)).ToList();
deviceList = deviceList.Where(x => !x.Id.StartsWith("WIA-", StringComparison.InvariantCulture)).ToList();
}
return deviceList;
}

View File

@ -73,7 +73,7 @@ internal class SaneScanDriver : IScanDriver
{
using var client = new SaneClient();
if (cancelToken.IsCancellationRequested) return;
using var device = client.OpenDevice(options.Device!.ID!);
using var device = client.OpenDevice(options.Device!.Id!);
if (cancelToken.IsCancellationRequested) return;
SetOptions(device, options);
// TODO: We apparently need to cancel even upon normal completion, i.e. one sane_cancel per sane_start

View File

@ -18,7 +18,7 @@ public class ScanOptionsValidator
if (requireDevice)
{
if (string.IsNullOrEmpty(options.Device?.ID))
if (string.IsNullOrEmpty(options.Device?.Id))
{
throw new ArgumentException("ScanOptions.Device.ID must be specified");
}

View File

@ -29,7 +29,7 @@ internal class TwainProgressEstimator
private static TimingKey GetTimingKey(ScanOptions options)
{
return new TimingKey(options.Device!.ID!, options.BitDepth, options.PageSize!);
return new TimingKey(options.Device!.Id!, options.BitDepth, options.PageSize!);
}
public TwainProgressEstimator(ScanOptions options, IScanEvents scanEvents)

View File

@ -67,7 +67,7 @@ internal class TwainSessionScanRunner
}
Debug.WriteLine("NAPS2.TW - Finding source");
_source = _session.FirstOrDefault(x => x.Name == _options.Device!.ID);
_source = _session.FirstOrDefault(x => x.Name == _options.Device!.Id);
if (_source == null)
{
throw new DeviceNotFoundException();

View File

@ -79,7 +79,7 @@ internal class WiaScanDriver : IScanDriver
public async Task Scan(WiaVersion wiaVersion)
{
using var deviceManager = new WiaDeviceManager(wiaVersion);
using var device = deviceManager.FindDevice(_options.Device!.ID!);
using var device = deviceManager.FindDevice(_options.Device!.Id!);
if (device.Version == WiaVersion.Wia20 && _options.UseNativeUI)
{
await DoWia20NativeTransfer(deviceManager, device);

View File

@ -1,22 +1,6 @@
namespace NAPS2.Scan;
// TODO: Can we make this a record and/or make properties non-nullable?
/// <summary>
/// The representation of a scanning device identified by a driver.
/// </summary>
public class ScanDevice
{
public ScanDevice(string? id, string? name)
{
ID = id;
Name = name;
}
public ScanDevice()
{
}
public string? ID { get; set; }
public string? Name { get; set; }
}
public record ScanDevice(string Id, string Name);

View File

@ -29,7 +29,7 @@ public class AssemblyHelper
private static string GetAssemblyAttributeValue<T>(Func<T, string> selector)
{
object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(T), false);
object[] attributes = Assembly.GetEntryAssembly()!.GetCustomAttributes(typeof(T), false);
if (attributes.Length == 0)
{
return "";
@ -37,20 +37,9 @@ public class AssemblyHelper
return selector((T) attributes[0]);
}
public static string Title
{
get
{
string title = GetAssemblyAttributeValue<AssemblyTitleAttribute>(x => x.Title);
if (string.IsNullOrEmpty(title))
{
title = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().CodeBase);
}
return title;
}
}
public static string Title => GetAssemblyAttributeValue<AssemblyTitleAttribute>(x => x.Title);
public static string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
public static string Version => Assembly.GetEntryAssembly()!.GetName().Version!.ToString();
public static string Description => GetAssemblyAttributeValue<AssemblyDescriptionAttribute>(x => x.Description);