Is there a way to close an open PDF file in VB.net programatically - vb.net

In my VB.net application I am opening a PDF file using
System.Diagnostics.Process.Start("c:\TEMP\MyFile.pdf").
Is it possible to Close this file Programatically in some event.

Yes, there is one way, though it is not a very elegant solution.
When you start the PDF process, you capture the process-id in some global variable:
Dim id As Integer 'Global variable
id = System.Diagnostics.Process.Start("C:\Temp\myfile.pdf").Id
Then, when you need to kill the process, just do:
System.Diagnostics.Process.GetProcessById(id).Kill()
(make sure that there is a process with this id that is actually running!)
You may also use the Process.HasExited property to see if the PDF has been closed, and may process your code based on that.

I don't think that it's possible to close a specific PDF file because they are not an independent process, they are sub processes in the task manager.
you can kill the adobe acrobat reader process it self.
Dim AcrobateInstance() As Process = Process.GetProcessesByName("AcroRd32")
If AcrobateInstance.Length > 0 Then
For value As Integer = 0 To AcrobateInstance.Length - 1
BillInstance(value).Kill()
Next
End If

This is in C# but may come in handy...
var myPDFEvent = System.Diagnostics.Process.Start(#"C:\Temp\myfile.pdf");
myPDFEvent.Exited += new EventHandler(myPDFEvent_Exited);
myPDFEvent.EnableRaisingEvents = true;
void myPDFEvent_Exited(object sender, EventArgs e)
{
System.IO.File.Delete(#"C:\Temp\myfile.pdf);
}

This might work:
Process1.Start()
Process1.WaitForExit()
If Process1.HasExited Then
System.IO.File.Delete(Your File Path)
End If
Make sure to add the Process Object onto the form from the toolbox and configure the startinfo section.
If you are having permission problems. Use the AppData folder. It has the necessary permissions that programs need to run

Related

Files not opening as read-only in VB.NET when read-only attribute set

I'm having trouble with a bit of code designed to intentionally open files as read-only. My team often needs to be able to peek into each others' files without locking the file owner out, so in a form being designed for document management I want users to be able to open files optionally as read-only.
Coming from VBA I'm still somewhat new to VB.NET and also bitwise operations generally, but I believe the "read-only" interpretation of this code from MS Docs has been correctly implemented:
Dim attributes As FileAttributes
attributes = File.GetAttributes(path)
If Not (attributes And FileAttributes.ReadOnly) = FileAttributes.ReadOnly Then
' Make file readonly.
File.SetAttributes(path, File.GetAttributes(path) Or FileAttributes.ReadOnly)
End If
' Open the file
System.Diagnostics.Process.Start(path)
' Reset the file to read/write.
attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly)
File.SetAttributes(path, attributes)
When I use "GetAttributes" before and after the line to open the file I get a return of 1 or sometimes as 33, which the FileAttributes enumeration documentation suggests is correct for what I'm trying to do. Before and after the attribute change "GetAttributes" returns 128 or in certain cases 32, which also should be correct.
However despite the fact the above code appears correctly implemented and seems to be producing the correct affect in the file's attributes, files opened this way (namely Excel files) open as read-write. I'm also fine with other ways of opening a file read-only provided that it can be used equally well on any document you would commonly encounter in an office setting (Excel, Word, etc.) with its default program. That being said, I've tried several methods and haven't had any success, and this one by far has seemed the cleanest and most promising.
Thanks in advance!
As described in comments, the file attributes are restored to their
previous state right after the Process.Start() command: the
application that opens the file has not been started yet; when it
finally access the file, the read-ony attribute has already been
removed.
A possible solution is to subscribe to the Process.Exited event and restore the original file attributes when the Process termination is notified.
A modified version of your code: the EnableRaisingEvents property causes a process to raise the Process.Exited event. I subscribed to the event using an in-line delegate (a Lambda), but I added an example that uses a standard delegate method using the AddressOf operator (since you said you have to learn about events).
Since we want to run a file and not an executable, we need to also set UseShellExecute = True, so the Shell will find and execute the registered application associated with the file extension.
If UseShellExecute = True is not specified, an exception is raised (the file is not an executable).
The name of the file to execute is assigned to the Process.StartInfo.FileName
When the Process terminates, the Exited event is raised. In the event handler, the file attributes are restored to the previous state.
Private Sub SomeMethod(filePath As String)
' filePath is File's Full path
Dim attributes As FileAttributes = File.GetAttributes(filePath)
File.SetAttributes(filePath, (attributes) Or FileAttributes.ReadOnly)
Dim proc As Process = New Process()
proc.StartInfo.FileName = filePath
proc.StartInfo.UseShellExecute = True
proc.EnableRaisingEvents = True
AddHandler proc.Exited,
Sub()
File.SetAttributes(filePath, attributes)
proc?.Dispose()
End Sub
proc.Start()
End Sub
If you want to use a standard method as the Exited event handler, you have to declare the filePath and attributes variables in a different scope. Neither can be a local variable, they won't be accessible from the method delegate.
If you need to run just one file, these can be instance fields (declared in the scope of the current class).
If you instead can have multiple processes running different files, all waiting for the associated applicationt to terminate, these informations should to be stored in a list of objects, a Dictionary or a similar container.
For example, using a Dictionary, declared as a Field:
(the Dictionary Key is the File path. If a file can be opened multiple times - a .txt file maybe, use a different identifier)
Private myRunningFiles As New Dictionary(Of String, FileAttributes)
' (...)
Private Sub SomeMethod(filePath As String)
Dim attributes As FileAttributes = File.GetAttributes(filePath)
If Not myRunningFiles.ContainsKey(filePath) Then
myRunningFiles.Add(filePath, attributes)
Else
' Notify that the file is already opened
Return
End If
Dim proc As Process = New Process()
' (... same ...)
AddHandler proc.Exited, AddressOf OnProcessExited
End Sub
Protected Sub OnProcessExited(sender As Object, e As EventArgs)
Dim proc = DirectCast(sender, Process)
Dim filePath = proc.StartInfo.FileName
Dim attributes = myRunningFiles(filePath)
File.SetAttributes(filePath, attributes)
myRunningFiles.Remove(filePath)
proc?.Dispose()
End Sub
Thanks to #Jimi I was able to come up with the solution. In my application Excel files are the most important to open in ReadOnly so I'm happy with an Excel-only solution for the time being. While his answer for using and releasing attributes was great it had the problem of not restoring the attributes to their default until the file is closed, which to my understanding would cause others to also open the file ReadOnly while the file is open. The point of this in my application is to "peek" at a file without locking other users on a network out of ReadWrite access, so unfortunately his-well-thought out solution won't work.
I was however able to use the /r switch with a bit of investigating. It was a bit tricky (for someone of my skill level) since some switches need to be placed before the file path and some after. Solution below:
Process.Start("EXCEL.exe", "/r " & Chr(34) & path & Chr(34))

multiple programming try to access same text file

I had created and text file using (AFL SCRIPTING LANGUAGE) this script will update (write) to a text file every 5 seconds. I will try to read file using vb.net, when I run the vb.net code form visual studio, everything works fine but (AFL script not able to update the text file), here is my vb.net code:
Dim FILE_NAME As New FileStream("C:\myreport\myfile.TXT", FileMode.Open, FileAccess.Read, FileShare.Read)
REM Dim FILE_NAME As String = "C:\myreport\myfile.TXT"
REM Dim TextLine As String
REM If System.IO.File.Exists(FILE_NAME) = True Then
Dim objReader As New System.IO.StreamReader(FILE_NAME)
Do While objReader.Peek() <> -1
MYSTRING(I) = objReader.ReadLine()
I = I + 1
Loop
REM End If
When I run the code above, ( AFL script not able to update the text file),
Put simply:
When I run the vb.net code (for accessing the text file), AFL script not able to update.
I had share read/write the folder (where the text file exists), no effect, same problem I am facing.
Unchecked "Enable visual studio hosting process", still problem not solved.
In .NET, the FileSystemWatcher class can help you with this problem. You can read the file each time the file watcher says that the files has changed. Here is the reference documentation for it.
You cannot read and write to a file from two processes at the same time. Well, technically you can, but it can lead to a race condition which is bad.
You need to implement some kind of shared locking mechanism around the file to prevent your two programs from fighting over it. Or, if you can guarantee that the consumer VB.NET program will have the file open for less than 5 seconds, you can go with MrAxel's solution and simply have the VB.NET program read the file every time it is updated.

Using the same file error

On a button click I have the following code to write what Is in my textboxes.
Dim file As System.IO.StreamWriter
file = My.Computer.FileSystem.OpenTextFileWriter("C:/Users/Nick/Documents/Dra.txt", False)
file.WriteLine(NameBasic)
file.WriteLine(LastBasic)
file.WriteLine(PhoneBasic)
file.WriteLine(NameEmer1)
On my form load, I load what is in the notepad from what was written, It is saying It is already being used(the file) which is true, how can I have two different functions(write, and read) manipulating the same file with out this error?
The process cannot access the file 'C:\Users\Nick\Documents\Dra.txt' because it is being used by another process.
And here is the code for my onformload
Dim read As System.IO.StreamReader
read = My.Computer.FileSystem.OpenTextFileReader("C:/Users/Nick/Documents/Dra.txt")
lblNameBasic.Text = read.ReadLine
I am sort of stuck on this problem, thank you
You need to close the file when you are done writing to it.
Dim file As System.IO.StreamWriter
file = My.Computer.FileSystem.OpenTextFileWriter("C:/Users/Nick/Documents/Dra.txt", False)
file.WriteLine(NameBasic)
file.WriteLine(LastBasic)
file.WriteLine(PhoneBasic)
file.WriteLine(NameEmer1)
file.Close()
To answer your question:
how can I have two different functions(write, and read) manipulating the same file with out this error?
If you really want to simultaneously read and write to the same file from two processes, you need to open the file with the FileShare.ReadWrite option. The My.Computer.FileSystem methods don't do that.¹
However, I suspect that you don't really wan't to do that. I suspect that you want to write and, after you are finished writing, you want to read. In that case, just closing the file after using it will suffice. The easiest way to do that is to use the Using statement:
Using file = My.Computer.FileSystem.OpenTextFileWriter(...)
file.WriteLine(...)
file.WriteLine(...)
...
End Using
This ensures that the file will be closed properly as soon as the Using block is left (either by normal execution or by an exception). In fact, it is good practice to wrap use of every object whose class implements IDisposable (such as StreamWriter) in a Using statement.
¹ According to the reference source, OpenTextFileWriter creates a New IO.StreamWriter(file, append, encoding), which in turn creates a new FileStream(..., FileShare.Read, ...).

Download file in VB.NET 2010

I have looked almost everywhere on the internet and I cannot find a way to download a file from the internet into a specific folder that works with VB.NET 2010. I would like to download a file called, for instance, example.txt, and download it into, for example, %HOMEDRIVE%%HOMEPATH%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup so that it will run automatically at system startup. All help is appreciated
Guessing something based on...
Using webClient = New WebClient()
Dim bytes = webClient.DownloadData("http://www.google.com")
File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyFileName.ext"), bytes)
End Using
As for the startup, VB.NET has a pretty ease way to add Registry keys...
My.Computer.Registry.SetValue
To set something like HKEY_CURRENT_USER\Software\Microsoft\CurrentVersion\Run
UPDATE
How to: Create a Registry Key and Set Its Values in Visual Basic
http://msdn.microsoft.com/en-us/library/cy6azwf7(v=VS.100).aspx
I would suggest using WebClient.DownloadFile. Use Environment.SpecialFolder.Startup to get the path to save the file.
Sub Main()
Using wc As New WebClient()
Dim startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup)
wc.DownloadFile("http://MyDomain.com/MyFile.txt", Path.Combine(startupPath, "test.txt"))
End Using
End Sub

