6

I'm using locked bitmaps a lot recently, and I keep getting "attempted to access invalid memory" errors. This is mostly because the bitmap has been moved in memory. Some people using GCHandle.Alloc() to allocate memory in the CLR and pin it. Does Bitmap.LockBits() do the same? I don't understand the difference between "locking" memory and "pinning" memory. Can you also explain the terminology and the differences if any?

Robin Rodricks
  • 99,791
  • 133
  • 372
  • 575

2 Answers2

10

GCHandle.Alloc is a more generic method, that allows you to allocate a handle to any managed object and pin it in memory (or not). Pinning memory prevents GC from moving it around, which is especially useful when you have to pass some data, for example an array, to a unmanaged code.

GCHandle.Alloc will not help you access bitmap's data in any way, because pinning this object will just prevent the managed object from moving around (the Bitmap object) (and being garbage collected).

Bitmap however is a wrapper around native GDI+'s BITMAP structure. It doesn't keep data in any managed array that you would have to pin, it just managed a native handle to GDI+ bitmap object. Because of that Bitmap.LockBits is a way of telling this bitmap that you are interested in accessing it's memory, and it's just a wrapper around GdipBitmapLockBits function. So your need of calling it has more to do with the fact that you are working with GDI+ bitmaps than with the fact, that you're working in managed environment with GC.

Once you have used LockBits you should be able to access it's memory using pointers through BitmapData.Scan0 - it's an address of first byte of data. You should not have problems as long, as you do not access memory behind BitmapData.Scan0 + Height * Stride.

And rememberto UnlockBits when you are done.

Marcin Deptuła
  • 11,171
  • 2
  • 30
  • 40
0

In your case an attempted to access invalid memory error is most probably caused by invalid memory allocation which you are doing in the unsafe part of code, e.g allocated array is smaller than number of pixels you are trying to put in that.

There is also no need to think about pinning the objects unless your image data is less than 85000 Bytes as only objects less than 85K will be moved in memory.

Another story would be if you pass the object to unmanaged code, for example in the c++ library for faster processing. In this case your exception is very possible if the passed image gets out of scope and will be garbage collected. In this case you can use GCHandle.Alloc(imageArray,GCHandleType.Pinned); and than call Free if you do not need it any longer.

Sergio Basurco
  • 2,998
  • 2
  • 15
  • 40
VladL
  • 11,959
  • 10
  • 58
  • 82
  • The part about Large Object Heap and objects over 85k is a red herring, and makes this answer worse. The recommendation to use `GCHandle.Alloc` on a pointer is no good either. – Ben Voigt Nov 10 '14 at 23:37
  • @BenVoigt I've red it twice and don't see why it's red herring or is not good. With this answer I've shared my own expirence which I had in my similar case http://stackoverflow.com/questions/14735520/ (read also comments ). So please explain your words. – VladL Nov 11 '14 at 12:17
  • This question is asking about the `BitmapData.Scan0` pointer gotten from `Bitmap.LockBits()`. `Bitmap` itself will fail if the data gets moved around, so there is never any reason for the `LockBits()` caller to try to pin the data. And if the buffer is controlled by `Bitmap`, which is the usual case, it doesn't get allocated from GC heap, so it is neither on the generational heap nor the large object heap. – Ben Voigt Nov 11 '14 at 13:02
  • @BenVoigt 1) The OP has clearly assumed that `This is mostly because the bitmap has been moved in memory.` My answer was that it should not happen if data > 85k, but you've called it red herring. `2) And if the buffer is controlled by Bitmap, which is the usual case, it doesn't get allocated from GC heap`. What is GC heap? In C# array is an object and objects are always allocated on the heap, in case of c# Bitmap it's a managed heap. Maybe you think that OP allocates the c buffer and fills it by himself, than that would be just your assumption as he could open an existing bitmap from the c# – VladL Nov 11 '14 at 14:17
  • But the pixel data is not stored in any object. It's stored in unmanaged memory, held by the Windows (not C#) BITMAP structure. That's why `LockBits` gives you back an `IntPtr` with the pixel data, not a `byte[]`. – Ben Voigt Nov 11 '14 at 19:59
  • @BenVoigt just a sample: `GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned); IntPtr pointer = pinnedArray.AddrOfPinnedObject();` Does that mean that `byteArray` is not managed any longer because I have a pointer to it's first byte? Everything stored on the heap has it's address so it's not a problem to get it. AFAIK .NET Bitmap class is completely managed. Otherwise .net dlls must be compiled unsafe. Do you have some proof for your words? – VladL Nov 11 '14 at 21:05
  • That's an array. This question is about bitmaps. If you create a bitmap from a managed array, you must pin it before creation of the bitmap and keep it pinned for as long as the Bitmap object exists. **Pinning is never an issue when calling `LockBits()`**. "As far as I know .NET Bitmap class is completely unmanaged." You're guessing, you don't know, what you guessed is wrong, and that is why you are completely unqualified to answer this question. – Ben Voigt Nov 12 '14 at 00:35
  • The .NET drawing stuff, at least in the WinForms generation, is a wrapper around the unmanaged GDI+ API. Did you even bother to read the other, older answer to this very question? And yes, the Microsoft DLLs are compiled unsafe. How else could they do things like write into strings (like `StringBuilder` does) or break type safety in `Buffer.BlockCopy()` and pretty much the entire Marshal class. – Ben Voigt Nov 12 '14 at 01:00
  • @BenVoigt Ok, I understand your critique. As OP didn't add any code, I couldn't know what kind of problems he might have. As I said I shared my expireince as I had to do something with this subject at the same time (2 other questions of mine: http://stackoverflow.com/questions/15386385 , http://stackoverflow.com/questions/14583826). This doesn't directly answer the question, but the info is not wrong and might help the people (2 found it helpful) who having this error and seeking for solution. I've got your point, thanks for down vote, good bye :) – VladL Nov 12 '14 at 07:30