Suppose you need to use Office interop in an application. (No options) Everyone knows that he creates office applications without an interface and does work through them.

Well, what if the user incorrectly finished working with the program? For example, killed through the task manager. It turns out that in the processes the applications of the office remain hanging.

Can this be prevented? If not, is it possible to understand when launching (re) the application that the processes contain garbage that was left from the previous launch and crash these processes without affecting the real office applications (for example, the user launched Excel.)

  • он создает приложения office без интерфейса - by default it’s the same with the interface, like ... - Andrew NOP
  • It doesn't matter ... - iluxa1810

2 answers 2

Alternatively, using Win32, you can combine the application and the processes launched from it into a job ( Job ) then the Interop processes will be terminated along with the parent application.

To do this, you will need to create a task using CreateJobObject , and then add the created processes to the task using AssignProcessToJobObject

There is a similar discussion in English: “ Kill child process when parent process is killed ”. From the @Ron response, you can take the code for the ChildProcessTracker wrapper ChildProcessTracker :

 /// <summary> /// Allows processes to be automatically killed if this parent process unexpectedly quits. /// This feature requires Windows 8 or greater. On Windows 7, nothing is done.</summary> /// <remarks>References: /// https://stackoverflow.com/a/4657392/386091 /// https://stackoverflow.com/a/9164742/386091 </remarks> public static class ChildProcessTracker { /// <summary> /// Add the process to be tracked. If our current process is killed, the child processes /// that we are tracking will be automatically killed, too. If the child process terminates /// first, that's fine, too.</summary> /// <param name="processHandle"></param> public static void AddProcess(int processHandle) { if (s_jobHandle != IntPtr.Zero) { bool success = AssignProcessToJobObject(s_jobHandle, new IntPtr(processHandle)); if (!success) throw new Win32Exception(); } } static ChildProcessTracker() { // This feature requires Windows 8 or later. To support Windows 7 requires // registry settings to be added if you are using Visual Studio plus an // app.manifest change. // https://stackoverflow.com/a/4232259/386091 // https://stackoverflow.com/a/9507862/386091 if (Environment.OSVersion.Version < new Version(6, 2)) return; // The job name is optional (and can be null) but it helps with diagnostics. // If it's not null, it has to be unique. Use SysInternals' Handle command-line // utility: handle -a ChildProcessTracker string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; s_jobHandle = CreateJobObject(IntPtr.Zero, jobName); var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION { LimitFlags = JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE }; // This is the key flag. When our process is killed, Windows will automatically // close the job handle, and when that happens, we want the child processes to // be killed, too. var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); extendedInfo.BasicLimitInformation = info; int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); try { Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) { throw new Win32Exception(); } } finally { Marshal.FreeHGlobal(extendedInfoPtr); } } [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); [DllImport("kernel32.dll")] static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); // Windows will automatically close any open job handles when our process terminates. // This can be verified by using SysInternals' Handle utility. When the job handle // is closed, the child processes will be killed. private static readonly IntPtr s_jobHandle; } public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, BasicUIRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, GroupInformation = 11 } [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public JOBOBJECTLIMIT LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public UInt32 ActiveProcessLimit; public Int64 Affinity; public UInt32 PriorityClass; public UInt32 SchedulingClass; } [Flags] public enum JOBOBJECTLIMIT : uint { JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000 } [StructLayout(LayoutKind.Sequential)] public struct IO_COUNTERS { public UInt64 ReadOperationCount; public UInt64 WriteOperationCount; public UInt64 OtherOperationCount; public UInt64 ReadTransferCount; public UInt64 WriteTransferCount; public UInt64 OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public IO_COUNTERS IoInfo; public UIntPtr ProcessMemoryLimit; public UIntPtr JobMemoryLimit; public UIntPtr PeakProcessMemoryUsed; public UIntPtr PeakJobMemoryUsed; } 