how to open a vb.application from another vb.application with parameters

i have 2 vb applications. this is the code for the first one which when a button is clicked it will check if the other application is already open. If not, it'll open that application -
Dim sComputer As String
sComputer = Environ("COMPUTERNAME")
Dim LocalByName As Process() = Process.GetProcessesByName("ticket.prices", sComputer)
If LocalByName.Length = 0 Then
System.Diagnostics.Process.Start("http://ticket.prices.application")
End If
this runs fine. but what i need is that the customerid on the application 1 that is calling application 2, should be transfered while opening app 2.
e.g -
Customer 10001 screen is open on app 1. When i click open app 2, the above code runs and opens app 2. how do i make app 2 open to customer 10001 screen. Is there any way to pass parameters while opening app 2 in System.Diagnostics.Process.Start ?
Use the version of 'Process.Start' that takes 2 strings, the second being the commandline parameters. See here for details.
You want the ProcessStartInfo class, or use the Start method taking to strings. ProcessStartInfo gives you a lot of options about how to start your program, which often comes in handy. Its good to get familiar with it.
Dim info as New ProcessStartInfo()
info.Arguments = "10001"
info.FileName = "exename"
Dim LocalByName as New Process()
LocalByName.StartInfo = info
LocalByName.Start()
Getting the arguments in the new program is accomplished via Environment.GetCommandLineArgs()
For Each arg As String In Environment.GetCommandLineArgs()
Console.WriteLine(arg)
Next arg
It looks like what you ultimately want to accomplish is getting the currently selected row from App 1 and passing that to the second program, though. Is this correct? That opens a whole new ball of wax involving interprocess communication.
EDIT: The simplest way to get the selected edit would be to write the id out to a text file. You have to be careful when doing this because if you just write System.IO.File.WriteAllText("selectedrow.txt", "123"), you'll write to the app's startup path directory. You'll want to get the exe's current path as below
Dim u as New Uri(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
dim exepath as String = System.IO.Path.GetDirectoryName(u.LocalPath)
dim fullPath as String = System.IO.Path.Combine(exepath, "selectedrow.txt")
System.IO.File.WriteAllText(fullpath, "123")
This will overwrite the text in the file every time you change rows. You want to wrap this in a try/catch block so as not to crash the program. Make sure you log the errors; don't just swallow them. To read the data, you just do
dim id as string = System.IO.File.ReadAllText(PathToFileYoureWritingToInTheOtherProgram)
in the other program.
This isn't necessarily the best way to go about things, but its the simplest way I know of off the top of my head.
You might could look at MessageQueues if you a better solution, but as long as you're not changing selected rows every 100ms, writing the file should work fine.