Open and save problems - vb.net

I have a function in my VB project where i scan an image and then I can change the contrast.
I scan it and saves it C:\temp\my_img.tif.
In the winform the image is displayed in a PictureBox.
If I in the contrast function set like img.Save("C:\temp\my_img.tif", ImageFormat.Tiff) I get "A generic error occurred in GDI+.". If I however set the filename to something else, it works just fine.
So, how do I release the used image before saving it?
The whole function, in short:
Sub setContrast(ByVal C As Single)
'filename(1) ia a "global" variable that stores the used file path, in this case "C:\temp\my_img.tif"
Dim img As Image = Image.FromFile(filename(1)) '<--- I get the image
'A bunch of contrast stuff in some rows.....
'Here, i should release the image...
img.Save(filename(1), ImageFormat.Tiff) '<---Tries to save
PictureBox1.Refresh()
End Sub

Save it using a different file name, and then, if necessary, delete the old file and rename the new file to match the old, having Disposed of the Image beforehand.
From Image.FromFile:
The file remains locked until the Image is disposed.
There's no wording anywhere else that says that this is somehow worked around if the same Image instance is trying to Save back to the file.

Related

Cannot save a freshly created bmp file, help needed

Hate to sound like a broken record, but I simply cannot get the program I wrote in Visual Basic to write a file when run standalone; but it works fine when run via the debugger.
Simplified, the code looks like this (and the bitmap image is being created and is being saved, i.e., the file does not already exist):
Using bmp As New Bitmap(imgHorSize, imgVerSize) ‘ the sizes are not too large
<build the bitmap>
Dim fName As String = “C:\Users\<name>\Desktop\temp\foobar.jpg"
bmp.Save(fName, ImageFormat.Jpeg)
End Using
When I run this outside the debugger, I get
System.UnauthorizedAccessException: Access to the path
'C:\Users<name>\Desktop\temp\foobar.jpg' is denied
I saw some notes that suggest there should be a \\ after C:. Other sources show using a / instead of a \. Not sure that either really matters (since the directory name is coming from the result of a FolderBrowserDialog). I have checked the folder permissions on temp and see that my account has full control. I added user Everyone, via the security tab, and gave it full control. Same problem. As an experiment, I changed the above code to
Using bmp As New Bitmap(imgHorSize, imgVerSize)
<build the bitmap>
Dim fName As String = “C:\Users\<name>\Desktop\temp\foobar.jpg"
Dim file As System.IO.StreamWriter
file = My.Computer.FileSystem.OpenTextFileWriter(fName, True)
file.WriteLine("Here is the first string.")
End Using
and I got the exact same error. I also tried creating a MemoryStream, but that also didn’t work.
So, the issue is probably not related to the bmp.Save process, but something more fundamental. I have been looking at various things and have tried a few of them, but nothing works. This should be super easy to do, so what am I doing wrong?
== follow up ==
To come up with something to share in its entirety, I just wrote the following, very simple program that basically does the same as the real program:
Imports System.Drawing
Imports System.Drawing.Imaging
Public Class Form1
Structure myImages
Dim strFName As String
Dim imgL As Image
End Structure
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim imgLI As myImages
imgLI.strFName = "C:\Users\<username>\Desktop\EinRiddle\ItemImages\An_Cat.jpg"
imgLI.imgL = New Bitmap(Image.FromFile(imgLI.strFName), 150, 150)
Dim bmp As New Bitmap(300, 400)
Using bmp
Dim gr As Graphics = Graphics.FromImage(bmp)
gr.Clear(Color.WhiteSmoke)
gr.DrawImage(imgLI.imgL, 20, 20)
bmp.Save("C:\Users\<username>\Desktop\testImg.jpg", ImageFormat.Jpeg)
gr.Dispose()
End Using
Application.Exit()
End
End Sub
End Class
I am getting the same error message...
Have you tried another directory? Often one cannot write a file to the desktop.
Have you tried another format (for example ImageFormat.Bmp)?
You use “ instead of " ... Is that the mistake? Ah, I guess this is not the real code and you wrote this from memory and and something made “
Sometimes it takes a while before the picture and graphics can be disposed because the image is saved lazily. Even if this is not welcomed, give it a try
bmp.Save("C:\Users\<username>\Desktop\testImg.jpg", ImageFormat.Jpeg)
Application.DoEvents()
gr.Dispose()
End Using
I found the answer: On this computer, my user files are on the D: drive. Thus, when I changed the path from C: to D:, it worked. Of course, why this is necessary, since there is symbolic link, I don't understand...

VBA - Excel Unable to dettect file size increase

