Internal loop and readme update.

This commit is contained in:
soukoku
2014-04-19 09:10:15 -04:00
parent 72760ddad2
commit 6a990619a8
5 changed files with 103 additions and 73 deletions

View File

@@ -2,6 +2,7 @@
using NTwain.Triplets;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Interop;
using System.Windows.Threading;
@@ -18,11 +19,10 @@ namespace NTwain
Dispatcher _dispatcher;
bool _started;
HwndSource _dummyWindow;
WindowsHook _hook;
private MessageLoop() { }
public void EnsureStarted()
public void EnsureStarted(WindowsHook.WndProcHook hook)
{
if (!_started)
{
@@ -33,12 +33,9 @@ namespace NTwain
{
Debug.WriteLine("NTwain message loop started.");
_dispatcher = Dispatcher.CurrentDispatcher;
if (Dsm.IsWin)
if (!Dsm.IsOnMono)
{
// start a windows msg loop for old twain to post msgs
// the style values are purely guesses here with
// CS_NOCLOSE, WS_DISABLED, and WS_EX_NOACTIVATE
_dummyWindow = new HwndSource(0x0200, 0x8000000, 0x8000000, 0, 0, "NTWAIN_LOOPER", IntPtr.Zero);
_hook = new WindowsHook(hook);
}
hack.Set();
Dispatcher.Run();
@@ -57,7 +54,7 @@ namespace NTwain
{
get
{
return _dummyWindow == null ? IntPtr.Zero : _dummyWindow.Handle;
return _hook == null ? IntPtr.Zero : _hook.Handle;
}
}
@@ -79,7 +76,6 @@ namespace NTwain
else
{
//_dispatcher.Invoke(DispatcherPriority.Normal, action);
// why use this instead of the single line above? for possible future use in mono!
var man = new ManualResetEvent(false);
_dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
@@ -96,14 +92,67 @@ namespace NTwain
man.Close();
}
}
}
public void AddHook(HwndSourceHook hook)
/// <summary>
/// Abstracts out wnd proc hook on Windows from MessageLoop class.
/// This allows things to not depend on PresentationCore.dll at runtime on mono.
/// Not that everything works yet in mono but it's something.
/// </summary>
class WindowsHook
{
public WindowsHook(WndProcHook hook)
{
if (_dummyWindow != null) { _dummyWindow.AddHook(hook); }
// hook into windows msg loop for old twain to post msgs.
// the style values are purely guesses here with
// CS_NOCLOSE, WS_DISABLED, and WS_EX_NOACTIVATE
var win = new HwndSource(0x0200, 0x8000000, 0x8000000, 0, 0, "NTWAIN_LOOPER", IntPtr.Zero);
Handle = win.Handle;
_hook = hook;
win.AddHook(WndProc);
}
public void RemoveHook(HwndSourceHook hook)
public delegate void WndProcHook(ref MESSAGE winMsg, ref bool handled);
WndProcHook _hook;
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (_dummyWindow != null) { _dummyWindow.RemoveHook(hook); }
if (_hook != null)
{
var winmsg = new MESSAGE(hwnd, msg, wParam, lParam);
_hook(ref winmsg, ref handled);
}
return IntPtr.Zero;
}
public IntPtr Handle { get; private set; }
/// <summary>
/// The MSG structure in Windows for TWAIN use.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct MESSAGE
{
public MESSAGE(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam)
{
_hwnd = hwnd;
_message = (uint)message;
_wParam = wParam;
_lParam = lParam;
_time = 0;
_x = 0;
_y = 0;
}
IntPtr _hwnd;
uint _message;
IntPtr _wParam;
IntPtr _lParam;
uint _time;
int _x;
int _y;
}
}
}

View File

@@ -32,6 +32,7 @@ namespace NTwain.Triplets
File.Exists(path);
#endif
}
internal static readonly bool IsOnMono = Type.GetType("Mono.Runtime") != null;
internal static readonly bool IsWin = Environment.OSVersion.Platform == PlatformID.Win32NT;
static readonly bool IsLinux = Environment.OSVersion.Platform == PlatformID.Unix;

View File

