mirror of
https://github.com/soukoku/ntwain.git
synced 2025-11-24 08:47:06 +08:00
Merge pull request #3 from soukoku/v4-dev
More thread marshaling stuff.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# TWAIN Application-Library
|
# TWAIN dotnet library
|
||||||
|
|
||||||
NOTE: THIS IS V4 DEV AND DOESN'T WORK YET.
|
NOTE: THIS IS V4 DEV AND DOESN'T WORK YET.
|
||||||
USE V3 BRANCH FOR WORKING VERSION.
|
USE V3 BRANCH FOR WORKING VERSION.
|
||||||
@@ -10,9 +10,9 @@ This is a library created to make working with
|
|||||||
V4 of this lib has these goals:
|
V4 of this lib has these goals:
|
||||||
|
|
||||||
* Targets latest TWAIN version (2.4 as of this writing)
|
* Targets latest TWAIN version (2.4 as of this writing)
|
||||||
* Supports all the TWAIN functions in the spec
|
* Supports all the TWAIN functions in the spec (directly or through dotnet wrapper)
|
||||||
* Works with both 32 and 64 bit data sources
|
* Works with both 32 or 64 bit data sources, from 32 or 64 bit apps.
|
||||||
* Works on non-Windows platforms using dotnet core or mono (maybe)
|
* Works on non-Windows platforms using dotnet core
|
||||||
|
|
||||||
## Using the lib
|
## Using the lib
|
||||||
|
|
||||||
|
|||||||
@@ -63,16 +63,16 @@ namespace ConsoleApp
|
|||||||
Console.WriteLine("ERROR: " + ex.ToString());
|
Console.WriteLine("ERROR: " + ex.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("------------------------------------------------");
|
Console.WriteLine("---------------------------------------------");
|
||||||
Console.WriteLine("Test in progress, press Enter to stop testing...");
|
Console.WriteLine("Test in progress, press Enter to stop testing");
|
||||||
Console.WriteLine("------------------------------------------------");
|
Console.WriteLine("---------------------------------------------");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
var rc = session.StepDown(NTwain.Data.TwainState.S1);
|
var rc = session.StepDown(NTwain.Data.TwainState.S1);
|
||||||
Console.WriteLine("StepDown RC=" + rc);
|
Console.WriteLine("Session StepDown RC=" + rc);
|
||||||
|
Console.WriteLine("-------------------");
|
||||||
|
Console.WriteLine("Press Enter to exit");
|
||||||
|
Console.WriteLine("-------------------");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
Console.WriteLine("----------------------");
|
|
||||||
Console.WriteLine("Press Enter to exit...");
|
|
||||||
Console.WriteLine("----------------------");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Session_SourceDisabled(object sender, EventArgs e)
|
private static void Session_SourceDisabled(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -1,31 +1,62 @@
|
|||||||
// code block to verify size of things on different platforms and archtectures
|
// code block to verify size of things on different platforms and archtectures
|
||||||
|
#if _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include "twain.h"
|
#include "twain.h"
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
bool is32 = sizeof(void *) == 4;
|
bool is32 = sizeof(void *) == 4;
|
||||||
bool is64 = sizeof(void *) == 8;
|
bool is64 = sizeof(void *) == 8;
|
||||||
std::cout << (is32 ? "32bit= " : "") << (is64 ? "64bit" : "") << std::endl;
|
std::cout << (is32 ? "32bit" : "") << (is64 ? "64bit" : "") << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "TW_STR32 = " << sizeof TW_STR32 << std::endl;
|
std::cout << "TW_STR32 = " << sizeof(TW_STR32) << std::endl;
|
||||||
std::cout << "TW_STR64 = " << sizeof TW_STR64 << std::endl;
|
std::cout << "TW_STR64 = " << sizeof(TW_STR64) << std::endl;
|
||||||
std::cout << "TW_STR128 = " << sizeof TW_STR128 << std::endl;
|
std::cout << "TW_STR128 = " << sizeof(TW_STR128) << std::endl;
|
||||||
std::cout << "TW_STR255 = " << sizeof TW_STR255 << std::endl;
|
std::cout << "TW_STR255 = " << sizeof(TW_STR255) << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "TW_INT8 = " << sizeof TW_INT8 << std::endl;
|
std::cout << "TW_INT8 = " << sizeof(TW_INT8) << std::endl;
|
||||||
std::cout << "TW_INT16 = " << sizeof TW_INT16 << std::endl;
|
std::cout << "TW_INT16 = " << sizeof(TW_INT16) << std::endl;
|
||||||
std::cout << "TW_INT32 = " << sizeof TW_INT32 << std::endl;
|
std::cout << "TW_INT32 = " << sizeof(TW_INT32) << std::endl;
|
||||||
std::cout << "TW_UINT8 = " << sizeof TW_UINT8 << std::endl;
|
std::cout << "TW_UINT8 = " << sizeof(TW_UINT8) << std::endl;
|
||||||
std::cout << "TW_UINT16 = " << sizeof TW_UINT16 << std::endl;
|
std::cout << "TW_UINT16 = " << sizeof(TW_UINT16) << std::endl;
|
||||||
std::cout << "TW_UINT32 = " << sizeof TW_UINT32 << std::endl;
|
std::cout << "TW_UINT32 = " << sizeof(TW_UINT32) << std::endl;
|
||||||
std::cout << "TW_BOOL = " << sizeof TW_BOOL << std::endl;
|
std::cout << "TW_BOOL = " << sizeof(TW_BOOL) << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "TW_HANDLE = " << sizeof TW_HANDLE << std::endl;
|
std::cout << "TW_IDENTITY = " << sizeof(TW_IDENTITY) << std::endl;
|
||||||
std::cout << "TW_MEMREF = " << sizeof TW_MEMREF << std::endl;
|
|
||||||
std::cout << "TW_UINTPTR= " << sizeof TW_UINTPTR << std::endl;
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "TW_IDENTITY = " << sizeof TW_IDENTITY << std::endl;
|
std::cout << "TW_HANDLE = " << sizeof(TW_HANDLE) << std::endl;
|
||||||
|
std::cout << "TW_MEMREF = " << sizeof(TW_MEMREF) << std::endl;
|
||||||
|
std::cout << "TW_UINTPTR= " << sizeof(TW_UINTPTR) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "TW_ONEVALUE = " << sizeof(TW_ONEVALUE) << std::endl;
|
||||||
|
std::cout << "TW_ARRAY = " << sizeof(TW_ARRAY) << std::endl;
|
||||||
|
std::cout << "TW_ENUMERATION = " << sizeof(TW_ENUMERATION) << std::endl;
|
||||||
|
std::cout << "TW_RANGE = " << sizeof(TW_RANGE) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "TW_GRAYRESPONSE = " << sizeof(TW_GRAYRESPONSE) << std::endl;
|
||||||
|
std::cout << "TW_RGBRESPONSE = " << sizeof(TW_RGBRESPONSE) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "TW_CALLBACK = " << sizeof(TW_CALLBACK) << std::endl;
|
||||||
|
std::cout << "TW_CALLBACK2 = " << sizeof(TW_CALLBACK2) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "TW_USERINTERFACE = " << sizeof(TW_USERINTERFACE) << std::endl;
|
||||||
|
std::cout << "TW_PENDINGXFERS = " << sizeof(TW_PENDINGXFERS) << std::endl;
|
||||||
|
std::cout << "TW_IMAGEMEMXFER = " << sizeof(TW_IMAGEMEMXFER) << std::endl;
|
||||||
|
std::cout << "TW_MEMORY = " << sizeof(TW_MEMORY) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "TW_CAPABILITY = " << sizeof(TW_CAPABILITY) << std::endl;
|
||||||
|
std::cout << "TW_CUSTOMDSDATA = " << sizeof(TW_CUSTOMDSDATA) << std::endl;
|
||||||
|
std::cout << "TW_EVENT = " << sizeof(TW_EVENT) << std::endl;
|
||||||
|
std::cout << "TW_INFO = " << sizeof(TW_INFO) << std::endl;
|
||||||
|
std::cout << "TW_FILESYSTEM = " << sizeof(TW_FILESYSTEM) << std::endl;
|
||||||
|
std::cout << "TW_JPEGCOMPRESSION = " << sizeof(TW_JPEGCOMPRESSION) << std::endl;
|
||||||
|
std::cout << "TW_PASSTHRU = " << sizeof(TW_PASSTHRU) << std::endl;
|
||||||
|
std::cout << "TW_STATUSUTF8 = " << sizeof(TW_STATUSUTF8) << std::endl;
|
||||||
|
std::cout << "TW_TWAINDIRECT = " << sizeof(TW_TWAINDIRECT) << std::endl;
|
||||||
int test;
|
int test;
|
||||||
std::cin >> test;
|
std::cin >> test;
|
||||||
}
|
}
|
||||||
|
|||||||
2265
size-test/twain.h
Normal file
2265
size-test/twain.h
Normal file
File diff suppressed because it is too large
Load Diff
122
src/NTwain/Data/Containers.cs
Normal file
122
src/NTwain/Data/Containers.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NTwain.Data
|
||||||
|
{
|
||||||
|
// use custom containers for twain container types to not have to worry about memory mgmt
|
||||||
|
// after giving it to consumers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Container for one value.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public struct OneValue<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type of the item.
|
||||||
|
/// </summary>
|
||||||
|
public ItemType Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value.
|
||||||
|
/// </summary>
|
||||||
|
public T Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores a group of associated individual values for a capability.
|
||||||
|
/// The values need have no relationship to one another aside from
|
||||||
|
/// being used to describe the same "value" of the capability
|
||||||
|
/// </summary>
|
||||||
|
public struct ArrayValue<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type of items in the array.
|
||||||
|
/// </summary>
|
||||||
|
public ItemType Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Array of values.
|
||||||
|
/// </summary>
|
||||||
|
public T[] ItemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An enumeration stores a list of individual values, with one of the items designated as the current
|
||||||
|
/// value. There is no required order to the values in the list.
|
||||||
|
/// </summary>
|
||||||
|
public struct EnumValue<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the byte offset of the item list from a Ptr to the first item.
|
||||||
|
/// </summary>
|
||||||
|
internal const int ValuesOffset = 14;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of items in the enumerated list.
|
||||||
|
/// </summary>
|
||||||
|
public ItemType Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The item number, or index (zero-based) into
|
||||||
|
/// <see cref="ItemList"/>, of the "current"
|
||||||
|
/// value for the capability.
|
||||||
|
/// </summary>
|
||||||
|
public int CurrentIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The item number, or index (zero-based) into
|
||||||
|
/// <see cref="ItemList"/>, of the "power-on"
|
||||||
|
/// value for the capability.
|
||||||
|
/// </summary>
|
||||||
|
public int DefaultIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The enumerated value list.
|
||||||
|
/// </summary>
|
||||||
|
public T[] ItemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Container for a range of values.
|
||||||
|
/// </summary>
|
||||||
|
public struct RangeValue
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type of items in the container.
|
||||||
|
/// </summary>
|
||||||
|
public ItemType Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The least positive/most negative value of the range.
|
||||||
|
/// </summary>
|
||||||
|
public int Min;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The most positive/least negative value of the range.
|
||||||
|
/// </summary>
|
||||||
|
public int Max;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delta between two adjacent values of the range.
|
||||||
|
/// e.g. Item2 - Item1 = StepSize;
|
||||||
|
/// </summary>
|
||||||
|
public int StepSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The device’s "power-on" value for the capability. If the application is
|
||||||
|
/// performing a MSG_SET operation and isn’t sure what the default
|
||||||
|
/// value is, set this field to <see cref="TwainConst.DontCare32"/>.
|
||||||
|
/// </summary>
|
||||||
|
public int DefaultValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value to which the device (or its user interface) is currently set to
|
||||||
|
/// for the capability.
|
||||||
|
/// </summary>
|
||||||
|
public int CurrentValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,9 +74,9 @@ namespace NTwain.Data
|
|||||||
partial class TW_ARRAY
|
partial class TW_ARRAY
|
||||||
{
|
{
|
||||||
// TODO: _itemType in mac is TW_UINT32?
|
// TODO: _itemType in mac is TW_UINT32?
|
||||||
TW_UINT16 _itemType;
|
public TW_UINT16 ItemType;
|
||||||
TW_UINT32 _numItems;
|
public TW_UINT32 NumItems;
|
||||||
object[] _itemList;
|
public IntPtr ItemList;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 2),
|
[StructLayout(LayoutKind.Sequential, Pack = 2),
|
||||||
@@ -181,12 +181,12 @@ namespace NTwain.Data
|
|||||||
partial class TW_ENUMERATION
|
partial class TW_ENUMERATION
|
||||||
{
|
{
|
||||||
// TODO: _itemType in mac is TW_UINT32?
|
// TODO: _itemType in mac is TW_UINT32?
|
||||||
TW_UINT16 _itemType;
|
public TW_UINT16 ItemType;
|
||||||
// TODO: these are UInt64 in linux 64 bit?
|
// TODO: these are UInt64 in linux 64 bit?
|
||||||
TW_UINT32 _numItems;
|
public TW_UINT32 NumItems;
|
||||||
TW_UINT32 _currentIndex;
|
public TW_UINT32 CurrentIndex;
|
||||||
TW_UINT32 _defaultIndex;
|
public TW_UINT32 DefaultIndex;
|
||||||
object[] _itemList;
|
public IntPtr ItemList;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||||
@@ -431,8 +431,8 @@ namespace NTwain.Data
|
|||||||
partial class TW_ONEVALUE
|
partial class TW_ONEVALUE
|
||||||
{
|
{
|
||||||
// TODO: mac is different?
|
// TODO: mac is different?
|
||||||
TW_UINT16 _itemType;
|
public TW_UINT16 ItemType;
|
||||||
TW_UINT32 _item;
|
public TW_UINT32 Item;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||||
@@ -468,12 +468,12 @@ namespace NTwain.Data
|
|||||||
partial class TW_RANGE
|
partial class TW_RANGE
|
||||||
{
|
{
|
||||||
// TODO: mac & linux are different?
|
// TODO: mac & linux are different?
|
||||||
TW_UINT16 _itemType;
|
public TW_UINT16 ItemType;
|
||||||
TW_UINT32 _minValue;
|
public TW_UINT32 MinValue;
|
||||||
TW_UINT32 _maxValue;
|
public TW_UINT32 MaxValue;
|
||||||
TW_UINT32 _stepSize;
|
public TW_UINT32 StepSize;
|
||||||
TW_UINT32 _defaultValue;
|
public TW_UINT32 DefaultValue;
|
||||||
TW_UINT32 _currentValue;
|
public TW_UINT32 CurrentValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
//[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||||
|
|||||||
@@ -526,39 +526,6 @@ namespace NTwain.Data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Stores a group of associated individual values for a capability.
|
|
||||||
// /// The values need have no relationship to one another aside from
|
|
||||||
// /// being used to describe the same "value" of the capability
|
|
||||||
// /// </summary>
|
|
||||||
// public partial class TW_ARRAY
|
|
||||||
// {
|
|
||||||
// /// <summary>
|
|
||||||
// /// The type of items in the array. All items in the array have the same size.
|
|
||||||
// /// </summary>
|
|
||||||
// public ItemType ItemType { get { return (ItemType)_itemType; } set { _itemType = (ushort)value; } }
|
|
||||||
|
|
||||||
// ///// <summary>
|
|
||||||
// ///// How many items are in the array.
|
|
||||||
// ///// </summary>
|
|
||||||
// //public int Count { get { return (int)_numItems; } set { _numItems = (uint)value; } }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Array of ItemType values starts here.
|
|
||||||
// /// </summary>
|
|
||||||
|
|
||||||
// public object[] ItemList
|
|
||||||
// {
|
|
||||||
// get { return _itemList; }
|
|
||||||
// set
|
|
||||||
// {
|
|
||||||
// _itemList = value;
|
|
||||||
// if (value != null) { _numItems = (uint)value.Length; }
|
|
||||||
// else { _numItems = 0; }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to get audio info.
|
/// Used to get audio info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1120,52 +1087,6 @@ namespace NTwain.Data
|
|||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// An enumeration stores a list of individual values, with one of the items designated as the current
|
|
||||||
// /// value. There is no required order to the values in the list.
|
|
||||||
// /// </summary>
|
|
||||||
// public partial class TW_ENUMERATION
|
|
||||||
// {
|
|
||||||
// /// <summary>
|
|
||||||
// /// The type of items in the enumerated list. All items in the array have the same size.
|
|
||||||
// /// </summary>
|
|
||||||
// public ItemType ItemType { get { return (ItemType)_itemType; } set { _itemType = (ushort)value; } }
|
|
||||||
// ///// <summary>
|
|
||||||
// ///// How many items are in the enumeration.
|
|
||||||
// ///// </summary>
|
|
||||||
// //public int Count { get { return (int)_numItems; } set { _numItems = (uint)value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The item number, or index (zero-based) into <see cref="ItemList"/>, of the "current"
|
|
||||||
// /// value for the capability.
|
|
||||||
// /// </summary>
|
|
||||||
// public int CurrentIndex { get { return (int)_currentIndex; } set { _currentIndex = (uint)value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The item number, or index (zero-based) into <see cref="ItemList"/>, of the "power-on"
|
|
||||||
// /// value for the capability.
|
|
||||||
// /// </summary>
|
|
||||||
// public int DefaultIndex { get { return (int)_defaultIndex; } set { _defaultIndex = (uint)value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The enumerated list: one value resides within each array element.
|
|
||||||
// /// </summary>
|
|
||||||
|
|
||||||
// public object[] ItemList
|
|
||||||
// {
|
|
||||||
// get { return _itemList; }
|
|
||||||
// set
|
|
||||||
// {
|
|
||||||
// _itemList = value;
|
|
||||||
// if (value != null) { _numItems = (uint)value.Length; }
|
|
||||||
// else { _numItems = 0; }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Gets the byte offset of the item list from a Ptr to the first item.
|
|
||||||
// /// </summary>
|
|
||||||
// internal const int ItemOffset = 14;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// /// <summary>
|
// /// <summary>
|
||||||
// /// This structure is used to pass specific information between the data source and the application
|
// /// This structure is used to pass specific information between the data source and the application
|
||||||
// /// through <see cref="TW_EXTIMAGEINFO"/>.
|
// /// through <see cref="TW_EXTIMAGEINFO"/>.
|
||||||
@@ -1954,21 +1875,6 @@ namespace NTwain.Data
|
|||||||
public uint SheetCount => _sheetCount;
|
public uint SheetCount => _sheetCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Container for one value.
|
|
||||||
// /// </summary>
|
|
||||||
// public partial class TW_ONEVALUE
|
|
||||||
// {
|
|
||||||
// /// <summary>
|
|
||||||
// /// The type of the item.
|
|
||||||
// /// </summary>
|
|
||||||
// public ItemType ItemType { get { return (ItemType)_itemType; } set { _itemType = (ushort)value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The value.
|
|
||||||
// /// </summary>
|
|
||||||
// public uint Item { get { return _item; } set { _item = value; } }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This structure holds the color palette information for buffered memory transfers of type
|
/// This structure holds the color palette information for buffered memory transfers of type
|
||||||
@@ -2053,41 +1959,6 @@ namespace NTwain.Data
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Container for a range of values.
|
|
||||||
// /// </summary>
|
|
||||||
// public partial class TW_RANGE
|
|
||||||
// {
|
|
||||||
// /// <summary>
|
|
||||||
// /// The type of items in the list.
|
|
||||||
// /// </summary>
|
|
||||||
// public ItemType ItemType { get { return (ItemType)_itemType; } set { _itemType = (ushort)value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The least positive/most negative value of the range.
|
|
||||||
// /// </summary>
|
|
||||||
// public uint MinValue { get { return _minValue; } set { _minValue = value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The most positive/least negative value of the range.
|
|
||||||
// /// </summary>
|
|
||||||
// public uint MaxValue { get { return _maxValue; } set { _maxValue = value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The delta between two adjacent values of the range.
|
|
||||||
// /// e.g. Item2 - Item1 = StepSize;
|
|
||||||
// /// </summary>
|
|
||||||
// public uint StepSize { get { return _stepSize; } set { _stepSize = value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The device’s "power-on" value for the capability. If the application is
|
|
||||||
// /// performing a MSG_SET operation and isn’t sure what the default
|
|
||||||
// /// value is, set this field to <see cref="TwainConst.DontCare32"/>.
|
|
||||||
// /// </summary>
|
|
||||||
// public uint DefaultValue { get { return _defaultValue; } set { _defaultValue = value; } }
|
|
||||||
// /// <summary>
|
|
||||||
// /// The value to which the device (or its user interface) is currently set to
|
|
||||||
// /// for the capability.
|
|
||||||
// /// </summary>
|
|
||||||
// public uint CurrentValue { get { return _currentValue; } set { _currentValue = value; } }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ///// <summary>
|
// ///// <summary>
|
||||||
// ///// This structure is used by the application to specify a set of mapping values to be applied to RGB
|
// ///// This structure is used by the application to specify a set of mapping values to be applied to RGB
|
||||||
// ///// color data. Use this structure for RGB data whose bit depth is up to, and including, 8-bits.
|
// ///// color data. Use this structure for RGB data whose bit depth is up to, and including, 8-bits.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace NTwain.Data.Win32
|
|||||||
struct MSG
|
struct MSG
|
||||||
{
|
{
|
||||||
public IntPtr hwnd;
|
public IntPtr hwnd;
|
||||||
public int message;
|
public uint message;
|
||||||
public IntPtr wParam;
|
public IntPtr wParam;
|
||||||
public IntPtr lParam;
|
public IntPtr lParam;
|
||||||
int time;
|
int time;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace NTwain.Data.Win32
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumerated values of window messages.
|
/// Enumerated values of window messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
enum WindowMessage
|
enum WindowMessage : uint
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs no operation. An application sends the WM_NULL message if it wants to post a message that the recipient window will ignore.
|
/// Performs no operation. An application sends the WM_NULL message if it wants to post a message that the recipient window will ignore.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace NTwain
|
|||||||
public ReturnCode ShowUI(IntPtr windowHandle, bool modal = false)
|
public ReturnCode ShowUI(IntPtr windowHandle, bool modal = false)
|
||||||
{
|
{
|
||||||
var rc = ReturnCode.Failure;
|
var rc = ReturnCode.Failure;
|
||||||
Session.Invoke(() =>
|
Session.InternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var ui = new TW_USERINTERFACE
|
var ui = new TW_USERINTERFACE
|
||||||
{
|
{
|
||||||
@@ -72,7 +72,7 @@ namespace NTwain
|
|||||||
public ReturnCode Enable(bool showUI, IntPtr windowHandle, bool modal = false)
|
public ReturnCode Enable(bool showUI, IntPtr windowHandle, bool modal = false)
|
||||||
{
|
{
|
||||||
var rc = ReturnCode.Failure;
|
var rc = ReturnCode.Failure;
|
||||||
Session.Invoke(() =>
|
Session.InternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var ui = new TW_USERINTERFACE
|
var ui = new TW_USERINTERFACE
|
||||||
{
|
{
|
||||||
|
|||||||
28
src/NTwain/Internals/ExceptionExtensions.cs
Normal file
28
src/NTwain/Internals/ExceptionExtensions.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NTwain.Internals
|
||||||
|
{
|
||||||
|
static class ExceptionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rethrows the specified excetion while keeping stack trace
|
||||||
|
/// if not null.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">The ex.</param>
|
||||||
|
public static void TryRethrow(this Exception ex)
|
||||||
|
{
|
||||||
|
if (ex != null)
|
||||||
|
{
|
||||||
|
typeof(Exception).GetMethod("PrepForRemoting",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(ex, new object[0]);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/NTwain/Resources/MsgText.Designer.cs
generated
27
src/NTwain/Resources/MsgText.Designer.cs
generated
@@ -68,5 +68,32 @@ namespace NTwain.Resources {
|
|||||||
return ResourceManager.GetString("MaxStringLengthExceeded", resourceCulture);
|
return ResourceManager.GetString("MaxStringLengthExceeded", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to No data type specified..
|
||||||
|
/// </summary>
|
||||||
|
internal static string NoDataTypesSpecified {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("NoDataTypesSpecified", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to This platform ({0}) is not supported..
|
||||||
|
/// </summary>
|
||||||
|
internal static string PlatformNotSupported {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PlatformNotSupported", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Source is not from this session..
|
||||||
|
/// </summary>
|
||||||
|
internal static string SourceNotThisSession {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SourceNotThisSession", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,4 +120,13 @@
|
|||||||
<data name="MaxStringLengthExceeded" xml:space="preserve">
|
<data name="MaxStringLengthExceeded" xml:space="preserve">
|
||||||
<value>The string value has exceeded the maximum length of {0}.</value>
|
<value>The string value has exceeded the maximum length of {0}.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="NoDataTypesSpecified" xml:space="preserve">
|
||||||
|
<value>No data type specified.</value>
|
||||||
|
</data>
|
||||||
|
<data name="PlatformNotSupported" xml:space="preserve">
|
||||||
|
<value>This platform ({0}) is not supported.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SourceNotThisSession" xml:space="preserve">
|
||||||
|
<value>Source is not from this session.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -8,6 +8,8 @@ namespace NTwain.Threading
|
|||||||
private ManualResetEventSlim waiter;
|
private ManualResetEventSlim waiter;
|
||||||
private Action action;
|
private Action action;
|
||||||
|
|
||||||
|
public Exception Error { get; private set; }
|
||||||
|
|
||||||
public ActionItem(Action action)
|
public ActionItem(Action action)
|
||||||
{
|
{
|
||||||
this.action = action;
|
this.action = action;
|
||||||
@@ -25,6 +27,10 @@ namespace NTwain.Threading
|
|||||||
{
|
{
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Error = ex;
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
waiter?.Set();
|
waiter?.Set();
|
||||||
|
|||||||
110
src/NTwain/Threading/DispatcherLoop.cs
Normal file
110
src/NTwain/Threading/DispatcherLoop.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using NTwain.Data.Win32;
|
||||||
|
using NTwain.Internals;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace NTwain.Threading
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an internal message pump on non-Windows using a background thread.
|
||||||
|
/// </summary>
|
||||||
|
class DispatcherLoop : IThreadContext
|
||||||
|
{
|
||||||
|
readonly TwainSession session;
|
||||||
|
BlockingCollection<ActionItem> actionQueue;
|
||||||
|
CancellationTokenSource stopToken;
|
||||||
|
|
||||||
|
Thread loopThread;
|
||||||
|
|
||||||
|
public DispatcherLoop(TwainSession session)
|
||||||
|
{
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
if (loopThread != null) return;
|
||||||
|
|
||||||
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(WinMsgLoop)}.{nameof(Start)}()");
|
||||||
|
|
||||||
|
actionQueue = new BlockingCollection<ActionItem>();
|
||||||
|
stopToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
// startWaiter ensures thread is running before this method returns
|
||||||
|
using (var startWaiter = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
loopThread = new Thread(new ThreadStart(() =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: starting msg pump...");
|
||||||
|
|
||||||
|
startWaiter.Set();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (actionQueue.TryTake(out ActionItem work, -1, stopToken.Token))
|
||||||
|
{
|
||||||
|
work.DoAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// clear queue
|
||||||
|
actionQueue.Dispose();
|
||||||
|
|
||||||
|
loopThread = null;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
loopThread.IsBackground = true;
|
||||||
|
loopThread.TrySetApartmentState(ApartmentState.STA);
|
||||||
|
loopThread.Start();
|
||||||
|
startWaiter.Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
if (!stopToken.IsCancellationRequested) stopToken.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSameThread()
|
||||||
|
{
|
||||||
|
return loopThread == Thread.CurrentThread || loopThread == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Invoke(Action action)
|
||||||
|
{
|
||||||
|
if (IsSameThread())
|
||||||
|
{
|
||||||
|
// ok
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// queue up work
|
||||||
|
using (var waiter = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
var work = new ActionItem(waiter, action);
|
||||||
|
actionQueue.TryAdd(work);
|
||||||
|
waiter.Wait();
|
||||||
|
work.Error.TryRethrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginInvoke(Action action)
|
||||||
|
{
|
||||||
|
actionQueue.TryAdd(new ActionItem(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/NTwain/Threading/IThreadContext.cs
Normal file
32
src/NTwain/Threading/IThreadContext.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NTwain.Threading
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for running some work on an associated thread.
|
||||||
|
/// </summary>
|
||||||
|
interface IThreadContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the context if supported.
|
||||||
|
/// </summary>
|
||||||
|
void Start();
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the context if supported.
|
||||||
|
/// </summary>
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the action synchronously on the associated thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
void Invoke(Action action);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the action asynchronously on the associated thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
void BeginInvoke(Action action);
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/NTwain/Threading/UIThreadContext.cs
Normal file
83
src/NTwain/Threading/UIThreadContext.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using NTwain.Internals;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NTwain.Threading
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="IThreadContext"/> using <see cref="SynchronizationContext"/>
|
||||||
|
/// for running actions on an UI thread.
|
||||||
|
/// </summary>
|
||||||
|
class UIThreadContext : IThreadContext
|
||||||
|
{
|
||||||
|
private readonly SynchronizationContext context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="UIThreadContext"/> using
|
||||||
|
/// the specified <see cref="SynchronizationContext"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public UIThreadContext(SynchronizationContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the action asynchronously on the <see cref="SynchronizationContext"/> thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
public void BeginInvoke(Action action)
|
||||||
|
{
|
||||||
|
if (action == null) return;
|
||||||
|
|
||||||
|
context.Post(o =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// TODO: do something
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the action synchronously on the <see cref="SynchronizationContext"/> thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
public void Invoke(Action action)
|
||||||
|
{
|
||||||
|
if (action == null) return;
|
||||||
|
|
||||||
|
Exception error = null;
|
||||||
|
context.Send(o =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = ex;
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
error.TryRethrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IThreadContext.Start()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IThreadContext.Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
using NTwain.Data.Win32;
|
using NTwain.Data.Win32;
|
||||||
|
using NTwain.Internals;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -15,15 +17,13 @@ namespace NTwain.Threading
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides an internal message pump on Windows using a background thread.
|
/// Provides an internal message pump on Windows using a background thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class WinMsgLoop
|
class WinMsgLoop : IThreadContext
|
||||||
{
|
{
|
||||||
static ushort classAtom;
|
static ushort classAtom;
|
||||||
static IntPtr hInstance;
|
static IntPtr hInstance;
|
||||||
static readonly int dequeMsg = UnsafeNativeMethods.RegisterWindowMessage("WinMsgLoopQueue");
|
static readonly uint dequeMsg = UnsafeNativeMethods.RegisterWindowMessageW("WinMsgLoopQueue");
|
||||||
|
// keep delegate around to prevent GC
|
||||||
const int CW_USEDEFAULT = -1;
|
static readonly WndProcDelegate WndProc = new WndProcDelegate((hWnd, msg, wParam, lParam) =>
|
||||||
|
|
||||||
static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
|
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Dummy window got msg {(WindowMessage)msg}.");
|
Debug.WriteLine($"Dummy window got msg {(WindowMessage)msg}.");
|
||||||
switch ((WindowMessage)msg)
|
switch ((WindowMessage)msg)
|
||||||
@@ -32,20 +32,22 @@ namespace NTwain.Threading
|
|||||||
UnsafeNativeMethods.PostQuitMessage(0);
|
UnsafeNativeMethods.PostQuitMessage(0);
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
}
|
}
|
||||||
return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wParam, lParam);
|
return UnsafeNativeMethods.DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
const int CW_USEDEFAULT = -1;
|
||||||
|
|
||||||
static void InitGlobal()
|
static void InitGlobal()
|
||||||
{
|
{
|
||||||
if (classAtom == 0)
|
if (classAtom == 0)
|
||||||
{
|
{
|
||||||
hInstance = UnsafeNativeMethods.GetModuleHandle(null);
|
hInstance = UnsafeNativeMethods.GetModuleHandleW(null);
|
||||||
|
|
||||||
var wc = new WNDCLASSEX();
|
var wc = new WNDCLASSEX();
|
||||||
wc.cbSize = Marshal.SizeOf(wc);
|
wc.cbSize = Marshal.SizeOf(wc);
|
||||||
wc.style = ClassStyles.CS_VREDRAW | ClassStyles.CS_HREDRAW;
|
wc.style = ClassStyles.CS_VREDRAW | ClassStyles.CS_HREDRAW;
|
||||||
|
|
||||||
var procPtr = Marshal.GetFunctionPointerForDelegate(new WndProcDelegate(WndProc));
|
var procPtr = Marshal.GetFunctionPointerForDelegate(WndProc);
|
||||||
wc.lpfnWndProc = procPtr;
|
wc.lpfnWndProc = procPtr;
|
||||||
|
|
||||||
wc.cbClsExtra = 0;
|
wc.cbClsExtra = 0;
|
||||||
@@ -58,7 +60,7 @@ namespace NTwain.Threading
|
|||||||
wc.lpszClassName = Guid.NewGuid().ToString("n");
|
wc.lpszClassName = Guid.NewGuid().ToString("n");
|
||||||
wc.hIconSm = IntPtr.Zero;
|
wc.hIconSm = IntPtr.Zero;
|
||||||
|
|
||||||
classAtom = UnsafeNativeMethods.RegisterClassEx(ref wc);
|
classAtom = UnsafeNativeMethods.RegisterClassExW(ref wc);
|
||||||
if (classAtom == 0)
|
if (classAtom == 0)
|
||||||
{
|
{
|
||||||
throw new Win32Exception();
|
throw new Win32Exception();
|
||||||
@@ -99,7 +101,7 @@ namespace NTwain.Threading
|
|||||||
{
|
{
|
||||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: starting msg pump...");
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: starting msg pump...");
|
||||||
|
|
||||||
hWnd = UnsafeNativeMethods.CreateWindowEx(WindowStylesEx.WS_EX_LEFT, classAtom, Guid.NewGuid().ToString(),
|
hWnd = UnsafeNativeMethods.CreateWindowExW(WindowStylesEx.WS_EX_LEFT, classAtom, Guid.NewGuid().ToString(),
|
||||||
WindowStyles.WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
WindowStyles.WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
IntPtr.Zero, IntPtr.Zero, hInstance, IntPtr.Zero);
|
IntPtr.Zero, IntPtr.Zero, hInstance, IntPtr.Zero);
|
||||||
if (hWnd == IntPtr.Zero)
|
if (hWnd == IntPtr.Zero)
|
||||||
@@ -119,7 +121,11 @@ namespace NTwain.Threading
|
|||||||
startWaiter.Set();
|
startWaiter.Set();
|
||||||
|
|
||||||
MSG msg = default;
|
MSG msg = default;
|
||||||
while (!stop && UnsafeNativeMethods.GetMessage(ref msg, IntPtr.Zero, 0, 0))
|
var msgRes = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!stop &&
|
||||||
|
(msgRes = UnsafeNativeMethods.GetMessageW(ref msg, IntPtr.Zero, 0, 0)) > 0)
|
||||||
{
|
{
|
||||||
UnsafeNativeMethods.TranslateMessage(ref msg);
|
UnsafeNativeMethods.TranslateMessage(ref msg);
|
||||||
if (!session.HandleWindowsMessage(ref msg))
|
if (!session.HandleWindowsMessage(ref msg))
|
||||||
@@ -134,10 +140,13 @@ namespace NTwain.Threading
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UnsafeNativeMethods.DispatchMessage(ref msg);
|
UnsafeNativeMethods.DispatchMessageW(ref msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
// clear queue
|
// clear queue
|
||||||
while (actionQueue.TryDequeue(out ActionItem dummy)) { }
|
while (actionQueue.TryDequeue(out ActionItem dummy)) { }
|
||||||
|
|
||||||
@@ -145,13 +154,14 @@ namespace NTwain.Threading
|
|||||||
hWnd = IntPtr.Zero;
|
hWnd = IntPtr.Zero;
|
||||||
stop = false;
|
stop = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
loopThread.IsBackground = true;
|
loopThread.IsBackground = true;
|
||||||
loopThread.SetApartmentState(ApartmentState.STA);
|
loopThread.SetApartmentState(ApartmentState.STA);
|
||||||
loopThread.Start();
|
loopThread.Start();
|
||||||
startWaiter.Wait();
|
startWaiter.Wait();
|
||||||
|
|
||||||
if (startErr != null) throw startErr;
|
startErr.TryRethrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,10 +191,7 @@ namespace NTwain.Threading
|
|||||||
return loopThread == Thread.CurrentThread || loopThread == null;
|
return loopThread == Thread.CurrentThread || loopThread == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs the action synchronously on the internal message pump thread.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="action"></param>
|
|
||||||
public void Invoke(Action action)
|
public void Invoke(Action action)
|
||||||
{
|
{
|
||||||
if (IsSameThread())
|
if (IsSameThread())
|
||||||
@@ -197,24 +204,23 @@ namespace NTwain.Threading
|
|||||||
// queue up work
|
// queue up work
|
||||||
using (var waiter = new ManualResetEventSlim())
|
using (var waiter = new ManualResetEventSlim())
|
||||||
{
|
{
|
||||||
actionQueue.Enqueue(new ActionItem(waiter, action));
|
var work = new ActionItem(waiter, action);
|
||||||
UnsafeNativeMethods.PostMessage(hWnd, dequeMsg, IntPtr.Zero, IntPtr.Zero);
|
actionQueue.Enqueue(work);
|
||||||
|
UnsafeNativeMethods.PostMessageW(hWnd, dequeMsg, IntPtr.Zero, IntPtr.Zero);
|
||||||
waiter.Wait();
|
waiter.Wait();
|
||||||
|
work.Error.TryRethrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs the action asynchronously on the internal message pump thread.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="action"></param>
|
|
||||||
public void BeginInvoke(Action action)
|
public void BeginInvoke(Action action)
|
||||||
{
|
{
|
||||||
if (hWnd == IntPtr.Zero) action();
|
if (hWnd == IntPtr.Zero) action();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
actionQueue.Enqueue(new ActionItem(action));
|
actionQueue.Enqueue(new ActionItem(action));
|
||||||
UnsafeNativeMethods.PostMessage(hWnd, dequeMsg, IntPtr.Zero, IntPtr.Zero);
|
UnsafeNativeMethods.PostMessageW(hWnd, dequeMsg, IntPtr.Zero, IntPtr.Zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,43 +228,48 @@ namespace NTwain.Threading
|
|||||||
[SuppressUnmanagedCodeSecurity]
|
[SuppressUnmanagedCodeSecurity]
|
||||||
internal static class UnsafeNativeMethods
|
internal static class UnsafeNativeMethods
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ThrowOnUnmappableChar = true, BestFitMapping = false)]
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode,
|
||||||
public static extern IntPtr GetModuleHandle(string modName);
|
SetLastError = true, ExactSpelling = true)]
|
||||||
|
public static extern IntPtr GetModuleHandleW([MarshalAs(UnmanagedType.LPWStr)] string modName);
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode,
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
SetLastError = true, ExactSpelling = true)]
|
||||||
public static extern bool GetMessage([In, Out] ref MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
|
public static extern int GetMessageW([In, Out] ref MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||||
public static extern bool TranslateMessage([In, Out] ref MSG lpMsg);
|
public static extern int TranslateMessage([In, Out] ref MSG lpMsg);
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||||
public static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
|
public static extern IntPtr DispatchMessageW([In] ref MSG lpmsg);
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode,
|
||||||
public static extern int PostMessage(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam);
|
SetLastError = true, ExactSpelling = true)]
|
||||||
|
public static extern int PostMessageW(IntPtr hWnd, uint msg, IntPtr wparam, IntPtr lparam);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll", ExactSpelling = true)]
|
||||||
public static extern void PostQuitMessage(int nExitCode);
|
public static extern void PostQuitMessage(int nExitCode);
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||||
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
public static extern IntPtr DefWindowProcW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto, BestFitMapping = false)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode,
|
||||||
public static extern int RegisterWindowMessage(string msg);
|
SetLastError = true, ExactSpelling = true)]
|
||||||
|
public static extern uint RegisterWindowMessageW([MarshalAs(UnmanagedType.LPWStr)] string msg);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode,
|
||||||
public static extern ushort RegisterClassEx([In] ref WNDCLASSEX lpwcx);
|
SetLastError = true, ExactSpelling = true)]
|
||||||
|
public static extern ushort RegisterClassExW([In] ref WNDCLASSEX lpwcx);
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
|
[DllImport("user32.dll", CharSet = CharSet.Unicode,
|
||||||
public static extern IntPtr CreateWindowEx(WindowStylesEx dwExStyle,
|
SetLastError = true, ExactSpelling = true)]
|
||||||
|
public static extern IntPtr CreateWindowExW(WindowStylesEx dwExStyle,
|
||||||
ushort lpszClassName,
|
ushort lpszClassName,
|
||||||
[MarshalAs(UnmanagedType.LPStr)] string lpszWindowName,
|
[MarshalAs(UnmanagedType.LPTStr)] string lpszWindowName,
|
||||||
WindowStyles style,
|
WindowStyles style,
|
||||||
int x, int y, int width, int height,
|
int x, int y, int width, int height,
|
||||||
IntPtr hWndParent, IntPtr hMenu, IntPtr hInst, IntPtr lpParam);
|
IntPtr hWndParent, IntPtr hMenu, IntPtr hInst, IntPtr lpParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true, ExactSpelling = true)]
|
||||||
public static extern bool DestroyWindow(IntPtr hWnd);
|
public static extern bool DestroyWindow(IntPtr hWnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using NTwain.Data;
|
using NTwain.Data;
|
||||||
using NTwain.Internals;
|
using NTwain.Internals;
|
||||||
|
using NTwain.Resources;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -57,7 +58,7 @@ namespace NTwain
|
|||||||
if (image) dg |= DataGroups.Image;
|
if (image) dg |= DataGroups.Image;
|
||||||
if (audio) dg |= DataGroups.Audio;
|
if (audio) dg |= DataGroups.Audio;
|
||||||
|
|
||||||
if (dg == DataGroups.None) throw new InvalidOperationException("No data type specified.");
|
if (dg == DataGroups.None) throw new InvalidOperationException(MsgText.NoDataTypesSpecified);
|
||||||
_dg = dg;
|
_dg = dg;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,7 @@ namespace NTwain
|
|||||||
case PlatformID.Unix:
|
case PlatformID.Unix:
|
||||||
case PlatformID.MacOSX:
|
case PlatformID.MacOSX:
|
||||||
default:
|
default:
|
||||||
throw new PlatformNotSupportedException($"This platform {_platform} is not supported.");
|
throw new PlatformNotSupportedException(string.Format(MsgText.PlatformNotSupported, _platform));
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
//using NTwain.Data;
|
|
||||||
//using NTwain.Triplets;
|
|
||||||
//using System;
|
|
||||||
//using System.Collections.Generic;
|
|
||||||
//using System.ComponentModel;
|
|
||||||
//using System.Linq;
|
|
||||||
//using System.Text;
|
|
||||||
|
|
||||||
//namespace NTwain
|
|
||||||
//{
|
|
||||||
// partial class TwainSession : IDisposable
|
|
||||||
// {
|
|
||||||
// private bool disposedValue = false; // To detect redundant calls
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Handles actual disposal logic.
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="disposing"></param>
|
|
||||||
// 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);
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Closes any open TWAIN objects.
|
|
||||||
// /// </summary>
|
|
||||||
// public void Dispose()
|
|
||||||
// {
|
|
||||||
// Dispose(true);
|
|
||||||
// // TODO: uncomment the following line if the finalizer is overridden above.
|
|
||||||
// // GC.SuppressFinalize(this);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -22,8 +22,10 @@ namespace NTwain
|
|||||||
ReturnCode Handle32BitCallback(TW_IDENTITY origin, TW_IDENTITY destination,
|
ReturnCode Handle32BitCallback(TW_IDENTITY origin, TW_IDENTITY destination,
|
||||||
DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
|
DataGroups dg, DataArgumentType dat, Message msg, IntPtr data)
|
||||||
{
|
{
|
||||||
|
// from the docs this needs to return Success
|
||||||
|
// before handling the msg thus BeginInvoke is used.
|
||||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
||||||
BeginInvoke(() =>
|
InternalBeginInvoke(() =>
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: in BeginInvoke {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
Debug.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: in BeginInvoke {nameof(Handle32BitCallback)}({dg}, {dat}, {msg}, {data})");
|
||||||
HandleSourceMsg(msg);
|
HandleSourceMsg(msg);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace NTwain
|
namespace NTwain
|
||||||
{
|
{
|
||||||
@@ -52,9 +53,12 @@ namespace NTwain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
internal protected virtual void OnSourceDisabled(SourceDisabledEventArgs e)
|
internal protected virtual void OnSourceDisabled(SourceDisabledEventArgs e)
|
||||||
|
{
|
||||||
|
ExternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var handler = SourceDisabled;
|
var handler = SourceDisabled;
|
||||||
handler?.Invoke(this, e);
|
handler?.Invoke(this, e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -67,9 +71,12 @@ namespace NTwain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
protected virtual void OnDeviceEventReceived(DeviceEventArgs e)
|
protected virtual void OnDeviceEventReceived(DeviceEventArgs e)
|
||||||
|
{
|
||||||
|
ExternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var handler = DeviceEventReceived;
|
var handler = DeviceEventReceived;
|
||||||
handler?.Invoke(this, e);
|
handler?.Invoke(this, e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -82,9 +89,12 @@ namespace NTwain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
protected virtual void OnTransferReady(TransferReadyEventArgs e)
|
protected virtual void OnTransferReady(TransferReadyEventArgs e)
|
||||||
|
{
|
||||||
|
ExternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var handler = TransferReady;
|
var handler = TransferReady;
|
||||||
handler?.Invoke(this, e);
|
handler?.Invoke(this, e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -92,6 +102,21 @@ namespace NTwain
|
|||||||
///// Occurs when data has been transferred.
|
///// Occurs when data has been transferred.
|
||||||
///// </summary>
|
///// </summary>
|
||||||
//public event EventHandler<DataTransferredEventArgs> DataTransferred;
|
//public event EventHandler<DataTransferredEventArgs> DataTransferred;
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// Raises the <see cref="DataTransferred"/> event.
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="e"></param>
|
||||||
|
//protected virtual void OnDataTransferred(DataTransferredEventArgs e)
|
||||||
|
//{
|
||||||
|
// ExternalInvoke(() =>
|
||||||
|
// {
|
||||||
|
// var handler = DataTransferred;
|
||||||
|
// handler?.Invoke(this, e);
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when an error has been encountered during transfer.
|
/// Occurs when an error has been encountered during transfer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -102,9 +127,12 @@ namespace NTwain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
protected virtual void OnTransferError(TransferErrorEventArgs e)
|
protected virtual void OnTransferError(TransferErrorEventArgs e)
|
||||||
|
{
|
||||||
|
ExternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var handler = TransferError;
|
var handler = TransferError;
|
||||||
handler?.Invoke(this, e);
|
handler?.Invoke(this, e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -119,9 +147,12 @@ namespace NTwain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="propertyName">Name of the property.</param>
|
/// <param name="propertyName">Name of the property.</param>
|
||||||
protected void RaisePropertyChanged(string propertyName)
|
protected void RaisePropertyChanged(string propertyName)
|
||||||
|
{
|
||||||
|
ExternalInvoke(() =>
|
||||||
{
|
{
|
||||||
var handler = PropertyChanged;
|
var handler = PropertyChanged;
|
||||||
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using NTwain.Data;
|
using NTwain.Data;
|
||||||
|
using NTwain.Resources;
|
||||||
using NTwain.Threading;
|
using NTwain.Threading;
|
||||||
using NTwain.Triplets;
|
using NTwain.Triplets;
|
||||||
using System;
|
using System;
|
||||||
@@ -19,43 +20,89 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
internal readonly TwainConfig Config;
|
internal readonly TwainConfig Config;
|
||||||
|
|
||||||
private IntPtr _hWnd;
|
|
||||||
// cache generated twain sources so if you get same source from same session it'll return the same object
|
// cache generated twain sources so if you get same source from same session it'll return the same object
|
||||||
readonly Dictionary<string, DataSource> _ownedSources = new Dictionary<string, DataSource>();
|
readonly Dictionary<string, DataSource> _ownedSources = new Dictionary<string, DataSource>();
|
||||||
// need to keep delegate around to prevent GC?
|
// need to keep delegate around to prevent GC
|
||||||
readonly Callback32 _callback32Delegate;
|
readonly Callback32 _callback32Delegate;
|
||||||
|
// for windows only
|
||||||
|
readonly IThreadContext _internalContext;
|
||||||
|
|
||||||
readonly WinMsgLoop _winMsgLoop;
|
private IntPtr _hWnd;
|
||||||
|
private IThreadContext _externalContext;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new <see cref="TwainSession"/>.
|
/// Constructs a new <see cref="TwainSession"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="config"></param>
|
/// <param name="config"></param>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
public TwainSession(TwainConfig config)
|
public TwainSession(TwainConfig config)
|
||||||
{
|
{
|
||||||
Config = config;
|
Config = config ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
SetSynchronizationContext(SynchronizationContext.Current);
|
||||||
switch (config.Platform)
|
switch (config.Platform)
|
||||||
{
|
{
|
||||||
case PlatformID.MacOSX:
|
case PlatformID.MacOSX:
|
||||||
case PlatformID.Unix:
|
case PlatformID.Unix:
|
||||||
|
_internalContext = new DispatcherLoop(this);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
_winMsgLoop = new WinMsgLoop(this);
|
_internalContext = new WinMsgLoop(this);
|
||||||
_callback32Delegate = new Callback32(Handle32BitCallback);
|
_callback32Delegate = new Callback32(Handle32BitCallback);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
public void Invoke(Action action)
|
/// Sets the optional synchronization context.
|
||||||
|
/// Because most TWAIN-related things are happening on a different thread,
|
||||||
|
/// this allows events to be raised on the thread associated with this context and
|
||||||
|
/// may be useful if you want to handle them in the UI thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Usually you want to use <see cref="SynchronizationContext.Current"/> while on the UI thread.</param>
|
||||||
|
public void SetSynchronizationContext(SynchronizationContext context)
|
||||||
{
|
{
|
||||||
if (_winMsgLoop != null) _winMsgLoop.Invoke(action);
|
if (context == null) _externalContext = null;
|
||||||
|
else _externalContext = new UIThreadContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronously invokes an action on the external user thread if possible.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
void ExternalInvoke(Action action)
|
||||||
|
{
|
||||||
|
if (_externalContext != null) _externalContext.Invoke(action);
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously invokes an action on the external user thread if possible.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
void ExternalBeginInvoke(Action action)
|
||||||
|
{
|
||||||
|
if (_externalContext != null) _externalContext.BeginInvoke(action);
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronously invokes an action on the internal TWAIN thread if possible.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
internal void InternalInvoke(Action action)
|
||||||
|
{
|
||||||
|
if (_internalContext != null) _internalContext.Invoke(action);
|
||||||
else action();
|
else action();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginInvoke(Action action)
|
/// <summary>
|
||||||
|
/// Asynchronously invokes an action on the internal TWAIN thread if possible.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
void InternalBeginInvoke(Action action)
|
||||||
{
|
{
|
||||||
if (_winMsgLoop != null) _winMsgLoop.BeginInvoke(action);
|
if (_internalContext != null) _internalContext.BeginInvoke(action);
|
||||||
else action();
|
else action();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +117,7 @@ namespace NTwain
|
|||||||
var rc = DGControl.Parent.OpenDSM(hWnd);
|
var rc = DGControl.Parent.OpenDSM(hWnd);
|
||||||
if (rc == ReturnCode.Success)
|
if (rc == ReturnCode.Success)
|
||||||
{
|
{
|
||||||
_winMsgLoop?.Start();
|
_internalContext?.Start();
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -84,7 +131,7 @@ namespace NTwain
|
|||||||
var rc = DGControl.Parent.CloseDSM(_hWnd);
|
var rc = DGControl.Parent.CloseDSM(_hWnd);
|
||||||
if (rc == ReturnCode.Success)
|
if (rc == ReturnCode.Success)
|
||||||
{
|
{
|
||||||
_winMsgLoop?.Stop();
|
_internalContext?.Stop();
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -116,6 +163,14 @@ namespace NTwain
|
|||||||
rc = DGControl.Identity.CloseDS(CurrentSource.Identity32);
|
rc = DGControl.Identity.CloseDS(CurrentSource.Identity32);
|
||||||
if (rc != ReturnCode.Success) return rc;
|
if (rc != ReturnCode.Success) return rc;
|
||||||
break;
|
break;
|
||||||
|
case TwainState.SourceEnabled:
|
||||||
|
rc = DGControl.UserInterface.DisableDS(ref _lastEnableUI, false);
|
||||||
|
if (rc != ReturnCode.Success) return rc;
|
||||||
|
break;
|
||||||
|
case TwainState.TransferReady:
|
||||||
|
case TwainState.Transferring:
|
||||||
|
_disableDSNow = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
@@ -210,7 +265,7 @@ namespace NTwain
|
|||||||
{
|
{
|
||||||
if (value.Session != this)
|
if (value.Session != this)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Source is not from this session.");
|
throw new InvalidOperationException(MsgText.SourceNotThisSession);
|
||||||
}
|
}
|
||||||
var rc = DGControl.Identity.Set(value);
|
var rc = DGControl.Identity.Set(value);
|
||||||
RaisePropertyChanged(nameof(DefaultSource));
|
RaisePropertyChanged(nameof(DefaultSource));
|
||||||
@@ -274,7 +329,7 @@ namespace NTwain
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"Session: {State}";
|
return State.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user