Memory Question

smithmrk
smithmrk used Ask the Experts™
on
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
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2012
Top Expert 2014

Commented:
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.

Author

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

Thanks,
Mark
Most Valuable Expert 2012
Top Expert 2014

Commented:
You can keep the images in memory as ByteArray instead of streams.
PMI ACP® Project Management

Prepare for the PMI Agile Certified Practitioner (PMI-ACP)® exam, which formally recognizes your knowledge of agile principles and your skill with agile techniques.

Author

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

Thanks,
Mark
Most Valuable Expert 2012
Top Expert 2014
Commented:
You can still use arraylist but instead of storing memory streams in it, you read the memory stream to a byte array and store that in the array list.

http://msdn.microsoft.com/en-GB/library/system.io.memorystream.toarray.aspx

Author

Commented:
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

Most Valuable Expert 2012
Top Expert 2014

Commented:
Looks fine. Does it work?

Author

Commented:
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

Author

Commented:
Thanks for you help!

Mark
Most Valuable Expert 2012
Top Expert 2014

Commented:
Glad it worked :-)

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial