diff --git a/NTwain.sln b/NTwain.sln
index 762ecc0..d31af3b 100644
--- a/NTwain.sln
+++ b/NTwain.sln
@@ -30,6 +30,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "csizes", "csizes\csizes.vcx
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinConsole32", "samples\WinConsole32\WinConsole32.csproj", "{4E2417E7-FDC3-46D7-B976-84A97B500B74}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinUI3", "samples\WinUI3\WinUI3.csproj", "{F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -122,6 +124,30 @@ Global
{4E2417E7-FDC3-46D7-B976-84A97B500B74}.Release|x64.Build.0 = Release|Any CPU
{4E2417E7-FDC3-46D7-B976-84A97B500B74}.Release|x86.ActiveCfg = Release|Any CPU
{4E2417E7-FDC3-46D7-B976-84A97B500B74}.Release|x86.Build.0 = Release|Any CPU
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|Any CPU.Build.0 = Debug|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|Any CPU.Deploy.0 = Debug|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|ARM64.Build.0 = Debug|ARM64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|x64.ActiveCfg = Debug|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|x64.Build.0 = Debug|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|x64.Deploy.0 = Debug|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|x86.ActiveCfg = Debug|x86
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|x86.Build.0 = Debug|x86
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Debug|x86.Deploy.0 = Debug|x86
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|Any CPU.ActiveCfg = Release|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|Any CPU.Build.0 = Release|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|Any CPU.Deploy.0 = Release|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|ARM64.ActiveCfg = Release|ARM64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|ARM64.Build.0 = Release|ARM64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|ARM64.Deploy.0 = Release|ARM64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|x64.ActiveCfg = Release|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|x64.Build.0 = Release|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|x64.Deploy.0 = Release|x64
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|x86.ActiveCfg = Release|x86
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|x86.Build.0 = Release|x86
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -130,6 +156,7 @@ Global
{7792A94E-D0B4-440D-8BD5-CA1CA548782C} = {707B4313-8EF8-4D0F-A95E-590783422187}
{C9666CB2-C9A6-48C8-AB51-D616A48058A7} = {707B4313-8EF8-4D0F-A95E-590783422187}
{4E2417E7-FDC3-46D7-B976-84A97B500B74} = {707B4313-8EF8-4D0F-A95E-590783422187}
+ {F94F91C8-ABC7-4CBC-9D2E-2EAC5DA8974E} = {707B4313-8EF8-4D0F-A95E-590783422187}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7459323B-44F6-4E07-8574-E1B4B525086B}
diff --git a/samples/WinUI3/App.xaml b/samples/WinUI3/App.xaml
new file mode 100644
index 0000000..9ecff01
--- /dev/null
+++ b/samples/WinUI3/App.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/WinUI3/App.xaml.cs b/samples/WinUI3/App.xaml.cs
new file mode 100644
index 0000000..45add8e
--- /dev/null
+++ b/samples/WinUI3/App.xaml.cs
@@ -0,0 +1,50 @@
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using Microsoft.UI.Xaml.Shapes;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Activation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace WinUI3
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public partial class App : Application
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ ///
+ /// Invoked when the application is launched.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
+ {
+ m_window = new MainWindow();
+ m_window.Activate();
+ }
+
+ private Window m_window;
+ }
+}
diff --git a/samples/WinUI3/Assets/LockScreenLogo.scale-200.png b/samples/WinUI3/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000..7440f0d
Binary files /dev/null and b/samples/WinUI3/Assets/LockScreenLogo.scale-200.png differ
diff --git a/samples/WinUI3/Assets/SplashScreen.scale-200.png b/samples/WinUI3/Assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000..32f486a
Binary files /dev/null and b/samples/WinUI3/Assets/SplashScreen.scale-200.png differ
diff --git a/samples/WinUI3/Assets/Square150x150Logo.scale-200.png b/samples/WinUI3/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..53ee377
Binary files /dev/null and b/samples/WinUI3/Assets/Square150x150Logo.scale-200.png differ
diff --git a/samples/WinUI3/Assets/Square44x44Logo.scale-200.png b/samples/WinUI3/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..f713bba
Binary files /dev/null and b/samples/WinUI3/Assets/Square44x44Logo.scale-200.png differ
diff --git a/samples/WinUI3/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/samples/WinUI3/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000..dc9f5be
Binary files /dev/null and b/samples/WinUI3/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/samples/WinUI3/Assets/StoreLogo.png b/samples/WinUI3/Assets/StoreLogo.png
new file mode 100644
index 0000000..a4586f2
Binary files /dev/null and b/samples/WinUI3/Assets/StoreLogo.png differ
diff --git a/samples/WinUI3/Assets/Wide310x150Logo.scale-200.png b/samples/WinUI3/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..8b4a5d0
Binary files /dev/null and b/samples/WinUI3/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/samples/WinUI3/MainWindow.xaml b/samples/WinUI3/MainWindow.xaml
new file mode 100644
index 0000000..6bf6ea2
--- /dev/null
+++ b/samples/WinUI3/MainWindow.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/samples/WinUI3/MainWindow.xaml.cs b/samples/WinUI3/MainWindow.xaml.cs
new file mode 100644
index 0000000..9dcd888
--- /dev/null
+++ b/samples/WinUI3/MainWindow.xaml.cs
@@ -0,0 +1,224 @@
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using NTwain;
+using NTwain.Data;
+using System;
+using System.Diagnostics;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace WinUI3
+{
+ ///
+ /// An empty window that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MainWindow : Window
+ {
+ TwainAppSession session;
+ readonly int _jpegQuality = 75;
+ readonly string saveFolder;
+ readonly Stopwatch watch = new();
+ readonly ImageCodecInfo _jpegEncoder;
+ readonly EncoderParameters _jpegParameters;
+ bool _useThreadForImag;
+ bool _useSystemDrawing;
+ bool _saveDisk;
+
+ public MainWindow()
+ {
+ this.InitializeComponent();
+
+ var libVer = FileVersionInfo.GetVersionInfo(typeof(TwainAppSession).Assembly.Location).ProductVersion;
+ Title = $"WinUI3 sample {(TWPlatform.Is32bit ? " 32bit" : " 64bit")} on NTwain {libVer}";
+
+ saveFolder = Path.Combine(Path.GetTempPath(), "ntwain-sample" + Path.DirectorySeparatorChar);
+ Directory.CreateDirectory(saveFolder);
+
+ session = new TwainAppSession();
+
+ session.StateChanged += Session_StateChanged;
+ session.SourceDisabled += Session_SourceDisabled;
+ session.TransferReady += Session_TransferReady;
+ session.Transferred += Session_Transferred;
+ session.TransferError += Session_TransferError;
+
+ this.Closed += MainWindow_Closed;
+
+ _jpegParameters = new EncoderParameters(1);
+ _jpegParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)_jpegQuality);
+ _jpegEncoder = ImageCodecInfo.GetImageEncoders().First(enc => enc.FormatID == ImageFormat.Jpeg.Guid);
+ }
+
+ private void Session_TransferError(TwainAppSession sender, TransferErrorEventArgs e)
+ {
+ if (e.Exception != null)
+ {
+ Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] transfer error {e.Exception}.");
+ }
+ else
+ {
+ Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] transfer error {e.Code}.");
+ }
+ }
+
+ private void MainWindow_Closed(object sender, WindowEventArgs args)
+ {
+ session.TryStepdown(STATE.S2);
+ }
+
+ private void Session_TransferReady(TwainAppSession sender, TransferReadyEventArgs e)
+ {
+ Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] transfer ready.");
+ }
+ private void Session_Transferred(TwainAppSession sender, TransferredEventArgs e)
+ {
+ Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] data transferred with info {e.ImageInfo}");
+ // if using a high-speed scanner, imaging handling could be a bottleneck
+ // so it's possible to pass the data to another thread while the scanning
+ // loop happens. Just remember to dispose it after.
+
+ if (_useThreadForImag)
+ {
+ // bad thread example but whatev. should use a dedicated thread of some sort for real
+ Task.Run(() =>
+ {
+ HandleTransferredData(e);
+ });
+ }
+ else
+ {
+ HandleTransferredData(e);
+ }
+ }
+ private void HandleTransferredData(TransferredEventArgs e)
+ {
+ try
+ {
+ // example of using some lib to handle image data
+ var saveFile = Path.Combine(saveFolder, (DateTime.Now.Ticks / 1000).ToString());
+ if (_useSystemDrawing)
+ {
+ using (var img = System.Drawing.Image.FromStream(e.Data.AsStream()))
+ {
+ if (img.PixelFormat == System.Drawing.Imaging.PixelFormat.Format1bppIndexed ||
+ img.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
+ {
+ // bw or gray
+ saveFile += ".png";
+ if (_saveDisk)
+ {
+ img.Save(saveFile, ImageFormat.Png);
+ Debug.WriteLine($"Saved image to {saveFile}");
+ }
+ else img.Save(new NoOpStream(), ImageFormat.Png);
+ }
+ else
+ {
+ // color
+ saveFile += ".jpg";
+ if (_saveDisk)
+ {
+ img.Save(saveFile, _jpegEncoder, _jpegParameters);
+ Debug.WriteLine($"Saved image to {saveFile}");
+ }
+ else img.Save(new NoOpStream(), _jpegEncoder, _jpegParameters);
+ }
+ }
+ }
+ else
+ {
+ using (var stream = e.Data?.AsStream())
+ {
+ using (var img = new ImageMagick.MagickImage(e.Data.AsSpan()))
+ {
+ var format = ImageMagick.MagickFormat.Png;
+ if (img.ColorType == ImageMagick.ColorType.Palette)
+ {
+ // bw or gray
+ saveFile += ".png";
+ }
+ else
+ {
+ // color
+ saveFile += ".jpg";
+ format = ImageMagick.MagickFormat.Jpeg;
+ img.Quality = _jpegQuality;
+ }
+ if (_saveDisk)
+ {
+ img.Write(saveFile, format);
+ Debug.WriteLine($"Saved image to {saveFile}");
+ }
+ else img.Write(new NoOpStream(), format);
+ }
+ }
+ }
+ }
+ catch { }
+ finally
+ {
+ e.Dispose();
+ }
+ }
+
+ private void Session_SourceDisabled(TwainAppSession sender, TW_IDENTITY_LEGACY e)
+ {
+ session.CloseSource();
+ DispatcherQueue.TryEnqueue(() =>
+ {
+ if (watch.IsRunning)
+ {
+ watch.Stop();
+ var dlg = new ContentDialog
+ {
+ Title = "Completed",
+ Content = $"Took {watch.Elapsed} to finish that transfer.",
+ CloseButtonText = "OK",
+ XamlRoot = Content.XamlRoot
+ };
+ _ = dlg.ShowAsync();
+
+ if (_saveDisk)
+ {
+ try
+ {
+ if (Directory.Exists(saveFolder))
+ {
+ using (Process.Start(new ProcessStartInfo { FileName = saveFolder, UseShellExecute = true })) { }
+ }
+ }
+ catch { }
+ }
+ }
+ });
+ }
+
+ private void Session_StateChanged(TwainAppSession sender, STATE e)
+ {
+
+ }
+
+ private async void myButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (session.State < STATE.S3)
+ await session.OpenDSMAsync();
+
+ if (session.ShowUserSelect().IsSuccess)
+ {
+ if (session.OpenSource(session.CurrentSource).IsSuccess)
+ {
+ if (session.EnableSource(true, false).IsSuccess)
+ {
+ _saveDisk = true;
+ watch.Restart();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/samples/WinUI3/NoOpStream.cs b/samples/WinUI3/NoOpStream.cs
new file mode 100644
index 0000000..aa2dcb8
--- /dev/null
+++ b/samples/WinUI3/NoOpStream.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+
+namespace WinUI3
+{
+ internal class NoOpStream : Stream
+ {
+ public override bool CanRead => false;
+
+ public override bool CanSeek => false;
+
+ public override bool CanWrite => true;
+
+ public override long Length => 0;
+
+ public override long Position { get => 0; set { } }
+
+ public override void Flush()
+ {
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/WinUI3/Package.appxmanifest b/samples/WinUI3/Package.appxmanifest
new file mode 100644
index 0000000..ba92253
--- /dev/null
+++ b/samples/WinUI3/Package.appxmanifest
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+ WinUI3
+ yinch
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/WinUI3/Properties/PublishProfiles/win10-arm64.pubxml b/samples/WinUI3/Properties/PublishProfiles/win10-arm64.pubxml
new file mode 100644
index 0000000..a7fdd16
--- /dev/null
+++ b/samples/WinUI3/Properties/PublishProfiles/win10-arm64.pubxml
@@ -0,0 +1,20 @@
+
+
+
+
+ FileSystem
+ ARM64
+ win10-arm64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+
+
+
\ No newline at end of file
diff --git a/samples/WinUI3/Properties/PublishProfiles/win10-x64.pubxml b/samples/WinUI3/Properties/PublishProfiles/win10-x64.pubxml
new file mode 100644
index 0000000..26ea7e5
--- /dev/null
+++ b/samples/WinUI3/Properties/PublishProfiles/win10-x64.pubxml
@@ -0,0 +1,20 @@
+
+
+
+
+ FileSystem
+ x64
+ win10-x64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+
+
+
\ No newline at end of file
diff --git a/samples/WinUI3/Properties/PublishProfiles/win10-x86.pubxml b/samples/WinUI3/Properties/PublishProfiles/win10-x86.pubxml
new file mode 100644
index 0000000..34d14d4
--- /dev/null
+++ b/samples/WinUI3/Properties/PublishProfiles/win10-x86.pubxml
@@ -0,0 +1,20 @@
+
+
+
+
+ FileSystem
+ x86
+ win10-x86
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+
+
+
\ No newline at end of file
diff --git a/samples/WinUI3/Properties/launchSettings.json b/samples/WinUI3/Properties/launchSettings.json
new file mode 100644
index 0000000..f3be64d
--- /dev/null
+++ b/samples/WinUI3/Properties/launchSettings.json
@@ -0,0 +1,10 @@
+{
+ "profiles": {
+ "WinUI3 (Package)": {
+ "commandName": "MsixPackage"
+ },
+ "WinUI3 (Unpackaged)": {
+ "commandName": "Project"
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/WinUI3/WinUI3.csproj b/samples/WinUI3/WinUI3.csproj
new file mode 100644
index 0000000..c870cce
--- /dev/null
+++ b/samples/WinUI3/WinUI3.csproj
@@ -0,0 +1,55 @@
+
+
+ WinExe
+ net6.0-windows10.0.19041.0
+ 10.0.17763.0
+ WinUI3
+ app.manifest
+ x86;x64;ARM64
+ win10-x86;win10-x64;win10-arm64
+ win10-$(Platform).pubxml
+ true
+ true
+
+ None
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
diff --git a/samples/WinUI3/app.manifest b/samples/WinUI3/app.manifest
new file mode 100644
index 0000000..0b0b4f4
--- /dev/null
+++ b/samples/WinUI3/app.manifest
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PerMonitorV2
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file