This one is driving me crazy. Any help is welcome.
I have a numeric simulaiton that constantly writes to a text file. The file size increases constantly. I need to report file size and check if it's still increasing or if it has ceased increasing.
VBA can't seem to detect the file's increased size, it keeps registering the same size. But, if I have windows explorer oppened on the file folder and press F5, the size increases in VBA.
I need to know if it is supposed to work this way because how windows indexes files or if I'm doing something wrong.
I have used filelen(), filesystemobject.filezise(), datelastmodified(),datelastacessed() and nothing...
I've already found a workarround: If I copy the file in question to a temp file and then read the temp file size, I can detect the change in size. But this is an ugly solution. I would very much like to just check the file size and get the correct result.
Sorry if it isn't very clear. I will be happy to clarify further, should it be necessary.
Here is the code I've been using, but to test it you would have to mimic the file writing situation, like in a video encoding for example, because if you do it with a text file edited in notepad, the act of saving it also updates the information to vba.
Public fileSizeLastStep as long
Public Sub sizeWatch()
dim fso as new fileSystemObject
dim fileToMeasure as File
dim fileSizeNow as long
Set fileToMeasure = fso.GetFile("C:\filename.txt")
fileSizeNow = fileToMeasure.Size
If fileSizeNow <> fileSizeLastStep Then
fileSizeLastStep = fileSizeNow
Set fso = Nothing
Call Application.OnTime(Now + TimeValue("00:02:00"), "sizeWatch")
Else
MsgBox "Simulation Finished!"
'calls whathever function that starts next simulation
End If
End Sub
Thank you for your time.
Perhaps it's an issue with this:
https://blogs.msdn.microsoft.com/oldnewthing/20111226-00/?p=8813
Quote from the article:
If you really need the actual file size right now, you can do what
the first customer did and call Get­File­Size. That function operates
on the actual file and not on the directory entry, so it gets the real
information and not the shadow copy. Mind you, if the file is being
continuously written-to, then the value you get is already wrong the
moment you receive it.
So try using GetFileSize instead.
Declare Function GetFileSize Lib "kernel32.dll" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long

Autodesk Inventor place part at user defined position

I'm trying to create a method that will place a file into an assembly, and I want it to be like when you choose place file in Inventor.
The file is been chosen by it's path. And now it needs to be placed. I know a way how to place the file at coordinates, but I want the file to be on the cursor and the user be able to choose where to drop it.
How do you do achieve this? I tried a programming help search but I can only find thing about the event and dialog.
FileDialog.InsertMode() As Boolean
Normally I just place and ground, but that not good now..
Public Function Place_and_Ground_Part(ByVal oDef As AssemblyComponentDefinition,
ByVal path As String) As ComponentOccurrence
' Set a reference to the assembly component definintion.
' This assumes an assembly document is open.
' Set a reference to the transient geometry object.
Dim oTG As TransientGeometry
oTG = oInvApp.TransientGeometry
' Create a matrix. A new matrix is initialized with an identity matrix.
Dim oMatrix As Matrix
oMatrix = oTG.CreateMatrix
' Set the translation portion of the matrix so the part will be positioned
' at (3,2,1).
oMatrix.SetTranslation(oTG.CreateVector(0, 0, 0))
' Add the occurrence.
Dim oOcc As ComponentOccurrence
oOcc = oDef.Occurrences.Add(path, oMatrix)
' Make sure the master part is grounded
oOcc.Grounded = True
Return oOcc
End Function
It certainly isn't obvious how to accomplish what you want, but it is possible, if you know how. The code below demonstrates using the PostPrivateEvent method where you post the filename of the file you want to insert onto an internal queue within Inventor. Next, it gets and runs the Place Component just the same as if the user were to start the command. The command first checks to see if a filename is on the private queue and if it is it takes that filename and skips the dialog step. This results in the user being able to drag and position the occurrence.
Public Function Place_and_Ground_Part(ByVal invApp As Application,
ByVal path As String) As ComponentOccurrence
' Post the filename to the private event queue.
invApp.CommandManager.PostPrivateEvent(Inventor.PrivateEventTypeEnum.kFileNameEvent, filename)
' Get the control definition for the Place Component command.
Dim ctrlDef As Inventor.ControlDefinition
ctrlDef = invApp.CommandManager.ControlDefinitions.Item("AssemblyPlaceComponentCmd")
' Execute the command.
ctrlDef.Execute()
Return Nothing
End Function
You've probably noticed that the function is returning Nothing. This is a problem using this approach because you execute the command and then turn control over to Inventor. It is possible to use events to watch and see if a new occurrence is placed and then get it but it complicates the code quite a bit since it's no longer a simple function.

Efficient use of (or alternative to) System.IO.MemoryStream

