2

I have been hitting my head against a wall for the last few days. I think I might be missing something very simple?

Ok so here is the trimmed down version which reproduces the error that I am getting. I have tried to include as much detail as possible but if you would like to some other information, simply let me know.


FORM LAYOUT


I have two forms.

  1. Let's call them Form1 and Form2.
  2. Form1 has 2 picture boxes. Let's call them PictureBox1 and PictureBox2. PictureBox1 has a BackgroundImage set as shown below.

enter image description here

  1. Form2 has only 1 picture box called PictureBox1. It has no image. It looks like this

enter image description here

On my Form1, I have this code.

Dim data As Byte() = Nothing
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    '~~> I am doing this just for demonstration purpose. The data is actually coming
    '~~> from SQL database where the image is stored
    Dim _img As Image = PictureBox1.BackgroundImage

    Using MS As New MemoryStream()
        PictureBox1.BackgroundImage.Save(MS, PictureBox1.BackgroundImage.RawFormat)
        data = MS.GetBuffer()
    End Using
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    If data IsNot Nothing Then
        Dim frm As New Form2

        With frm
            Using ms As New MemoryStream(data, 0, data.Length)
                ms.Write(data, 0, data.Length)

                '~~> Set the image of picbox#2 in form 1
                PictureBox2.BackgroundImage = Image.FromStream(ms, True)

                '~~> Set the image of picbox#1 in form 2
                .PictureBox1.BackgroundImage = Image.FromStream(ms, True)
            End Using

            .ShowDialog()
        End With
    End If
End Sub

Target Framework: 4.8.1


PROBLEM STATEMENT


If I comment out the line .PictureBox1.BackgroundImage = Image.FromStream(ms, True) which sets the image of PictureBox1 in Form2 and if I run this code, everything works fine. The PictureBox2 in Form1 get populated and the Form2 is shown with no image(obviously). However, if I uncomment the line and then run the code, I get System.OutOfMemoryException:Out of memory. as shown below.

enter image description here


THIS WORKS


If I use .PictureBox1.Image = Image.FromStream(ms, True), then it works just fine as shown below.

enter image description here


QUESTION: I would like to understand why am I getting this System.OutOfMemoryException:Out of memory. error.

Siddharth Rout
  • 147,039
  • 17
  • 206
  • 250
  • Can you try resetting the memory stream position by `MS.Seek(0, SeekOrigin.Begin)` before calling `Image.FromStream`; – Mat J Nov 07 '22 at 05:29
  • Thanks @MatJ: Yes this was something which I had tried but that did not help. However, I tried it again now. Same error. – Siddharth Rout Nov 07 '22 at 05:33
  • Note: `.Image` works but not `.BackgroundImage` – Siddharth Rout Nov 07 '22 at 05:34
  • Use two different MemoryStreams. Also, this: `MS.GetBuffer()` is **very** wrong, you need `MS.ToArray()` – Jimi Nov 07 '22 at 08:05
  • @Jimi **1.** Using different MS did not help. Still getting the error. **2.** Yes you are right regarding `MS.ToArray()`. Also changing it to `ToArray()` also did not help. Same error. – Siddharth Rout Nov 07 '22 at 10:39
  • You have to use different MemoryStream objects, but you also need to avoid disposing of these objects -- Using `.ToArray()` is necessary, because `.GetBuffer()` is simply wrong, but it doesn't solve this problem, it's meant to solve the next one that you still don't know you have – Jimi Nov 07 '22 at 11:27
  • 1
    If you do different attempts, you need to show that code. You might have made other mistakes -- This is what your code should look like: `dim f = new Form2() f.pictureBox1.BackgroundImage = Image.FromStream(new MemoryStream(data), true, false) f.pictureBox2.BackgroundImage = Image.FromStream(new MemoryStream(data), true, false) f.ShowDialog()` -- BUT, you should avoid setting the Access Modifier of Controls to `Public`. You can instead have a public Method that accepts an `Image` as argument (or a collection of Images, for that matter), then the Form decides what to do with those objects – Jimi Nov 07 '22 at 11:51
  • 1
    You cannot dispose of the Stream that is used as the data origin of an Image. That's done when you dispose of the Image. See [the documentation](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.image.fromstream) about it: *[...] You must keep the stream open for the lifetime of the Image [...]* – Jimi Nov 07 '22 at 11:54
  • The following may be helpful: https://stackoverflow.com/a/68567606/10024425, https://stackoverflow.com/a/70321177/10024425, and https://stackoverflow.com/a/66616751/10024425 – Tu deschizi eu inchid Nov 07 '22 at 14:16
  • **1 of 2** @Jimi This works `f.pictureBox1.BackgroundImage = Image.FromStream(new MemoryStream(data), true, false) `. However I would still like to understand Why was the code failing for `.BackgroundImage` and not for `.Image`. Also an interesting thing happened...you mentioned "You must keep the stream open for the lifetime of the Image". So i commented your code and ran my old code to ensure that i was getting the error because I wanted to test what would happen if I moved `.ShowDialog()` inside `Using-EndUsing`. And guess what. The orginal code worked flawlessly. O_O – Siddharth Rout Nov 07 '22 at 17:18
  • **2 of 2** @Jimi Now the code that I posted above is not giving errors anymore. I now need to understand what did your code fix. This is getting mysterious... Thanks again for posting your comments. – Siddharth Rout Nov 07 '22 at 17:19
  • 1
    The Image Property treats the bitmap data slightly differently (i.e., it copies the content to an internal buffer), but you cannot rely on this type of behavior, you always need to keep the Stream *alive*, then dispose of the Image when not needed anymore, which also releases the Stream (i.e., if assigned, `pictureBox1.Image?.Dispose()`) -- If you have moved the call to `.ShowDialog()` inside the `using` block (which is actually a `Try / Finally` block internally), then the stream is disposed only when you exit the `using` block, which happens when you close the Modal dialog – Jimi Nov 07 '22 at 17:40
  • 1
    Also to mention, when you show a Form with `ShowDialog()`, you have to declare the instance of the Form with a `Using` statement (or call `Close()` after `ShowDialog()` returns), since a Modal Form cannot dispose of itself, as it instead happens when you call `Show()` (I'm not referring to default instances of Forms - VB.Net specific, unfortunately - but to instance of Forms created with the `New` keyword) – Jimi Nov 07 '22 at 17:49
  • Another problem here you'll get in `Form1` after closing `Form2`. The steam created - and should be kept alive as mentioned - and used to create Form1``PictureBox2.BackgroundImage` will be disposed of outside the scope of the `Using` block. Perhaps calling `GC.Collect()` method could speed things up to see the result. Consider keeping the image itself instead of its byte array. `.Clone` it where it's necessary to do so. `.Dispose()` of the **clones** when they are no longer needed or should be replaced. – dr.null Nov 07 '22 at 19:13
  • Here's a question. And perhaps this is lost a little in the simplification of the example. But using streams at all really seems like an incredibly complicated way to pass an image around forms. Why not just pass the image instance? – Hursey Nov 07 '22 at 19:41
  • @Jimi: My investigation is on regarding why my old code has started working again after I ran your code. But that is a different thing altogether. If you would like to post an answer, I would gladly accept. – Siddharth Rout Nov 08 '22 at 03:27

0 Answers0