Avatar of smithmrk
smithmrk
Flag for United States of America asked on

Memory Question

OK, I need to understand something because I'm confused!

I have this loop here that adds images to a ArrayList from Memory.
Then I loop through the ArrayList to create a Multipage TIFF File.

What I don't understand is if I close the MemoryStream after I create each image it still adds the images to the ArrayList...but when I go to read them I get an error that the Stream is closed?

This WORKS:
For icount As Int16 = 0 To 2
            Dim Ticket As New Bitmap(1312, 574)
            Dim ticketGraphics As Graphics = Graphics.FromImage(Ticket)
            Dim MyFont As New Font("Arial", 20, FontStyle.Regular)
            Dim memstrm As IO.MemoryStream = New IO.MemoryStream

            ticketGraphics.DrawString("-- Test Ticket --", MyFont, Brushes.Black, 520, 90)
            ticketGraphics.DrawString(icount.ToString, MyFont, Brushes.Black, 520, 120)
            Ticket.Save(memstrm, System.Drawing.Imaging.ImageFormat.Tiff)

            MultipageImages.Add(memstrm)
            memstrm.Flush()
        Next

This DOES NOT WORK after I add them all to the ArrayList:
For icount As Int16 = 0 To 2
            Dim Ticket As New Bitmap(1312, 574)
            Dim ticketGraphics As Graphics = Graphics.FromImage(Ticket)
            Dim MyFont As New Font("Arial", 20, FontStyle.Regular)
            Dim memstrm As IO.MemoryStream = New IO.MemoryStream

            ticketGraphics.DrawString("-- Test Ticket --", MyFont, Brushes.Black, 520, 90)
            ticketGraphics.DrawString(icount.ToString, MyFont, Brushes.Black, 520, 120)
            Ticket.Save(memstrm, System.Drawing.Imaging.ImageFormat.Tiff)

            MultipageImages.Add(memstrm)
            memstrm.Flush()
           memstrm.Close()
       Next

Private Sub CreateMultiPage_ImageFile()
        ImageWriter = New IO.FileStream("C:\MemoryTest.tif", FileMode.Create)

        Dim saveEncoder As Encoder
        Dim compressionEncoder As Encoder
        Dim SaveEncodeParam As EncoderParameter
        Dim EncoderParams As New EncoderParameters(1)
        Dim codecInfo As ImageCodecInfo = getCodecForString("TIFF")

        saveEncoder = Encoder.SaveFlag
        compressionEncoder = Encoder.Compression

        ' Save the first page:.
        SaveEncodeParam = New EncoderParameter(saveEncoder, CLng(EncoderValue.MultiFrame))
        EncoderParams.Param(0) = SaveEncodeParam

        Dim TicketImage As Image = Image.FromStream(DirectCast(MultipageImages(0), Stream))
       TicketImage.Save(ImageWriter, codecInfo, EncoderParams)

        For iCount As Int16 = 1 To MultipageImages.Count - 1

            ' Save the next set of pages:
            SaveEncodeParam = New EncoderParameter(saveEncoder, CLng(EncoderValue.FrameDimensionPage))
            EncoderParams.Param(0) = SaveEncodeParam

            Dim NextImage As Image = Image.FromStream(DirectCast(MultipageImages(iCount), Stream))
            TicketImage.SaveAdd(NextImage, EncoderParams)
        Next

        SaveEncodeParam = New EncoderParameter(saveEncoder, CLng(EncoderValue.Flush))
        EncoderParams.Param(0) = SaveEncodeParam
        TicketImage.SaveAdd(EncoderParams)

        MultipageImages.Clear()
        MultipageImages = Nothing
        ImageWriter.Close()
        ImageWriter.Dispose()
    End Sub

The error occurs when trying to load the Image From the ArrayList into an Image...but it works fine if I don't close the Memory Stream after each image...why?

Just trying to manage the memory to the best to not overload it.

Thanks,
Mark
Visual Basic.NET

Avatar of undefined
Last Comment
Nasir Razzaq

8/22/2022 - Mon
Nasir Razzaq

You are just adding a reference to the original stream to the array so when you access it, it still tries to access the original memstream.
smithmrk

ASKER
OK, it this a good practice or should I be doing something different to use the memory resources to the best?

Thanks,
Mark
Nasir Razzaq

You can keep the images in memory as ByteArray instead of streams.
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
smithmrk