I have the following code which I am using to populate a ImageList from a SQLite database with images stored as blobs.
Public Sub populateImagesStyles()
ShoeImages1.Images.Clear()
StyleImagesLView.Items.Clear()
Dim s As SQLiteDataReader
Dim rcount As Integer = 0
dbLocalQuery = New SQLiteCommand("SELECT id, image FROM tblImages", dbLocal)
s = dbLocalQuery.ExecuteReader()
While s.Read()
rcount += 1
ShoeImages1.Images.Add(CStr(s("id")), byte2img(s("image")))
StyleImagesLView.Items.Add(CStr(s("id")), CStr(s("id")))
End While
s.Close()
Here is the byte2img function...
Public Function byte2img(ByVal imgByte As Byte()) As Image
Dim imgMemoryStream As System.IO.MemoryStream = New System.IO.MemoryStream(imgByte)
byte2img = Drawing.Image.FromStream(imgMemoryStream)
End Function
The database contains over 250 images and this process is completed twice on load to populate two different ImageList, because I need the images displayed at two different sizes.
When the process runs on loading the form, it causes the process to consume between 800MB and 1GB of system memory, unless I manually run the process again from an form control, which seems to trigger garbage collection.
Stepping through the loading process, it is clear that it is the byte2img process that is causing the memory usage to escalate - what is the best way to mitigate this?
Also, if anyone can think of a more efficient process to execute this, i'm all ears. The images have to be stored in the database file because I need to be able to just package the .db file and send it to a remote location at a moments notice, so I can't mess with folders with images.
All help appreciated.
You are creating a lot of memory streams without disposing of them. Try this:
Public Function byte2img(ByVal imgByte As Byte()) As Image
Dim img As Image
Try
Using ms As New MemoryStream(imgbyte)
img = Drawing.Image.FromStream(ms)
End Using ' auto dispose of the MS
Catch ex As Exception
' report possibly bad/missing imgByte()
' resulting in an error in either place
End Try
Return img
End Function
An imprecise way to detect this kind of thing is to watch the HANDLES count in TaskManager.
Ok, I've found a solution/workaround that seems to work - call the PopulateImageStyles sub when a user visits the specific TabPage the ImageList resides on.
For some arbitrary reason, when run this way (as above, when called on the form), the process never proceeds to consume more than 50-60 MB of working memory.
I'll add a Background Worker so that the process can execute without hanging the form.

Visual Basic (2010) - Using variables in embedded text files?

Ive always been able to just search for what I need on here, and I've usually found it fairly easily, but this seems to be an exception.
I'm writing a program in Visual Basic 2010 Express, it's a fairly simple text based adventure game.
I have a story, with multiple possible paths based on what button/option you choose.
The text of each story path is saved in its own embedded resource .txt file. I could just write the contents of the text files straight into VB, and that would solve my problem, but that's not the way I want to do this, because that would end up looking really messy.
My problem is that I need to use variable names within my story, here's an example of the contents of one of the embedded text files,
"When "+playername+" woke up, "+genderheshe+" didn't recognise "+genderhisher+" surroundings."
I have used the following code to read the file into my text box
Private Sub frmAdventure_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim thestorytext As String
Dim imageStream As Stream
Dim textStreamReader As StreamReader
Dim assembly As [Assembly]
assembly = [assembly].GetExecutingAssembly()
imageStream = assembly.GetManifestResourceStream("Catastrophe.CatastropheStoryStart.png")
textStreamReader = New StreamReader(assembly.GetManifestResourceStream("Catastrophe.CatastropheStoryStart.txt"))
thestorytext = textStreamReader.ReadLine()
txtAdventure.Text = thestorytext
End Sub
Which works to an extent, but displays it exactly as it is in the text file, keeps the quotes and the +s and the variable names instead of removing the quotes and the +s and replacing the variable names with what's stored within the variables.
Can anyone tell me what I need to change or add to make this work?
Thanks, and apologies if this has been answered somewhere and I just didn't recognise it as the solution or didn't know what to search to find it or something.
Since your application is compiled, you cannot just put some of your VB code in the text file and have it executed when it is read.
What you can do, and what is usually done, is that you leave certain tags inside your text file, then locate them and replace them with the actual values.
For example:
When %playername% woke up, %genderheshe% didn`t recognise %genderhisher% surroundings.
Then in your code, you would find all the tags:
Dim matches = Regex.Matches(thestorytext, "%(\w+?)%")
For Each match in matches
' the tag name is now in: match.Groups(1).Value
' replace the tag with the value and replace it back into the original string
Next
Of course the big problem still remains - which is how to fill in the actual values. Unfortunately, there is no clean way to do this, especially using any local variables.
You can either manually maintain a Dictionary of tag names and their values, or use Reflection to get the values directly at the runtime. While it should be used carefully (speed, security, ...), it will work just fine for your case.
Assuming you have all your variables defined as properties in the same class (Me) as the code that reads and processes this text, the code will look like this:
Dim matches = Regex.Matches(thestorytext, "%(\w+?)%")
For Each match in matches
Dim tag = match.Groups(1).Value
Dim value = Me.GetType().GetField(tag).GetValue(Me)
thestorytext = thestorytext.Replace(match.Value, value) ' Lazy code
Next
txtAdventure.Text = thestorytext
If you don't use properties, but only fields, change the line to this:
Dim value = Me.GetType().GetField(tag).GetValue(Me)
Note that this example is rough and the code will happily crash if the tags are misspelled or not existing (you should do some error checking), but it should get you started.