Why does my code throw a generic error in GDI+? - vb.net

I'm trying to convert a batch of .pngs to .jpgs, as in this question:
For Each file As String In Directory.EnumerateFiles(path).Where(Function(s) s.EndsWith(".png"))
Dim newfile As String = IO.Path.Combine(newpath, IO.Path.GetFileNameWithoutExtension(file) & ".jpg")
Using png As Bitmap = Bitmap.FromFile(file)
Using jpg As New Bitmap(png.Width, png.Height)
Using g As Graphics = Graphics.FromImage(jpg)
g.Clear(Color.FromArgb(255, 212, 208, 200))
g.DrawImageUnscaled(png, 0, 0)
jpg.Save(newfile, ImageFormat.Jpeg)
End Using
End Using
End Using
Next
The call to jpg.Save, however, with a "generic error" in GDI+. Originally outside of the innermost Using statement, I moved the call inwards as per this answer, but it didn't change anything. I have verified that newfile contains a valid path, and that the program has write access to the directory. What am I missing?

I know you are saying that you are sure about the file path, but here I can see that newfile you are passing to the IO.Path.Combine have a null value.
I just initialize the newfile string with the Path string, and run your code successfully without any problem:
Dim newfile As String = Path
newfile = IO.Path.Combine(newpath, IO.Path.GetFileNameWithoutExtension(file) & ".jpg")

I've tried your code (rewritten in C#) and discovered one thing: the exception occurs only if you try to save the new image inside the same directory from which you are listing the PNGs. If you change the code to write images to a different directory, the code works without problems.
I cannot really give you an explanation of why this is happening (other than perhaps Bitmap.FromFile() is locking up the directory somehow). There are a lot of GDI+ Bitmap quirks.
BTW: I managed to rework my C# code so it doesn't throw exceptions, I'll rewrite your VB snippet in an analogous way (I haven't tested the VB code though):
For Each file As String In Directory.EnumerateFiles(path).Where(Function(s) s.EndsWith(".png"))
Dim newfile As String = IO.Path.Combine(newpath, IO.Path.GetFileNameWithoutExtension(file) & ".jpg")
Using jpg As New Bitmap(png.Width, png.Height)
Using g As Graphics = Graphics.FromImage(jpg)
Using png As Bitmap = Bitmap.FromFile(file)
g.Clear(Color.FromArgb(255, 212, 208, 200))
g.DrawImageUnscaled(png, 0, 0)
End Using
jpg.Save(newfile, ImageFormat.Jpeg)
End Using
End Using
Next
Hope it works.

Related

Remove double quotes in the content of text files

