6

Referencing How to get IntPtr from byte[] in C#

I am attempting to read the data that an IntPtr is referencing into a byte [] and then back into another IntPtr. The pointer is referencing an image captured from a scanner device so I have also made the assumption that capturing this information should be placed into a byte array.

I am also not sure if the Marshal.SizeOf() method will return the size of the data the IntPtr is referencing or the size of the pointer itself.

My issue is I am receiving the error "Type 'System.Byte[]' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed"

IntPtr bmpptr = Twain.GlobalLock (hImage);

try
{
     byte[] _imageTemp = new byte[Marshal.SizeOf(bmpptr)];
     Marshal.Copy(bmpptr, _imageTemp, 0, Marshal.SizeOf(bmpptr));

     IntPtr unmanagedPointer = Marshal.AllocHGlobal(
         Marshal.SizeOf(_imageTemp));

     try
     {
           Marshal.Copy(_imageTemp, 0, unmanagedPointer, 
               Marshal.SizeOf(_imageTemp));

           Gdip.SaveDIBAs(
               string.Format("{0}\\{1}.{2}", CaptureFolder, "Test", "jpg"), 
               unmanagedPointer, false);
     }
     finally
     {
           Marshal.FreeHGlobal(unmanagedPointer);
     }
}
catch (Exception e)
{
      Scanner.control.Test = e.Message;
}
Community
  • 1
  • 1
Scruffy The Janitor
  • 472
  • 3
  • 5
  • 13
  • Why cant you just use `bmpptr` in the call to `Gdip.SaveDIBAs`? – SwDevMan81 Dec 08 '10 at 16:14
  • Thats what the code I am modifying is doing today, this control is being used as an active x control and we are trying to get rid of client side storage of the images. Gdip.SaveDIBAs is saving the image to local storage, I want to be able to send the byte [] to the server for image creation. This code is my attempt to prototype that functionality. – Scruffy The Janitor Dec 08 '10 at 16:17
  • Ok, so you just want to go from the unmanaged `bmpptr` to a `byte[]`? – SwDevMan81 Dec 08 '10 at 16:20
  • End state, I want to take the unmanaged bmpptr to a byte array, Ajax it to the server, then reuse the Gdip.SaveDIBAs method to create the image server side by taking the byte [] and creating the same unmanaged pointer there. – Scruffy The Janitor Dec 08 '10 at 16:22
  • This really isn't close. Any kind of half decent Twain interop library lets you get a HBITMAP out of it. From which you can create an Image and Save() to an arbitrary stream. Which is what you are now doing the hard way by pinvoking GDI+. Like this one: http://www.codeproject.com/KB/dotnet/twaindotnet.aspx – Hans Passant Dec 08 '10 at 16:48

2 Answers2

7

The exception thrown by the call to SizeOf is correct; the method has no way of knowing what the length of the array that you are passing to it is, it just has a pointer.

To that end, the easiest way to get the data is to call the static Copy method on the Marshal class, passing the pointer to the unmanaged data, the index and number of bytes to read, as well as a pre-allocated buffer of bytes to marshal the data into.

As for getting the size of the array, as Anton Tykhyy pointed out in the comments, it appears (be very careful here) that the call to Twain.GlobalLock(hImage) is using memory allocated by GlobalAlloc, which means that you can make a call to the GlobalSize API function through the P/Invoke layer to get the size.

If it is not a handle to something allocated by a call to GlobalAlloc then you need to find out how the Twain module is allocating the memory and use the appropriate mechanism to determine the length of the memory pointed to by the IntPtr.

Community
  • 1
  • 1
casperOne
  • 73,706
  • 19
  • 184
  • 253
  • @Scruffy: you need to obtain the length of the array by some other means; try `GlobalSize` (http://msdn.microsoft.com/en-us/library/aa366593.aspx) – Anton Tykhyy Dec 08 '10 at 16:23
  • Thanks for your help, I did use that MSDN document as a reference to get started. Although I am still unclear how much to allocate to the size of the byte [] from the IntPtr. – Scruffy The Janitor Dec 08 '10 at 16:31
  • @Scruffy: Updated the answer to reflect how to (possibly) find the length. – casperOne Dec 08 '10 at 16:38
2

1- I assume bmpptr is already an unmanaged pointer, if so why do you need to 'round-trip' this back to an unmanaged pointer unmanagedPointer?

2- Marshal.SizeOf will not give you the size of the unmanaged memory allocation.

Using Marshal.Copy, you can copy the contents of unmanaged memory to a managed byte array, but you will need to know how many bytes need to be moved from the unmanaged buffer to the managed buffer. The API should ideally provide this information.

Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
  • The comments above, I will be sending the byte array to the server from the client and then creating the images instead of using client storage. – Scruffy The Janitor Dec 08 '10 at 16:24
  • @Scruffy, then the same approach should apply. Your problem is determining the size of the buffer. Marshal.SizeOf is not going to work, you need to get that info from the twain API. – Chris Taylor Dec 08 '10 at 16:30