Start experimenting with configuration builder-type initialization.

This commit is contained in:
Eugene Wang
2018-11-10 16:33:59 -05:00
parent c19cc9f444
commit 05f59a257c
6 changed files with 419 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NTwain.Data
{
/// <summary>
/// The logical state of a TWAIN session.
/// </summary>
public enum TwainState
{
/// <summary>
/// Just a default value.
/// </summary>
Invalid = 0,
/// <summary>
/// The starting state, nothing loaded.
/// </summary>
S1 = 1,
/// <summary>
/// The DSM library has been loaded.
/// </summary>
S2 = 2,
/// <summary>
/// The DSM has been opened.
/// </summary>
S3 = 3,
/// <summary>
/// A data source has been opened, ready for configuration.
/// </summary>
S4 = 4,
/// <summary>
/// A data source has been enabled, GUI up or waiting to transfer first image.
/// </summary>
S5 = 5,
/// <summary>
/// Data is ready for transfer from the source.
/// </summary>
S6 = 6,
/// <summary>
/// Data is being transferred.
/// </summary>
S7 = 7,
/// <summary>
/// The starting state, nothing loaded.
/// </summary>
DsmUnloaded = S1,
/// <summary>
/// The DSM library has been loaded.
/// </summary>
DsmLoaded = S2,
/// <summary>
/// The DSM has been opened.
/// </summary>
DsmOpened = S3,
/// <summary>
/// A data source has been opened, ready for configuration.
/// </summary>
SourceOpened = S4,
/// <summary>
/// A data source has been enabled, GUI up or waiting to transfer first image.
/// </summary>
SourceEnabled = S5,
/// <summary>
/// Data is ready for transfer from the source.
/// </summary>
TransferReady = S6,
/// <summary>
/// Data is being transferred.
/// </summary>
Transferring = S7
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NTwain.Internals
{
/// <summary>
/// Interface that provides the correct methods for managing memory on data exchanged with TWAIN sources.
/// </summary>
interface IMemoryManager
{
/// <summary>
/// Function to allocate memory. Calls to this must be coupled with <see cref="Free"/> later.
/// </summary>
/// <param name="size">The size in bytes.</param>
/// <returns>Handle to the allocated memory.</returns>
IntPtr Allocate(uint size);
/// <summary>
/// Function to free memory.
/// </summary>
/// <param name="handle">The handle from <see cref="Allocate"/>.</param>
void Free(IntPtr handle);
/// <summary>
/// Function to lock some memory. Calls to this must be coupled with <see cref="Unlock"/> later.
/// </summary>
/// <param name="handle">The handle to allocated memory.</param>
/// <returns>Handle to the lock.</returns>
IntPtr Lock(IntPtr handle);
/// <summary>
/// Function to unlock a previously locked memory region.
/// </summary>
/// <param name="handle">The same handle passed <see cref="Lock"/>.</param>
void Unlock(IntPtr handle);
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace NTwain.Internals
{
// probably wrong
class LinuxMemoryManager : IMemoryManager
{
#region IMemoryManager Members
public IntPtr Allocate(uint size)
{
return Marshal.AllocHGlobal((int)size);
}
public void Free(IntPtr handle)
{
Marshal.FreeHGlobal(handle);
}
public IntPtr Lock(IntPtr handle)
{
return handle;
}
public void Unlock(IntPtr handle)
{
// no op
}
#endregion
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
namespace NTwain.Internals
{
/// <summary>
/// Provides methods for managing memory on data exchanged with twain sources using old win32 methods.
/// </summary>
class WinMemoryManager : IMemoryManager
{
public IntPtr Allocate(uint size)
{
IntPtr retVal = UnsafeNativeMethods.WinGlobalAlloc(0x0040, new UIntPtr(size));
if (retVal == IntPtr.Zero)
{
throw new Win32Exception();
}
return retVal;
}
public void Free(IntPtr handle)
{
UnsafeNativeMethods.WinGlobalFree(handle);
}
public IntPtr Lock(IntPtr handle)
{
return UnsafeNativeMethods.WinGlobalLock(handle);
}
public void Unlock(IntPtr handle)
{
UnsafeNativeMethods.WinGlobalUnlock(handle);
}
[SuppressUnmanagedCodeSecurity]
static class UnsafeNativeMethods
{
#region mem stuff for twain 1.x
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalAlloc")]
internal static extern IntPtr WinGlobalAlloc(uint uFlags, UIntPtr dwBytes);
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalFree")]
internal static extern IntPtr WinGlobalFree(IntPtr hMem);
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalLock")]
internal static extern IntPtr WinGlobalLock(IntPtr handle);
[DllImport("kernel32", SetLastError = true, EntryPoint = "GlobalUnlock")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool WinGlobalUnlock(IntPtr handle);
#endregion
}
}
}

113
src/NTwain/TwainConfig.cs Normal file
View File

@@ -0,0 +1,113 @@
using NTwain.Data;
using NTwain.Internals;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
namespace NTwain
{
public class TwainConfig
{
internal TwainConfig() { }
public bool PreferLegacyDsm { get; internal set; }
public string AppName { get; internal set; }
internal DataGroups DataGroup { get; set; }
internal IMemoryManager MemoryManager { get; set; }
}
public class TwainConfigurationBuilder
{
private bool _legacy;
private string _appName;
private string _companyName;
private DataGroups _dg = DataGroups.Image;
private bool _64bit;
private PlatformID _platform;
/// <summary>
/// Default ctor.
/// </summary>
public TwainConfigurationBuilder()
{
_64bit = IntPtr.Size == 8;
_platform = Environment.OSVersion.Platform;
}
/// <summary>
/// Specifies which DSM to use.
/// </summary>
/// <param name="legacyDsm"></param>
/// <returns></returns>
public TwainConfigurationBuilder UseDsm(bool legacyDsm = false)
{
if (legacyDsm)
{
if (_64bit) throw new InvalidOperationException("Cannot use legacy DSM under 64bit.");
}
_legacy = legacyDsm;
return this;
}
/// <summary>
/// Specifies what kind of data the app can to handle from TWAIN devices.
/// </summary>
/// <param name="image"></param>
/// <param name="audio"></param>
/// <returns></returns>
public TwainConfigurationBuilder HandlesDataType(bool image = true, bool audio = false)
{
DataGroups dg = DataGroups.None;
if (image) dg |= DataGroups.Image;
if (audio) dg |= DataGroups.Audio;
if (dg == DataGroups.None) throw new InvalidOperationException("No data type specified.");
_dg = dg;
return this;
}
/// <summary>
/// Defines the app info that will interface with TWAIN.
/// </summary>
/// <param name="appName"></param>
/// <param name="companyName"></param>
/// <returns></returns>
public TwainConfigurationBuilder DefineApp(string appName, string companyName = null)
{
_appName = appName;
_companyName = companyName;
return this;
}
/// <summary>
/// Defines the app info that will interface with TWAIN.
/// </summary>
/// <param name="appAssembly">Assembly containing the app info.</param>
/// <returns></returns>
public TwainConfigurationBuilder DefineApp(Assembly appAssembly)
{
var info = FileVersionInfo.GetVersionInfo(appAssembly.Location);
return DefineApp(info.ProductName, info.CompanyName);
}
/// <summary>
/// Builds the final configuration object.
/// </summary>
/// <returns></returns>
public TwainConfig Build()
{
return new TwainConfig
{
PreferLegacyDsm = _legacy,
AppName = _appName,
DataGroup = _dg
};
}
}
}

View File

@@ -0,0 +1,93 @@
using NTwain.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace NTwain
{
public class TwainSession : INotifyPropertyChanged, IDisposable
{
private TwainConfig _config;
public TwainSession(TwainConfig config)
{
_config = config;
var rc = Open();
if (rc != ReturnCode.Success)
{
}
}
private TwainState _state;
/// <summary>
/// Gets the logical state of the session.
/// </summary>
public TwainState State
{
get { return _state; }
internal set
{
_state = value;
RaisePropertyChanged(nameof(State));
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string property)
{
var handle = PropertyChanged;
handle?.Invoke(this, new PropertyChangedEventArgs(property));
}
ReturnCode Open()
{
if (State < TwainState.DsmOpened)
{
}
throw new NotImplementedException();
}
ReturnCode StepDown(TwainState targetState)
{
throw new NotImplementedException();
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
StepDown(TwainState.DsmLoaded);
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~TwainSession() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion
}
}