Is there something that needs to be done with the following code to release the memory it uses?
Dim objImage As MemoryStream
Dim objwebClient As WebClient
Dim sURL As String = Trim(m_StationInterface.PicLocation)
objwebClient = New WebClient
objImage = New MemoryStream(objwebClient.DownloadData(sURL))
m_imgLiftingEye.Image = Image.FromStream(objImage)
The code is on a popup form that shouldn't ever get disposed. A new image is loaded onto the form every time it pops up. However, the process size for the application continues to grow each time it makes it through that code block.
I've tried objImage.Close() and .Flush(), objWebClient.Dispose(). The process size still grows by a good 4mb after every call. It's like the old image is kept in memory.
Image implements IDisposable, so you should Dispose the old image before replacing it with a new one.
Something like (bear with me, I haven't used VB in a while):
Dim objImage As MemoryStream
Dim objwebClient As WebClient
Dim sURL As String = Trim(m_StationInterface.PicLocation)
objwebClient = New WebClient
objImage = New MemoryStream(objwebClient.DownloadData(sURL))
If m_imgLiftingEye.Image Is Not Nothing Then
m_imgLiftingEye.Image.Dispose()
End If
m_imgLiftingEye.Image = Image.FromStream(objImage)
Try this:
Function GetImage() As Image
Using wc As New WebClient(), _
ms As New MemoryStream(wc.DownloadData(m_StationInterface.PicLocation.Trim())
GetImage = Image.FromStream(ms).Clone()
End Using
End Function
MemoryStream implements the IDisposable interface, so you should call Dispose on that object when you are done using it:
objImage = New MemoryStream(objwebClient.DownloadData(sURL))
m_imgLiftingEye.Image = Image.FromStream(objImage)
objImage.Dispose()
I would guess your conclusion was right; the image (in the memory stream) does remain in the memory.
Update: as Marc pointed out Image.FromStream requires the stream to remain open for the lifetime of the image. To resolve this the MemoryStream variable should be declared in same scope as the image (as a field in the form). When loading the image, there should first be a check whether the MemoryStream already is open and if so, close and dispose it before using the variable for a new stream (let's assume that we call it m_imageStream). Since the image also implements IDisposable, the same is true for that one:
If Not m_imageStream Is Nothing Then
m_imageStream.Dispose()
End If
If m_imgLiftingEye.Image Is Not Nothing Then
m_imgLiftingEye.Image.Dispose()
End If
m_imageStream = New MemoryStream(objwebClient.DownloadData(sURL))
m_imgLiftingEye.Image = Image.FromStream(m_imageStream)
I know I already gave one answer, but I've been thinking since then...
You said that this form should never be disposed. In that case, when exactly is this image load happening? My previous answer assumed it was during the form Shown event. However, if it's during the form Load event, it should only happen once total.
That is, unless more than one instance of the form is being created. If that's the case, and the previous form isn't being reused, you're ending up with multiple copies of the same form loaded in memory, each with its own copy of the image.
You could try
set objImage = nothing
set objwebClient = nothing
Often, like with ADO, if you don't explicitly set it to nothing it doesn't get released properly.
Related
Title says it all, How do I save a file from the application resources to the desktop for example?
In the future it will create a directory and store it in there, essentially I'm making a game installer, but none of this matters right now.
I have tried about 6 different code methods and all of them failed, kept throwing errors about not being able to convert an icon/image to a 1-dimensional array. Or that "Icon" or "Image" is not a member of System.Drawing.Icon
Any help would be appreciated
Thank you!
EDIT: Posting some code I've tried.
#1 ('Length' is not a member of System.Drawing.Icon)
Dim File01 As System.IO.FileStream = New System.IO.FileStream("C:\Users\" + Environment.UserName + "\Desktop\" + "EXAMPLE_Icon.ico", IO.FileMode.Create) File01.Write(My.Resources.EXAMPLE_Icon, 0, My.Resources.EXAMPLE_Icon.length) File01.Close()
#2 ('System.Drawing.Icon' cannot be converted to '1-dimensional array of Byte)
Dim filePath = Path.Combine("C:\Users\" + Environment.UserName + "\Desktop\", "EXAMPLE_Icon.ico") File.WriteAllBytes(filePath, My.Resources.EXAMPLE_Icon)
#3 ('RawFormat' is not a member of 'System.Drawing.Icon')
Dim filePath = Path.Combine("C:\Users\" + Environment.UserName + "\Desktop\", "EXAMPLE_Icon.ico") Using icon = My.Resources.EXAMPLE_Icon icon.Save("C:\Users\" + Environment.UserName + "\Desktop\", icon.RawFormat) End Using
Build the destination path using Path.Combine() when the path is composited.
Many known System locations have a predefined value in Environment.SpecialFolder: in your case, Environment.SpecialFolder.Desktop. Environment.GetFolderPath() accepts one of these values and returns the correct file system path.
This returns the Path of the Desktop folder for the current User:
Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
The Icon object has a Save() method that accepts a Stream as argument.
You can pass a FileStream to save the Icon to a File, preserving its format, or a MemoryStream, then use File.WriteAllBytes() to save the MemoryStream's buffer calling its ToArray() method.
Using a Resource name, as a string, using ResourceManager.GetObject():
Dim iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "myIcon.ico")
Using stream As New FileStream(iconPath, FileMode.Create, FileAccess.Write, FileShare.None),
ico = TryCast(My.Resources.ResourceManager.GetObject("myIcon"), Icon)
ico?.Save(stream)
End Using
Or, depending on what fits better in the context of your operations:
Using stream As New FileStream(iconPath, FileMode.Create, FileAccess.Write, FileShare.None)
My.Resources.myIcon.Save(stream)
End Using
GC.Collect()
Note: here, I'm calling GC.Collect() after calling Save() directly on the Icon object generated by My.Resources. The Docs don't exactly explain it: My.Resources is a factory, it doesn't return the object stored in the Project's Resources, it generates a new object (a different one each time) from the data stored as a Resource. With My.Resources.myIcon.Save(stream), we create a new object, save its data to disc, but never actually dispose of it. Calling GC.Collect() right after, the memory used is reclaimed immediately. If we don't, that memory is never reclaimed (we're leaking).
It's probably better to assign the generated object a to temporary variable declared with a Using statement. The two methods that follow can make use of GC.Collect(), calling it right after, to reclaim the allocated memory immediately, but it's not strictly required, we're not really leaking resources.
Open up Visual Studio's Diagnostic Tools to test the behavior with and without GC.Collect(), calling these methods multiple times.
Using stream As New FileStream(iconPath, FileMode.Create, FileAccess.Write, FileShare.None),
ico = My.Resources.myIcon
ico.Save(stream)
End Using
' GC.Collect() <= Not strictly required, but test the difference
Or using a MemoryStream:
Using ms As New MemoryStream(),
ico As Icon = My.Resources.myIcon
ico.Save(ms)
ms.Position = 0
File.WriteAllBytes(iconPath, ms.ToArray())
End Using
' GC.Collect() <= Not strictly required, but test the difference
Dim docu As New XmlDocument()
docu.load("C:\bigfile.xml")
Dim tempNode As XmlNode
tempNode = docu.SelectSingleNode("/header/type")
someArray(someIndex) = tempNode.innerText
...do something more...
I am using XmlDocument() to load a huge XML document (100~300MB)
When I open the document and read it as string, my application uses about 900MB of RAM.
I wonder why it happens and how can I prevent it ?
Note that : even, the XmlDocument does not have Dispose() to remove allocated things.
Although I need the whole string of the huge XML file in later part of the app, the /header/type.innerText is only a single word
More of source :
Private Sub setInfo(ByVal notePath As String)
Dim _NOTE As XDocument
_NOTE = XDocument.Load(notePath)
If (From node In _NOTE...<title> Select node).Value = "" Then
lvlist.Items.Add("No Title")
Else
lvlist.Items.Add((From node In _NOTE...<title> Select node).Value)
End If
lvlist.Items(lvlist.Items.Count - 1).SubItems.Add((From node In _NOTE...<group> Select node).Count)
End Sub
It reads XML document, counts tags and retrieves string value. That's all.
After having those values, _NOTE (XDocument) is of no use at that time.
XmlReader will probably solve your need. From MSDN:
Represents a reader that provides fast, noncached, forward-only access to XML data.
Right, XmlDocument does not have a Dispose method. It is put into memory. It does follow an object life cycle. So if you create the object in a Function and return the string you want then it will free itself as it losses scope from the Function.
I am using following code to put JPG's into a DataGridView's Image cell.
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = Image.FromFile(strFileName)
DataGridView4.Rows.Add()
DataGridView4.Rows(DataGridView4.Rows().Count - 1).Cells(0).Value = inImg
End If
The problem is that I need to save this file from within the program, but i get the message that the file is beeing used by another program.
So i tried to add inImg.Dispose() before the end if, but then the program doesnt display the images anymore in the DataGridView.
How can i add images in the DataGridView without locking them?
thanks
When you use the Image.FromFile(strFileName) method to create the Image, the method locks the file until you release the Image. The exact reason is explained below. And it's why you can't access more than one time to the same image file with this method.
You could instead:
use the Image.FromStream(stream) method.
that you use with a New FileStream or a MemoryStream that you create from the image file.
Here are possible implementation of a custom SafeImageFromFile method that doesn't lock the image file:
Public Shared Function SafeImageFromFile(path As String) As Image
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End using
End Function
Or
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Using ms As New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Using
End Function
Usage
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = SafeImageFromFile(strFileName)
Dim index as integer = DataGridView4.Rows.Add()
DataGridView4.Rows(index).Cells(0).Value = inImg
End If
Important note
Here I create the FileStream or a MemoryStream using a Using statement to make sure the stream is released. It works fine on my system and it seems it work for you too, though MSDN says about Image.FromStream(stream) method:
You must keep the stream open for the lifetime of the Image.
The reason of this sentence is explain here: KB814675 Bitmap and Image constructor dependencies
GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.
Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later. Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.
To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.
So know the code above could generate GDIexceptions because of releasing the stream using Using. It could happen when you save the image from the file or during the image creation. From this thread Loading an image from a stream without keeping the stream open and Hans Passant's comment they fixed several problems with indexed pixel formats in the Vista version of gdiplus.dll., it would happen only on XP.
To avoid this you need to keep the stream open. The methods would be:
Public Shared Function SafeImageFromFile(path As String) As Image
Dim fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End Function
Or
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Dim ms = New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Function
But those last methods have some disadvantage like not releasing the stream (memory issue) and they violate rule CA2000 Dispose objects before losing scope .
The KB article gives some workarounds:
Create a Non-Indexed Image
This approach requires that the new image be in a non-indexed pixel
format (more than 8 bits-per-pixel), even if the original image was in
an indexed format. This workaround uses the Graphics.DrawImage()
method to copy the image to a new Bitmap object:
Construct the original Bitmap from the stream, from the memory, or from the file.
Create a new Bitmap of the same size, with a pixel format of more than 8 bits-per-pixel (BPP).
Use the Graphics.FromImage() method to obtain a Graphics object for the second Bitmap.
Use Graphics.DrawImage() to draw the first Bitmap onto the second Bitmap.
Use Graphics.Dispose() to dispose of the Graphics.
Use Bitmap.Dispose() to dispose of the first Bitmap.
Create an Indexed Image
This workaround creates a Bitmap object in an indexed format:
Construct the original Bitmap from the stream, from the memory, or from the file.
Create a new Bitmap with the same size and pixel format as the first Bitmap.
Use the Bitmap.LockBits() method to lock the whole image for both Bitmap objects in their native pixel format.
Use either the Marshal.Copy function or another memory copying function to copy the image bits from the first Bitmap to the second Bitmap.
Use the Bitmap.UnlockBits() method to unlock both Bitmap objects.
Use Bitmap.Dispose() to dispose of the first Bitmap.
Here is an implementation of Non-Indexed Image creation, based on KB article and this answer https://stackoverflow.com/a/7972963/2387010 Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
Private Shared Function SafeImageFromFile(path As String) As Bitmap
Dim img As Bitmap = Nothing
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
End Using
Return img
End Function
Someone indicated that what is important is that the FileStream is opened in read mode (FileAccess.Read).
True, but it makes more sens if you don't use Using statement and so you don't release the stream, or in multi threads context: FileAccess.Write is inappropriate, and FileAccess.ReadWrite is not required, but open the stream with FileAccess.Read mode won't prevent to have an IO.Exception if another program (or yours in multi threads context) has opened the file with another mode than FileAccess.Read.
If you want to be able to display the image and at the same time be able to save data to the file, Since you don't lock the file with those methods, you should be able to save the image (delete/overwrite the previous file) using the Image.Save method.
# Chris: Opening approximately 100 large (3400x2200) images with your final code, I was receiving an invalid argument crash on [img = new bitmap(...], I have seen this before opening an image of zero size, but that was not the case here. I added fs.dispose and successfully opened thousands of images of the same size of the same set as the first test without issue. I'm interested in your comments on this.
Private Function SafeImageFromFile(FilePath As String) As Image
Dim img As Bitmap = Nothing
Using fs As New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fs.Dispose()
End Using
Return img
End Function
This works without issue, ran 4189 images 3400x2200 through it (twice) without issue, this moves the filestream outside of the function and re-uses it. Im closing the file to release the write lock. Im pointing a picturebox at this image in a loop for my test.
Private fsIMG As FileStream
Private Function SafeImageFromFile(FilePath As String) As Image
'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file
Dim img As Bitmap = Nothing
fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fsIMG)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fsIMG.Close()
Return img
End Function
After searching the internet for long time I found out I can use this code without any error.
Private fsIMG As FileStream
Private Function SafeImageFromFile(FilePath As String) As Image
'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file
Dim img As Bitmap = Nothing
fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fsIMG)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fsIMG.Close()
Return img
End Function
I encountered the same situation and used this code:
' Create memory stream from file
Dim ms As New MemoryStream()
' Open image file
Using fs As New FileStream(.FileName, FileMode.Open, FileAccess.Read)
' Save to memory stream
fs.CopyTo(ms)
End Using
' Create image from the file's copy in memory
Dim img = Image.FromStream(ms)
I didn't dispose the memory stream because it allows to save the image later using exactly the same encoding as the original file, using this code:
img.Save(someOtherStream, img.RawFormat)
I have developed a Visual Basic.net application that uses serialization to save an object. I am wanting to open and save this object in two different Visual Basic.net applications.
What is the best way to do this? Do I need to create a class library to do this?
Can I please have some help for this?
EDIT
I am wanting to be able to open and save the object in both applications.
Depending on how complicated your data is, you should be able to simply mark your data's class with a <Serializable> attribute. You can then simply call the Serialize method in one application, save to disk, then read the file into your other application and call Deserialize.
You will need to define the class in both applications, which is easiest to do by sharing a common library.
See the MDSN example for basic serialization.
You can write/read to xml, then you would just have to check the folder where you save them from the other app to see if a new file has been created or updated.
Function to serialize object and write to xml
Public Function MyObjectFileGeneration()
Try
Dim strPath As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase)
strPath = Replace(strPath, "file:\", "")
Dim myObj = Form1.MyObject
Dim objStreamWriter As New StreamWriter(strPath & "\MyFolder\MyObj.xml", False)
Dim x As New XmlSerializer(s.GetType)
x.Serialize(objStreamWriter, MyObj)
objStreamWriter.Close()
Return True
Catch ex As Exception
'Do something here if failure...
Return False
End Try
End Function
Function to read xml and de-serialize to object
Public Function GetMyObjFromXMLFile() As MyObj
'check if file exists first...
If xmlFileExists() Then
Dim strPath As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase)
Dim objStreamReader As New StreamReader(Replace(strPath & "\MyFolder\MyObj.xml", "file:\", ""))
Dim MyObj As New MyObject
Dim x As New XmlSerializer(MyObj.GetType)
MyObj = x.Deserialize(objStreamReader)
objStreamReader.Close()
Return MyObj
Else
Return New MyObj
End If
End Function
I wish there was an easy way to do this, but unfortunately, I hit this wall also..
Serializable data can only be reread by the SAME application. (it gives you a lovely error message about this.) I tried using a serialized connection for simplified packet transfers, unsuccessfully..
Depending on how good you are at programming, I have a recommendation on this one..
Serialize your variables to a memorystream, then cut the header section out and shove it to another file stream, then when you reload it, save a variable to a memorystream to get the new header, then attach the remaining data, then read serialization..
haven't tried it yet, but when I get back to it, this is my next method.
I did see an XML method, but recommend using a compression/encryption library to keep your data safe.. did see some simple ways to possibly do that..
Sorry, I don't have code on this round, but when I investigate it, I will append this response..
I'm opening a file called tempImage.jpg and showing it on a form in a PictureBox. I then click a button called Clear and the file is removed from the PictureBox using PictureBox2.Image = Nothing, however I'm unable to delete the file as it is locked open. How can I release it so I can delete it? I'm using VB.NET and a forms app.
Thanks
When you use PictureBox2.Image = Nothing you're waiting for the garbage collector to finalize the resource before it releases it. You want to release it immediately, so you need to dispose of the image:
Image tmp = PictureBox2.Image
PictureBox2.Image = Nothing
tmp.Dispose()
If you're using Image.FromFile, you need to call .Dispose() on the image. When you go to clear it out, do something like...
Image currentImage = pictureBox.Image
pictureBox.Image = Nothing
currentImage.Dispose()
That will release the file.
take control of the file
'to use the image
Dim fs As New IO.FileStream("c:\foopic.jpg", IO.FileMode.Open, IO.FileAccess.Read)
PictureBox1.Image = Image.FromStream(fs)
'to release the image
PictureBox1.Image = Nothing
fs.Close()
is there a equivalent to using in vb.net
this is what i would do in c#
using( filestream fs = new filestream)
{
//whatever you want to do in here
}
//closes after your done
As i can't comment yet (not enough experience points), this is an answer for the above
"is there a equivalent to using in vb.net"
Yes, in .Net 2.0 and above you can use "Using".
In .Net 1.0 and 1.1 however, you would need to dispose if the object in the finally block
Dim fs As System.IO.FileStream = Nothing
Try
'Do stuff
Finally
'Always check to make sure the object isnt nothing (to avoid nullreference exceptions)
If fs IsNot Nothing Then
fs.Close()
fs = Nothing
End If
End Try
Adding the closing of the stream in the finally block ensures that it will get closed no matter what (as opposed to the connection getting opened, a line of code bombing out beneath before the stream is closed, and the stream staying open and locking the file)