Okay, here's the deal...
I have a Windows (XP) program in .NET 2.0 (C#) which allows users to rename a given .pdf file. (The filename is "structurally descriptive", as it lays out simple info about what's in the file itself.) On the program's only form, there is a LinkLabel object which allows the user to open the .pdf itself, so that they can see what they are renaming.
The trick is that, when the user makes the appropriate change(s) and clicks the "Save" button, I want the Acrobat window showing the .pdf to close, the save performed, a "next" file to be retrieved, and a new window to immediately open displaying that next file.
Here's the relevant code snippets:
private void OpenViewer()
{
// NOTE: pdfView is of type Process, in case you're not familiar with
// Process.Start().
pdfView = System.Diagnostics.Process.Start(lnkFile.Links[0].LinkData.ToString());
}
private bool KillViewer()
{
bool result = (pdfView != null);
if (pdfView != null)
{
pdfView.CloseMainWindow();
pdfView.Close();
pdfView.Dispose();
pdfView = null;
GC.Collect();
// Verify that the lock is available before you return, as returning basically says:
// "Yup, the file's available."
bool locked = false;
StreamWriter sw = null;
do
{
try
{
sw = new StreamWriter(new FileStream(lnkFile.Links[0].LinkData.ToString(), FileMode.Open));
locked = false;
}
catch (Exception)
{
locked = true;
}
} while (locked);
sw.Dispose();
}
return result;
}
private void SomeButtonEvent
{
// Record whether a viewer was open in the first place.
bool viewerActive = KillViewer();
PerformFileLockingMethod();
GetNextFile()
if(viewerActive)
{
OpenViewer();
}
}
Notice in KillViewer() that there's basically a lock-grabbing loop to make sure that the program doesn't try to rename the working file until after the pdf viewer has fully released the lock.
The problem is this: sometimes this all works beautifully, and sometimes KillViewer breaks down on the CloseMainWindow() call, with an InvalidOperationException, details = "Process has exited, so the requested information is not available.". This would be fairly straightforward if it weren't for two things...
1: pdfView.HasExited = true
AND
2: The darned pdf viewer is STILL OPEN!!!
How in the world is this possible? Is there a process command I should be using to ensure the window closes? FYI, the program references nothing outside of either System.* namespaces, or internally built class which also ultimately reference only System.*.
Thanks.
Try this instead..
pdfView.Kill();
pdfView.WaitForExit();
After further investigation, I think I've determined what was going on.
I didn't detail workflow details because I couldn't reliably replicate the situation. After further attempts, I found two reliable situations...
Click on the link multiple times and then click save.
Click on the link, close the viewer window, and click save.
In each of these cases, the problem boiled down to the Process pointed to by pdfViewer becoming out of sync with what the user was doing.
If the link was clicked on multiple times, then the active viewer was on a process not connected with pdfViewer's process, hence the seemingly impossible situation detailed above.
If the link was clicked on and the window closed, the pdfViewer variable would remain, leaving a process with HasExited = true.
The take home lesson of all this is as follows: If you're running a separate process from your main user interface, make ABSOLUTELY SURE that you cover every possible situation that could occur with the external process.
For the record, Nick Guerrera deserves points for directing me towards the process IDs. That ultimately solved it.
Related
I'm working on small C# application, that application has one DataGrid on main screen and items in it, application has one purpose: when user press F9 it will loop throught DataGrid items and write that items from datagrid in notepad and send that notepad to a FISCAL PRINTER (that means put that notepad file to path where printer is listening). (* Explanation what is fiscal printer: that is small printer which is waiting for notepad files on some location i.e (C:\MyFiles), and if you give him correct file he will proceed it ->it means paper with items will come out of printer, price, item title etc, and printer will put his own file where he's telling is everything ok, and also printer will write there one special number that is called FISCAL NUMBER ID*), now lets back to C# code!
So I did everything, everything is working fine, but there is one issue:
When I leave correct file in folder where printer is listening, printer will take it, and my application will keep listening for printer's file because application must read that FISCAL NUMBER ID from that printers file.
And here comes my problem, because application must listen for printer to answer, and to read his file to put that FISCAL NUMBER ID to database etc that takes a lot time, like 3-4 seconds, so while that is happening it is impossible to click anywhere on my application or smth like that, it simply freezes!
So my question is how could I put this method in a thread or something so it can do her job while my application is still responsive to user clicks/requests.
Here is my code when user PRESS F9:
if (e.Key == Key.F9)
{
try
{
if (dtgStavke.Items.Count > 0)
{
Printer printer = new Printer();
printer.AdressAnswer = "C:\\MyPrinterFiles\\";
printer.AdressError = "C:\\MyPrinterFiles\\Errors\\";
printer.AdressExit = "C:\\MyPrinterFiles\\Good\\";
Bill newBill = new Bill();
newBill.OrdinalNumber = Convert.ToInt32(BillController.Instance.GetNextBillOrdinalNumber())+1;
newBill.Reversed = false;
newBill.Fiscalized = false;
newBill.BFF = 0;
newBill.BRR = 0;
newBill.TotalAmount = stavkeTemp.Sum(x => (x.Price*x.Quantity));
newBill.TotalWithoutTax = totalWithoutTAXGlobal;
newBill.TotalTax = totalTaxGlobal;
if (_globalCustomer != null)
{
if (_globalCustomer.Status == 1)
{
newBill.CompanyId = _globalCustomer.Id;
}
else
newBill.PersonId = _globalCustomer.Id;
}
else
{
newBill.CompanyId = 1;
newBill.PersonId = 1;
newBill.UserId=1;
}
Bill lastInsertedBill = BillController.Instance.Save(newBill);
}
bool succeed = Print(lastInsertedBill, p, printer, _globalCustomer, false);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
How could I isolate this to a new thread or something like that, I tried few things but it is definately not working?
There is Print method and everything is happening there:
file making
waiting for answer to read printer's file
again updating database with that fiscal printer id from that printers file
So must of the work is happenig there, so It would be nice to isolate it somehow, to keep my application responsive to user requestes while Print method is doing her job
bool succeed = Print(lastInsertedBill, p, printer, _globalCustomer, false);
Thanks guys
Cheers!
The easy answer would be if you could make it with async await like Sparrow already mentioned.
The call to Print would afterwards look like this:
bool succeed = await Task.Run(()=>Print(lastInsertedBill, p, printer, _globalCustomer, false)).ConfigureAwait(false);
The method signature of the code where your whole code construct is in has to change as well. So imagine your current signature looks like this:
public void KeyPressed(Object o, KeyPressEventArgs e)
It should change to look afterwards like this:
public async Task KeyPressed(Object o, KeyPressEventArgs e)
These changes would make your Print method run in a different Task (which is simply said a more efficient way of utilizing other Threads). So in this way your UI-Thread is free to do stuff while another Background-Thread does your Print job.
If you want to read up on async await I'd recommend this blog by Stpehen Cleary as a starting point. And a book that helped me a lot regarding async await was "Async in C# 5.0" by Alex Davies. It has only 108 pages, is really good written and you gain all the knowledge you need.
In my program, I'm trying to use a List of employees and mark them as fired (this is just for fun, of course). I can make the program do anything that I want except remember what I've already done. So, I'm trying to save the list in a file, then read that file back into the program when the program starts. Unfortunately, when the program is forced to close, the same function that is supposed to read the file causes the program to crash. I don't know that I can debug the program since I'm performing a task, closing the program (which closes the debugger) and then opening the program again...
Here's my function that is awaited when a "start" button is clicked.
async private Task readJsonAsync()
{
var jsonSerializer = new DataContractSerializer(typeof(List<Employee>));
try
{
var myStream = await ApplicationDate.Current.LocalFolder.OpenStreamForReadAsync(JSONFILENAME);
employees = (List<Employee>)jsonSerializer.ReadObject(myStream);
}
catch (FileNotFoundException ex)
{
employees = Employee.Initialize();
}
}
When I save the List, it seems to work. When I reopen the app after force closing on the emulator, I get "The program '[3156] employeeTest05.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'" before I can even see the "start" button. When I reopen the app on a device after force closing, I can get into the app, but I can't start using the program because it crashes without an error on the screen, and the debugger is closed.
Am I going about this the wrong way? I care how the properties of the Employee objects get saved, only that they are saved and can be retrieved.
The case you want is called Tomestoning. You can read the best practices here:
App activation and deactivation for Windows Phone 8.
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
// TODO: call your save function
// PhoneApplicationService.Current.State["your_key"] = your_value;
}
And if you want a generic read and write example. Please refer to my answer on saving/loading a list as an xml document here. You can modify it to use your JSON.
Generic Read and Write XML
In the top of Form1 i did:
private Process zipFileDirectoryProcess;
In the constructor i did:
zipFileDirectoryProcess = new Process();
zipFileDirectoryProcess.StartInfo.FileName = "explorer.exe";
zipFileDirectoryProcess.StartInfo.CreateNoWindow = true;
zipFileDirectoryProcess.EnableRaisingEvents = true;
zipFileDirectoryProcess.Exited += new EventHandler(zipFileDirectoryProcess_Exited);
Then i have a method i call it from a button click event:
private void Compress()
{
zipFileDirectoryProcess.StartInfo.Arguments = zipFileDirectoryProcess.StartInfo.Arguments = "/select," + Path.GetFullPath(t);
zipFileDirectoryProcess.Start();
zipFileDirectoryProcess.WaitForExit();
this.TopMost = true;
}
And then in the bottom the Exited event:
private void zipFileDirectoryProcess_Exited(object sender, EventArgs e)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
this.TopMost = false;
}));
}
What i wanted to do is only when i close the process window after started it in the method only if closed the window/process then do the Exited event.
The problem is that once the process started after 2-3 seconds its jumping automatic to the Exited event.
How can i fix it ? Tried examples cant figure out.
Tried to add this line:
zipFileDirectoryProcess.WaitForExit();
But no effect.
zipFileDirectoryProcess.StartInfo.FileName = "explorer.exe";
Trying to start Windows Explorer again when it is already running, and it is always running, will have a disappointing outcome. It is a "heavy" process and it intentionally tries the minimize the number of running copies. Otherwise known as a "single-instance app". There are lots like that, the Microsoft Office programs are single instance apps for example.
So what really happens is that explorer.exe actually starts up, but sees that another instance is already running. And uses process interop to ask that first instance to do the job that you asked it to do. Since you didn't ask it to do anything, you just get another window, displayed by the first instance. The one that you started immediately quits, it doesn't have anything else to do.
So, yes, you'll see that the Exited event fires without you doing anything. Accurately telling you that the explorer.exe process you started did in fact quit. Easy to see in the Taskmgr.exe Processes tab btw. Waiting for that window to be closed is never going to work, it is displayed by the original instance of explorer.exe.
This will just not work the way you hope it will work. What you are actually trying to do is not quite obvious but can be guessed at. Creating a ZIP archive is not difficult, there are excellent libraries available for C# to get the job done, no point in asking another program to do it for you. DotNetZip and SharpZipLib are very popular. It got finally added to .NET as well in version 4.5, Microsoft finally getting over the lost Stacker lawsuit, about time. If you really, really want another program to do it for you then use a console mode zipper like 7-zip.
To show output folder in windows explorer to the user, it's simply enough to do this:
Process.Start("explorer.exe", OutputDir);
I try to open 2 pdf files in Adobe Reader from my C# code. Lets call them A and B and A is opened before B.
Now when I try to kill the process associated with file A file B also closes down because they are linked to same process. Is there a way to close File A without closing file B.
Also when I first try to kill the process associated with File B , nothing happens and File B still remains open.
How should I go about in solving the above two scenarios.
I have handle of both the files. Is there a way I can close the handle
Sounds to me like you should be using the Interapplication Communication API for Acrobat, which has the facility to open and close documents. What you're doing is fairly inelegant compared with what you can get with IAC (pdf documentation here).
you can find the process of PDF viewer of A by following code.
using System.Diagnostics;
public bool FindAndKillProcess(string name)
{
//here we're going to get a list of all running processes on
//the computer
foreach (Process clsProcess in Process.GetProcesses()) {
//now we're going to see if any of the running processes
//match the currently running processes by using the StartsWith Method,
//this prevents us from incluing the .EXE for the process we're looking for.
//. Be sure to not
//add the .exe to the name you provide, i.e: NOTEPAD,
//not NOTEPAD.EXE or false is always returned even if
//notepad is running
if (clsProcess.ProcessName.StartsWith(name))
{
//since we found the proccess we now need to use the
//Kill Method to kill the process. Remember, if you have
//the process running more than once, say IE open 4
//times the loop thr way it is now will close all 4,
//if you want it to just close the first one it finds
//then add a return; after the Kill
clsProcess.Kill();
//process killed, return true
return true;
}
}
//process not found, return false
return false;
}
then call above method.
FindAndKillProcess("AcroRd32.exe");
so you can kill the process of PDF viewer.
TRY:
if (clsProcess.ProcessName.Contains(name))
INSTEAD:
if (clsProcess.ProcessName.StartsWith(name))
using System.Diagnostics;
public bool FindAndKillProcess(string name)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains(name))
{
//To know if it works
//MessageBox.Show(clsProcess);
clsProcess.Kill();
return true;
}
}
//process not found, return false
return false;
}
////// call the function:
FindAndKillProcess("AcroRd32");
////// if you have been saved all the variables also you can close you main form
FindAndKillProcess("Form_Name");
I think one way to do this would be to find that instance of the program and close it from your application. Here is an example of how to find the window and close it: http://www.mycsharpcorner.com/Post.aspx?postID=32
Since you have 2 instances of Adobe reader open you will want to determine which is which. You can search by the text in the frame. If you have a copy of spy++ (or a similar alternative) it makes working with outside GUI components much easier because, you can find out so much about that window, including the name, the window handle and more.
How can I solve this error?
"The requested resource is in use. (Exception from HRESULT: 0x800700AA)".
This appears while navigating to a different website using the WebBrowser control in C# .NET. Why?
The WebBrowser control is considered "in use" if either a navigation action is currently being processed, or any blocking dialog from the control is currently open (including context menu, Javascript alerts, NTLM login dialog, etc.). You can use the WebBrowser.IsBusy property to detect these states.
If due to a currently incomplete navigation action, you could try to stop the current navigation (if you indeed want to stop when the page is not completed loaded) or add the new navigation to a request queue and use a timer to wait until WebBrowser.IsBusy returns false.
If instead the busy state is due to one or more open blocking dialogs, you could do the same wait technique and perhaps Messagebox.Show() the user a message that pending navigation is delayed due to an open dialog window.
I had this same issue. Calling WebBrowser.Stop() did not help, and WebBrowser.IsBusy never became false.
It turns out that if the page creates any sort of dialog (alert() popups, javascript errors, NTLM login popups etc.) you can't navigate away from the page until the dialog is closed.
My solution was to prevent the dialogs from showing in the first place. Apparently preventing all of these popups is simple; just set
webBrowser.ScriptErrorsSuppressed = true;
bool go = false;
string SiteContent1 = string.Empty;
string SiteContent2 = string.Empty;
int index = 0;
WebBrowser wb = new WebBrowser();
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
if (go)
{
SiteContent2 = wb.DocumentText;
// Code to compare to contents of the webbrowser
index++;
go = false;
steps = 1;
}
if (!go)
{
if (index >= TotalSiteCount)
{
Stop();
}
else if (steps == 1)
{
wb.Navigate(UrltocompareList[index].Url1);
}
else if (steps == 2)
{
SiteContent1 = wb.DocumentText;
wb.Navigate(UrltocompareList[index].Url2);
go = true;
}
steps++;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
UrltocompareList is a collection of 2 sites to compare.
TotalSiteCount is the number of items in UrltocompareList.
The form for this inherit IOleClientSite to remove media such as images, videos and no active X download to have a faster rendering time in webbrowser control.
I use this method instead of system.net.webclient to get the html of a webpage then compare them.
I got this error when it hits the wb.Navigate method.
An issue I ran into when running specflow tests with watin in windows 10 is that win10 by default uses MS Edge, so I had never opened IE, and when watin started it IE was stuck on the prompt for using default settings. Selecting options, closing browser and running tests again worked for me.
Just something to watch
This can be solved pretty easily.
This error occurs when the browser commits an action while he's already performing an action.
For example, you are navigating to some website while you rightclick in the web browser.
To solve this, I did the follow:
//if my webbrowser isn't performing any actions
if(!myWebBrowser.IsBusy)
{
//Navigate
myWebBrowser.Navigate("http://www.google.com");
}
First Try
1- Please Check Navigate URL's (if you check, please check again compiled folder)
2- Delete WebBrowser Control and Add New
Me forget copy original file App.Path + "\error.html" and see this problem.
Guarantee Method
I Fix This Error in VB6
Add WebBrowserControl wb(0) (Name wb , Index=0)
And Before Ever Navigate
For i = 1 To wb.UBound
Unload wb(i)
Next
Load wb(1)
wb(0).Visible = False
wb(1).Visible = true
wb(1).Navigate URL