Print to XPS without a Save As dialog - vb.net

How can I save an xps file by printing to a virtual printer without using the Save File As dialog? When I call the print method, a dialog automatically pops up asking the user to specify the file name and path. This only works when creating brand new files; it throws an error 'you do not have permission to write to that file...' if I attempt to overwrite an existing file. Anyways, I want the user to be able to specify the file name in my own dialog, not the one that is autamatically called by the printDocument's Print method.
Public Event PrintPage As System.Drawing.Printing.PrintPageEventHandler
Private WithEvents Doc As New Printing.PrintDocument
Public Sub SaveXPSFile()
Doc.PrinterSettings.PrinterName = "Microsoft XPS Document Writer"
Doc.PrinterSettings.PrintFileName = "C:\Users\POConnell\Documents\t.xps"
Doc.Print()
Doc.Dispose()
End Sub
Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles Doc.PrintPage
'drawing commands go here
End sub

It's a couple of months late, but here we go.
2 questions, two answers.
Question 1:
How can I save an xps file by printing to a virtual printer without using the Save File As dialog?
Answer 1: You were close. I think you're looking for
Doc.DefaultPageSettings.PrinterSettings.PrintToFile = True
Doc.DefaultPageSettings.PrinterSettings.PrintFileName = "C:\Users\POConnell\Documents\t.xps"
Here is my implementation:
(Legal paper size, landscape mode)
Using prn As New PrintDocument
With prn
.PrinterSettings.PrinterName = "Microsoft XPS Document Writer"
AddHandler .PrintPage, _
AddressOf Me.PrintPageHandler
.DefaultPageSettings.Landscape = landscape
.DefaultPageSettings.PaperSize = New PaperSize("Legal", 850, 1400)
If My.Computer.FileSystem.FileExists("C:\temp\Log.oxps") Then My.Computer.FileSystem.DeleteFile("C:\temp\Log.oxps")
.DefaultPageSettings.PrinterSettings.PrintToFile = True
.DefaultPageSettings.PrinterSettings.PrintFileName = "C:\temp\Log.oxps"
.Print()
RemoveHandler .PrintPage, _
AddressOf Me.PrintPageHandler
End With
End Using
As you can see, I use the oxps file format, but it should still work just the same for you.
Question 2: it throws an error 'you do not have permission to write to that file...' if I attempt to overwrite an existing file.
Answer 2: Check if the file already exists prior to printing the file, and delete it if it does. Of course it will fail attempting to create a file that already exists.
For some reason using My.Computer.FileSystem.DeleteFile is faster than the traditional Kill() and System.IO.File.Delete, which both require the thread to sleep for ~1-200ms prior to recreating the file, or else a different access denied error will occur.
Hopefully this helps someone in the future!

Related

Deleting a file after closing

I'm trying to get my app to delete a pdf file after the Acrobat Reader (with this file) closes.
If I do this:
Process.Start("d:\test.pdf")
My.Computer.FileSystem.DeleteFile("d:\test.pdf")
Acrobat gives me an error: "There was an error opening this document. This file cannot be found."
Any help will be appreciated, Thanks
EDIT
I modified the code a little bit...
Dim MyProc = Process.Start(path)
MyProc.WaitForExit()
My.Computer.FileSystem.DeleteFile(path)
And it deletes the PDF after it is closed, but not if there is another PDF opened...
You could just use the event from your process to get the job done.
Dim MyProc = System.Diagnostics.Process.Start("d:\test.pdf")
AddHandler MyProc.Exited, Sub() My.Computer.FileSystem.DeleteFile("d:\test.pdf")
u can add a timer and set timer to check if acrobart.exe is running :
Public sub Btn1_click
Process.Start("file path here")
Timer1.start
Public sub Timer1_tick
Dim pr As Process() = Process.GetProcessesByName("acrobat") 'change acrobat with necessary exe name as i can't remember the exact name of acrobat.exe , it could be acrobat32 or acrobat64 or just acrobat as mentioned
If pr.Length = 0 Then
timer1.stop
My.Computer.FileSystem.DeleteFile("d:\test.pdf")
Else
End If
This will get the job done!

How to print HTML without WebBrowser control and Acrobat window?

