diff --git a/NTwain.Net35/NTwain.Net35.csproj b/NTwain.Net35/NTwain.Net35.csproj
index 86bd627..7591022 100644
--- a/NTwain.Net35/NTwain.Net35.csproj
+++ b/NTwain.Net35/NTwain.Net35.csproj
@@ -279,6 +279,9 @@
TwainSession.cs
+
+ TwainSessionInternal.cs
+
TwainSource.Caps.cs
diff --git a/NTwain/ITwainSession.cs b/NTwain/ITwainSession.cs
index 15ec9c3..5a25bb1 100644
--- a/NTwain/ITwainSession.cs
+++ b/NTwain/ITwainSession.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
+using System.Threading;
namespace NTwain
{
@@ -13,6 +14,25 @@ namespace NTwain
///
public interface ITwainSession : INotifyPropertyChanged
{
+
+ ///
+ /// [Experimental] Gets or sets the optional synchronization context when not specifying a on .
+ /// This allows events to be raised on the thread associated with the context. This is experimental is not recommended for use.
+ ///
+ ///
+ /// The synchronization context.
+ ///
+ SynchronizationContext SynchronizationContext { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
+ ///
+ ///
+ /// true if state value is enforced; otherwise, false.
+ ///
+ bool EnforceState { get; set; }
+
+
///
/// Gets the currently open source.
///
@@ -37,6 +57,27 @@ namespace NTwain
/// The state.
int State { get; }
+
+ ///
+ /// Quick flag to check if the DSM has been opened.
+ ///
+ bool IsDsmOpen { get; }
+
+ ///
+ /// Quick flag to check if a source has been opened.
+ ///
+ bool IsSourceOpen { get; }
+
+ ///
+ /// Quick flag to check if a source has been enabled.
+ ///
+ bool IsSourceEnabled { get; }
+
+ ///
+ /// Quick flag to check if a source is in the transferring state.
+ ///
+ bool IsTransferring { get; }
+
///
/// Try to show the built-in source selector dialog and return the selected source.
/// This is not recommended and is only included for completeness.
@@ -99,5 +140,36 @@ namespace NTwain
///
///
TWStatusUtf8 GetStatusUtf8();
+
+
+ ///
+ /// Occurs when has changed.
+ ///
+ event EventHandler StateChanged;
+ ///
+ /// Occurs when has changed.
+ ///
+ event EventHandler SourceChanged;
+ ///
+ /// Occurs when source has been disabled (back to state 4).
+ ///
+ event EventHandler SourceDisabled;
+ ///
+ /// Occurs when the source has generated an event.
+ ///
+ event EventHandler DeviceEvent;
+ ///
+ /// Occurs when a data transfer is ready.
+ ///
+ event EventHandler TransferReady;
+ ///
+ /// Occurs when data has been transferred.
+ ///
+ event EventHandler DataTransferred;
+ ///
+ /// Occurs when an error has been encountered during transfer.
+ ///
+ event EventHandler TransferError;
+
}
}
diff --git a/NTwain/Internals/ITwainSessionInternal.cs b/NTwain/Internals/ITwainSessionInternal.cs
index 1945b65..038f2e6 100644
--- a/NTwain/Internals/ITwainSessionInternal.cs
+++ b/NTwain/Internals/ITwainSessionInternal.cs
@@ -16,14 +16,6 @@ namespace NTwain.Internals
MessageLoopHook MessageLoopHook { get; }
- ///
- /// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
- ///
- ///
- /// true if state value is enforced; otherwise, false.
- ///
- bool EnforceState { get; set; }
-
///
/// Changes the state right away.
///
diff --git a/NTwain/NTwain.csproj b/NTwain/NTwain.csproj
index 9aac987..204c10c 100644
--- a/NTwain/NTwain.csproj
+++ b/NTwain/NTwain.csproj
@@ -92,6 +92,7 @@
+
diff --git a/NTwain/TwainSession.cs b/NTwain/TwainSession.cs
index 6f7e0a0..f3061c2 100644
--- a/NTwain/TwainSession.cs
+++ b/NTwain/TwainSession.cs
@@ -18,7 +18,7 @@ namespace NTwain
///
/// Basic class for interfacing with TWAIN. You should only have one of this per application process.
///
- public class TwainSession : ITwainSessionInternal, IWinMessageFilter
+ public partial class TwainSession : ITwainSessionInternal, IWinMessageFilter
{
///
/// Initializes a new instance of the class.
@@ -40,6 +40,7 @@ namespace NTwain
if (appId == null) { throw new ArgumentNullException("appId"); }
_appId = appId;
+ _ownedSources = new Dictionary();
((ITwainSessionInternal)this).ChangeState(1, false);
#if DEBUG
// defaults to false on release since it's only useful during dev
@@ -52,13 +53,14 @@ namespace NTwain
TWIdentity _appId;
TWUserInterface _twui;
-
- readonly Dictionary _ownedSources = new Dictionary();
+ // cache generated twain sources so if you get same source from one session it'll return the same object
+ readonly Dictionary _ownedSources;
TwainSource GetSourceInstance(ITwainSessionInternal session, TWIdentity sourceId)
{
TwainSource source = null;
- var key = string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}", sourceId.Manufacturer, sourceId.ProductFamily, sourceId.ProductName);
+ Debug.WriteLine("Source id = " + sourceId.Id);
+ var key = string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}|{3}", sourceId.Id, sourceId.Manufacturer, sourceId.ProductFamily, sourceId.ProductName);
if (_ownedSources.ContainsKey(key))
{
source = _ownedSources[key];
@@ -69,27 +71,9 @@ namespace NTwain
}
return source;
}
+
+ #region ITwainSession Members
- ///
- /// Gets or sets the optional synchronization context when not specifying a on .
- /// This allows events to be raised on the thread associated with the context. This is experimental is not recommended for use.
- ///
- ///
- /// The synchronization context.
- ///
- public SynchronizationContext SynchronizationContext { get; set; }
-
-
- #region ITwainSessionInternal Members
-
- MessageLoopHook _msgLoopHook;
- MessageLoopHook ITwainSessionInternal.MessageLoopHook { get { return _msgLoopHook; } }
-
- ///
- /// Gets the app id used for the session.
- ///
- /// The app id.
- TWIdentity ITwainSessionInternal.AppId { get { return _appId; } }
///
/// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
@@ -99,84 +83,15 @@ namespace NTwain
///
public bool EnforceState { get; set; }
- void ITwainSessionInternal.ChangeState(int newState, bool notifyChange)
- {
- _state = newState;
- if (notifyChange)
- {
- OnPropertyChanged("State");
- SafeAsyncSyncableRaiseOnEvent(OnStateChanged, StateChanged);
- }
- }
+ ///
+ /// [Experimental] Gets or sets the optional synchronization context when not specifying a on .
+ /// This allows events to be raised on the thread associated with the context. This is experimental is not recommended for use.
+ ///
+ ///
+ /// The synchronization context.
+ ///
+ public SynchronizationContext SynchronizationContext { get; set; }
- ICommittable ITwainSessionInternal.GetPendingStateChanger(int newState)
- {
- return new TentativeStateCommitable(this, newState);
- }
-
- void ITwainSessionInternal.ChangeCurrentSource(TwainSource source)
- {
- CurrentSource = source;
- OnPropertyChanged("CurrentSource");
- SafeAsyncSyncableRaiseOnEvent(OnSourceChanged, SourceChanged);
- }
-
- void ITwainSessionInternal.SafeSyncableRaiseEvent(DataTransferredEventArgs e)
- {
- SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, e);
- }
- void ITwainSessionInternal.SafeSyncableRaiseEvent(TransferErrorEventArgs e)
- {
- SafeSyncableRaiseOnEvent(OnTransferError, TransferError, e);
- }
- void ITwainSessionInternal.SafeSyncableRaiseEvent(TransferReadyEventArgs e)
- {
- SafeSyncableRaiseOnEvent(OnTransferReady, TransferReady, e);
- }
-
- DGAudio _dgAudio;
- DGAudio ITwainSessionInternal.DGAudio
- {
- get
- {
- if (_dgAudio == null) { _dgAudio = new DGAudio(this); }
- return _dgAudio;
- }
- }
-
- DGControl _dgControl;
- DGControl ITwainSessionInternal.DGControl
- {
- get
- {
- if (_dgControl == null) { _dgControl = new DGControl(this); }
- return _dgControl;
- }
- }
-
- DGImage _dgImage;
- DGImage ITwainSessionInternal.DGImage
- {
- get
- {
- if (_dgImage == null) { _dgImage = new DGImage(this); }
- return _dgImage;
- }
- }
-
- DGCustom _dgCustom;
- DGCustom ITwainSessionInternal.DGCustom
- {
- get
- {
- if (_dgCustom == null) { _dgCustom = new DGCustom(this); }
- return _dgCustom;
- }
- }
-
- #endregion
-
- #region ITwainSession Members
///
/// Gets the currently open source.
@@ -250,6 +165,26 @@ namespace NTwain
}
}
+ ///
+ /// Quick flag to check if the DSM has been opened.
+ ///
+ public bool IsDsmOpen { get { return State > 2; } }
+
+ ///
+ /// Quick flag to check if a source has been opened.
+ ///
+ public bool IsSourceOpen { get { return State > 3; } }
+
+ ///
+ /// Quick flag to check if a source has been enabled.
+ ///
+ public bool IsSourceEnabled { get { return State > 4; } }
+
+ ///
+ /// Quick flag to check if a source is in the transferring state.
+ ///
+ public bool IsTransferring { get { return State > 5; } }
+
///
/// Opens the data source manager. This must be the first method used
/// before using other TWAIN functions. Calls to this must be followed by
@@ -348,6 +283,13 @@ namespace NTwain
///
public ReturnCode OpenSource(string sourceName)
{
+ var curSrc = CurrentSource;
+ if (curSrc != null)
+ {
+ // TODO: close any open sources first
+
+ }
+
var hit = GetSources().Where(s => string.Equals(s.Name, sourceName)).FirstOrDefault();
if (hit != null)
{
@@ -378,142 +320,6 @@ namespace NTwain
return stat;
}
-
- #endregion
-
- #region INotifyPropertyChanged Members
-
- ///
- /// Occurs when a property value changes.
- ///
- public event PropertyChangedEventHandler PropertyChanged;
-
- ///
- /// Raises the event.
- ///
- /// Name of the property.
- protected void OnPropertyChanged(string propertyName)
- {
- var syncer = SynchronizationContext;
- if (syncer == null)
- {
- try
- {
- var hand = PropertyChanged;
- if (hand != null) { hand(this, new PropertyChangedEventArgs(propertyName)); }
- }
- catch { }
- }
- else
- {
- syncer.Post(o =>
- {
- try
- {
- var hand = PropertyChanged;
- if (hand != null) { hand(this, new PropertyChangedEventArgs(propertyName)); }
- }
- catch { }
- }, null);
- }
- }
-
- #endregion
-
- #region privileged calls that causes state change in TWAIN
-
-
- ///
- /// Enables the source to start transferring.
- ///
- /// The mode.
- /// if set to true any driver UI will display as modal.
- /// The window handle if modal.
- ///
- ReturnCode ITwainSessionInternal.EnableSource(SourceEnableMode mode, bool modal, IntPtr windowHandle)
- {
- var rc = ReturnCode.Failure;
-
- _msgLoopHook.Invoke(() =>
- {
- Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: EnableSource with {1}.", Thread.CurrentThread.ManagedThreadId, mode));
-
- // app v2.2 or higher uses callback2
- if (_appId.ProtocolMajor >= 2 && _appId.ProtocolMinor >= 2)
- {
- var cb = new TWCallback2(HandleCallback);
- var rc2 = ((ITwainSessionInternal)this).DGControl.Callback2.RegisterCallback(cb);
-
- if (rc2 == ReturnCode.Success)
- {
- Debug.WriteLine("Registered callback2 OK.");
- _callbackObj = cb;
- }
- }
- else
- {
- var cb = new TWCallback(HandleCallback);
-
- var rc2 = ((ITwainSessionInternal)this).DGControl.Callback.RegisterCallback(cb);
-
- if (rc2 == ReturnCode.Success)
- {
- Debug.WriteLine("Registered callback OK.");
- _callbackObj = cb;
- }
- }
-
- _twui = new TWUserInterface();
- _twui.ShowUI = mode == SourceEnableMode.ShowUI;
- _twui.ModalUI = modal;
- _twui.hParent = windowHandle;
-
- if (mode == SourceEnableMode.ShowUIOnly)
- {
- rc = ((ITwainSessionInternal)this).DGControl.UserInterface.EnableDSUIOnly(_twui);
- }
- else
- {
- rc = ((ITwainSessionInternal)this).DGControl.UserInterface.EnableDS(_twui);
- }
-
- if (rc != ReturnCode.Success)
- {
- _callbackObj = null;
- }
- });
- return rc;
- }
-
- bool _disabling;
- ReturnCode ITwainSessionInternal.DisableSource()
- {
- var rc = ReturnCode.Failure;
- if (!_disabling) // temp hack as a workaround to this being called from multiple threads (xfer logic & closedsreq msg)
- {
- _disabling = true;
- try
- {
- _msgLoopHook.Invoke(() =>
- {
- Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
-
- rc = ((ITwainSessionInternal)this).DGControl.UserInterface.DisableDS(_twui);
- if (rc == ReturnCode.Success)
- {
- _callbackObj = null;
- SafeAsyncSyncableRaiseOnEvent(OnSourceDisabled, SourceDisabled);
- }
- });
- }
- finally
- {
- _disabling = false;
- }
- }
- return rc;
- }
-
///
/// Forces the stepping down of an opened source when things gets out of control.
/// Used when session state and source state become out of sync.
@@ -567,9 +373,6 @@ namespace NTwain
EnforceState = origFlag;
}
- #endregion
-
- #region custom events and overridables
///
/// Occurs when has changed.
@@ -601,6 +404,49 @@ namespace NTwain
public event EventHandler TransferError;
+ #endregion
+
+ #region INotifyPropertyChanged Members
+
+ ///
+ /// Occurs when a property value changes.
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// Raises the event.
+ ///
+ /// Name of the property.
+ protected void OnPropertyChanged(string propertyName)
+ {
+ var syncer = SynchronizationContext;
+ if (syncer == null)
+ {
+ try
+ {
+ var hand = PropertyChanged;
+ if (hand != null) { hand(this, new PropertyChangedEventArgs(propertyName)); }
+ }
+ catch { }
+ }
+ else
+ {
+ syncer.Post(o =>
+ {
+ try
+ {
+ var hand = PropertyChanged;
+ if (hand != null) { hand(this, new PropertyChangedEventArgs(propertyName)); }
+ }
+ catch { }
+ }, null);
+ }
+ }
+
+ #endregion
+
+ #region events overridables
+
///
/// Raises event and if applicable marshal it asynchronously to the thread
/// without exceptions.
diff --git a/NTwain/TwainSessionInternal.cs b/NTwain/TwainSessionInternal.cs
new file mode 100644
index 0000000..46115f9
--- /dev/null
+++ b/NTwain/TwainSessionInternal.cs
@@ -0,0 +1,200 @@
+using NTwain.Data;
+using NTwain.Internals;
+using NTwain.Triplets;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace NTwain
+{
+ // for internal pieces since the main twain session file is getting too long
+
+ partial class TwainSession
+ {
+ #region ITwainSessionInternal Members
+
+ MessageLoopHook _msgLoopHook;
+ MessageLoopHook ITwainSessionInternal.MessageLoopHook { get { return _msgLoopHook; } }
+
+ ///
+ /// Gets the app id used for the session.
+ ///
+ /// The app id.
+ TWIdentity ITwainSessionInternal.AppId { get { return _appId; } }
+
+ void ITwainSessionInternal.ChangeState(int newState, bool notifyChange)
+ {
+ _state = newState;
+ if (notifyChange)
+ {
+ OnPropertyChanged("State");
+ SafeAsyncSyncableRaiseOnEvent(OnStateChanged, StateChanged);
+ }
+ }
+
+ ICommittable ITwainSessionInternal.GetPendingStateChanger(int newState)
+ {
+ return new TentativeStateCommitable(this, newState);
+ }
+
+ void ITwainSessionInternal.ChangeCurrentSource(TwainSource source)
+ {
+ CurrentSource = source;
+ OnPropertyChanged("CurrentSource");
+ SafeAsyncSyncableRaiseOnEvent(OnSourceChanged, SourceChanged);
+ }
+
+ void ITwainSessionInternal.SafeSyncableRaiseEvent(DataTransferredEventArgs e)
+ {
+ SafeSyncableRaiseOnEvent(OnDataTransferred, DataTransferred, e);
+ }
+ void ITwainSessionInternal.SafeSyncableRaiseEvent(TransferErrorEventArgs e)
+ {
+ SafeSyncableRaiseOnEvent(OnTransferError, TransferError, e);
+ }
+ void ITwainSessionInternal.SafeSyncableRaiseEvent(TransferReadyEventArgs e)
+ {
+ SafeSyncableRaiseOnEvent(OnTransferReady, TransferReady, e);
+ }
+
+ DGAudio _dgAudio;
+ DGAudio ITwainSessionInternal.DGAudio
+ {
+ get
+ {
+ if (_dgAudio == null) { _dgAudio = new DGAudio(this); }
+ return _dgAudio;
+ }
+ }
+
+ DGControl _dgControl;
+ DGControl ITwainSessionInternal.DGControl
+ {
+ get
+ {
+ if (_dgControl == null) { _dgControl = new DGControl(this); }
+ return _dgControl;
+ }
+ }
+
+ DGImage _dgImage;
+ DGImage ITwainSessionInternal.DGImage
+ {
+ get
+ {
+ if (_dgImage == null) { _dgImage = new DGImage(this); }
+ return _dgImage;
+ }
+ }
+
+ DGCustom _dgCustom;
+ DGCustom ITwainSessionInternal.DGCustom
+ {
+ get
+ {
+ if (_dgCustom == null) { _dgCustom = new DGCustom(this); }
+ return _dgCustom;
+ }
+ }
+
+
+ ///
+ /// Enables the source to start transferring.
+ ///
+ /// The mode.
+ /// if set to true any driver UI will display as modal.
+ /// The window handle if modal.
+ ///
+ ReturnCode ITwainSessionInternal.EnableSource(SourceEnableMode mode, bool modal, IntPtr windowHandle)
+ {
+ var rc = ReturnCode.Failure;
+
+ _msgLoopHook.Invoke(() =>
+ {
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: EnableSource with {1}.", Thread.CurrentThread.ManagedThreadId, mode));
+
+ // app v2.2 or higher uses callback2
+ if (_appId.ProtocolMajor >= 2 && _appId.ProtocolMinor >= 2)
+ {
+ var cb = new TWCallback2(HandleCallback);
+ var rc2 = ((ITwainSessionInternal)this).DGControl.Callback2.RegisterCallback(cb);
+
+ if (rc2 == ReturnCode.Success)
+ {
+ Debug.WriteLine("Registered callback2 OK.");
+ _callbackObj = cb;
+ }
+ }
+ else
+ {
+ var cb = new TWCallback(HandleCallback);
+
+ var rc2 = ((ITwainSessionInternal)this).DGControl.Callback.RegisterCallback(cb);
+
+ if (rc2 == ReturnCode.Success)
+ {
+ Debug.WriteLine("Registered callback OK.");
+ _callbackObj = cb;
+ }
+ }
+
+ _twui = new TWUserInterface();
+ _twui.ShowUI = mode == SourceEnableMode.ShowUI;
+ _twui.ModalUI = modal;
+ _twui.hParent = windowHandle;
+
+ if (mode == SourceEnableMode.ShowUIOnly)
+ {
+ rc = ((ITwainSessionInternal)this).DGControl.UserInterface.EnableDSUIOnly(_twui);
+ }
+ else
+ {
+ rc = ((ITwainSessionInternal)this).DGControl.UserInterface.EnableDS(_twui);
+ }
+
+ if (rc != ReturnCode.Success)
+ {
+ _callbackObj = null;
+ }
+ });
+ return rc;
+ }
+
+ bool _disabling;
+ ReturnCode ITwainSessionInternal.DisableSource()
+ {
+ var rc = ReturnCode.Failure;
+ if (!_disabling) // temp hack as a workaround to this being called from multiple threads (xfer logic & closedsreq msg)
+ {
+ _disabling = true;
+ try
+ {
+ _msgLoopHook.Invoke(() =>
+ {
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: DisableSource.", Thread.CurrentThread.ManagedThreadId));
+
+ rc = ((ITwainSessionInternal)this).DGControl.UserInterface.DisableDS(_twui);
+ if (rc == ReturnCode.Success)
+ {
+ _callbackObj = null;
+ SafeAsyncSyncableRaiseOnEvent(OnSourceDisabled, SourceDisabled);
+ }
+ });
+ }
+ finally
+ {
+ _disabling = false;
+ }
+ }
+ return rc;
+ }
+
+
+ #endregion
+
+ }
+}
diff --git a/README.md b/README.md
index 03de566..63d73dd 100644
--- a/README.md
+++ b/README.md
@@ -75,7 +75,7 @@ When you're ready to get into transfer mode, just call Enable() on the source ob
```
#!c#
-sources.Enable(...);
+myDS.Enable(...);
```