ASKER
OK...how do I convert / change my code to use a Byte Array vs. the Array List?

Thanks,
Mark
ASKER CERTIFIED SOLUTION
Nasir Razzaq

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
smithmrk

ASKER
How does this look?

Imports System.IO
Imports System.Xml
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

Public Class Form1
    Private ImageWriter As IO.FileStream
    Private MultipageImages As New ArrayList
    Private ImageByteArray As Byte()

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        For icount As Int16 = 0 To 4
            Dim Ticket As New Bitmap(1312, 574)
            Dim ticketGraphics As Graphics = Graphics.FromImage(Ticket)
            Dim MyFont As New Font("Arial", 20, FontStyle.Regular)
            Dim memstrm As IO.MemoryStream = New IO.MemoryStream

            ticketGraphics.DrawString("-- Test Ticket --", MyFont, Brushes.Black, 520, 90)
            ticketGraphics.DrawString(icount.ToString, MyFont, Brushes.Black, 520, 120)
            Ticket.Save(memstrm, System.Drawing.Imaging.ImageFormat.Tiff)

            ImageByteArray = memstrm.ToArray
            MultipageImages.Add(ImageByteArray)
            memstrm.Flush()
            memstrm.Close()
            memstrm.Dispose()
            Ticket.Dispose()
            ticketGraphics.Dispose()
            MyFont.Dispose()
            ImageByteArray = Nothing
        Next

        Call CreateMultiPage_ImageFile()
        MessageBox.Show("Done")
    End Sub

    Private Sub CreateMultiPage_ImageFile()
        ImageWriter = New IO.FileStream("C:\MemoryTest.tif", FileMode.Create)

        Dim saveEncoder As Encoder
        Dim compressionEncoder As Encoder
        Dim SaveEncodeParam As EncoderParameter
        Dim EncoderParams As New EncoderParameters(1)
        Dim codecInfo As ImageCodecInfo = getCodecForString("TIFF")

        saveEncoder = Encoder.SaveFlag
        compressionEncoder = Encoder.Compression

        ' Save the first page:.
        SaveEncodeParam = New EncoderParameter(saveEncoder, CLng(EncoderValue.MultiFrame))
        EncoderParams.Param(0) = SaveEncodeParam

        ImageByteArray = MultipageImages(0)
        Dim memstrm As IO.MemoryStream = New IO.MemoryStream(ImageByteArray)

        Dim TicketImage As Image = Image.FromStream(memstrm)
        TicketImage.Save(ImageWriter, codecInfo, EncoderParams)
        memstrm.Flush()
        memstrm.Close()
        memstrm.Dispose()
        ImageByteArray = Nothing

        For iCount As Int16 = 1 To MultipageImages.Count - 1
            ' Save the next set of pages:
            SaveEncodeParam = New EncoderParameter(saveEncoder, CLng(EncoderValue.FrameDimensionPage))
            EncoderParams.Param(0) = SaveEncodeParam

            ImageByteArray = MultipageImages(iCount)
            memstrm = New IO.MemoryStream(ImageByteArray)

            Dim NextImage As Image = Image.FromStream(memstrm)
            TicketImage.SaveAdd(NextImage, EncoderParams)
            memstrm.Flush()
            memstrm.Close()
            memstrm.Dispose()
            NextImage.Dispose()
            ImageByteArray = Nothing
        Next

        SaveEncodeParam = New EncoderParameter(saveEncoder, CLng(EncoderValue.Flush))
        EncoderParams.Param(0) = SaveEncodeParam
        TicketImage.SaveAdd(EncoderParams)

        MultipageImages.Clear()
        MultipageImages = Nothing
        ImageWriter.Close()
        ImageWriter.Dispose()
    End Sub

    Private Function getCodecForString(ByVal type As String) As ImageCodecInfo
        Dim info As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders()

        For i As Integer = 0 To info.Length - 1
            Dim EnumName As String = type.ToString()
            If info(i).FormatDescription.Equals(EnumName) Then
                Return info(i)
            End If
        Next

        Return Nothing
    End Function
End Class

Open in new window

Nasir Razzaq

Looks fine. Does it work?
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
smithmrk

ASKER
YES, Thank You!
I'm just trying to make my program as efficent as possible to make the best of memory and resources.

Thanks again,
Mark
smithmrk

ASKER
Thanks for you help!

Mark
Nasir Razzaq

Glad it worked :-)
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23