diff --git a/NTwain/TwainSource.cs b/NTwain/TwainSource.cs
new file mode 100644
index 0000000..34140f6
--- /dev/null
+++ b/NTwain/TwainSource.cs
@@ -0,0 +1,193 @@
+using NTwain.Data;
+using NTwain.Internals;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace NTwain
+{
+ public partial class TwainSource : INotifyPropertyChanged
+ {
+ ITwainSessionInternal _session;
+
+ internal TwainSource(ITwainSessionInternal session, TWIdentity sourceId)
+ {
+ _session = session;
+ Identity = sourceId;
+ }
+
+ ///
+ /// Opens the source for capability negotiation.
+ ///
+ ///
+ public ReturnCode Open()
+ {
+ var rc = ReturnCode.Failure;
+ MessageLoop.Instance.Invoke(() =>
+ {
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));
+
+ rc = _session.DGControl.Identity.OpenDS(this);
+ });
+ return rc;
+ }
+
+ ///
+ /// Closes the source.
+ ///
+ ///
+ public ReturnCode Close()
+ {
+ var rc = ReturnCode.Failure;
+ MessageLoop.Instance.Invoke(() =>
+ {
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));
+
+ rc = _session.DGControl.Identity.CloseDS();
+ if (rc == ReturnCode.Success)
+ {
+ SupportedCaps = null;
+ }
+ });
+ return rc;
+ }
+
+
+ ///
+ /// Enables the source to start transferring.
+ ///
+ /// The mode.
+ /// if set to true any driver UI will display as modal.
+ /// The window handle if modal.
+ ///
+ public ReturnCode StartTransfer(SourceEnableMode mode, bool modal, IntPtr windowHandle)
+ {
+ return _session.EnableSource(mode, modal, windowHandle);
+ }
+
+ ///
+ /// Gets the source status. Only call this at state 4 or higher.
+ ///
+ /// The session.
+ ///
+ public TWStatus GetStatus()
+ {
+ TWStatus stat;
+ _session.DGControl.Status.GetSource(out stat);
+ return stat;
+ }
+
+ internal TWIdentity Identity { get; private set; }
+
+ ///
+ /// Gets the source's product name.
+ ///
+ ///
+ /// The name.
+ ///
+ public string Name { get { return Identity.ProductName; } }
+
+ ///
+ /// Gets the source's manufacturer name.
+ ///
+ ///
+ /// The manufacturer.
+ ///
+ public string Manufacturer { get { return Identity.Manufacturer; } }
+
+ ///
+ /// Gets the source's product family.
+ ///
+ ///
+ /// The product family.
+ ///
+ public string ProductFamily { get { return Identity.ProductFamily; } }
+
+ ///
+ /// Gets the version information.
+ ///
+ ///
+ /// The version.
+ ///
+ public TWVersion Version { get { return Identity.Version; } }
+
+ ///
+ /// Gets the supported data group.
+ ///
+ ///
+ /// The data group.
+ ///
+ public DataGroups DataGroup { get { return Identity.DataGroup; } }
+
+ ///
+ /// Gets the supported TWAIN protocol major number.
+ ///
+ ///
+ /// The protocol major number.
+ ///
+ public int ProtocolMajor { get { return Identity.ProtocolMajor; } }
+
+ ///
+ /// Gets the supported TWAIN protocol minor number.
+ ///
+ ///
+ /// The protocol minor number.
+ ///
+ public int ProtocolMinor { get { return Identity.ProtocolMinor; } }
+
+
+ static readonly CapabilityId[] _emptyCapList = new CapabilityId[0];
+
+ private IList _supportedCaps;
+ ///
+ /// Gets the supported caps for this source.
+ ///
+ ///
+ /// The supported caps.
+ ///
+ public IList SupportedCaps
+ {
+ get
+ {
+ if (_supportedCaps == null && _session.State > 3)
+ {
+ _supportedCaps = GetCapabilityValues(CapabilityId.CapSupportedCaps).CastToEnum(false);
+ }
+ return _supportedCaps ?? _emptyCapList;
+ }
+ private set
+ {
+ _supportedCaps = value;
+ OnPropertyChanged("SupportedCaps");
+ }
+ }
+
+ #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)
+ {
+ try
+ {
+ var hand = PropertyChanged;
+ if (hand != null) { hand(this, new PropertyChangedEventArgs(propertyName)); }
+ }
+ catch { }
+ }
+
+ #endregion
+ }
+}