diff --git a/src/NTwain/Data/TWAINH.cs b/src/NTwain/Data/TWAINH.cs index bdd7d12..f801fbb 100644 --- a/src/NTwain/Data/TWAINH.cs +++ b/src/NTwain/Data/TWAINH.cs @@ -1509,6 +1509,10 @@ namespace NTwain.Data private TW_INFO Info_097; private TW_INFO Info_098; private TW_INFO Info_099; + + // 200 seems overkill for anyone to request + // at once so I cut it down in half + //private TW_INFO Info_100; //private TW_INFO Info_101; //private TW_INFO Info_102; diff --git a/src/NTwain/Data/TWAINH_EXTRAS.cs b/src/NTwain/Data/TWAINH_EXTRAS.cs index 2b2dbde..d7d69a3 100644 --- a/src/NTwain/Data/TWAINH_EXTRAS.cs +++ b/src/NTwain/Data/TWAINH_EXTRAS.cs @@ -881,6 +881,7 @@ namespace NTwain.Data /// public void Free(IMemoryManager memMgr) { + #region don't open this Info_000.Free(memMgr); Info_001.Free(memMgr); Info_002.Free(memMgr); @@ -981,42 +982,110 @@ namespace NTwain.Data Info_097.Free(memMgr); Info_098.Free(memMgr); Info_099.Free(memMgr); + #endregion } } partial struct TW_INFO { - //public unsafe TValue ReadTWTYData(int itemIndex = 0) where TValue : struct - //{ - // if (ReturnCode != TWRC.SUCCESS || NumItems == 0) return default; - - // var sz = ItemType.GetItemTypeSize(); - // if (ItemType == TWTY.HANDLE || (sz * NumItems) > 4) - // { - // // read as ptr - // // this is probably not correct - // //((IntPtr)Item.ToPointer()).ReadTWTYData(ItemType; - // } - // else - // { - // // read as 4 byte number - // } - //} + /// + /// Quick check to see if the pointer is really + /// a pointer or actual data (ugh). + /// + public bool IsDataAPointer => + ItemType == TWTY.HANDLE || (ItemType.GetItemTypeSize() * NumItems) > IntPtr.Size; // should it be intptr.size or just 4? /// - /// Frees any memory used by this if necessary. + /// Try to read out the item as the type specified in . + /// This ONLY works if the data is not a pointer (see ). + /// For pointers you'd read it yourself with + /// . + /// Unless it's a handle () to non-twain-strings, then you'd use + /// . + /// + /// + /// + /// + public unsafe TValue ReadNonPointerData() where TValue : struct + { + if (ReturnCode != TWRC.SUCCESS || NumItems == 0 || IsDataAPointer) return default; + + // we can try a trick and make a pointer to this numeric data + // and re-use our pointer reader. There's a good chance this is wrong in many ways. + // TODO: test this idea in some unit test + var value = TWPlatform.Is32bit ? Item.ToUInt32() : Item.ToUInt64(); // the value should be 32bit from the spec but not sure how it'll work in 64bit + + var fakePtr = (IntPtr)(&value); + + return fakePtr.ReadTWTYData(ItemType, 0); + } + + /// + /// Try to read a null-terminated string from the item. + /// + /// + /// If item is an array specify which string to read + /// + public unsafe string? ReadHandleString(IMemoryManager memMgr, int index = 0) + { + if (index < 0 || index >= NumItems || !IsDataAPointer) return default; + + // why is twain being difficult and not use TW_STR* like a normal person. + // what even is the encoding for those things? Imma yolo it. + string? value; + var itemAsPtr = (IntPtr)Item.ToPointer(); // this is also iffy + + if (NumItems == 1) + { + // if 1, item is already the pointer to the string + value = LockAndReadNullTerminatedString(memMgr, itemAsPtr); + } + else + { + // if more than 1, item points to an array of pointers that each points to their own string + var lockPtr = memMgr.Lock(itemAsPtr); + lockPtr += (IntPtr.Size * index); + // is this even correct? I hope it is + var subItemPtr = Marshal.PtrToStructure(lockPtr); + value = LockAndReadNullTerminatedString(memMgr, subItemPtr); + memMgr.Unlock(itemAsPtr); + } + return value; + } + + private string? LockAndReadNullTerminatedString(IMemoryManager memMgr, IntPtr data) + { + var lockPtr = memMgr.Lock(data); + // yolo as ansi, should work in most cases + var value = Marshal.PtrToStringAnsi(lockPtr); + memMgr.Unlock(data); + return value; + } + + /// + /// Frees all DS-allocated memory if necessary. /// /// internal unsafe void Free(IMemoryManager memMgr) { - if (ReturnCode == TWRC.SUCCESS && Item != UIntPtr.Zero && - (ItemType == TWTY.HANDLE || (ItemType.GetItemTypeSize() * NumItems) > 4)) + if (ReturnCode != TWRC.SUCCESS || !IsDataAPointer) return; + + var itemAsPtr = (IntPtr)Item.ToPointer(); // this is also iffy + if (ItemType == TWTY.HANDLE && NumItems > 1) { - // this is probably not correct - memMgr.Free((IntPtr)Item.ToPointer()); - Item = UIntPtr.Zero; // this is only a copy but whatev + // must go into each handle in the array and free them individually :( + var lockPtr = memMgr.Lock(itemAsPtr); + for (var i = 0; i < NumItems; i++) + { + // is this even correct? I hope it is + var subItemPtr = Marshal.PtrToStructure(lockPtr); + memMgr.Free(subItemPtr); + lockPtr += IntPtr.Size; + } } + memMgr.Free(itemAsPtr); + Item = UIntPtr.Zero; } } } diff --git a/src/NTwain/Data/ValueReader.cs b/src/NTwain/Data/ValueReader.cs index 75c8661..1ab5c42 100644 --- a/src/NTwain/Data/ValueReader.cs +++ b/src/NTwain/Data/ValueReader.cs @@ -430,7 +430,7 @@ namespace NTwain.Data /// The twain type. /// Index of the item if pointer is array. /// - static TValue ReadTWTYData(this IntPtr intptr, TWTY type, int itemIndex) where TValue : struct + public static TValue ReadTWTYData(this IntPtr intptr, TWTY type, int itemIndex) where TValue : struct { var isEnum = typeof(TValue).IsEnum;