I am using a legacy application where all the source code is in vb.net. I am checking if the file exists and if the condition is true replace all the " in the contents of the file. For instance "text" to be replaced as text. I am using the below code.
vb.net
Dim FileFullPath As String
FileFullPath = "\\Fileshare\text\sample.txt"
If File.Exists(FileFullPath) Then
Dim stripquote As String = FileFullPath
stripquote = stripquote.Replace("""", "").Trim()
Else
'
End If
I get no errors and at the same time the " is not being replaced in the content of the file.
Data:
ID, Date, Phone, Comments
1,05/13/2021,"123-000-1234","text1"
2,05/13/2021,"123-000-2345","text2"
3,05/13/2021,"123-000-3456","text2"
Output:
1,05/13/2021,123-000-1234,text1
2,05/13/2021,123-000-2345,text2
3,05/13/2021,123-000-3456,text2
You can read each line of the file, remove the double-quotes, write that to a temporary file, then when all the lines are done delete the original and move/rename the temporary file as the filename:
Imports System.IO
'...
Sub RemoveDoubleQuotes(filename As String)
Dim tmpFilename = Path.GetTempFileName()
Using sr As New StreamReader(filename)
Using sw As New StreamWriter(tmpFilename)
While Not sr.EndOfStream
sw.WriteLine(sr.ReadLine().Replace("""", ""))
End While
End Using
End Using
File.Delete(filename)
File.Move(tmpFilename, filename)
End Sub
Add error handling as desired.
The best way to go about this depends on the potential size of the file. If the file is relatively small then there's no point processing it line by line and certainly not using a TextFieldParser. Just read the data in, process it and write it out:
File.WriteAllText(FileFullPath,
File.ReadAllText(FileFullPath).
Replace(ControlChars.Quote, String.Empty))
Only if the file is potentially large and reading it all in one go would require too much memory should you consider processing it line by line. In that case, I'd go this way:
'Let the system create a temp file.
Dim tempFilePath = Path.GetTempFileName()
'Open the temp file for writing text.
Using tempFile As New StreamWriter(tempFilePath)
'Open the source file and read it line by line.
For Each line In File.ReadLines(FileFullPath)
'Remove double-quotes from the current line and write the result to the temp file.
tempFile.WriteLine(line.Replace(ControlChars.Quote, String.Empty))
Next
End Using
'Overwrite the source file with the temp file.
File.Move(tempFilePath, FileFullPath, True)
Note the use of File.ReadLines rather than File.ReadAllLines. The former will only read one line at a time where the latter reads every line before you can process any of them.
EDIT:
Note that this:
File.Move(tempFilePath, FileFullPath, True)
only works in .NET Core 3.0 and later, including .NET 5.0. If you're targeting .NET Framework then you have three other options:
Delete the original file (File.Delete) and then move the temp file (File.Move).
Copy the temp file (File.Copy) and then delete the temp file (File.Delete).
Call My.Computer.FileSystem.MoveFile to move the temp file and overwrite the original file in one go.
TextFieldParser is probably the way to go.
Your code with a few changes.
Static doubleQ As String = New String(ControlChars.Quote, 2)
Dim FileFullPath As String
FileFullPath = "\\Fileshare\text\sample.txt"
If IO.File.Exists(FileFullPath) Then
Dim stripquote As String = IO.File.ReadAllText(FileFullPath)
stripquote = stripquote.Replace(doubleQ, "").Trim()
Else
'
End If
Note the static declaration. I adopted this approach because it confused the heck out of me.

Add a path to a code VB.net / visual basic

how do I add a path to a code where "HERE_HAS_TO_BE_A_PATH" is. When I do, Im getting an error message. The goal is to be able to specific the path where is the final text file saved.
Thanks!
Here is a code:
Dim newFile As IO.StreamWriter = IO.File.CreateText("HERE_HAS_TO_BE_A_PATH")
Dim fix As String
fix = My.Computer.FileSystem.ReadAllText("C:\test.txt")
fix = Replace(fix, ",", ".")
My.Computer.FileSystem.WriteAllText("C:\test.txt", fix, False)
Dim query = From data In IO.File.ReadAllLines("C:\test.txt")
Let name As String = data.Split(" ")(0)
Let x As Decimal = data.Split(" ")(1)
Let y As Decimal = data.Split(" ")(2)
Let z As Decimal = data.Split(" ")(3)
Select name & " " & x & "," & y & "," & z
For i As Integer = 0 To query.Count - 1
newFile.WriteLine(query(i))
Next
newFile.Close()
1) Use a literal string:
The easiest way is replacing "HERE_HAS_TO_BE_A_PATH" with the literal path to desired output target, so overwriting it with "C:\output.txt":
Dim newFile As IO.StreamWriter = IO.File.CreateText("C:\output.txt")
2) Check permissions and read/write file references are correct:
There's a few reasons why you might be having difficulties, if you're trying to read and write into the root C:\ directory you might be having permissions issues.
Also, go line by line to make sure that the input and output files are correct every time you are using one or the other.
3) Make sure the implicit path is correct for non-fully qualified paths:
Next, when you test run the program, it's not actually in the same folder as the project folder, in case you're using a relative path, it's in a subfolder "\bin\debug", so for a project named [ProjectName], it compiles into this folder by default:
C:\path\to\[ProjectName]\bin\Debug\Program.exe
In other words, if you are trying to type in a path name as a string to save the file to and you don't specify the full path name starting from the C:\ drive, like "output.txt" instead of "C:\output.txt", it's saving it here:
C:\path\to\[ProjectName]\bin\Debug\output.txt
To find out exactly what paths it's defaulting to, in .Net Framework you can check against these:
Application.ExecutablePath
Application.StartupPath
4) Get user input via SaveFileDialogue
In addition to a literal string ("C:\output.txt") if you want the user to provide input, since it looks like you're using .Net Framework (as opposed to .Net Core, etc.), the easiest way to set a file name to use in your program is using the built-in SaveFileDialogue object in System.Windows.Forms (like you see whenever you try to save a file with most programs), you can do so really quickly like so:
Dim SFD As New SaveFileDialog
SFD.Filter = "Text Files|*.txt"
SFD.ShowDialog()
' For reuse, storing file path to string
Dim myFilePath As String = SFD.FileName
Dim newFile As IO.StreamWriter = IO.File.CreateText(myFilePath) ' path var
' Do the rest of your code here
newFile.Close()
5) Get user input via console
In case you ever want to get a path in .Net Core, i.e. with a console, the Main process by default accepts a String array called args(), here's a different version that lets the user add a path as the first parameter when running the program, or if one is not provided it asks the user for input:
Console.WriteLine("Hello World!")
Dim myFilePath = ""
If args.Length > 0 Then
myFilePath = args(0)
End If
If myFilePath = "" Then
Console.WriteLine("No file name provided, please input file name:")
While (myFilePath = "")
Console.Write("File and Path: ")
myFilePath = Console.ReadLine()
End While
End If
Dim newFile As IO.StreamWriter = IO.File.CreateText(myFilePath) ' path var
' Do the rest of your code here
newFile.Close()
6) Best practices: Close & Dispose vs. Using Blocks
In order to keep the code as similar to yours as possible, I tried to change only the pieces that needed changing. Vikyath Rao and Mary respectively pointed out a simplified way to declare it as well as a common best practice.
For more information, check out these helpful explanations:
Can any one explain why StreamWriter is an Unmanaged Resource. and
Should I call Close() or Dispose() for stream objects?
In summary, although streams are managed and should garbage collect automatically, due to working with the file system unmanaged resources get involved, which is the primary reason why it's a good idea to manually dispose of the object. Your ".close()" does this. Overrides for both the StreamReader and StreamWriter classes call the ".dispose()" method, however it is still common practice to use a Using .. End Using block to avoid "running with scissors" as Enigmativity puts it in his post, in other words it makes sure that you don't go off somewhere else in the program and forget to dispose of the open filestream.
Within your program, you could simply replace the "Dim newFile As IO.StreamWriter = IO.File.CreateText("C:\output.txt")" and "newFile.close()" lines with the opening and closing statements for the Using block while using the simplified syntax, like so:
'Dim newFile As IO.StreamWriter = IO.File.CreateText(myFilePath) ' old
Using newFile As New IO.StreamWriter(myFilePath) ' new
Dim fix As String = "Text from somewhere!"
newFile.WriteLine(fix)
' other similar operations here
End Using ' new -- ensures disposal
'newFile.Close() ' old
You can write that in this way. The stream writer automatically creates the file.
Dim newFile As New StreamWriter(HERE_HAS_TO_BE_A_PATH)
PS: I cannot mention all these in the comment section as I have reputations less than 50, so I wrote my answer. Please feel free to tell me if its wrong
regards,
vikyath

