From de788ce91d2af09ace958dccc74e670eedb1afe2 Mon Sep 17 00:00:00 2001 From: Eugene Wang <8755753+soukoku@users.noreply.github.com> Date: Thu, 20 Nov 2025 22:36:36 -0500 Subject: [PATCH] Test reading some values boxed. --- src/NTwain/Data/TWAINH_EXTRAS.cs | 2 +- src/NTwain/Data/ValueReader.cs | 223 +++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 1 deletion(-) diff --git a/src/NTwain/Data/TWAINH_EXTRAS.cs b/src/NTwain/Data/TWAINH_EXTRAS.cs index aadcbc4..af61a6f 100644 --- a/src/NTwain/Data/TWAINH_EXTRAS.cs +++ b/src/NTwain/Data/TWAINH_EXTRAS.cs @@ -258,7 +258,7 @@ namespace NTwain.Data /// A more dotnet-friendly representation of . /// /// - public class Enumeration where TValue : struct + public class Enumeration { public int CurrentIndex; diff --git a/src/NTwain/Data/ValueReader.cs b/src/NTwain/Data/ValueReader.cs index 759d910..0b5ab63 100644 --- a/src/NTwain/Data/ValueReader.cs +++ b/src/NTwain/Data/ValueReader.cs @@ -84,6 +84,52 @@ namespace NTwain.Data } + /// + /// Reads a boxed one value out of a cap. This can only be done once if memory is freed. + /// + /// + /// + /// + /// + /// + public static object? ReadOneValueBoxed(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) + { + if (cap.ConType != TWON.ONEVALUE || cap.hContainer == IntPtr.Zero) return default; + + var lockedPtr = memMgr.Lock(cap.hContainer); + + try + { + TWTY itemType; + // Mac has a level of indirection and a different structure (ick)... + if (TWPlatform.IsMacOSX) + { + // Crack the container... + var onevalue = MarshalTo(lockedPtr); + itemType = (TWTY)onevalue.ItemType; + lockedPtr += Marshal.SizeOf(onevalue); + } + else + { + // Crack the container... + var onevalue = MarshalTo(lockedPtr); + itemType = onevalue.ItemType; + lockedPtr += Marshal.SizeOf(onevalue); + } + + return ReadTWTYDataBoxed(lockedPtr, itemType, 0); + } + finally + { + if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); + if (freeMemory) + { + memMgr.Free(cap.hContainer); + cap.hContainer = IntPtr.Zero; + } + } + } + /// /// Reads a one value out of a cap. This can only be done once if memory is freed. /// @@ -130,6 +176,78 @@ namespace NTwain.Data } } + public static Enumeration ReadEnumerationBoxed(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) + { + Enumeration retVal = new(); + + if (cap.ConType != TWON.ENUMERATION || cap.hContainer == IntPtr.Zero) return retVal; + + var lockedPtr = memMgr.Lock(cap.hContainer); + + try + { + TWTY itemType; + int count = 0; + + // Mac has a level of indirection and a different structure (ick)... + if (TWPlatform.IsMacOSX) + { + // Crack the container... + var twenumerationmacosx = MarshalTo(lockedPtr); + itemType = (TWTY)twenumerationmacosx.ItemType; + count = (int)twenumerationmacosx.NumItems; + retVal.DefaultIndex = (int)twenumerationmacosx.DefaultIndex; + retVal.CurrentIndex = (int)twenumerationmacosx.CurrentIndex; + lockedPtr += Marshal.SizeOf(twenumerationmacosx); + } + // Windows or the 2.4+ Linux DSM... + else + { + // Crack the container... + var twenumeration = MarshalTo(lockedPtr); + itemType = twenumeration.ItemType; + count = (int)twenumeration.NumItems; + retVal.DefaultIndex = (int)twenumeration.DefaultIndex; + retVal.CurrentIndex = (int)twenumeration.CurrentIndex; + lockedPtr += Marshal.SizeOf(twenumeration); + } + // The -2.3 Linux DSM... + //else if (twain.m_blFound020302Dsm64bit && (twain.m_linuxdsm == TWAIN.LinuxDsm.Is020302Dsm64bit)) + //{ + // // Crack the container... + // var twenumerationlinux64 = MarshalTo(lockedPtr); + // itemType = twenumerationlinux64.ItemType; + // count = (int)twenumerationlinux64.NumItems; + // retVal.DefaultIndex = (int)twenumerationlinux64.DefaultIndex; + // retVal.CurrentIndex = (int)twenumerationlinux64.CurrentIndex; + // lockedPtr += Marshal.SizeOf(twenumerationlinux64); + //} + // This shouldn't be possible, but what the hey... + //else + //{ + // Log.Error("This is serious, you win a cookie for getting here..."); + // return retVal; + //} + + retVal.Items = new object[count]; + + for (var i = 0; i < count; i++) + { + retVal.Items[i] = ReadTWTYDataBoxed(lockedPtr, itemType, i); + } + } + finally + { + if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); + if (freeMemory) + { + memMgr.Free(cap.hContainer); + cap.hContainer = IntPtr.Zero; + } + } + return retVal; + } + public static Enumeration ReadEnumeration(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct { Enumeration retVal = new(); @@ -202,6 +320,53 @@ namespace NTwain.Data return retVal; } + public static IList ReadArrayBoxed(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) + { + if (cap.ConType != TWON.ARRAY || cap.hContainer == IntPtr.Zero) return Array.Empty(); + + var lockedPtr = memMgr.Lock(cap.hContainer); + + try + { + TWTY itemType; + uint count; + + // Mac has a level of indirection and a different structure (ick)... + if (TWPlatform.IsMacOSX) + { + // Crack the container... + var twarraymacosx = MarshalTo(lockedPtr); + itemType = (TWTY)twarraymacosx.ItemType; + count = twarraymacosx.NumItems; + lockedPtr += Marshal.SizeOf(twarraymacosx); + } + else + { + // Crack the container... + var twarray = MarshalTo(lockedPtr); + itemType = twarray.ItemType; + count = twarray.NumItems; + lockedPtr += Marshal.SizeOf(twarray); + } + + var arr = new object[count]; + for (var i = 0; i < count; i++) + { + arr[i] = ReadTWTYDataBoxed(lockedPtr, itemType, i); + } + return arr; + } + finally + { + if (lockedPtr != IntPtr.Zero) memMgr.Unlock(cap.hContainer); + if (freeMemory) + { + memMgr.Free(cap.hContainer); + cap.hContainer = IntPtr.Zero; + } + } + } + public static IList ReadArray(this ref TW_CAPABILITY cap, IMemoryManager memMgr, bool freeMemory = true) where TValue : struct { if (cap.ConType != TWON.ARRAY || cap.hContainer == IntPtr.Zero) return Array.Empty(); @@ -373,6 +538,64 @@ namespace NTwain.Data } + + /// + /// Read the pointer content as a boxed value specified by , except . + /// + /// A locked pointer to the data pointer. If data is array this is the 0th item. + /// The twain type. + /// Index of the item if pointer is array. + /// + public static object ReadTWTYDataBoxed(this IntPtr intptr, TWTY type, int itemIndex) + { + switch (type) + { + default: + throw new NotSupportedException($"Unsupported item type {type} for reading."); + // TODO: verify if needs to read int32 for small types + case TWTY.HANDLE: + intptr += IntPtr.Size * itemIndex; + return MarshalTo(intptr); + case TWTY.INT8: + intptr += 1 * itemIndex; + return MarshalTo(intptr); + case TWTY.UINT8: + intptr += 1 * itemIndex; + return MarshalTo(intptr); + case TWTY.INT16: + intptr += 2 * itemIndex; + return MarshalTo(intptr); + case TWTY.BOOL: + case TWTY.UINT16: + intptr += 2 * itemIndex; + return MarshalTo(intptr); + case TWTY.INT32: + intptr += 4 * itemIndex; + return MarshalTo(intptr); + case TWTY.UINT32: + intptr += 4 * itemIndex; + return MarshalTo(intptr); + case TWTY.FIX32: + intptr += 4 * itemIndex; + return MarshalTo(intptr); + case TWTY.FRAME: + intptr += 16 * itemIndex; + return MarshalTo(intptr); + case TWTY.STR32: + intptr += TW_STR32.Size * itemIndex; + return MarshalTo(intptr); + case TWTY.STR64: + intptr += TW_STR64.Size * itemIndex; + return MarshalTo(intptr); + case TWTY.STR128: + intptr += TW_STR128.Size * itemIndex; + return MarshalTo(intptr); + case TWTY.STR255: + intptr += TW_STR255.Size * itemIndex; + return MarshalTo(intptr); + } + } + /// /// Read the pointer content as a value specified by , except . ///