I know my question is not focused on a precise problem but Google ran out of results. I'm trying to make a little app in Visual Basic .Net and have a HTML string which needs to be printed to a specific printer, and the problem is that i've tried to:
write out to a HTML file then print it with a WebBrowser: the problem is that i can't print to a specific printer, only to the default one.
convert it to a PDF with htmlToPdf package but: (1) it needs Acrobat Reader AND file association in Windows, (2) it opens the Acrobat Reader which is not quite professional.
EDIT
Thanks to a solution provided by the first commenter, i've made it partially. The problem is that the first document is printed perfectly, but the next ones are printed to the first specified printer (in Devices and Printers the default printer changes but the target printer remains the first one) Here is the code:
Public Sub PrintHTML(ByVal text As String, ByVal printer As String)
Try
LogData("Changing default printer to " & printer)
Dim wtype = Type.GetTypeFromProgID("WScript.Network")
Dim instance = Activator.CreateInstance(wtype)
wtype.InvokeMember("SetDefaultPrinter", System.Reflection.BindingFlags.InvokeMethod, Nothing, instance, New Object() {printer})
Catch ex As Exception
LogData("Changing failed...")
LogData(ex.ToString)
Finally
LogData("PrintHTML Init with " & printer)
Me.wbForPrint.Navigate("about:blank")
If Not WebBrowserReadyState.Interactive = WebBrowserReadyState.Complete Then
Me.wbForPrint.Stop()
End If
Me.wbForPrint.DocumentText = text
AddHandler (Me.wbForPrint.DocumentCompleted), AddressOf HTMLDocumentCompleted
End Try
End Sub
If the printing needs to be automatic with any user input then you could use this code to change the default printer, and then restore the default printer back to what it was once you have done the printing (source: http://codesnippets.fesslersoft.de/how-to-set-the-default-printer-in-c-and-vb-net/)
Public Shared Sub SetDefaultPrinter(ByVal printername As String)
Dim type As var = Type.GetTypeFromProgID("WScript.Network")
Dim instance As var = Activator.CreateInstance(type)
type.InvokeMember("SetDefaultPrinter", System.Reflection.BindingFlags.InvokeMethod, Nothing, instance, New Object() {printername})
End Sub
Or if you want the user to choose which printer to send to, you could try:
WebBrowser1.ShowPrintPreviewDialog()
or
WebBrowser1.ShowPrintDialog()

Hangman System.IO.IOException' occurred in mscorlib.dll

i am creating a hangman game that is to be used on a few computers, i have created the hangman game itself but i am using the "load form" function to create the list when the program first starts, but i am having this issue.
An unhandled exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file 'h:\Bryson\words.txt' because it is being used by another process.
Using sw As StreamWriter = File.CreateText("h:\Bryson\words.txt")
^^that line is where the error pops up^^
I have inserted some in code Comments to make life easier. If anyone can help thanks in advance :)
'USED TO CREATE HANGMAN FILE IF NOT FOUND
Private Sub main_Load(sender As Object, e As EventArgs) Handles MyBase.Load
fofound = False
fifound = False
MsgBox("remove this and change file path and fix qu2 quiz")
'DESIGNER USE
Dim path As String = "h:\Bryson\words.txt"
'CREATE VAR FOR PATH
If System.IO.Directory.Exists("h:\Bryson") Then
'CHECKS IF FOLDER EXISTS
fofound = True
Else
'IF IT DOES THEN IT MOVES ON
System.IO.Directory.CreateDirectory("h:\Bryson")
'IF NOT IT CREATES THE FOLDER
fofound = True
If File.Exists("h:\Bryson\test\words.txt") Then
'CHECKS IF FILE EXISTS
fifound = True
Else
'IF IT DOES IT MOVES ON
IO.File.Create("h:\Bryson\words.txt")
'IF NOT IT CREATES IT
FileClose()
End If
End If
If fofound And fifound = True Then
Else
Using sw As StreamWriter = File.CreateText("h:\Bryson\words.txt")
'CRASH POINT The process cannot access the file 'C:\Bryson\words.txt'
'because it Is being used by another process.
sw.WriteLine("Hangman")
sw.WriteLine("computer")
sw.WriteLine("electrode")
sw.WriteLine("independent")
sw.WriteLine("stream")
sw.WriteLine("enforcing")
End Using
'WRITES TO FILE
MsgBox("file created")
'DESIGNER USE
FileClose()
'CLOSES FILE
End If
End Sub
FileClose() is a legacy function from VB6 and will not affect anything in the System.IO namespace. To close a file you need to call .Close() or .Dispose() on the stream that has opened the file (wrapping the stream in a Using block does this automatically).
Your problem is this line:
IO.File.Create("h:\Bryson\words.txt")
The method creates a new file and opens a FileStream to it which locks the file. Since you never close the returned FileStream your file will remain locked until you close your application.
The File.Create() call is completely unnecessary though because File.CreateText() will create the file if it doesn't exist. So you should just remove the above line.