VB.NET 2010 - Extracting an application resource to the Desktop

I am trying to extract an application resource from My.Resources.FILE
I have discovered how to do this with DLL & EXE files, but I still need help with the code for extracting PNG & ICO files.
Other file types also. (If possible)
Here is my current code that works with DLL & EXE files.
Dim File01 As System.IO.FileStream = New System.IO.FileStream("C:\Users\" + Environment.UserName + "\Desktop\" + "SAMPLE.EXE", IO.FileMode.Create)
File01.Write(My.Resources.SAMPLE, 0, My.Resources.SAMPLE.Length)
File01.Close()
First things first, the code you have is bad. When using My.Resources, every time you use a property, you extract a new copy of the data. That means that your second line is getting the data to write twice, with the second time being only to get its length. At the very least, you should be getting the data only once and assigning it to a variable, then using that variable twice. You should also be using a Using statement to create and destroy the FileStream. Even better though, just call File.WriteAllBytes, which means that you don't have to create your own FileStream or know the length of the data to write. You should also not be constructing the file path that way.
Dim filePath = Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "SAMPLE.EXE")
File.WriteAllBytes(filePath, My.Resources.SAMPLE)
As for your question, the important thing to understand here is that it really has nothing to do with resources. The question is really how to save data of any particular type and that is something that you can look up for yourself. When you get the value of a property from My.Resources, the type of the data you get will depend on the type of the file you embedded in first place. In the case of a binary file, e.g. DLL or EXE, you will get back a Byte array and so you save that data to a file in the same way as you would any other Byte array. In the case of an image file, e.g. PNG, you will get back an Image object, so you save that like you would any other Image object, e.g.
Dim filePath = Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "PICTURE.PNG")
Using picture = My.Resources.PICTURE
picture.Save(filePath, picture.RawFormat)
End Using
For an ICO file you will get back an Icon object. I'll leave it to you to research how to save an Icon object to a file.
EDIT:
It's important to identify what the actual problem is that you're trying to solve. You can obviously get an object from My.Resources so that is not the problem. You need to determine what type that object is and determine how to save an object of that type. How to do that will be the same no matter where that object comes from, so the resources part is irrelevant. Think about what it is that you have to do and write a method to do it, then call that method.
In your original case, you could start like this:
Dim data = My.Resources.SAMPLE
Once you have written that - even as you write it - Intellisense will tell you that the data is a Byte array. Your actual problem is now how to save a Byte array to a file, so write a method that does that:
Private Sub SaveToFile(data As Byte(), filePath As String)
'...
End Sub
You can now which you want to do first: write code to call that method as appropriate for your current scenario or write the implementation of the method. There are various specific ways to save binary data, i.e. a Byte array, to a file but, as I said, the simplest is File.WriteAllBytes:
Private Sub SaveToFile(data As Byte(), filePath As String)
File.WriteAllBytes(filePath, data)
End Sub
As for calling the method, you need to data, which you already have, and the file path:
Dim data = My.Resources.SAMPLE
Dim folderPath = My.Computer.FileSystem.SpecialDirectories.Desktop
Dim fileName = "SAMPLE.EXE"
Dim filePath = Path.Combine(folderPath, fileName)
SaveToFile(data, filePath)
Simple enough. You need to follow the same steps for any other resource. If you embedded a PNG file then you would find that the data is an Image object or, more specifically, a Bitmap. Your task is then to learn how to save such an object to a file. It shouldn't take you long to find out that the Image class has its own Save method, so you would use that in your method:
Private Sub SaveToFile(data As Image, filePath As String)
data.Save(filePath, data.RawFormat)
End Sub
The code to call the method is basically as before, with the exception that an image object needs to be disposed when you're done with it:
Dim data = My.Resources.PICTURE
Dim folderPath = My.Computer.FileSystem.SpecialDirectories.Desktop
Dim fileName = "SAMPLE.EXE"
Dim filePath = Path.Combine(folderPath, fileName)
SaveToFile(data, filePath)
data.Dispose()
The proper way to create and dispose an object in a narrow scope like this is with a Using block:
Dim folderPath = My.Computer.FileSystem.SpecialDirectories.Desktop
Dim fileName = "SAMPLE.EXE"
Dim filePath = Path.Combine(folderPath, fileName)
Using data = My.Resources.PICTURE
SaveToFile(data, filePath)
End Using
Now it is up to you to carry out the same steps for an ICO file. If you are a hands on learner then get your hands on.

