diff --git a/src/NTwain/Data/TwainTypes.cs b/src/NTwain/Data/TwainTypes.cs index 0db20d9..2651c2e 100644 --- a/src/NTwain/Data/TwainTypes.cs +++ b/src/NTwain/Data/TwainTypes.cs @@ -89,21 +89,19 @@ namespace NTwain.Data [StructLayout(LayoutKind.Sequential, Pack = 2)] - partial class TW_CALLBACK + partial struct TW_CALLBACK { - [MarshalAs(UnmanagedType.FunctionPtr)] - CallbackDelegate _callBackProc; - TW_UINT32 _refCon; - TW_INT16 _message; + public TW_MEMREF CallBackProc; + TW_UINT32 RefCon; + TW_INT16 Message; } [StructLayout(LayoutKind.Sequential, Pack = 2)] - partial class TW_CALLBACK2 + partial struct TW_CALLBACK2 { - [MarshalAs(UnmanagedType.FunctionPtr)] - CallbackDelegate _callBackProc; - TW_UINTPTR _refCon; - TW_INT16 _message; + public TW_MEMREF CallBackProc; + TW_UINTPTR RefCon; + TW_INT16 Message; } [StructLayout(LayoutKind.Sequential, Pack = 2)] @@ -542,6 +540,7 @@ namespace NTwain.Data TW_HANDLE _hParent; } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate ReturnCode CallbackDelegate(TW_IDENTITY origin, TW_IDENTITY destination, DataGroups dg, DataArgumentType dat, Message msg, TW_MEMREF data); diff --git a/src/NTwain/Data/TwainTypesExtended.cs b/src/NTwain/Data/TwainTypesExtended.cs index 9423c80..6221ad9 100644 --- a/src/NTwain/Data/TwainTypesExtended.cs +++ b/src/NTwain/Data/TwainTypesExtended.cs @@ -570,71 +570,6 @@ namespace NTwain.Data public string Name { get { return _name; } } } - - /// - /// Used in Callback mechanism for sending messages from the Source to the Application. - /// Applications version 2.2 or higher must use . - /// - partial class TW_CALLBACK - { - /// - /// Initializes a new instance of the class. - /// - /// The callback function’s entry point. - public TW_CALLBACK(CallbackDelegate callback) - { - _callBackProc = callback; - } - - ///// - ///// An application defined reference constant. - ///// - ///// - ///// The reference constant. - ///// - //public uint RefCon { get { return _refCon; } set { _refCon = value; } } - - ///// - ///// Initialized to any valid DG_CONTROL / DAT_NULL message. - ///// - ///// - ///// The message. - ///// - //public short Message { get { return _message; } set { _message = value; } } - } - /// - /// Used in the Callback mechanism for sending messages from the Source to the Application. - /// - partial class TW_CALLBACK2 - { - /// - /// Initializes a new instance of the class. - /// - /// The callback function’s entry point. - public TW_CALLBACK2(CallbackDelegate callback) - { - _callBackProc = callback; - } - - ///// - ///// An application defined reference constant. It has a different size on different - ///// platforms. - ///// - ///// - ///// The reference constant. - ///// - //public UIntPtr RefCon { get { return _refCon; } set { _refCon = value; } } - - ///// - ///// Initialized to any valid DG_CONTROL / DAT_NULL message. - ///// - ///// - ///// The message. - ///// - //public short Message { get { return _message; } set { _message = value; } } - } - - // /// // /// Used by an application either to get information about, or control the setting of a capability. // /// diff --git a/src/NTwain/Triplets/DGControl.Callback.cs b/src/NTwain/Triplets/DGControl.Callback.cs index b823028..9cc7a53 100644 --- a/src/NTwain/Triplets/DGControl.Callback.cs +++ b/src/NTwain/Triplets/DGControl.Callback.cs @@ -7,10 +7,10 @@ namespace NTwain.Triplets { internal Callback(TwainSession session) : base(session) { } - public ReturnCode RegisterCallback(TW_CALLBACK callback) + public ReturnCode RegisterCallback(ref TW_CALLBACK callback) { return NativeMethods.DsmWin32(Session.Config.AppWin32, Session.CurrentSource.Identity, - DataGroups.Control, DataArgumentType.Callback2, Message.RegisterCallback, callback); + DataGroups.Control, DataArgumentType.Callback, Message.RegisterCallback, ref callback); } } } \ No newline at end of file diff --git a/src/NTwain/Triplets/DGControl.Callback2.cs b/src/NTwain/Triplets/DGControl.Callback2.cs index 62c6a5c..3f3707e 100644 --- a/src/NTwain/Triplets/DGControl.Callback2.cs +++ b/src/NTwain/Triplets/DGControl.Callback2.cs @@ -7,10 +7,10 @@ namespace NTwain.Triplets { internal Callback2(TwainSession session) : base(session) { } - public ReturnCode RegisterCallback(TW_CALLBACK2 callback) + public ReturnCode RegisterCallback(ref TW_CALLBACK2 callback) { return NativeMethods.DsmWin32(Session.Config.AppWin32, Session.CurrentSource.Identity, - DataGroups.Control, DataArgumentType.Callback2, Message.RegisterCallback, callback); + DataGroups.Control, DataArgumentType.Callback2, Message.RegisterCallback, ref callback); } } } \ No newline at end of file diff --git a/src/NTwain/Triplets/DGControl.Identity.cs b/src/NTwain/Triplets/DGControl.Identity.cs index 35a3d09..958db81 100644 --- a/src/NTwain/Triplets/DGControl.Identity.cs +++ b/src/NTwain/Triplets/DGControl.Identity.cs @@ -16,7 +16,6 @@ namespace NTwain.Triplets { Session.State = TwainState.S3; Session.CurrentSource = null; - Session.UpdateCallback(); } return rc; } @@ -51,7 +50,7 @@ namespace NTwain.Triplets { Session.CurrentSource = Session.GetSourceSingleton(source); Session.State = TwainState.S4; - Session.UpdateCallback(); + Session.RegisterCallback(); } return rc; } diff --git a/src/NTwain/Triplets/DGControl.Parent.cs b/src/NTwain/Triplets/DGControl.Parent.cs index 324b300..5679af1 100644 --- a/src/NTwain/Triplets/DGControl.Parent.cs +++ b/src/NTwain/Triplets/DGControl.Parent.cs @@ -10,7 +10,7 @@ namespace NTwain.Triplets { internal Parent(TwainSession session) : base(session) { } - public ReturnCode OpenDSM(ref IntPtr hWnd) + public ReturnCode OpenDSM(IntPtr hWnd) { var rc = NativeMethods.DsmWin32(Session.Config.AppWin32, null, DataGroups.Control, DataArgumentType.Parent, Message.OpenDSM, ref hWnd); @@ -29,14 +29,14 @@ namespace NTwain.Triplets } else { - rc = CloseDSM(ref hWnd); + rc = CloseDSM(hWnd); } } } return rc; } - public ReturnCode CloseDSM(ref IntPtr hWnd) + public ReturnCode CloseDSM(IntPtr hWnd) { var rc = NativeMethods.DsmWin32(Session.Config.AppWin32, null, DataGroups.Control, DataArgumentType.Parent, Message.CloseDSM, ref hWnd); diff --git a/src/NTwain/Triplets/NativeMethods.Win32.cs b/src/NTwain/Triplets/NativeMethods.Win32.cs index 8f94cfc..0834d5b 100644 --- a/src/NTwain/Triplets/NativeMethods.Win32.cs +++ b/src/NTwain/Triplets/NativeMethods.Win32.cs @@ -70,7 +70,7 @@ namespace NTwain.Triplets DataGroups dg, DataArgumentType dat, Message msg, - [In, Out]TW_CALLBACK data); + ref TW_CALLBACK data); [DllImport(WinDsmDll, EntryPoint = EntryName)] public static extern ReturnCode DsmWin32( @@ -79,7 +79,7 @@ namespace NTwain.Triplets DataGroups dg, DataArgumentType dat, Message msg, - [In, Out]TW_CALLBACK2 data); + ref TW_CALLBACK2 data); [DllImport(WinDsmDll, EntryPoint = EntryName)] public static extern ReturnCode DsmWin32( diff --git a/src/NTwain/TwainSession.MsgHandling.cs b/src/NTwain/TwainSession.MsgHandling.cs new file mode 100644 index 0000000..f5ed4f9 --- /dev/null +++ b/src/NTwain/TwainSession.MsgHandling.cs @@ -0,0 +1,21 @@ +using NTwain.Data; +using NTwain.Triplets; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace NTwain +{ + partial class TwainSession + { + private void HandleSourceMsg(Message msg) + { + + } + } +} diff --git a/src/NTwain/TwainSession.cs b/src/NTwain/TwainSession.cs index 56864bf..f4de1dd 100644 --- a/src/NTwain/TwainSession.cs +++ b/src/NTwain/TwainSession.cs @@ -3,8 +3,11 @@ using NTwain.Triplets; using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using System.Text; +using System.Threading; namespace NTwain { @@ -14,10 +17,12 @@ namespace NTwain public partial class TwainSession { internal readonly TwainConfig Config; + private IntPtr _hWnd; - object _callbackObj; // kept around in this class so it doesn't get gc'ed // cache generated twain sources so if you get same source from same session it'll return the same object readonly Dictionary _ownedSources = new Dictionary(); + // need to keep delegate around to prevent GC? + readonly CallbackDelegate _callbackDelegate; /// /// Constructs a new . @@ -26,6 +31,14 @@ namespace NTwain public TwainSession(TwainConfig config) { Config = config; + switch (config.Platform) + { + case PlatformID.MacOSX: + case PlatformID.Unix: + default: + _callbackDelegate = new CallbackDelegate(HandleWin32Callback); + break; + } } /// @@ -36,7 +49,7 @@ namespace NTwain public ReturnCode Open(IntPtr hWnd) { _hWnd = hWnd; - return DGControl.Parent.OpenDSM(ref hWnd); + return DGControl.Parent.OpenDSM(hWnd); } /// @@ -52,7 +65,7 @@ namespace NTwain switch (State) { case TwainState.DsmOpened: - rc = DGControl.Parent.CloseDSM(ref _hWnd); + rc = DGControl.Parent.CloseDSM(_hWnd); if (rc != ReturnCode.Success) return rc; break; case TwainState.SourceOpened: @@ -64,38 +77,37 @@ namespace NTwain return rc; } - internal void UpdateCallback() + internal void RegisterCallback() { - if (State < TwainState.S4) - { - _callbackObj = null; - } - else + if (State == TwainState.S4) { + var callbackPtr = Marshal.GetFunctionPointerForDelegate(_callbackDelegate); + var rc = ReturnCode.Failure; // per the spec (8-10) apps for 2.2 or higher uses callback2 so try this first if (Config.AppWin32.ProtocolMajor > 2 || (Config.AppWin32.ProtocolMajor >= 2 && Config.AppWin32.ProtocolMinor >= 2)) { - var cb = new TW_CALLBACK2(HandleCallback); - rc = DGControl.Callback2.RegisterCallback(cb); - - if (rc == ReturnCode.Success) + var cb = new TW_CALLBACK2 { CallBackProc = callbackPtr }; + rc = DGControl.Callback2.RegisterCallback(ref cb); + if (rc == ReturnCode.Success) Debug.WriteLine("Registed Callback2 success."); + else { - _callbackObj = cb; + } } if (rc != ReturnCode.Success) { // always try old callback - var cb = new TW_CALLBACK(HandleCallback); + var cb = new TW_CALLBACK { CallBackProc = callbackPtr }; - rc = DGControl.Callback.RegisterCallback(cb); + rc = DGControl.Callback.RegisterCallback(ref cb); - if (rc == ReturnCode.Success) + if (rc == ReturnCode.Success) Debug.WriteLine("Registed Callback success."); + else { - _callbackObj = cb; + } } } @@ -108,8 +120,7 @@ namespace NTwain /// public IEnumerable GetSources() { - TW_IDENTITY srcId; - var rc = DGControl.Identity.GetFirst(out srcId); + var rc = DGControl.Identity.GetFirst(out TW_IDENTITY srcId); while (rc == ReturnCode.Success) { yield return GetSourceSingleton(srcId); @@ -124,8 +135,7 @@ namespace NTwain { get { - TW_IDENTITY src; - if (DGControl.Identity.GetDefault(out src) == ReturnCode.Success) + if (DGControl.Identity.GetDefault(out TW_IDENTITY src) == ReturnCode.Success) { return GetSourceSingleton(src); } @@ -151,8 +161,7 @@ namespace NTwain /// public DataSource ShowSourceSelector() { - TW_IDENTITY id; - if (DGControl.Identity.UserSelect(out id) == ReturnCode.Success) + if (DGControl.Identity.UserSelect(out TW_IDENTITY id) == ReturnCode.Success) { return GetSourceSingleton(id); } @@ -182,35 +191,12 @@ namespace NTwain return source; } - ReturnCode HandleCallback(TW_IDENTITY origin, TW_IDENTITY destination, + ReturnCode HandleWin32Callback(TW_IDENTITY origin, TW_IDENTITY destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data) { - //if (origin != null && CurrentSource != null && origin.Id == CurrentSource.Identity.Id && State >= S5) - //{ - // // spec says we must handle this on the thread that enabled the DS. - // // by using the internal dispatcher this will be the case. - - // // In any event the trick to get this thing working is to return from the callback first - // // before trying to process the msg or there will be unpredictable errors. - - // // changed to sync invoke instead of begininvoke for hp scanjet. - // if (origin.ProductName.IndexOf("scanjet", StringComparison.OrdinalIgnoreCase) > -1) - // { - // _msgLoopHook?.Invoke(() => - // { - // HandleSourceMsg(msg); - // }); - // } - // else - // { - // _msgLoopHook?.BeginInvoke(() => - // { - // HandleSourceMsg(msg); - // }); - // } - // return ReturnCode.Success; - //} - return ReturnCode.Failure; + Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(HandleWin32Callback)}({dg}, {dat}, {msg}, {data})"); + HandleSourceMsg(msg); + return ReturnCode.Success; } } }