@@ -29,7 +29,7 @@ namespace NTwain
((ITwainStateInternal)this).ChangeState(1, false);
EnforceState = true;
MessageLoop.Instance.EnsureStarted();
MessageLoop.Instance.EnsureStarted(HandleWndProcMessage);
}
TWIdentity _appId;
@@ -369,12 +369,6 @@ namespace NTwain
}
}
if (_callbackObj == null)
{
// must use msg loop if callback is not available
MessageLoop.Instance.AddHook(HandleWndProcMessage);
}
_twui = new TWUserInterface();
_twui.ShowUI = mode == SourceEnableMode.ShowUI;
_twui.ModalUI = modal;
@@ -407,14 +401,7 @@ namespace NTwain
rc = DGControl.UserInterface.DisableDS(_twui);
if (rc == ReturnCode.Success)
{
if (_callbackObj == null)
{
MessageLoop.Instance.RemoveHook(HandleWndProcMessage);
}
else
{
_callbackObj = null;
}
_callbackObj = null;
SafeAsyncSyncableRaiseOnEvent(OnSourceDisabled, SourceDisabled);
}
});
@@ -616,19 +603,18 @@ namespace NTwain
#region TWAIN logic during xfer work
//[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
IntPtr HandleWndProcMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
void HandleWndProcMessage(ref NTwain.WindowsHook.MESSAGE winMsg, ref bool handled)
{
// this handles the message from a typical WndProc message loop and check if it's from the TWAIN source.
if (State >= 5)
{
// transform it into a pointer for twain
var winmsg = new MESSAGE(hwnd, msg, wParam, lParam);
IntPtr msgPtr = IntPtr.Zero;
try
{
// no need to do another lock call when using marshal alloc
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winmsg));
Marshal.StructureToPtr(winmsg, msgPtr, false);
msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winMsg));
Marshal.StructureToPtr(winMsg, msgPtr, false);
var evt = new TWEvent();
evt.pEvent = msgPtr;
@@ -644,8 +630,6 @@ namespace NTwain
if (msgPtr != IntPtr.Zero) { Marshal.FreeHGlobal(msgPtr); }
}
}
return IntPtr.Zero;
}
ReturnCode HandleCallback(TWIdentity origin, TWIdentity destination, DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
@@ -1223,31 +1207,5 @@ namespace NTwain
#endregion
#endregion
/// <summary>
/// The MSG structure in Windows for TWAIN use.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct MESSAGE
{
public MESSAGE(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam)
{
_hwnd = hwnd;
_message = (uint)message;
_wParam = wParam;
_lParam = lParam;
_time = 0;
_x = 0;
_y = 0;
}
IntPtr _hwnd;
uint _message;
IntPtr _wParam;
IntPtr _lParam;
uint _time;
int _x;
int _y;
}
}
}

View File

@@ -32,3 +32,15 @@ example for handling DIB image in native transfer using the CommonWin32 lib.
Because it hosts its own message thread, the event callbacks will likely be from another thread.
If you would like things marshalled to a "UI" thread then set the SynchronizationContext property
to the one from the UI thread. This part is highly experimental.
64-bit OS
--------------------------------------
If the application process is running in 64-bit then you will need to make sure you have the
newer data source manager (twaindsm.dll) installed.
[DSM from TWAIN.org](http://sourceforge.net/projects/twain-dsm/files/TWAIN%20DSM%202%20Win/)
Otherwise just compile and run the app as x86 and it'll use the 32-bit version (twain_32.dll) that comes with Windows.
If you really want to test 64-bit drivers for whatever reason, you most likely will have to use
the test one from TWAIN.org since there are no known 64-bit TWAIN DS drivers at the time of writing.
[Sample DS from TWAIN.org](http://sourceforge.net/projects/twain-samples/files/TWAIN%202%20Sample%20Data%20Source/TWAIN%20DS%202.1.3/)

View File

@@ -38,47 +38,57 @@ namespace Tester
static void DoTwainWork()
{
Console.WriteLine("Getting ready to do twain stuff on thread {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Getting ready to do twain stuff on thread {0}.", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
var rc = twain.OpenManager();
if (rc == ReturnCode.Success)
{
rc = twain.OpenSource("TWAIN2 FreeImage Software Scanner");
if (rc == ReturnCode.Success)
var hit = twain.GetSources().Where(s => string.Equals(s.ProductName, "TWAIN2 FreeImage Software Scanner")).FirstOrDefault();
if (hit == null)
{
rc = twain.EnableSource(SourceEnableMode.NoUI, false, IntPtr.Zero);
Console.WriteLine("The sample source \"TWAIN2 FreeImage Software Scanner\" is not installed.");
twain.CloseManager();
}
else
{
twain.CloseManager();
rc = twain.OpenSource(hit.ProductName);
if (rc == ReturnCode.Success)
{
Console.WriteLine("Start capture from the sample source.");
rc = twain.EnableSource(SourceEnableMode.NoUI, false, IntPtr.Zero);
}
else
{
twain.CloseManager();
}
}
}
}
static void twain_SourceDisabled(object sender, EventArgs e)
{
Console.WriteLine("Source disabled on thread {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Source disabled on thread {0}.", Thread.CurrentThread.ManagedThreadId);
var rc = twain.CloseSource();
rc = twain.CloseManager();
}
static void twain_TransferReady(object sender, TransferReadyEventArgs e)
{
Console.WriteLine("Got xfer ready on thread {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Got xfer ready on thread {0}.", Thread.CurrentThread.ManagedThreadId);
}
static void twain_DataTransferred(object sender, DataTransferredEventArgs e)
{
if (e.NativeData != IntPtr.Zero)
{
Console.WriteLine("Got twain data on thread {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Got twain data on thread {0}.", Thread.CurrentThread.ManagedThreadId);
}
else
{
Console.WriteLine("No twain data on thread {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("No twain data on thread {0}.", Thread.CurrentThread.ManagedThreadId);
}
}
}