2014-04-03 07:13:15 +08:00
using System ;
2014-04-03 07:01:21 +08:00
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using NTwain.Triplets ;
using NTwain.Data ;
using NTwain.Values ;
using System.Runtime.InteropServices ;
using System.Windows.Forms ;
using System.Windows.Interop ;
using System.Diagnostics ;
using System.Security.Permissions ;
using System.IO ;
using System.ComponentModel ;
using System.Threading ;
namespace NTwain
{
/// <summary>
/// Provides a session for working with TWAIN api in an application.
/// </summary>
2014-04-06 04:48:28 +08:00
public class TwainSession : ITwainStateInternal , IMessageFilter , INotifyPropertyChanged
2014-04-03 07:01:21 +08:00
{
/// <summary>
/// Initializes a new instance of the <see cref="TwainSession" /> class.
/// </summary>
/// <param name="appId">The app id.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public TwainSession ( TWIdentity appId )
{
if ( appId = = null ) { throw new ArgumentNullException ( "appId" ) ; }
2014-04-06 06:33:21 +08:00
_appId = appId ;
2014-04-03 07:01:21 +08:00
State = 1 ;
EnforceState = true ;
}
#region properties
object _callbackObj ;
SynchronizationContext _syncer ;
2014-04-06 06:33:21 +08:00
TWIdentity _appId ;
2014-04-03 07:01:21 +08:00
/// <summary>
/// Gets the app id used for the session.
/// </summary>
/// <value>The app id.</value>
2014-04-06 06:33:21 +08:00
TWIdentity ITwainStateInternal . GetAppId ( ) { return _appId ; }
2014-04-03 07:01:21 +08:00
/// <summary>
/// Gets the source id used for the session.
/// </summary>
/// <value>The source id.</value>
2014-04-06 04:48:28 +08:00
public TWIdentity SourceId { get ; private set ; }
2014-04-03 07:01:21 +08:00
/// <summary>
/// Gets the current state number as defined by the TWAIN spec.
/// </summary>
/// <value>The state.</value>
2014-04-04 19:25:11 +08:00
public int State { get ; private set ; }
2014-04-03 07:01:21 +08:00
/// <summary>
/// Gets or sets a value indicating whether callback is used parts of source communication
/// if supported. May be required if things don't work. This does not take effect if
/// the source is already open.
/// </summary>
/// <value>
/// <c>true</c> to disable callback; otherwise, <c>false</c>.
/// </value>
public bool DisableCallback { get ; set ; }
/// <summary>
/// Gets or sets a value indicating whether calls to triplets will verify the current twain session state.
/// </summary>
/// <value>
/// <c>true</c> if state value is enforced; otherwise, <c>false</c>.
/// </value>
public bool EnforceState { get ; set ; }
DGAudio _dgAudio ;
/// <summary>
/// Gets the triplet operations defined for audio data group.
/// </summary>
/// <value>The DG audio.</value>
public DGAudio DGAudio
{
get
{
if ( _dgAudio = = null ) { _dgAudio = new DGAudio ( this ) ; }
return _dgAudio ;
}
}
DGControl _dgControl ;
/// <summary>
/// Gets the triplet operations defined for control data group.
/// </summary>
/// <value>The DG control.</value>
public DGControl DGControl
{
get
{
if ( _dgControl = = null ) { _dgControl = new DGControl ( this ) ; }
return _dgControl ;
}
}
DGImage _dgImage ;
/// <summary>
/// Gets the triplet operations defined for image data group.
/// </summary>
/// <value>The DG image.</value>
public DGImage DGImage
{
get
{
if ( _dgImage = = null ) { _dgImage = new DGImage ( this ) ; }
return _dgImage ;
}
}
private IList < CapabilityId > _supportedCaps ;
/// <summary>
/// Gets the supported caps for the current source.
/// </summary>
/// <value>
/// The supported caps.
/// </value>
public IList < CapabilityId > SupportedCaps
{
get
{
if ( _supportedCaps = = null & & State > 3 )
{
_supportedCaps = this . GetCapabilities ( ) ;
}
return _supportedCaps ? ? new CapabilityId [ 0 ] ;
}
private set
{
_supportedCaps = value ;
RaisePropertyChanged ( "SupportedCaps" ) ;
}
}
#endregion
2014-04-04 19:25:11 +08:00
#region state transition calls
2014-04-03 07:01:21 +08:00
2014-04-06 04:48:28 +08:00
void ITwainStateInternal . ChangeState ( int newState , bool notifyChange )
2014-04-03 07:01:21 +08:00
{
2014-04-04 19:25:11 +08:00
Debug . WriteLine ( "TWAIN State = " + newState ) ;
State = newState ;
if ( notifyChange ) { RaisePropertyChanged ( "State" ) ; }
}
2014-04-06 04:48:28 +08:00
ICommitable ITwainStateInternal . GetPendingStateChanger ( int newState )
{
return new TentativeStateCommitable ( this , newState ) ;
}
void ITwainStateInternal . ChangeSourceId ( TWIdentity sourceId )
2014-04-04 19:25:11 +08:00
{
2014-04-06 04:48:28 +08:00
SourceId = sourceId ;
RaisePropertyChanged ( "SourceId" ) ;
2014-04-03 07:01:21 +08:00
}
HandleRef _parentHandle ;
/// <summary>
/// Opens the data source manager.
/// </summary>
/// <param name="handle">The handle. On Windows = points to the window handle (hWnd) that will act as the Source’ s
/// "parent". On Macintosh = should be a NULL value.</param>
/// <returns></returns>
public ReturnCode OpenManager ( HandleRef handle )
{
Debug . WriteLine ( string . Format ( "Thread {0}: OpenManager." , Thread . CurrentThread . ManagedThreadId ) ) ;
_parentHandle = handle ;
2014-04-06 06:33:21 +08:00
var rc = DGControl . Parent . OpenDsm ( handle . Handle ) ;
2014-04-03 07:01:21 +08:00
if ( rc = = ReturnCode . Success )
{
// if twain2 then get mem management stuff
2014-04-06 06:33:21 +08:00
if ( ( _appId . DataFunctionalities & DataFunctionalities . Dsm2 ) = = DataFunctionalities . Dsm2 )
2014-04-03 07:01:21 +08:00
{
TWEntryPoint entry ;
rc = DGControl . EntryPoint . Get ( out entry ) ;
if ( rc = = ReturnCode . Success )
{
2014-04-06 04:48:28 +08:00
MemoryManager . Instance . UpdateEntryPoint ( entry ) ;
2014-04-03 07:01:21 +08:00
Debug . WriteLine ( "Using TWAIN2 memory functions." ) ;
}
else
{
CloseManager ( ) ;
}
}
}
return rc ;
}
/// <summary>
/// Closes the data source manager.
/// </summary>
/// <returns></returns>
public ReturnCode CloseManager ( )
{
Debug . WriteLine ( string . Format ( "Thread {0}: CloseManager." , Thread . CurrentThread . ManagedThreadId ) ) ;
2014-04-06 06:33:21 +08:00
var rc = DGControl . Parent . CloseDsm ( _parentHandle . Handle ) ;
2014-04-03 07:01:21 +08:00
if ( rc = = ReturnCode . Success )
{
_parentHandle = default ( HandleRef ) ;
2014-04-06 04:48:28 +08:00
MemoryManager . Instance . UpdateEntryPoint ( null ) ;
2014-04-03 07:01:21 +08:00
}
return rc ;
}
/// <summary>
/// Loads the specified source into main memory and causes its initialization.
/// </summary>
/// <param name="sourceProductName">Name of the source.</param>
/// <returns></returns>
public ReturnCode OpenSource ( string sourceProductName )
{
var source = new TWIdentity ( ) ;
source . ProductName = sourceProductName ;
return OpenSource ( source ) ;
}
/// <summary>
/// Loads the specified Source into main memory and causes its initialization.
/// </summary>
/// <param name="sourceId">The source id.</param>
/// <returns></returns>
public ReturnCode OpenSource ( TWIdentity sourceId )
{
if ( sourceId = = null ) { throw new ArgumentNullException ( "sourceId" ) ; }
Debug . WriteLine ( string . Format ( "Thread {0}: OpenSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
var rc = DGControl . Identity . OpenDS ( sourceId ) ;
if ( rc = = ReturnCode . Success )
{
SupportedCaps = this . GetCapabilities ( ) ;
// TODO: does it work?
_syncer = SynchronizationContext . Current ? ? new SynchronizationContext ( ) ;
if ( ! DisableCallback )
{
// app v2.2 or higher uses callback2
2014-04-06 06:33:21 +08:00
if ( _appId . ProtocolMajor > = 2 & & _appId . ProtocolMinor > = 2 )
2014-04-03 07:01:21 +08:00
{
2014-04-06 06:33:21 +08:00
var cb = new TWCallback2 ( CallbackHandler ) ;
2014-04-03 07:01:21 +08:00
var rc2 = DGControl . Callback2 . RegisterCallback ( cb ) ;
if ( rc2 = = ReturnCode . Success )
{
Debug . WriteLine ( "Registered callback2." ) ;
_callbackObj = cb ;
}
}
else
{
2014-04-06 06:33:21 +08:00
var cb = new TWCallback ( CallbackHandler ) ;
2014-04-03 07:01:21 +08:00
var rc2 = DGControl . Callback . RegisterCallback ( cb ) ;
if ( rc2 = = ReturnCode . Success )
{
Debug . WriteLine ( "Registered callback." ) ;
_callbackObj = cb ;
}
}
}
}
return rc ;
}
ReturnCode CallbackHandler ( TWIdentity origin , TWIdentity dest ,
DataGroups dg , DataArgumentType dat , Values . Message msg , IntPtr data )
{
2014-04-06 04:48:28 +08:00
if ( origin ! = null & & SourceId ! = null & & origin . Id = = SourceId . Id )
2014-04-03 07:01:21 +08:00
{
Debug . WriteLine ( string . Format ( "Thread {0}: GOT TWAIN callback for msg {1}." , Thread . CurrentThread . ManagedThreadId , msg ) ) ;
// spec says should handle this on the thread that enabled the DS,
// but it's already the same and doesn't work (failure + seqError) w/o jumping to another thread and back.
// My guess is the DS needs to see the Success first before letting transfer happen
// so this is an artificial delay to make it happen.
// TODO: find a better method.
ThreadPool . QueueUserWorkItem ( o = >
{
_syncer . Send ( blah = >
{
HandleSourceMsg ( origin , dest , dg , dat , msg , data ) ;
} , null ) ;
} , null ) ;
return ReturnCode . Success ;
}
return ReturnCode . Failure ;
}
/// <summary>
/// When an application is finished with a Source, it must formally close the session between them
/// using this operation. This is necessary in case the Source only supports connection with a single
/// application (many desktop scanners will behave this way). A Source such as this cannot be
/// accessed by other applications until its current session is terminated
/// </summary>
/// <returns></returns>
public ReturnCode CloseSource ( )
{
Debug . WriteLine ( string . Format ( "Thread {0}: CloseSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
var rc = DGControl . Identity . CloseDS ( ) ;
if ( rc = = ReturnCode . Success )
{
_callbackObj = null ;
SupportedCaps = null ;
}
return rc ;
}
TWUserInterface _twui ;
/// <summary>
/// Enables the source for data acquisition.
/// </summary>
/// <param name="mode">The mode.</param>
/// <param name="modal">if set to <c>true</c> any driver UI will display as modal.</param>
/// <param name="windowHandle">The window handle if modal.</param>
/// <returns></returns>
public ReturnCode EnableSource ( SourceEnableMode mode , bool modal , HandleRef windowHandle )
{
Debug . WriteLine ( string . Format ( "Thread {0}: EnableSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
_twui = new TWUserInterface ( ) ;
_twui . ShowUI = mode = = SourceEnableMode . ShowUI ;
_twui . ModalUI = modal ;
_twui . hParent = windowHandle . Handle ;
if ( mode = = SourceEnableMode . ShowUIOnly )
{
return DGControl . UserInterface . EnableDSUIOnly ( _twui ) ;
}
else
{
return DGControl . UserInterface . EnableDS ( _twui ) ;
}
}
/// <summary>
/// Disables the source to end data acquisition.
/// </summary>
/// <returns></returns>
ReturnCode DisableSource ( )
{
Debug . WriteLine ( string . Format ( "Thread {0}: DisableSource." , Thread . CurrentThread . ManagedThreadId ) ) ;
var rc = DGControl . UserInterface . DisableDS ( _twui ) ;
if ( rc = = ReturnCode . Success )
{
var hand = SourceDisabled ;
if ( hand ! = null )
{
try
{
hand ( this , EventArgs . Empty ) ;
}
catch { }
}
}
return rc ;
}
#endregion
#region consumer to handle
/// <summary>
/// Occurs when source has been disabled (back to state 4).
/// </summary>
public event EventHandler SourceDisabled ;
/// <summary>
/// Occurs when a data transfer is ready.
/// </summary>
public event EventHandler < TransferReadyEventArgs > TransferReady ;
/// <summary>
/// Occurs when the source has generated an event.
/// </summary>
public event EventHandler < DeviceEventArgs > DeviceEvent ;
/// <summary>
/// Occurs when data has been transferred.
/// </summary>
public event EventHandler < DataTransferredEventArgs > DataTransferred ;
private void DoTransferRoutine ( )
{
TWPendingXfers pending = new TWPendingXfers ( ) ;
var rc = ReturnCode . Success ;
do
{
2014-04-03 20:10:49 +08:00
IList < FileFormat > formats = Enumerable . Empty < FileFormat > ( ) . ToList ( ) ;
2014-04-03 07:01:21 +08:00
IList < Compression > compressions = Enumerable . Empty < Compression > ( ) . ToList ( ) ;
bool canDoFileXfer = this . CapGetImageXferMech ( ) . Contains ( XferMech . File ) ;
2014-04-03 20:10:49 +08:00
var curFormat = this . GetCurrentCap < FileFormat > ( CapabilityId . ICapImageFileFormat ) ;
2014-04-03 07:01:21 +08:00
var curComp = this . GetCurrentCap < Compression > ( CapabilityId . ICapCompression ) ;
TWImageInfo imgInfo ;
bool skip = false ;
if ( DGImage . ImageInfo . Get ( out imgInfo ) ! = ReturnCode . Success )
{
// bad!
skip = true ;
}
try
{
formats = this . CapGetImageFileFormat ( ) ;
}
catch { }
try
{
compressions = this . CapGetCompression ( ) ;
}
catch { }
// ask consumer for cancel in case of non-ui multi-page transfers
TransferReadyEventArgs args = new TransferReadyEventArgs ( pending , formats , curFormat , compressions ,
curComp , canDoFileXfer , imgInfo ) ;
args . CancelCurrent = skip ;
var hand = TransferReady ;
if ( hand ! = null )
{
try
{
hand ( this , args ) ;
}
catch { }
}
if ( ! args . CancelAll & & ! args . CancelCurrent )
{
2014-04-03 20:10:49 +08:00
Values . XferMech mech = this . GetCurrentCap < XferMech > ( CapabilityId . ICapXferMech ) ;
2014-04-03 07:01:21 +08:00
if ( args . CanDoFileXfer & & ! string . IsNullOrEmpty ( args . OutputFile ) )
{
var setXferRC = DGControl . SetupFileXfer . Set ( new TWSetupFileXfer
{
FileName = args . OutputFile ,
Format = args . ImageFormat
} ) ;
if ( setXferRC = = ReturnCode . Success )
{
mech = XferMech . File ;
}
}
// I don't know how this is supposed to work so it probably doesn't
//this.CapSetImageFormat(args.ImageFormat);
//this.CapSetImageCompression(args.ImageCompression);
#region do xfer
// TODO: expose all swallowed exceptions somehow later
IntPtr dataPtr = IntPtr . Zero ;
IntPtr lockedPtr = IntPtr . Zero ;
string file = null ;
try
{
ReturnCode xrc = ReturnCode . Cancel ;
switch ( mech )
{
2014-04-03 20:10:49 +08:00
case Values . XferMech . Native :
2014-04-03 07:01:21 +08:00
xrc = DGImage . ImageNativeXfer . Get ( ref dataPtr ) ;
break ;
2014-04-03 20:10:49 +08:00
case Values . XferMech . File :
2014-04-03 07:01:21 +08:00
xrc = DGImage . ImageFileXfer . Get ( ) ;
if ( File . Exists ( args . OutputFile ) )
{
file = args . OutputFile ;
}
break ;
2014-04-03 20:10:49 +08:00
case Values . XferMech . MemFile :
2014-04-03 07:01:21 +08:00
// not supported yet
//TWImageMemXfer memxfer = new TWImageMemXfer();
//xrc = DGImage.ImageMemXfer.Get(memxfer);
break ;
}
if ( xrc = = ReturnCode . XferDone )
{
State = 7 ;
try
{
var dtHand = DataTransferred ;
if ( dtHand ! = null )
{
if ( dataPtr ! = IntPtr . Zero )
{
2014-04-06 06:33:21 +08:00
lockedPtr = MemoryManager . Instance . Lock ( dataPtr ) ;
2014-04-03 07:01:21 +08:00
}
dtHand ( this , new DataTransferredEventArgs ( lockedPtr , file ) ) ;
}
}
catch { }
}
//}
//else if (group == DataGroups.Audio)
//{
// var xrc = DGAudio.AudioNativeXfer.Get(ref dataPtr);
// if (xrc == ReturnCode.XferDone)
// {
// State = 7;
// try
// {
// var dtHand = DataTransferred;
// if (dtHand != null)
// {
2014-04-06 04:48:28 +08:00
// lockedPtr = MemoryManager.Instance.MemLock(dataPtr);
2014-04-03 07:01:21 +08:00
// dtHand(this, new DataTransferredEventArgs(lockedPtr));
// }
// }
// catch { }
// }
//}
}
finally
{
State = 6 ;
// data here is allocated by source so needs to use shared mem calls
if ( lockedPtr ! = IntPtr . Zero )
{
2014-04-06 06:33:21 +08:00
MemoryManager . Instance . Unlock ( lockedPtr ) ;
2014-04-03 07:01:21 +08:00
lockedPtr = IntPtr . Zero ;
}
if ( dataPtr ! = IntPtr . Zero )
{
2014-04-06 06:33:21 +08:00
MemoryManager . Instance . Free ( dataPtr ) ;
2014-04-03 07:01:21 +08:00
dataPtr = IntPtr . Zero ;
}
}
#endregion
}
if ( args . CancelAll )
{
rc = DGControl . PendingXfers . Reset ( pending ) ;
if ( rc = = ReturnCode . Success )
{
// if audio exit here
//if (group == DataGroups.Audio)
//{
// //???
// return;
//}
}
}
else
{
rc = DGControl . PendingXfers . EndXfer ( pending ) ;
}
} while ( rc = = ReturnCode . Success & & pending . Count ! = 0 ) ;
State = 5 ;
DisableSource ( ) ;
}
#endregion
#region messaging use
ReturnCode HandleSourceMsg ( TWIdentity origin , TWIdentity destination ,
DataGroups dg , DataArgumentType dat , NTwain . Values . Message msg , IntPtr data )
{
Debug . WriteLine ( string . Format ( "Thread {0}: HandleSourceMsg at state {1} with DG={2} DAT={3} MSG={4}." , Thread . CurrentThread . ManagedThreadId , State , dg , dat , msg ) ) ;
ReturnCode rc = ReturnCode . Success ;
switch ( msg )
{
case Values . Message . XferReady :
if ( State < 6 )
State = 6 ;
// this is the meat of all twain stuff
DoTransferRoutine ( ) ;
break ;
case Values . Message . DeviceEvent :
TWDeviceEvent de ;
rc = DGControl . DeviceEvent . Get ( out de ) ;
if ( rc = = ReturnCode . Success )
{
var hand = this . DeviceEvent ;
if ( hand ! = null )
{
try
{
hand ( this , new DeviceEventArgs ( de ) ) ;
}
catch { }
}
}
break ;
case Values . Message . CloseDSReq :
case Values . Message . CloseDSOK :
// even though it says closeDS it's really disable.
// dsok is sent if source is enabled with uionly
// some sources send this at other states so do a step down
if ( State > 5 )
{
ForceStepDown ( 4 ) ;
}
else if ( State = = 5 )
{
// needs this state check since some source sends this more than once
DisableSource ( ) ;
}
break ;
}
return rc ;
}
/// <summary>
/// Forces the stepping down of an opened source ignoring return values.
/// Used when session state and source state become out of sync.
/// </summary>
/// <param name="targetState">State of the target.</param>
public void ForceStepDown ( int targetState )
{
Debug . WriteLine ( string . Format ( "Thread {0}: ForceStepDown." , Thread . CurrentThread . ManagedThreadId ) ) ;
bool origFlag = EnforceState ;
EnforceState = false ;
// From the twain spec
// Stepping Back Down the States
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
// DG_CONTROL / DAT_USERINTERFACE / MSG_DISABLEDS → state 5 to 4
// DG_CONTROL / DAT_IDENTITY / MSG_CLOSEDS → state 4 to 3
// Ignore the status returns from the calls prior to the one yielding the desired state. For instance, if a
// call during scanning returns TWCC_SEQERROR and the desire is to return to state 5, then use the
// following commands.
// DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER → state 7 to 6
// DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET → state 6 to 5
// Being sure to confirm that DG_CONTROL / DAT_PENDINGXFERS / MSG_RESET returned
// success, the return status from DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER may
// be ignored.
if ( targetState < 7 )
{
DGControl . PendingXfers . EndXfer ( new TWPendingXfers ( ) ) ;
}
if ( targetState < 6 )
{
DGControl . PendingXfers . Reset ( new TWPendingXfers ( ) ) ;
}
if ( targetState < 5 )
{
DisableSource ( ) ;
}
if ( targetState < 4 )
{
CloseSource ( ) ;
}
if ( targetState < 3 )
{
CloseManager ( ) ;
}
EnforceState = origFlag ;
}
/// <summary>
/// Handles the message from a message loop.
/// </summary>
/// <param name="msgPtr">Pointer to message structure.</param>
/// <returns>True if handled by TWAIN.</returns>
bool HandleLoopMsgEvent ( ref IntPtr msgPtr )
{
TWEvent evt = new TWEvent ( ) ;
evt . pEvent = msgPtr ;
var rc = DGControl . Event . ProcessEvent ( evt ) ;
2014-04-03 20:10:49 +08:00
HandleSourceMsg ( null , null , DataGroups . Control , DataArgumentType . Null , evt . TWMessage , IntPtr . Zero ) ;
2014-04-03 07:01:21 +08:00
return rc = = ReturnCode . DSEvent ;
}
/// <summary>
/// Message loop processor for winform.
/// Use this by adding the <see cref="TwainSession"/> as an <see cref="IMessageFilter "/>.
/// </summary>
/// <param name="m">The message to be dispatched. You cannot modify this message.</param>
/// <returns>
/// true to filter the message and stop it from being dispatched; false to allow the message to continue to the next filter or control.
/// </returns>
//[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
bool IMessageFilter . PreFilterMessage ( ref System . Windows . Forms . Message m )
{
if ( State > 3 )
{
MSG winmsg = default ( MSG ) ;
winmsg . hwnd = m . HWnd ;
winmsg . lParam = m . LParam ;
winmsg . message = m . Msg ;
winmsg . wParam = m . WParam ;
IntPtr msgPtr = IntPtr . Zero ;
try
{
// no need to lock for marshal alloc
2014-04-06 07:39:41 +08:00
//msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
msgPtr = Marshal . AllocHGlobal ( Marshal . SizeOf ( winmsg ) ) ;
2014-04-03 07:01:21 +08:00
Marshal . StructureToPtr ( winmsg , msgPtr , false ) ;
return HandleLoopMsgEvent ( ref msgPtr ) ;
}
finally
{
if ( msgPtr ! = IntPtr . Zero )
2014-04-06 07:39:41 +08:00
Marshal . FreeHGlobal ( msgPtr ) ;
//MemoryManager.Instance.Free(msgPtr);
2014-04-03 07:01:21 +08:00
}
}
return false ;
}
/// <summary>
/// Message loop processor for wpf.
/// Use this as the target of <see cref="HwndSourceHook"/> delegate.
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="msg">The message ID.</param>
/// <param name="wParam">The message's wParam value.</param>
/// <param name="lParam">The message's lParam value.</param>
/// <param name="handled">A value that indicates whether the message was handled. Set the value to true if the message was handled; otherwise, false.</param>
/// <returns></returns>
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand)]
public IntPtr PreFilterMessage ( IntPtr hwnd , int msg , IntPtr wParam , IntPtr lParam , ref bool handled )
{
// always pass message since it works whether there's a callback or not?
if ( State > 3 ) // && _callbackObj == null)
{
MSG winmsg = default ( MSG ) ;
winmsg . hwnd = hwnd ;
winmsg . lParam = lParam ;
winmsg . message = msg ;
winmsg . wParam = wParam ;
IntPtr msgPtr = IntPtr . Zero ;
try
{
// no need to lock for marshal alloc
2014-04-06 07:39:41 +08:00
//msgPtr = MemoryManager.Instance.Allocate((uint)Marshal.SizeOf(winmsg));
msgPtr = Marshal . AllocHGlobal ( Marshal . SizeOf ( winmsg ) ) ;
2014-04-03 07:01:21 +08:00
Marshal . StructureToPtr ( winmsg , msgPtr , false ) ;
handled = HandleLoopMsgEvent ( ref msgPtr ) ;
}
finally
{
if ( msgPtr ! = IntPtr . Zero )
2014-04-06 07:39:41 +08:00
Marshal . FreeHGlobal ( msgPtr ) ;
//MemoryManager.Instance.Free(msgPtr);
2014-04-03 07:01:21 +08:00
}
}
return IntPtr . Zero ;
}
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged ;
void RaisePropertyChanged ( string property )
{
var hand = PropertyChanged ;
if ( hand ! = null ) { hand ( this , new PropertyChangedEventArgs ( property ) ) ; }
}
#endregion
}
}