, and register using it Excel processes as a child:

 [DllImport("user32.dll")] static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId); var app = new Microsoft.Office.Interop.Excel.Application(); int processId; GetWindowThreadProcessId(app.Hwnd, out processId); ChildProcessTracker.AddProcess(processId); 

and then the OS will kill them along with the parent process.

I recommend reading the documentation and reading the comments in the discussion by reference, there may be nuances with the support of different versions of the OS and Visual Studio, when running with limited rights, when launching the parent application from other processes.

If not, is it possible to understand when launching (re) the application that the processes contain garbage that was left from the previous launch and crash these processes without affecting the real office applications (for example, the user launched Excel.)

I doubt the existence of a reliable method. The Excel process started by the old process is no different from the process started by the current / other applications / user. You can, as suggested by @VadimTagil, write code that will save the running processes in the permanent storage (disk / DB / registry) and kill them according to a certain logic. Another option, to make the formation of Excel in separate processes, tasks, independent of the main application.

In general, if in the normal course of the application the garbage does not remain, then problems will arise only in exceptional cases (externally killed process), the processing of which can be left to the user. Make sure that Marshal.ReleaseComObject is called for all objects and that all processes are terminated and perhaps you can do without the task.

  • one
    Powerful stuff. It is strange that the wrapper for Net is not made out of the box - iluxa1810
  • @ iluxa1810 Yes, on the one hand, they regret resources, on the other there are differences between different versions of Windows. In general, with regard to process management, there is no “managed” alternative. - default locale
  • Hmm, but this is not a wrapper over what you describe msdn.microsoft.com/en-us/library/cc853426(v=vs.85).aspx ? - iluxa1810
  • @ iluxa1810 did not work with this, so I can’t say for sure. Judging by the description of HPC is a package for server clusters. - default locale

I suppose the essence of the issue is in the method of separating the "garbage" from normal Excel processes. Being able to do this, you can already make something up: remove junk processes when you start the main program, or periodically with a background service when the main program is closed.

The most correct solution: add the Id of all Interop processes created to the database, if they are completed correctly, delete it. Accordingly, if the application is not completed correctly in the database, Id will still have dead processes that can be nailed on the next start (after making sure that these are still existing Excel processes, as they could have been nailed by something else and the same Id was already reused by the system for another programs).

But if you want a simpler method, any Excel process that does not have a visible main window can be considered a “junk”:

 using System.Diagnostics; using System.Runtime.InteropServices; static class Program { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool IsWindowVisible(IntPtr hWnd); public static bool IsProcessDead(Process pr) { IntPtr hwnd = pr.MainWindowHandle; if (hwnd == IntPtr.Zero) return true; return !IsWindowVisible(hwnd); } void ClearProcesses() { Process[] prs=Process.GetProcessesByName("excel"); foreach (Process proc in prs) { if(IsProcessDead(proc))proc.Kill(); } } } 

If there are no other programs on the target machine that use invisible Excel for their own purposes, this can be considered a normal assumption.

  • And if there are invisible exclaves that do something in other programs? Is it possible to find out his paternal process, and if he is already gone, then kill him? - iluxa1810
  • @ iluxa1810 You can, through the functions Process32First and Process32Next, see msdn.microsoft.com/ru-ru/library/windows/desktop/… But this is doubtful, since at the end of the process its id value can be issued by the system to another newly created process. And it’s not a fact that the "parent process" field of all child processes is automatically reset. It is better to remember the id of your processes if you do not want to nail too much. - MSDN.WhiteKnight September
  • Like, not necessarily these functions to use. It seems that a list of processes can be obtained through the diagnostic space. Writing an id in a database is not a panacea ... what if the user pins the excel process with his hands? -> some kind of new program can start with this id. - iluxa1810
  • @ iluxa1810 I did not find how to define .net. "But what if the user pins the excel process with his hands? -> some new program can start with this id" - only Excel processes will be beaten. The likelihood that the new id will also be issued to the Excel is still quite small. - MSDN.WhiteKnight