Download a file from the web periodically

I am creating an application which on startup (MainWindow loaded) starts a BackgroundWorker, which on DoWork checks whether there is a newer version of the file (DatasSource for an Autocompletebox) available. If so, I download and merge this with the existing file and create a new file.
Now I want to do this on startup and also periodically (like 30 minutes). So I created a threading.Timer [it's a private member in MainWindow class] and initialize it in RunWorkerCompleted of the backgroundWorker (as mentioned above). The timer goes to the callback successfully but at the file download code (just a fyi, a different namespace and different class) it just terminates and I can't figure out why?
I have tried using Windows.Timers.Timer, ThreadPool.RegisterWaitForSingleObject() but no luck...
Can anyone point me to the right direction? I am open to any solution.
Download code:
Public Sub MergeHistoryFile()
/*Check the directory if there are any downloaded files(.tmp);if there are;just delete them*/
/*some code which checks if file on web is modified;if yes download file*/
Try
Dim waiter As Threading.AutoResetEvent = New AutoResetEvent(False)
_downloader = New WebClient()
AddHandler _downloader.DownloadDataCompleted, AddressOf Me.DownloaderFileCompleted
_downloader.DownloadDataAsync(New Uri(path_file), waiter)
waiter.WaitOne()
Catch ex As Exception
Throw ex
End Try
/*some more code which checks if there something new in the downloaded file;if yes merge the local and the downloaded file reinitialize the autocomplebox*/
End Sub
Private _downloadCancelled As Boolean = False
Private Sub DownloaderFileCompleted(ByVal sender As Object, ByVal e As System.Net.DownloadDataCompletedEventArgs)
If IsNothing(e.Error) Then
If Not (IsNothing(e.Result)) Then
Using fs As New FileStream(Path.Combine(HistoryPath, "_tempDownladedFile.tmp"), FileMode.CreateNew)
fs.Write(e.Result, 0, e.Result.Count)
End Using
CType(e.UserState, Threading.AutoResetEvent).Set()
End If
Else
_downloadCancelled = True
_downloader.CancelAsync()
End If
End Sub
There are several problems with this code, as I pointed out in my comment.
I think your primary problem is that when you create the file, you're passing FileMode.CreateNew, which is going to fail if the file already exists. As the documentation says:
CreateNew Specifies that the operating system should create a new file. This requires FileIOPermissionAccess.Write permission. If the file already exists, an IOException exception is thrown.
You probably want FileMode.Create.
So what happens is that the FileStream constructor throws an exception, which causes your DownloadFileCompleted method to exit without ever setting the event that tells the caller to stop waiting.

How to use RichEditBox in Visual Basic to save the file?

I want to save whatever is being typed in RichEditBox. Below is my example. I was able to successfully with TextBox by using "mytext.Text" but there is no such option that I could see of RichEditBox.
Private Async Function Button_Click(sender As Object, e As RoutedEventArgs) As Task
Dim savePicker As New FileSavePicker
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary
' Dropdown of file types the user can save the file as
savePicker.FileTypeChoices.Add(".txt", New List(Of String) From {".txt"})
' Default file name if the user does not type one in or select a file to replace
savePicker.SuggestedFileName = "New Document"
Dim file As StorageFile = Await savePicker.PickSaveFileAsync
If file IsNot Nothing Then
' Prevent updates to the remote version of the file until we finish making changes and call CompleteUpdatesAsync
CachedFileManager.DeferUpdates(file)
' Write to file
Await FileIO.WriteTextAsync(file, txtfile.)
' Let Windows know that we are finished changing the file so the other app can update the remote version of the file.
' Completing updates may require windows to ask for user input
Dim status As FileUpdateStatus = Await CachedFileManager.CompleteUpdatesAsync(file)
End If
End Function
Assuming a Windows.UI.Xaml.Controls.RichEditBox, you can retrieve the text with the GetText method of the control's Document property. You can also write the document directly to a stream with the SaveToStream method of the Document property.