Expose wndproc hook method and test reset TW_PENDINGXFERS before use.

This commit is contained in:
Eugene Wang
2024-10-10 11:06:27 -04:00
parent cea9a990b9
commit de017f3439
3 changed files with 5067 additions and 5041 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -12,94 +12,102 @@ using MSG = NTwain.Data.MSG;
namespace NTwain namespace NTwain
{ {
// contains parts for winform/wpf message loop integration // contains parts for winform/wpf message loop integration
partial class TwainAppSession : IMessageFilter partial class TwainAppSession : IMessageFilter
{ {
HwndSource? _wpfhook; HwndSource? _wpfhook;
/// <summary> /// <summary>
/// Registers this session for use in a Winform UI thread. /// Registers this session for use in a Winform UI thread.
/// </summary> /// </summary>
public void AddWinformFilter() public void AddWinformFilter()
{
Application.AddMessageFilter(this);
}
/// <summary>
/// Unregisters this session if previously registered with <see cref="AddWinformFilter"/>.
/// </summary>
public void RemoveWinformFilter()
{
Application.RemoveMessageFilter(this);
}
/// <summary>
/// Registers this session for use in a WPF UI thread.
/// This requires the hwnd used in <see cref="OpenDSM"/>
/// be a valid WPF window handle.
/// </summary>
public void AddWpfHook()
{
if (_wpfhook == null)
{
_wpfhook = HwndSource.FromHwnd(_hwnd);
_wpfhook.AddHook(WpfHook);
}
}
/// <summary>
/// Unregisters this session if previously registered with <see cref="AddWpfHook"/>.
/// </summary>
public void RemoveWpfHook()
{
if (_wpfhook != null)
{
_wpfhook.RemoveHook(WpfHook);
_wpfhook = null;
}
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
return WndProc(m.HWnd, m.Msg, m.WParam, m.LParam);
}
IntPtr WpfHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = WndProc(hwnd, msg, wParam, lParam);
return IntPtr.Zero;
}
private bool WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
{
// this handles the message from a typical WndProc message loop and checks if it's for the TWAIN source.
bool handled = false;
if (_state >= STATE.S5)
{
WIN_MESSAGE winMsg = new()
{ {
hwnd = hWnd, Application.AddMessageFilter(this);
message = (uint)msg, }
wParam = wParam, /// <summary>
lParam = lParam /// Unregisters this session if previously registered with <see cref="AddWinformFilter"/>.
}; /// </summary>
// no need to do another lock call when using marshal alloc public void RemoveWinformFilter()
if (_procEvent.pEvent == IntPtr.Zero) {
_procEvent.pEvent = Marshal.AllocHGlobal(Marshal.SizeOf(winMsg)); Application.RemoveMessageFilter(this);
Marshal.StructureToPtr(winMsg, _procEvent.pEvent, true); }
if (!_closeDsRequested) /// <summary>
{ /// Registers this session for use in a WPF UI thread.
var rc = DGControl.Event.ProcessEvent(ref _appIdentity, ref _currentDS, ref _procEvent); /// This requires the hwnd used in <see cref="OpenDSM"/>
handled = rc == TWRC.DSEVENT; /// be a valid WPF window handle.
if (_procEvent.TWMessage != 0 && (handled || rc == TWRC.NOTDSEVENT)) /// </summary>
{ public void AddWpfHook()
//Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] CheckIfTwainMessage at state {State} with MSG={_procEvent.TWMessage}."); {
HandleSourceMsg((MSG)_procEvent.TWMessage); if (_wpfhook == null)
} {
_wpfhook = HwndSource.FromHwnd(_hwnd);
_wpfhook.AddHook(WpfHook);
}
}
/// <summary>
/// Unregisters this session if previously registered with <see cref="AddWpfHook"/>.
/// </summary>
public void RemoveWpfHook()
{
if (_wpfhook != null)
{
_wpfhook.RemoveHook(WpfHook);
_wpfhook = null;
}
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
return WndProc(m.HWnd, m.Msg, m.WParam, m.LParam);
}
IntPtr WpfHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = WndProc(hwnd, msg, wParam, lParam);
return IntPtr.Zero;
}
/// <summary>
/// Method to check window message and handle it if it's for the TWAIN source.'
/// </summary>
/// <param name="hWnd"></param>
/// <param name="msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
public bool WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
{
// this handles the message from a typical WndProc message loop and checks if it's for the TWAIN source.
bool handled = false;
if (_state >= STATE.S5)
{
WIN_MESSAGE winMsg = new()
{
hwnd = hWnd,
message = (uint)msg,
wParam = wParam,
lParam = lParam
};
// no need to do another lock call when using marshal alloc
if (_procEvent.pEvent == IntPtr.Zero)
_procEvent.pEvent = Marshal.AllocHGlobal(Marshal.SizeOf(winMsg));
Marshal.StructureToPtr(winMsg, _procEvent.pEvent, true);
if (!_closeDsRequested)
{
var rc = DGControl.Event.ProcessEvent(ref _appIdentity, ref _currentDS, ref _procEvent);
handled = rc == TWRC.DSEVENT;
if (_procEvent.TWMessage != 0 && (handled || rc == TWRC.NOTDSEVENT))
{
//Debug.WriteLine($"[thread {Environment.CurrentManagedThreadId}] CheckIfTwainMessage at state {State} with MSG={_procEvent.TWMessage}.");
HandleSourceMsg((MSG)_procEvent.TWMessage);
}
}
}
return handled;
} }
}
return handled;
} }
}
} }
#endif #endif