Convert BMP file to PDF

I want to convert a file format BMP to PDF in Visual Studio using Visual Basic technology and I use the PDFsharp library to do this.
I recieved the following error:
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in PdfSharp-wpf.dll
Additional information: The file 'E:...\bin\Debug-1493104802' does not exist.
I copied the whole contents of form to .bmp and it runs correctly. You can see my code:
Call SendMessage(TableLayoutPanel2.Handle, WM_PRINT, hdc, _ EDrawingOptions.PRF_CHILDREN Or _ EDrawingOptions.PRF_CLIENT Or _ EDrawingOptions.PRF_NONCLIENT Or _ EDrawingOptions.PRF_OWNED)
myGraphics.ReleaseHdc(hdc)
Dim doc As New PdfDocument()
doc.Pages.Add(New PdfPage())
Dim xgr As XGraphics = XGraphics.FromPdfPage(doc.Pages(0))
Dim img As XImage = XImage.FromFile(myGraphics.GetHdc)
xgr.DrawImage(img, 0, 0)
doc.Save("E:\out.pdf")
doc.Close()
' myBmp.Save("E:\out.bmp")
myGraphics.Dispose()
myGraphics = Nothing
myBmp = Nothing
After looking at your code it looks like you are generating an image using myGraphics, if that is the case you may want to save this to a tempfile, load it into the pdf and then you can delete it after you save the pdf.
Use the information on the following link to save the file.
https://stackoverflow.com/a/2881188/7436406
From your code snippet:
Dim img As XImage = XImage.FromFile(myGraphics.GetHdc)
You need a file name, but you pass an HDC? Great.
Maybe the compiler implicitly calls ToString() to make this compile. But there is no file.
Save the image to a Stream and then call XImage.FromStream to get that image in PDFsharp.

