Assuming // Windows uses a DIB, the first usigned short is 40...
if (u16Magic == 40)
Is not exactly true.
For instance the Canoscan Lide 90, 100, 200 sends a DIB back but yoour code does not recognize it and sends back a null bitmap.
I added an else to the NativeTobitmap method inorder for this to work.
I also cleaned up a leak in the code that assumes if (u16Magic == 40).
I have pasted the code below.
You can diff to see the changes.
I hope it is OK to paste code here.
/// <summary>
/// Get .NET 'Bitmap' object from memory DIB via stream constructor.
/// This should work for most DIBs.
/// </summary>
/// <param name="a_platform">Our operating system</param>
/// <param name="a_intptrNative">The pointer to something (presumably a BITMAP or a TIFF image)</param>
/// <returns>C# Bitmap of image</returns>
private static Bitmap NativeToBitmap(Platform a_platform, IntPtr a_intptrNative)
{
// We need the first two bytes to decide if we have a DIB or a TIFF...
ushort u16Magic;
u16Magic = (ushort)Marshal.PtrToStructure(a_intptrNative, typeof(ushort));
// Windows uses a DIB, the first usigned short is 40... if (u16Magic == 40) { byte[] bBitmap; BITMAPFILEHEADER bitmapfileheader; BITMAPINFOHEADER bitmapinfoheader; // Our incoming DIB is a bitmap info header... bitmapinfoheader = (BITMAPINFOHEADER)Marshal.PtrToStructure(a_intptrNative, typeof(BITMAPINFOHEADER)); // Build our file header... bitmapfileheader = new BITMAPFILEHEADER(); bitmapfileheader.bfType = 0x4D42; // "BM" bitmapfileheader.bfSize = (uint)Marshal.SizeOf(typeof(BITMAPFILEHEADER)) + bitmapinfoheader.biSize + (bitmapinfoheader.biClrUsed * 4) + bitmapinfoheader.biSizeImage; bitmapfileheader.bfOffBits = (uint)Marshal.SizeOf(typeof(BITMAPFILEHEADER)) + bitmapinfoheader.biSize + (bitmapinfoheader.biClrUsed * 4); // Copy the file header into our byte array... IntPtr intptr = Marshal.AllocHGlobal(Marshal.SizeOf(bitmapfileheader)); Marshal.StructureToPtr(bitmapfileheader, intptr, true); bBitmap = new byte[bitmapfileheader.bfSize]; Marshal.Copy(intptr, bBitmap, 0, Marshal.SizeOf(bitmapfileheader)); Marshal.FreeHGlobal(intptr); intptr = IntPtr.Zero; // Copy the rest of the DIB into our byte array...... Marshal.Copy(a_intptrNative, bBitmap, Marshal.SizeOf(typeof(BITMAPFILEHEADER)), (int)bitmapfileheader.bfSize - Marshal.SizeOf(typeof(BITMAPFILEHEADER))); Bitmap bitmap = null; // Now we can turn the in-memory bitmap file into a Bitmap object... using (MemoryStream memorystream = new MemoryStream(bBitmap)) { // Unfortunately the stream has to be kept with the bitmap... using (Bitmap bitmapStream = new Bitmap(memorystream)) { // So we make a copy (ick)... bitmap = new Bitmap(bitmapStream); bitmapStream.Dispose(); } // Cleanup... //bitmapStream.Dispose(); memorystream.Close(); //bitmapStream = null; //memorystream = null; } bBitmap = null; // Return our bitmap... return (bitmap); } // Linux and Mac OS X use TIFF. We'll handle a simple Intel TIFF ("II")... else if (u16Magic == 0x4949) { int iTiffSize; ulong u64; ulong u64Pointer; ulong u64TiffHeaderSize; ulong u64TiffTagSize; byte[] abTiff; TIFFHEADER tiffheader; TIFFTAG tifftag; // Init stuff... tiffheader = new TIFFHEADER(); tifftag = new TIFFTAG(); u64TiffHeaderSize = (ulong)Marshal.SizeOf(tiffheader); u64TiffTagSize = (ulong)Marshal.SizeOf(tifftag); // Find the size of the image so we can turn it into a memory stream... iTiffSize = 0; tiffheader = (TIFFHEADER)Marshal.PtrToStructure(a_intptrNative, typeof(TIFFHEADER)); for (u64 = 0; u64 < 999; u64++) { u64Pointer = (ulong)a_intptrNative + u64TiffHeaderSize + (u64TiffTagSize * u64); tifftag = (TIFFTAG)Marshal.PtrToStructure((IntPtr)u64Pointer, typeof(TIFFTAG)); // StripOffsets... if (tifftag.u16Tag == 273) { iTiffSize += (int)tifftag.u32Value; } // StripByteCounts... if (tifftag.u16Tag == 279) { iTiffSize += (int)tifftag.u32Value; } } // No joy... if (iTiffSize == 0) { return (null); } // Copy the data to our byte array... abTiff = new byte[iTiffSize]; Marshal.Copy(a_intptrNative, abTiff, 0, iTiffSize); // Move the image into a memory stream... MemoryStream memorystream = new MemoryStream(abTiff); // Turn the memory stream into an in-memory TIFF image... Image imageTiff = Image.FromStream(memorystream); // Convert the in-memory tiff to a Bitmap object... Bitmap bitmap = new Bitmap(imageTiff); // Cleanup... abTiff = null; memorystream = null; imageTiff = null; // Return our bitmap... return (bitmap); } else { IntPtr dibhand; IntPtr bmpptr; IntPtr pixptr; dibhand = a_intptrNative; if (dibhand != IntPtr.Zero) { bmpptr = GlobalLock(dibhand); pixptr = GetPixelInfo(bmpptr); IntPtr img = IntPtr.Zero; int st = GdipCreateBitmapFromGdiDib(bmpptr, pixptr, ref img); if ((st != 0) || (img == IntPtr.Zero)) return (null); MethodInfo mi = typeof(Bitmap).GetMethod("FromGDIplus", BindingFlags.Static | BindingFlags.NonPublic); Bitmap bmp = null; if ((st == 0) && (img != IntPtr.Zero)) // success bmp = (Bitmap)mi.Invoke(null, new object[] { img }); //GlobalFree(dibhand); GlobalUnlock(dibhand); dibhand = IntPtr.Zero; Bitmap b = new Bitmap(bmp); bmp.Dispose(); bmp = null; return b; } } // Uh-oh... return (null); } Thanks neticous PS: you all have done some very handle work here. Thank you.
I added some using statements so the above would compile.
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Reflection;
Thank you, it really helped.
Thanks for the help. I found the root cause of the problem. The GlobalLock is the key, with a GPTR it returns the same value, with a GHND it returns the pointer. I managed to get a scanner that exhibits the problem as described in this ticket, and confirmed the fix, you'll see the way I did it in the code update that should come out tomorrow. I'll leave the ticket open for confirmation that it's working...
The new code is up, please let me know if it makes a difference...