views:

302

answers:

2

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.

A: 

Try this instead..

pdfView.Kill();
pdfView.WaitForExit();
cableguy
be careful the above code, you are going to run into a lot of issues with trying to kill pdfs.
Woot4Moo
how so? please elaborate.
cableguy
A: 

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...

  1. Click on the link multiple times and then click save.
  2. 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.

  1. 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.

  2. 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.

Felix Cartwright