GZipstream cuts off data from original file when decompressing

For a long time I have been trying to debug why my parsing counts were off when downloading files to be parsed and been made to look really dumb about this. I did some debugging and found that the file I download when trying to decompress using GZipStream shows that it misses data from the original file. Here is my code for decompressing:
Using originalFileStream As FileStream = fileItem.OpenRead()
Dim currentFileName As String = fileItem.FullName
Dim newFileName = currentFileName.Remove(currentFileName.Length - fileItem.Extension.Length)
newFile = newFileName
Using decompressedFileStream As FileStream = File.Create(newFileName)
Using decompressionStream As GZipStream = New GZipStream(originalFileStream, CompressionMode.Decompress)
decompressionStream.CopyTo(decompressedFileStream)
Console.WriteLine("Decompressed: {0}", fileItem.Name)
decompressionStream.Close()
originalFileStream.Close()
End Using
End Using
End Using
Now what I do is return the newfile to the calling function and read the contents from there:
Dim responseData As String = inputFile.ReadToEnd
Now pasting the url in the browser and downloading from there and then opening using winrar I can see the data is not the same. Now this does not happen all the time as some files parse and decompress correctly. Each downloaded file has check counter to compare how many posts I am supposed to be parsing from it and that triggered me to see the mismatch in counts.
EDIT
Here is what I found in addition. If I read the problem file (as I said that only some files happen this way) by individual lines I will get all the data:
Dim rData As String = inputFile.ReadLine
If Not rData = "" Then
While Not inputFile.EndOfStream
rData = inputFile.ReadLine + vbNewLine + rData
End While
getIndividualPosts(rData)
End If
Now if I try to read an individual line from a file that is not problematic it will return nothing and so I will have to readtoEnd. Can anyone explain this odd behavior and is it related to the GZIPSTREAM or some error in my code.