View File

@@ -8,521 +8,534 @@ using System.Runtime.InteropServices;
namespace NTwain namespace NTwain
{ {
// this file contains various xfer methods // this file contains various xfer methods
partial class TwainAppSession partial class TwainAppSession
{
/// <summary>
/// Can only be called in state 7, so it's hidden here and
/// only exposed in data transferred event.
/// </summary>
/// <param name="container"></param>
/// <returns></returns>
internal STS GetExtendedImageInfo(ref TW_EXTIMAGEINFO container)
{ {
return WrapInSTS(DGImage.ExtImageInfo.Get(ref _appIdentity, ref _currentDS, ref container)); /// <summary>
} /// Can only be called in state 7, so it's hidden here and
/// only exposed in data transferred event.
/// </summary>
/// <summary> /// <param name="container"></param>
/// Start the transfer loop. /// <returns></returns>
/// This should be called after receiving internal STS GetExtendedImageInfo(ref TW_EXTIMAGEINFO container)
/// <see cref="MSG.XFERREADY"/> in the background thread.
/// </summary>
void EnterTransferRoutine()
{
// TODO: currently implemented routine doesn't quite work for audio as described in spec
// default options if source doesn't support changing them or whatever
bool xferImage = true;
bool xferAudio = false;
if (DGControl.XferGroup.Get(ref _appIdentity, ref _currentDS, out DG xferType) == TWRC.SUCCESS)
{
xferAudio = (xferType & DG.AUDIO) == DG.AUDIO;
var dsName = _currentDS.ProductName.ToString();
// check for Plustek OpticSlim 2680H, this scanner returns wrong xferGroup after first scanning
if (dsName.IndexOf("Plustek", StringComparison.OrdinalIgnoreCase) > -1 &&
dsName.IndexOf("OpticSlim", StringComparison.OrdinalIgnoreCase) > -1 &&
dsName.IndexOf("2680H", StringComparison.OrdinalIgnoreCase) > -1)
{ {
xferImage = true; return WrapInSTS(DGImage.ExtImageInfo.Get(ref _appIdentity, ref _currentDS, ref container));
} }
else
/// <summary>
/// Start the transfer loop.
/// This should be called after receiving
/// <see cref="MSG.XFERREADY"/> in the background thread.
/// </summary>
void EnterTransferRoutine()
{ {
// some DS end up getting none but we will assume it's image // TODO: currently implemented routine doesn't quite work for audio as described in spec
xferImage = xferType == 0 || (xferType & DG.IMAGE) == DG.IMAGE;
}
}
var imgXferMech = TWSX.NATIVE;
var audXferMech = TWSX.NATIVE;
if (xferImage) imgXferMech = Caps.ICAP_XFERMECH.GetCurrent().FirstOrDefault();
else if (xferAudio) audXferMech = Caps.ACAP_XFERMECH.GetCurrent().FirstOrDefault();
TW_PENDINGXFERS pending = default; // default options if source doesn't support changing them or whatever
var sts = WrapInSTS(DGControl.PendingXfers.Get(ref _appIdentity, ref _currentDS, ref pending)); bool xferImage = true;
if (sts.RC == TWRC.SUCCESS) bool xferAudio = false;
{ if (DGControl.XferGroup.Get(ref _appIdentity, ref _currentDS, out DG xferType) == TWRC.SUCCESS)
do {
{ xferAudio = (xferType & DG.AUDIO) == DG.AUDIO;
var readyArgs = new TransferReadyEventArgs(pending.Count, (TWEJ)pending.EOJ); var dsName = _currentDS.ProductName.ToString();
try // check for Plustek OpticSlim 2680H, this scanner returns wrong xferGroup after first scanning
{ if (dsName.IndexOf("Plustek", StringComparison.OrdinalIgnoreCase) > -1 &&
TransferReady?.Invoke(this, readyArgs); dsName.IndexOf("OpticSlim", StringComparison.OrdinalIgnoreCase) > -1 &&
} dsName.IndexOf("2680H", StringComparison.OrdinalIgnoreCase) > -1)
catch { } // don't let consumer kill the loop if they have exception {
xferImage = true;
}
else
{
// some DS end up getting none but we will assume it's image
xferImage = xferType == 0 || (xferType & DG.IMAGE) == DG.IMAGE;
}
}
if (readyArgs.Cancel == CancelType.EndNow || _closeDsRequested) var imgXferMech = TWSX.NATIVE;
{ var audXferMech = TWSX.NATIVE;
// TODO: need to call EndXfer first? if (xferImage) imgXferMech = Caps.ICAP_XFERMECH.GetCurrent().FirstOrDefault();
sts = WrapInSTS(DGControl.PendingXfers.Reset(ref _appIdentity, ref _currentDS, ref pending)); else if (xferAudio) audXferMech = Caps.ACAP_XFERMECH.GetCurrent().FirstOrDefault();
if (sts.RC == TWRC.SUCCESS && xferImage) State = STATE.S5;
} TW_PENDINGXFERS pending = new TW_PENDINGXFERS();
else if (readyArgs.Cancel == CancelType.SkipCurrent) var sts = WrapInSTS(DGControl.PendingXfers.Get(ref _appIdentity, ref _currentDS, ref pending));
{
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS) if (sts.RC == TWRC.SUCCESS)
{ {
if (xferImage) do
{
State = pending.Count == 0 ? STATE.S5 : STATE.S6;
}
else if (xferAudio)
{
State = STATE.S6;
}
}
}
else
{
if (readyArgs.Cancel == CancelType.Graceful)
{
// ignore rc in this and continue transfer as normal
DGControl.PendingXfers.StopFeeder(ref _appIdentity, ref _currentDS, ref pending);
}
try
{
if (readyArgs.Cancel != CancelType.SkipCurrent)
{
// transfer normally and only if someone's listening
// to DataTransferred event
if (xferImage)
{ {
switch (imgXferMech) var readyArgs = new TransferReadyEventArgs(pending.Count, (TWEJ)pending.EOJ);
{ try
case TWSX.NATIVE: {
sts = TransferNativeImage(ref pending); TransferReady?.Invoke(this, readyArgs);
break; }
case TWSX.FILE: catch { } // don't let consumer kill the loop if they have exception
sts = TransferFileImage(ref pending);
break; if (readyArgs.Cancel == CancelType.EndNow || _closeDsRequested)
case TWSX.MEMORY: {
sts = TransferMemoryImage(ref pending); // TODO: need to call EndXfer first?
break; pending = new TW_PENDINGXFERS();
case TWSX.MEMFILE: sts = WrapInSTS(DGControl.PendingXfers.Reset(ref _appIdentity, ref _currentDS, ref pending));
sts = TransferMemoryFileImage(ref pending); if (sts.RC == TWRC.SUCCESS && xferImage) State = STATE.S5;
break; }
} else if (readyArgs.Cancel == CancelType.SkipCurrent)
} {
else if (xferAudio) pending = new TW_PENDINGXFERS();
{ sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
switch (audXferMech) if (sts.RC == TWRC.SUCCESS)
{ {
case TWSX.NATIVE: if (xferImage)
sts = TransferNativeAudio(ref pending); {
break; State = pending.Count == 0 ? STATE.S5 : STATE.S6;
case TWSX.FILE: }
sts = TransferFileAudio(ref pending); else if (xferAudio)
break; {
} State = STATE.S6;
} }
HandleXferCode(ref sts, ref pending); }
} }
else
{
if (readyArgs.Cancel == CancelType.Graceful)
{
// ignore rc in this and continue transfer as normal
pending = new TW_PENDINGXFERS();
DGControl.PendingXfers.StopFeeder(ref _appIdentity, ref _currentDS, ref pending);
}
try
{
if (readyArgs.Cancel != CancelType.SkipCurrent)
{
// transfer normally and only if someone's listening
// to DataTransferred event
if (xferImage)
{
switch (imgXferMech)
{
case TWSX.NATIVE:
sts = TransferNativeImage(ref pending);
break;
case TWSX.FILE:
sts = TransferFileImage(ref pending);
break;
case TWSX.MEMORY:
sts = TransferMemoryImage(ref pending);
break;
case TWSX.MEMFILE:
sts = TransferMemoryFileImage(ref pending);
break;
}
}
else if (xferAudio)
{
switch (audXferMech)
{
case TWSX.NATIVE:
sts = TransferNativeAudio(ref pending);
break;
case TWSX.FILE:
sts = TransferFileAudio(ref pending);
break;
}
}
HandleXferCode(ref sts, ref pending);
}
}
catch (Exception ex)
{
try
{
TransferError?.Invoke(this, new TransferErrorEventArgs(ex));
}
catch { }
}
}
} while (sts.RC == TWRC.SUCCESS && pending.Count != 0);
} }
catch (Exception ex)
HandleXferCode(ref sts, ref pending);
if (State >= STATE.S5)
{ {
try DisableSource();
{
TransferError?.Invoke(this, new TransferErrorEventArgs(ex));
}
catch { }
} }
} _inTransfer = false;
} while (sts.RC == TWRC.SUCCESS && pending.Count != 0); }
}
HandleXferCode(ref sts, ref pending); private void HandleXferCode(ref STS sts, ref TW_PENDINGXFERS pending)
{
switch (sts.RC)
{
case TWRC.SUCCESS:
case TWRC.XFERDONE:
// ok to keep going
break;
case TWRC.CANCEL:
// might eventually have option to cancel this or all like transfer ready
try
{
TransferCanceled?.Invoke(this, new TransferCanceledEventArgs());
}
catch { }
pending = new TW_PENDINGXFERS();
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
pending = new TW_PENDINGXFERS();
sts = WrapInSTS(DGControl.PendingXfers.Reset(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS) State = STATE.S5;
break;
default:
// TODO: raise error event
switch (sts.STATUS.ConditionCode)
{
case TWCC.DAMAGEDCORNER:
case TWCC.DOCTOODARK:
case TWCC.DOCTOOLIGHT:
case TWCC.FOCUSERROR:
case TWCC.NOMEDIA:
case TWCC.PAPERDOUBLEFEED:
case TWCC.PAPERJAM:
pending = new TW_PENDINGXFERS();
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
break;
case TWCC.OPERATIONERROR:
var indicators = Caps.CAP_INDICATORS.GetCurrent().FirstOrDefault();
if (_userInterface.ShowUI == 0 && indicators == TW_BOOL.False)
{
// todo: alert user and drop to S4
pending = new TW_PENDINGXFERS();
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
pending = new TW_PENDINGXFERS();
sts = WrapInSTS(DGControl.PendingXfers.Reset(ref _appIdentity, ref _currentDS, ref pending));
}
break;
}
break;
}
}
if (State >= STATE.S5) private STS TransferFileAudio(ref TW_PENDINGXFERS pending)
{ {
DisableSource(); // assuming user already configured the transfer in transferready event,
} // get what will be transferred
_inTransfer = false; var rc = DGControl.SetupFileXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPFILEXFER fileSetup);
} if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
private void HandleXferCode(ref STS sts, ref TW_PENDINGXFERS pending) // and just start it
{ var sts = WrapInSTS(DGAudio.AudioFileXfer.Get(ref _appIdentity, ref _currentDS));
switch (sts.RC) if (sts.RC == TWRC.XFERDONE)
{ {
case TWRC.SUCCESS: State = STATE.S7;
case TWRC.XFERDONE: try
// ok to keep going {
break; DGAudio.AudioInfo.Get(ref _appIdentity, ref _currentDS, out TW_AUDIOINFO info);
case TWRC.CANCEL: var args = new TransferredEventArgs(info, fileSetup);
// might eventually have option to cancel this or all like transfer ready Transferred?.Invoke(this, args);
try }
{ catch { }
TransferCanceled?.Invoke(this, new TransferCanceledEventArgs());
} pending = new TW_PENDINGXFERS();
catch { }
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
sts = WrapInSTS(DGControl.PendingXfers.Reset(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS) State = STATE.S5;
break;
default:
// TODO: raise error event
switch (sts.STATUS.ConditionCode)
{
case TWCC.DAMAGEDCORNER:
case TWCC.DOCTOODARK:
case TWCC.DOCTOOLIGHT:
case TWCC.FOCUSERROR:
case TWCC.NOMEDIA:
case TWCC.PAPERDOUBLEFEED:
case TWCC.PAPERJAM:
pending = default;
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
break;
case TWCC.OPERATIONERROR:
var indicators = Caps.CAP_INDICATORS.GetCurrent().FirstOrDefault();
if (_userInterface.ShowUI == 0 && indicators == TW_BOOL.False)
{
// todo: alert user and drop to S4
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending)); sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
sts = WrapInSTS(DGControl.PendingXfers.Reset(ref _appIdentity, ref _currentDS, ref pending)); if (sts.RC == TWRC.SUCCESS)
} {
break; State = STATE.S6;
} }
break; }
} return sts;
}
private STS TransferFileAudio(ref TW_PENDINGXFERS pending)
{
// assuming user already configured the transfer in transferready event,
// get what will be transferred
var rc = DGControl.SetupFileXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPFILEXFER fileSetup);
if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
// and just start it
var sts = WrapInSTS(DGAudio.AudioFileXfer.Get(ref _appIdentity, ref _currentDS));
if (sts.RC == TWRC.XFERDONE)
{
State = STATE.S7;
try
{
DGAudio.AudioInfo.Get(ref _appIdentity, ref _currentDS, out TW_AUDIOINFO info);
var args = new TransferredEventArgs(info, fileSetup);
Transferred?.Invoke(this, args);
} }
catch { }
; private STS TransferNativeAudio(ref TW_PENDINGXFERS pending)
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS)
{ {
State = STATE.S6; IntPtr dataPtr = IntPtr.Zero;
} IntPtr lockedPtr = IntPtr.Zero;
}
return sts;
}
private STS TransferNativeAudio(ref TW_PENDINGXFERS pending)
{
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
try
{
var sts = WrapInSTS(DGAudio.AudioNativeXfer.Get(ref _appIdentity, ref _currentDS, out dataPtr));
if (sts.RC == TWRC.XFERDONE)
{
State = STATE.S7;
lockedPtr = Lock(dataPtr);
BufferedData? data = default;
// TODO: don't know how to read wav/aiff from pointer yet
if (data != null)
{
try try
{ {
DGAudio.AudioInfo.Get(ref _appIdentity, ref _currentDS, out TW_AUDIOINFO info); var sts = WrapInSTS(DGAudio.AudioNativeXfer.Get(ref _appIdentity, ref _currentDS, out dataPtr));
var args = new TransferredEventArgs(info, data); if (sts.RC == TWRC.XFERDONE)
Transferred?.Invoke(this, args); {
} State = STATE.S7;
catch lockedPtr = Lock(dataPtr);
{ BufferedData? data = default;
data.Dispose();
}
}
}
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending)); // TODO: don't know how to read wav/aiff from pointer yet
if (sts.RC == TWRC.SUCCESS)
{
State = STATE.S6;
}
return sts;
}
finally
{
if (lockedPtr != IntPtr.Zero) Unlock(dataPtr);
if (dataPtr != IntPtr.Zero) Free(dataPtr);
}
}
private STS TransferMemoryImage(ref TW_PENDINGXFERS pending) if (data != null)
{ {
var rc = DGControl.SetupMemXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPMEMXFER memSetup); try
if (rc != TWRC.SUCCESS) return WrapInSTS(rc); {
rc = DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info); DGAudio.AudioInfo.Get(ref _appIdentity, ref _currentDS, out TW_AUDIOINFO info);
if (rc != TWRC.SUCCESS) return WrapInSTS(rc); var args = new TransferredEventArgs(info, data);
rc = DGImage.ImageLayout.Get(ref _appIdentity, ref _currentDS, out TW_IMAGELAYOUT layout); Transferred?.Invoke(this, args);
if (rc != TWRC.SUCCESS) return WrapInSTS(rc); }
catch
{
data.Dispose();
}
}
}
uint buffSize = memSetup.DetermineBufferSize(); pending = new TW_PENDINGXFERS();
var memPtr = Alloc(buffSize); sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS)
TW_IMAGEMEMXFER memXfer = TW_IMAGEMEMXFER.DONTCARE(); {
TW_IMAGEMEMXFER_MACOSX memXferOSX = TW_IMAGEMEMXFER_MACOSX.DONTCARE(); State = STATE.S6;
memXfer.Memory = new TW_MEMORY }
{ return sts;
Flags = (uint)(TWMF.APPOWNS | TWMF.POINTER),
Length = buffSize,
TheMem = memPtr
};
memXferOSX.Memory = memXfer.Memory;
byte[] dotnetBuff = BufferedData.MemPool.Rent((int)buffSize);
try
{
do
{
rc = TWPlatform.IsMacOSX ?
DGImage.ImageMemXfer.Get(ref _appIdentity, ref _currentDS, ref memXferOSX) :
DGImage.ImageMemXfer.Get(ref _appIdentity, ref _currentDS, ref memXfer);
if (rc == TWRC.SUCCESS || rc == TWRC.XFERDONE)
{
try
{
var written = TWPlatform.IsMacOSX ?
memXferOSX.BytesWritten : memXfer.BytesWritten;
IntPtr lockedPtr = Lock(memPtr);
// assemble!
//Marshal.Copy(lockedPtr, dotnetBuff, 0, (int)written);
//outStream.Write(dotnetBuff, 0, (int)written);
} }
finally finally
{ {
Unlock(memPtr); if (lockedPtr != IntPtr.Zero) Unlock(dataPtr);
if (dataPtr != IntPtr.Zero) Free(dataPtr);
} }
}
} while (rc == TWRC.SUCCESS);
}
finally
{
if (memPtr != IntPtr.Zero) Free(memPtr);
BufferedData.MemPool.Return(dotnetBuff);
}
if (rc == TWRC.XFERDONE)
{
try
{
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out info);
//var args = new DataTransferredEventArgs(info, null, outStream.ToArray());
//DataTransferred?.Invoke(this, args);
} }
catch { }
var sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending)); private STS TransferMemoryImage(ref TW_PENDINGXFERS pending)
if (sts.RC == TWRC.SUCCESS)
{ {
State = pending.Count == 0 ? STATE.S5 : STATE.S6; var rc = DGControl.SetupMemXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPMEMXFER memSetup);
} if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
return sts; rc = DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
} if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
return WrapInSTS(rc); rc = DGImage.ImageLayout.Get(ref _appIdentity, ref _currentDS, out TW_IMAGELAYOUT layout);
} if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
private STS TransferMemoryFileImage(ref TW_PENDINGXFERS pending) uint buffSize = memSetup.DetermineBufferSize();
{ var memPtr = Alloc(buffSize);
var rc = DGControl.SetupFileXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPFILEXFER fileSetup);
if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
rc = DGControl.SetupMemXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPMEMXFER memSetup);
if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
uint buffSize = memSetup.DetermineBufferSize(); TW_IMAGEMEMXFER memXfer = TW_IMAGEMEMXFER.DONTCARE();
var memPtr = Alloc(buffSize); TW_IMAGEMEMXFER_MACOSX memXferOSX = TW_IMAGEMEMXFER_MACOSX.DONTCARE();
memXfer.Memory = new TW_MEMORY
{
Flags = (uint)(TWMF.APPOWNS | TWMF.POINTER),
Length = buffSize,
TheMem = memPtr
};
memXferOSX.Memory = memXfer.Memory;
TW_IMAGEMEMXFER memXfer = TW_IMAGEMEMXFER.DONTCARE(); byte[] dotnetBuff = BufferedData.MemPool.Rent((int)buffSize);
TW_IMAGEMEMXFER_MACOSX memXferOSX = TW_IMAGEMEMXFER_MACOSX.DONTCARE();
memXfer.Memory = new TW_MEMORY
{
Flags = (uint)(TWMF.APPOWNS | TWMF.POINTER),
Length = buffSize,
TheMem = memPtr
};
memXferOSX.Memory = memXfer.Memory;
// TODO: how to get actual file size before hand? Is it imagelayout?
// otherwise will just write to stream with lots of copies
byte[] dotnetBuff = BufferedData.MemPool.Rent((int)buffSize);
using var outStream = new MemoryStream();
try
{
do
{
rc = TWPlatform.IsMacOSX ?
DGImage.ImageMemFileXfer.Get(ref _appIdentity, ref _currentDS, ref memXferOSX) :
DGImage.ImageMemFileXfer.Get(ref _appIdentity, ref _currentDS, ref memXfer);
if (rc == TWRC.SUCCESS || rc == TWRC.XFERDONE)
{
try try
{ {
var written = TWPlatform.IsMacOSX ? do
memXferOSX.BytesWritten : memXfer.BytesWritten; {
rc = TWPlatform.IsMacOSX ?
DGImage.ImageMemXfer.Get(ref _appIdentity, ref _currentDS, ref memXferOSX) :
DGImage.ImageMemXfer.Get(ref _appIdentity, ref _currentDS, ref memXfer);
IntPtr lockedPtr = Lock(memPtr); if (rc == TWRC.SUCCESS || rc == TWRC.XFERDONE)
Marshal.Copy(lockedPtr, dotnetBuff, 0, (int)written); {
outStream.Write(dotnetBuff, 0, (int)written); try
{
var written = TWPlatform.IsMacOSX ?
memXferOSX.BytesWritten : memXfer.BytesWritten;
IntPtr lockedPtr = Lock(memPtr);
// assemble!
//Marshal.Copy(lockedPtr, dotnetBuff, 0, (int)written);
//outStream.Write(dotnetBuff, 0, (int)written);
}
finally
{
Unlock(memPtr);
}
}
} while (rc == TWRC.SUCCESS);
} }
finally finally
{ {
Unlock(memPtr); if (memPtr != IntPtr.Zero) Free(memPtr);
BufferedData.MemPool.Return(dotnetBuff);
} }
}
} while (rc == TWRC.SUCCESS);
}
finally
{
if (memPtr != IntPtr.Zero) Free(memPtr);
BufferedData.MemPool.Return(dotnetBuff);
}
if (rc == TWRC.XFERDONE)
{ if (rc == TWRC.XFERDONE)
try {
{ try
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info); {
// ToArray bypasses the XferMemPool but I guess this will have to do for now DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out info);
var args = new TransferredEventArgs(this, info, fileSetup, new BufferedData(outStream.ToArray(), (int)outStream.Length, false)); //var args = new DataTransferredEventArgs(info, null, outStream.ToArray());
Transferred?.Invoke(this, args); //DataTransferred?.Invoke(this, args);
}
catch { }
pending = new TW_PENDINGXFERS();
var sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS)
{
State = pending.Count == 0 ? STATE.S5 : STATE.S6;
}
return sts;
}
return WrapInSTS(rc);
} }
catch { }
var sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending)); private STS TransferMemoryFileImage(ref TW_PENDINGXFERS pending)
if (sts.RC == TWRC.SUCCESS)
{ {
State = pending.Count == 0 ? STATE.S5 : STATE.S6; var rc = DGControl.SetupFileXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPFILEXFER fileSetup);
} if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
return sts; rc = DGControl.SetupMemXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPMEMXFER memSetup);
} if (rc != TWRC.SUCCESS) return WrapInSTS(rc);
return WrapInSTS(rc);
}
private STS TransferFileImage(ref TW_PENDINGXFERS pending) uint buffSize = memSetup.DetermineBufferSize();
{ var memPtr = Alloc(buffSize);
// assuming user already configured the transfer in transferready event,
// get what will be transferred
DGControl.SetupFileXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPFILEXFER fileSetup);
// and just start it
var sts = WrapInSTS(DGImage.ImageFileXfer.Get(ref _appIdentity, ref _currentDS));
if (sts.RC == TWRC.XFERDONE)
{
State = STATE.S7;
try
{
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
var args = new TransferredEventArgs(this, info, fileSetup, default);
Transferred?.Invoke(this, args);
}
catch { }
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending)); TW_IMAGEMEMXFER memXfer = TW_IMAGEMEMXFER.DONTCARE();
if (sts.RC == TWRC.SUCCESS) TW_IMAGEMEMXFER_MACOSX memXferOSX = TW_IMAGEMEMXFER_MACOSX.DONTCARE();
{ memXfer.Memory = new TW_MEMORY
State = pending.Count == 0 ? STATE.S5 : STATE.S6; {
} Flags = (uint)(TWMF.APPOWNS | TWMF.POINTER),
} Length = buffSize,
return sts; TheMem = memPtr
} };
memXferOSX.Memory = memXfer.Memory;
private STS TransferNativeImage(ref TW_PENDINGXFERS pending) // TODO: how to get actual file size before hand? Is it imagelayout?
{ // otherwise will just write to stream with lots of copies
IntPtr dataPtr = IntPtr.Zero; byte[] dotnetBuff = BufferedData.MemPool.Rent((int)buffSize);
IntPtr lockedPtr = IntPtr.Zero; using var outStream = new MemoryStream();
try
{
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
var sts = WrapInSTS(DGImage.ImageNativeXfer.Get(ref _appIdentity, ref _currentDS, out dataPtr));
if (sts.RC == TWRC.XFERDONE)
{
State = STATE.S7;
lockedPtr = Lock(dataPtr);
BufferedData? data = default;
if (ImageTools.IsDib(lockedPtr))
{
data = ImageTools.GetBitmapData(lockedPtr);
}
else if (ImageTools.IsTiff(lockedPtr))
{
data = ImageTools.GetTiffData(lockedPtr);
}
else
{
// PicHandle?
// don't support more formats :(
}
if (data != null)
{
try try
{ {
// some sources do not support getting image info in state 7 so do
// it's up there in the beginning now. {
var args = new TransferredEventArgs(this, info, null, data); rc = TWPlatform.IsMacOSX ?
Transferred?.Invoke(this, args); DGImage.ImageMemFileXfer.Get(ref _appIdentity, ref _currentDS, ref memXferOSX) :
DGImage.ImageMemFileXfer.Get(ref _appIdentity, ref _currentDS, ref memXfer);
if (rc == TWRC.SUCCESS || rc == TWRC.XFERDONE)
{
try
{
var written = TWPlatform.IsMacOSX ?
memXferOSX.BytesWritten : memXfer.BytesWritten;
IntPtr lockedPtr = Lock(memPtr);
Marshal.Copy(lockedPtr, dotnetBuff, 0, (int)written);
outStream.Write(dotnetBuff, 0, (int)written);
}
finally
{
Unlock(memPtr);
}
}
} while (rc == TWRC.SUCCESS);
} }
catch finally
{ {
data.Dispose(); if (memPtr != IntPtr.Zero) Free(memPtr);
BufferedData.MemPool.Return(dotnetBuff);
} }
}
if (rc == TWRC.XFERDONE)
{
try
{
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
// ToArray bypasses the XferMemPool but I guess this will have to do for now
var args = new TransferredEventArgs(this, info, fileSetup, new BufferedData(outStream.ToArray(), (int)outStream.Length, false));
Transferred?.Invoke(this, args);
}
catch { }
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending)); pending = new TW_PENDINGXFERS();
if (sts.RC == TWRC.SUCCESS) var sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
{ if (sts.RC == TWRC.SUCCESS)
State = pending.Count == 0 ? STATE.S5 : STATE.S6; {
} State = pending.Count == 0 ? STATE.S5 : STATE.S6;
}
return sts;
}
return WrapInSTS(rc);
} }
return sts;
}
finally
{
if (lockedPtr != IntPtr.Zero) Unlock(dataPtr);
if (dataPtr != IntPtr.Zero) Free(dataPtr);
}
}
} private STS TransferFileImage(ref TW_PENDINGXFERS pending)
{
// assuming user already configured the transfer in transferready event,
// get what will be transferred
DGControl.SetupFileXfer.Get(ref _appIdentity, ref _currentDS, out TW_SETUPFILEXFER fileSetup);
// and just start it
var sts = WrapInSTS(DGImage.ImageFileXfer.Get(ref _appIdentity, ref _currentDS));
if (sts.RC == TWRC.XFERDONE)
{
State = STATE.S7;
try
{
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
var args = new TransferredEventArgs(this, info, fileSetup, default);
Transferred?.Invoke(this, args);
}
catch { }
pending = new TW_PENDINGXFERS();
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS)
{
State = pending.Count == 0 ? STATE.S5 : STATE.S6;
}
}
return sts;
}
private STS TransferNativeImage(ref TW_PENDINGXFERS pending)
{
IntPtr dataPtr = IntPtr.Zero;
IntPtr lockedPtr = IntPtr.Zero;
try
{
DGImage.ImageInfo.Get(ref _appIdentity, ref _currentDS, out TW_IMAGEINFO info);
var sts = WrapInSTS(DGImage.ImageNativeXfer.Get(ref _appIdentity, ref _currentDS, out dataPtr));
if (sts.RC == TWRC.XFERDONE)
{
State = STATE.S7;
lockedPtr = Lock(dataPtr);
BufferedData? data = default;
if (ImageTools.IsDib(lockedPtr))
{
data = ImageTools.GetBitmapData(lockedPtr);
}
else if (ImageTools.IsTiff(lockedPtr))
{
data = ImageTools.GetTiffData(lockedPtr);
}
else
{
// PicHandle?
// don't support more formats :(
}
if (data != null)
{
try
{
// some sources do not support getting image info in state 7 so
// it's up there in the beginning now.
var args = new TransferredEventArgs(this, info, null, data);
Transferred?.Invoke(this, args);
}
catch
{
data.Dispose();
}
}
pending = new TW_PENDINGXFERS();
sts = WrapInSTS(DGControl.PendingXfers.EndXfer(ref _appIdentity, ref _currentDS, ref pending));
if (sts.RC == TWRC.SUCCESS)
{
State = pending.Count == 0 ? STATE.S5 : STATE.S6;
}
}
return sts;
}
finally
{
if (lockedPtr != IntPtr.Zero) Unlock(dataPtr);
if (dataPtr != IntPtr.Zero) Free(dataPtr);
}
}
}
} }