开发者

Application.Quit() method failing to clear process

开发者 https://www.devze.com 2023-01-06 19:36 出处:网络
I\'ve seen a lot of posts returned from a Google search about this, but none of the solutions referenced in them clear this up for me.So, I thought I\'d try myself.

I've seen a lot of posts returned from a Google search about this, but none of the solutions referenced in them clear this up for me. So, I thought I'd try myself.

After this block of code:

PowerPoint.Application powerPoint = new Microsoft.Office.Interop.PowerPoint.Application();
powerPoint.Visible = Office.MsoTriState.msoTrue;
Microsoft.Office.Interop.PowerPoint.Presentation ppt = null;enter code here

I can issue the ppt.Quit(); command and Powerpoint will close and no Process is left running.

However, if after this code I do this:

ppt = powerPoint.Presentations.Open(localCopyOfPPT,  
                                    Microsoft.Office.Core.MsoTriState.msoCTrue,
                                    Microsoft.Office.Core.MsoTriState.msoTri开发者_如何学运维StateMixed,
                                    Microsoft.Office.Core.MsoTriState.msoTrue);
ppt.Close();
powerPoint.Quit();

Then, the Quit() won't work. Something about opening the presentation, even if I then close it, prevents the Quit() from working, it appears.

Anybody have any thoughts about how I can get the application to quit correctlY?


The following KB Aritcle might help you get to the bottom of the problem. http://support.microsoft.com/kb/317109

You might need to explicity call System.Runtime.InteropServices.Marshal.ReleaseComObject on your ppt instance.


powerPoint.Presentations.Open(..)

Note the use of the Presentations object. COM uses manual memory management based on reference counting, every COM interface has an AddRef() and a Release() method. The AddRef call is automatic when you obtain an object. When you're done with it you have to call the Release() method. Using the Presentations object here adds a reference to the Presentations object. Which in turn adds a reference to the internal application object.

That's very incompatible with memory management in the .NET framework. It is automatic, the garbage collector takes care of it. Which it does for COM objects too, the interop wrapper has a finalizer, it decrements the reference count when it sees that no .NET references are left on the COM object.

Perhaps you see where this is going: PowerPoint cannot exit until all object references are released. Which cannot happen until the garbage collector runs and the finalizer thread completed. Your call to the Quit() method does not make the garbage collector run. Only GC.Collect() + GC.WaitForPendingFinalizers can do that.

You can also take the manual approach. It requires Marshal.ReleaseComObject(). Doing this is difficult to get right, note that you don't have a reference to the Presentations object stored anywhere in your code. You'd have to completely rewrite your code to keep track of these references so you can call ReleaseComObject() on them.

I cannot recommend this. If you really really want PowerPoint to quit then the better way is to make sure all your references are null and call GC.Collect() and GC.WFPF. I cannot recommend this either. It will quit, eventually. Don't worry about it.


I face Same problem in my work...You try below code it's working

PowerPoint.Application powerPoint = new Microsoft.Office.Interop.PowerPoint.Application();
    //powerPoint.Visible = Office.MsoTriState.msoTrue;
    Microsoft.Office.Interop.PowerPoint.Presentation ppt = null;

    try
    {
        ppt = powerPoint.Presentations.Open(localCopyOfPPT,  
                                            Microsoft.Office.Core.MsoTriState.msoCTrue,
                                            Microsoft.Office.Core.MsoTriState.msoTriStateMixed,
                                            Microsoft.Office.Core.MsoTriState.msoFalse);
     ppt.Close();
     Marshal.FinalReleaseComObject(ppt);
     }catch(){}finally
    {  
        powerPoint.Quit();
        Marshal.FinalReleaseComObject(powerPoint);
        GC.Collect();
    }


Try the below.

GC.Collect();
GC.WaitForPendingFinalizers();

You may have to use this in this way also

GC.Collect();
GC.WaitForPendingFinalizers();

GC.Collect();
GC.WaitForPendingFinalizers();

Because, see the last point in this page http://code.msdn.microsoft.com/office/CSAutomatePowerPoint-b312d416


Alternative for your problem. find the process once job is done and then kill it.

code:

Process[] processes = Process.GetProcessesByName("powerpnt");
for (int i = 0; i < processes.Count(); i++)
{
    processes[i].Kill();
}

Namespace: System.Diagnostics


If you have multiple instances of PowerPoint open you could use something along these lines - This is the easiest way I found to close PowerPoint applications that will not clear when asked to quit.

This is how I end up killing/closing PowerPoint (specified by document path that has been opened)

