0

I have installed a program ("CmisSync.exe") that runs as a small icon in the Windows system tray.

From C# I want to start it, and then exit it gracefully (as if a user had clicked "Exit" in the program's menu). Here is my attempt:

Process process = Process.Start(@"C:\Programs\CmisSync\CmisSync.exe");

// Wait for CmisSync's configuration/UI to start properly.
Thread.Sleep(5000);

// Close as if the user had clicked "Exit".
process.CloseMainWindow();

// Wait for CmisSync to finish what it is doing and exit normally.
// This might take a few minutes if a big sync is going on.
process.WaitForExit();

Unfortunately, CloseMainWindow does not make the program stop.

Maybe it is due to the program not actually have a main window? It only runs in the tray.

  • Close does not make the program stop either, by design.
  • Kill is not graceful, it does not let the program finish its current UI loop. Unlike the UNIX equivalent, the command does not seem to accept arguments indicating how brutal the kill should be.

The tray icon is implemented using System.Windows.Forms.Form

Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
  • 1
    Have you checked WM_CLOSE, which can be sent using PostMessage() to running programs, and most will shut down more or less gracefully? If it has no window, you can use EnumWindows() and match the process ID using GetWindowThreadProcessId(). – Stacking For Heap Mar 02 '18 at 08:28
  • That ought to be accurate, it won't have anything resembling a main window until you operate its context menu. There is no good mechanism to reliably discover the hidden window that listens for the tray icon notifications, so you are pretty stuck. Assuming you know the process name, you could enumerate the threads in the process and look at their windows with EnumThreadWindows. But if this programmer got it right then he used a message-only window, you can't poke it. Have a look with Spy++ first. – Hans Passant Mar 02 '18 at 09:17
  • https://blogs.msdn.microsoft.com/oldnewthing/20141013-00/?p=43863 – Hans Passant Mar 02 '18 at 09:21
  • If that thread doesn't have a window, any WM_* message will be lost. If it has a hidden window, since you have the ThreadID, you could try with [PostThreadMessage](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644946(v=vs.85).aspx). Another way, that I've never tested, is to attach a console to the process, and use [GenerateConsoleCtrlEvent](https://docs.microsoft.com/en-us/windows/console/generateconsolectrlevent) to send CTRL_* signals. [This blog post](http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/) has some informations. – Jimi Mar 02 '18 at 13:48

1 Answers1

0

Yes, as you have discovered Process.CloseMainWindow only closes a program with a main window i.e you will most likely find the MainWindowHandle will be IntPtr.Zero

Process.CloseMainWindow Method ()

Closes a process that has a user interface by sending a close message to its main window

Additionally, AFAIK WM_CLOSE or any other message wont help you either

However, you could create a message only window, or have your mainwindow set to invisible so you could message it in standard ways or call Process.CloseMainWindow.

However there are also many other IPC techniques you could use to the same affect, or even WCF. Anyway, since you're in control of the sync tray icon program, you can employ anything really (well except techniques that require a window evidently)

Interprocess Communications

Update

Since its open source, you can either edit as desired or have a look at the internals of it and you might find a way to gracefully close it.

TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • "Anyway, since you're in control of the sync tray icon program" - but he is not, that's third party program (with open source, but still). – Evk Mar 02 '18 at 08:53
  • @Evk ahh yes the first line haha, my i should learn to read questions better – TheGeneral Mar 02 '18 at 08:54