/// <summary>
    /// Close open PowerPoint document
    /// </summary>
    /// <param name="path">Path to document</param>
    /// <param name="saveChanges">Save changes to document</param>
    public void PowerPointCloseOpenDocument(String path, Boolean saveChanges = true)
    {
        ppApp = getPowerPointApp(path);
        PowerPoint.Presentation pp = null;

        if (!String.IsNullOrEmpty(path))
        {
            foreach (PowerPoint.Presentation p in ppApp.Presentations)
            {
                if (p.FullName.Equals(path, StringComparison.CurrentCultureIgnoreCase))
                {
                    try
                    {
                        pp = p;
                    }
                    catch (Exception)
                    { }
                    break;
                }
            }
        }
        if(saveChanges)
        {
            if(pp!=null)
            {
                pp.Save();
            }
        }
        if(pp!= null)
        {
            Marshal.FinalReleaseComObject(pp);
        }
        if(null != ppApp)
        {
            Marshal.FinalReleaseComObject(ppApp);
        }
        var procs = FileUtil.WhoIsLocking(path);
        if(procs!= null)
        {
            foreach(var proc in procs)
            {
                proc.Kill();
            }
        }
        GC.Collect();
        GC.WaitForPendingFinalizers();

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

private PowerPoint.Application getPowerPointApp(String path = "")
    {
        try
        {
            PowerPoint.Application ppapp = null;
            try
            {
                if (!String.IsNullOrEmpty(path))
                {
                    ppapp = ((PowerPoint.Presentation)System.Runtime.InteropServices.Marshal.BindToMoniker(path)).Application;
                }
            }
            catch (Exception) { }
            if (ppapp == null)
            {
                try
                {
                    ppapp = (PowerPoint.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("PowerPoint.Application");
                }
                catch (Exception)
                {
                    ppapp = new PowerPoint.Application();
                    ppapp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
                }
            }
            if (ppapp != null)
            {
                ppapp.DisplayAlerts = Microsoft.Office.Interop.PowerPoint.PpAlertLevel.ppAlertsNone;
            }
            try { ppapp.Activate(); }
            catch (Exception) { }
            return ppapp;
        }
        catch (Exception)
        {
            return (PowerPoint.Application)Activator.CreateInstance(Type.GetTypeFromProgID("PowerPoint.Application"));
        }
    }

The File Util Class which provides you with the list of processes currently locking a document.

static public class FileUtil
{
    [StructLayout(LayoutKind.Sequential)]
    struct RM_UNIQUE_PROCESS
    {
        public int dwProcessId;
        public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
    }

    const int RmRebootReasonNone = 0;
    const int CCH_RM_MAX_APP_NAME = 255;
    const int CCH_RM_MAX_SVC_NAME = 63;

    enum RM_APP_TYPE
    {
        RmUnknownApp = 0,
        RmMainWindow = 1,
        RmOtherWindow = 2,
        RmService = 3,
        RmExplorer = 4,
        RmConsole = 5,
        RmCritical = 1000
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct RM_PROCESS_INFO
    {
        public RM_UNIQUE_PROCESS Process;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
        public string strAppName;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
        public string strServiceShortName;

        public RM_APP_TYPE ApplicationType;
        public uint AppStatus;
        public uint TSSessionId;
        [MarshalAs(UnmanagedType.Bool)]
        public bool bRestartable;
    }

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
    static extern int RmRegisterResources(uint pSessionHandle,
                                          UInt32 nFiles,
                                          string[] rgsFilenames,
                                          UInt32 nApplications,
                                          [In] RM_UNIQUE_PROCESS[] rgApplications,
                                          UInt32 nServices,
                                          string[] rgsServiceNames);

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
    static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

    [DllImport("rstrtmgr.dll")]
    static extern int RmEndSession(uint pSessionHandle);

    [DllImport("rstrtmgr.dll")]
    static extern int RmGetList(uint dwSessionHandle,
                                out uint pnProcInfoNeeded,
                                ref uint pnProcInfo,
                                [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
                                ref uint lpdwRebootReasons);

    /// <summary>
    /// Find out what process(es) have a lock on the specified file.
    /// </summary>
    /// <param name="path">Path of the file.</param>
    /// <returns>Processes locking the file</returns>
    /// <remarks>See also:
    /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
    /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
    /// 
    /// </remarks>
    static public List<Process> WhoIsLocking(string path)
    {
        uint handle;
        string key = Guid.NewGuid().ToString();
        List<Process> processes = new List<Process>();

        int res = RmStartSession(out handle, 0, key);
        if (res != 0) throw new Exception("Could not begin restart session.  Unable to determine file locker.");

        try
        {
            const int ERROR_MORE_DATA = 234;
            uint pnProcInfoNeeded = 0,
                 pnProcInfo = 0,
                 lpdwRebootReasons = RmRebootReasonNone;

            string[] resources = new string[] { path }; // Just checking on one resource.

            res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);

            if (res != 0) throw new Exception("Could not register resource.");

            //Note: there's a race condition here -- the first call to RmGetList() returns
            //      the total number of process. However, when we call RmGetList() again to get
            //      the actual processes this number may have increased.
            res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

            if (res == ERROR_MORE_DATA)
            {
                // Create an array to store the process results
                RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                pnProcInfo = pnProcInfoNeeded;

                // Get the list
                res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                if (res == 0)
                {
                    processes = new List<Process>((int)pnProcInfo);

                    // Enumerate all of the results and add them to the 
                    // list to be returned
                    for (int i = 0; i < pnProcInfo; i++)
                    {
                        try
                        {
                            processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                        }
                        // catch the error -- in case the process is no longer running
                        catch (ArgumentException) { }
                    }
                }
                else throw new Exception("Could not list processes locking resource.");
            }
            else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
        }
        finally
        {
            RmEndSession(handle);
        }

        return processes;
    }       
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号