How to kill remote desktop session for local computer only - c#

I'm using the following code to kill a remote desktop session and the application running in it. It works fine, the only problem is that it kills the specified application for all users.
How do I keep this to just the local machine running a session?
We have multiple users logging in and running this application from a server on their local machines. Most are running using work resources, but some use remote desktop.
No matter how they are logged in when I run my code all users loose their sessions.
private void btnCloseSession_Click(object sender, EventArgs e)
{
if (!runningExclusiveProcess)
{
runningExclusiveProcess = true;
btnCloseSession.Enabled = false;
//check and close Labware if running
if (chkCloseLabware.Checked == true)
{
if (chkExit.Checked == true)
{
KillLabWare();
Close();
}
else
{
KillLabWare();
}
}
Process[] my = Process.GetProcessesByName("mstsc");
//loop thru list to get selected item(s)
ListBox.SelectedObjectCollection selectedItems = new ListBox.SelectedObjectCollection(lstOpenSessions);
selectedItems = lstOpenSessions.SelectedItems;
try
{
//remove credentials
string szTestx = "/delete:GOJO.NET/" + cboServer.Text;
ProcessStartInfo infox = new ProcessStartInfo("cmdkey.exe", szTestx);
Process procx = new Process();
procx.StartInfo = infox;
procx.Start();
if (lstOpenSessions.SelectedIndex != -1)
{
for (int i = selectedItems.Count - 1; i >= 0; i--)
{
//loop thru process to match process vs. list selection(s)
foreach (Process remote in my)
{
if (remote.MainWindowTitle == selectedItems[i].ToString())
{
KillRS(remote.MainWindowTitle);
lstOpenSessions.Items.Remove(selectedItems[i]);
}
}
if (lstOpenSessions.Items.Contains(selectedItems[i].ToString()))
{
lstOpenSessions.Items.Remove(selectedItems[i]);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
}
// If your task is synchronous, then undo your flag here:
runningExclusiveProcess = false;
btnCloseSession.Enabled = true;
}
}
public void KillLabWare()
{
ConnectionOptions con = new ConnectionOptions();
con.Username = cboUserName.Text;
con.Password = txtPassWord.Text;
string strIPAddress = cboServer.Text;
ManagementScope scope = new
ManagementScope(#"\\" + strIPAddress + #"\root\cimv2", con);
scope.Connect();
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Process WHERE Name='Labware.exe'");
ManagementObjectSearcher searcher = new
ManagementObjectSearcher(scope, query);
ManagementObjectCollection objectCollection = searcher.Get();
foreach (ManagementObject managementObject in objectCollection)
{
managementObject.InvokeMethod("Terminate", null);
}
}
private void KillRS(string rwt)
{
foreach (Process p in Process.GetProcesses())
{
if (p.MainWindowTitle == rwt)
{
p.Kill();
}
}
}
public static void KillRemoteProcess(Process p, string user, string password)
{
new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "TaskKill.exe",
Arguments = string.Format("/pid {0} /s {1} /u {2} /p {3}", p.Id, p.MachineName, user, password),
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true
}
}.Start();
}

It sounds like you are trying to force a specific user to log out? Is this because you find that users are forgetting to log out and constantly consuming licenses?
LabWare Application allows for a time out interval (in minutes) to be set on each user where after the interval has passed, the user will be logged out (licence no longer consumed).
For more information see page 204 of the LabWare 7 Technical Manual.
Alternativley if this is for a scheduler (service or cluster instance) session, this can also be controlled by the application. You can either manually change the shutdown and keep alive flags on the instance record on the Services table (if using Service Manager) or you can write a LIMS Basic event trigger/automation script or scheduled subroutine (or have this as a button on a Visual workflow) to do this for you.
HTH.

If you wanna kill remote desktop session or disconnect current RDP session, please read this article:
WTSDisconnectSession function
but if you logout current user, it also disconnect RDP session, here is the code:
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
class Program
{
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern bool WTSDisconnectSession(IntPtr hServer, int sessionId, bool bWait);
const int WTS_CURRENT_SESSION = -1;
static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
static void Main(string[] args)
{
if (!WTSDisconnectSession(WTS_CURRENT_SERVER_HANDLE,
WTS_CURRENT_SESSION, false))
throw new Win32Exception();
}
}
I hope it works, fill free for further info, comment plz
Happy Coding 😎

Related

Single instance dotnetcore cli app on linux

I am interested in how to inforce a single instance policy for dotnetcore console apps. To my surprise it seems like there isn't much out there on the topic. I found this one stacko, How to restrict a program to a single instance, but it doesnt seem to work for me on dotnetcore with ubuntu. Anyone here do this before?
Variation of #MusuNaji's solution at: How to restrict a program to a single instance
private static bool AlreadyRunning()
{
Process[] processes = Process.GetProcesses();
Process currentProc = Process.GetCurrentProcess();
logger.LogDebug("Current proccess: {0}", currentProc.ProcessName);
foreach (Process process in processes)
{
if (currentProc.ProcessName == process.ProcessName && currentProc.Id != process.Id)
{
logger.LogInformation("Another instance of this process is already running: {pid}", process.Id);
return true;
}
}
return false;
}
This is a little more difficult on .NET core than it should be, due to the problem of mutex checking on Linux/MacOS (as reported above). Also Theyouthis's solution isn't helpful as all .NET core apps are run via the CLI which has a process name of 'dotnet' which if you are running multiple .NET core apps on the same machine the duplicate instance check will trigger incorrectly.
A simple way to do this that is also multi-platform robust is to open a file for write when the application starts, and close it at the end. If the file fails to open it is due to another instance running concurrently and you can handle that in the try/catch. Using FileStream to open the file will also create it if it doesn't first exist.
try
{
lockFile = File.OpenWrite("SingleInstance.lck");
}
catch (Exception)
{
Console.WriteLine("ERROR - Server is already running. End that instance before re-running. Exiting in 5 seconds...");
System.Threading.Thread.Sleep(5000);
return;
}
Here is my implementation using Named pipes. It supports passing arguments from the second instance.
Note: I did not test on Linux or Mac but it should work in theory.
Usage
public static int Main(string[] args)
{
instanceManager = new SingleInstanceManager("8A3B7DE2-6AB4-4983-BBC0-DF985AB56703");
if (!instanceManager.Start())
{
return 0; // exit, if same app is running
}
instanceManager.SecondInstanceLaunched += InstanceManager_SecondInstanceLaunched;
// Initialize app. Below is an example in WPF.
app = new App();
app.InitializeComponent();
return app.Run();
}
private static void InstanceManager_SecondInstanceLaunched(object sender, SecondInstanceLaunchedEventArgs e)
{
app.Dispatcher.Invoke(() => new MainWindow().Show());
}
Your Copy-and-paste code
public class SingleInstanceManager
{
private readonly string applicationId;
public SingleInstanceManager(string applicationId)
{
this.applicationId = applicationId;
}
/// <summary>
/// Detect if this is the first instance. If it is, start a named pipe server to listen for subsequent instances. Otherwise, send <see cref="Environment.GetCommandLineArgs()"/> to the first instance.
/// </summary>
/// <returns>True if this is tthe first instance. Otherwise, false.</returns>
public bool Start()
{
using var client = new NamedPipeClientStream(applicationId);
try
{
client.Connect(0);
}
catch (TimeoutException)
{
Task.Run(() => StartListeningServer());
return true;
}
var args = Environment.GetCommandLineArgs();
using (var writer = new BinaryWriter(client, Encoding.UTF8))
{
writer.Write(args.Length);
for (int i = 0; i < args.Length; i++)
{
writer.Write(args[i]);
}
}
return false;
}
private void StartListeningServer()
{
var server = new NamedPipeServerStream(applicationId);
server.WaitForConnection();
using (var reader = new BinaryReader(server, Encoding.UTF8))
{
var argc = reader.ReadInt32();
var args = new string[argc];
for (int i = 0; i < argc; i++)
{
args[i] = reader.ReadString();
}
SecondInstanceLaunched?.Invoke(this, new SecondInstanceLaunchedEventArgs { Arguments = args });
}
StartListeningServer();
}
public event EventHandler<SecondInstanceLaunchedEventArgs> SecondInstanceLaunched;
}
public class SecondInstanceLaunchedEventArgs
{
public string[] Arguments { get; set; }
}
Unit test
[TestClass]
public class SingleInstanceManagerTests
{
[TestMethod]
public void SingleInstanceManagerTest()
{
var id = Guid.NewGuid().ToString();
var manager = new SingleInstanceManager(id);
string[] receivedArguments = null;
var correctArgCount = Environment.GetCommandLineArgs().Length;
manager.SecondInstanceLaunched += (sender, e) => receivedArguments = e.Arguments;
var instance1 = manager.Start();
Thread.Sleep(200);
var manager2 = new SingleInstanceManager(id);
Assert.IsFalse(manager2.Start());
Thread.Sleep(200);
Assert.IsTrue(instance1);
Assert.IsNotNull(receivedArguments);
Assert.AreEqual(correctArgCount, receivedArguments.Length);
var receivedArguments2 = receivedArguments;
var manager3 = new SingleInstanceManager(id);
Thread.Sleep(200);
Assert.IsFalse(manager3.Start());
Assert.AreNotSame(receivedArguments, receivedArguments2);
Assert.AreEqual(correctArgCount, receivedArguments.Length);
}
}
The downside of deandob's solution is that one can launch the application from another path. So you may prefer some static path or a tmp path for all users.
Here is my attempt:
//second instance launch guard
var tempPath = Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine)
??
Path.GetTempPath();
var lockPath = Path.Combine(tempPath, "SingleInstance.lock");
await using var lockFile = File.OpenWrite(lockPath);
here I'm trying to get TEMP system variable at the scope of machine (not the user TEMP) and if its empty - fallback to the user's temp folder on windows or shared /tmp on some linuxes.

Keeping console app alive and event-driven while processes run in the background

I am trying to write a console application that acts as a "job manager" by running processes in the background. These processes would be running JScript files with arguments passed in. This console application will be distributed across many machines, and will pull from a centralized source (ie. database) to get jobs. The purpose of this application is to eliminate the need for individualized batch files on all of these machines.
I am having trouble keeping the application alive. In the code that I included, you can see in my main function that I am making an initial call to the JobManger's StartNewJobs() method. After this initial call to this method, I'd like my application to then be event-driven, only waking up and running when a process has exited, allowing me to start a new process. The problem I am running into is that once the main() function finishes (when the initial StartNewJobs() method finishes) the console closes and the program ends.
My question is what is the proper way to keep my console application alive and allow it to be event-driven rather than procedural? I know I can probably throw in a while(true) at the end of the main function, but that seems sloppy and incorrect.
Batch file we are trying to replace:
C:\Windows\SysWOW64\cscript.exe c:\temp\somejscriptfile.js 49f1bdd8-5e6b-40cc-92bc-eb20c237a959
C:\Windows\SysWOW64\cscript.exe c:\temp\somejscriptfile.js 654e3783-a1b6-43be-8027-c7d060bf131f
...
Program.cs:
using DistributedJobs.Data;
using DistributedJobs.Logging;
using DistributedJobs.Models;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System;
namespace DistributedJobs
{
class Program
{
static void Main(string[] args)
{
//Get intial objects/settings
ILogger logger = new Logger(Properties.Settings.Default.LoggingLevel, EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>());
IDataProvider dataProvider = new SQLDataProvider();
DMSPollingJobType availableJobTypes = DMSPollingJobType.FlatFile;
if (Properties.Settings.Default.SupportsVPN)
{
availableJobTypes |= DMSPollingJobType.VPN;
}
String executableLocation = Properties.Settings.Default.ExecutableLocation;
String jsLocation = Properties.Settings.Default.JSLocation;
Int32 maxProcesses = Properties.Settings.Default.MaxProcesses;
//Create job manager and start new processes/jobs
DMSJobManager jobManager = new DMSJobManager(logger, dataProvider, availableJobTypes, executableLocation, jsLocation, maxProcesses);
jobManager.StartNewJobs();
}
}
}
JobManager.cs:
using DistributedJobs.Models;
using System.Diagnostics;
using System;
using System.Collections.Generic;
using DistributedJobs.Logging;
namespace DistributedJobs.Data
{
public class JobManager
{
private IDataProvider DataProvider;
private ILogger Logger;
private Dictionary<Job, Process> RunningProcesses;
private JobType AvailableJobTypes;
private String ExecutableLocation;
private String JSLocation;
private Int32 MaxProcesses;
public Boolean CanStartNewJob
{
get
{
Boolean canStartNewJob = false;
if (RunningProcesses.Count < MaxProcesses)
{
canStartNewJob = true;
}
foreach (KeyValuePair<Job, Process> entry in RunningProcesses)
{
if (entry.Key.JobType != JobType.FlatFile)
{
canStartNewJob = false;
break;
}
}
return canStartNewJob;
}
}
public JobManager(ILogger logger, IDataProvider dataProvider, JobType availableJobTypes, String executableLocation, String jsLocation, Int32 maxProcesses)
{
Logger = logger;
DataProvider = dataProvider;
RunningProcesses = new Dictionary<Job, Process>();
AvailableJobTypes = availableJobTypes;
ExecutableLocation = executableLocation;
JSLocation = jsLocation;
MaxProcesses = maxProcesses;
}
public void StartNewJobs()
{
while (CanStartNewJob)
{
Job newJob = DataProvider.GetNextScheduledJob(AvailableJobTypes);
if (newJob != null)
{
Process newProcess = CreateNewProcess(newJob);
RunningProcesses.Add(newJob, newProcess);
newProcess.Start();
}
}
}
public Process CreateNewProcess(Job job)
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = ExecutableLocation;
startInfo.Arguments = JSLocation + " " + job.JobID.ToString();
startInfo.UseShellExecute = false;
Process retProcess = new Process()
{
StartInfo = startInfo,
EnableRaisingEvents = true
};
retProcess.Exited += new EventHandler(JobFinished);
return retProcess;
}
public void JobFinished(object sender, EventArgs e)
{
Job finishedJob = null;
foreach (KeyValuePair<Job, Process> entry in RunningProcesses)
{
if ((Process)sender == entry.Value)
{
finishedJob = entry.Key;
break;
}
}
if (finishedJob != null)
{
RunningProcesses.Remove(finishedJob);
StartNewJobs();
}
}
}
}
You could try using Application.Run()(System.Windows.Forms). This will start a standard message loop.
So at the end of your Main method just add a Application.Run():
static void Main(string[] args)
{
//Get intial objects/settings
ILogger logger = new Logger(Properties.Settings.Default.LoggingLevel, EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>());
IDataProvider dataProvider = new SQLDataProvider();
DMSPollingJobType availableJobTypes = DMSPollingJobType.FlatFile;
if (Properties.Settings.Default.SupportsVPN)
{
availableJobTypes |= DMSPollingJobType.VPN;
}
String executableLocation = Properties.Settings.Default.ExecutableLocation;
String jsLocation = Properties.Settings.Default.JSLocation;
Int32 maxProcesses = Properties.Settings.Default.MaxProcesses;
//Create job manager and start new processes/jobs
DMSJobManager jobManager = new DMSJobManager(logger, dataProvider, availableJobTypes, executableLocation, jsLocation, maxProcesses);
jobManager.StartNewJobs();
// start message loop
Application.Run();
}

Restart remote server from client

I have accessed remote server but it can be some problem. So i want to restart the remote via client side using c#. Is that possible to restart?
EDIT: See #amitdayama's answer below for a more reasonable approach
Yes this is possible.
First, add this using namespace statements:
using System.Diagnostics;
using System.Runtime.InteropServices;
To shut down your computer, use:
Process.Start("shutdown","/s /t 0"); // starts the shutdown application
// the argument /s is to shut down the computer
// the argument /t 0 is to tell the process that
// the specified operation needs to be completed
// after 0 seconds
To restart your computer, use:
Process.Start("shutdown","/r /t 0"); // the argument /r is to restart the computer
Source: Codeproject.com
using System;
using System.Management;
namespace WMI3
{
class Class1
{
static void Main(string[] args)
{
Console.WriteLine("Computer details retrieved using Windows Management Instrumentation (WMI)");
//Connect to the remote computer
ConnectionOptions co = new ConnectionOptions();
co.Username = "username";
co.Password = "Pass";
string serverName="servername";
System.Management.ManagementScope ms = new System.Management.ManagementScope(servername + "\\root\\cimv2", co);
//Query remote computer across the connection
System.Management.ObjectQuery oq = new System.Management.ObjectQuery("SELECT * FROM Win32_OperatingSystem");
ManagementObjectSearcher query1 = new ManagementObjectSearcher(ms,oq);
ManagementObjectCollection queryCollection1 = query1.Get();
foreach( ManagementObject mo in queryCollection1 )
{
string[] ss={""};
mo.InvokeMethod("Reboot",ss);
Console.WriteLine(mo.ToString());
}
}
}
}
This is my solution which supports silent mode, "fire and forget" and delayed reboot. In can simply become enhanced with an individual logon for the process start.
public static bool RebootRemoteMachineSOVersion(ContentControl parentControl, string remoteHostNameOrIp, int waitSeconds = 60, bool silent = false, bool waitForExit = true)
{
waitSeconds = Math.Max(0, waitSeconds);
if (!silent && MessageBox.Show($"Reboot remote computer ({ remoteHostNameOrIp }) in { waitSeconds } seconds?", "Reboot remote machine", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
{
return false;
//<-----------
}
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.FileName = "shutdown.exe";
processInfo.Arguments = $#"-r -t { waitSeconds } -m \\{ remoteHostNameOrIp }";
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
processInfo.CreateNoWindow = true;
Process proc;
try
{
proc = Process.Start(processInfo);
if (waitForExit) proc.WaitForExit();
else return true;
//<----------
}
catch (Exception ex)
{
if (!silent) MessageBox.Show($"An error happened:\n\n{ ex.Message }", "Reboot remote machine", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
//<-----------
}
{
string message = "";
const int ERROR_BAD_NETPATH = 53;
const int ERROR_SHUTDOWN_IN_PROGRESS = 1115;
const int RPC_S_UNKNOWN_IF = 1717;
switch (proc.ExitCode)
{
case 0:
if (!silent) MessageBox.Show($"Remote computer is rebooting ({ remoteHostNameOrIp }) in { waitSeconds } seconds.", "Reboot remote computer", MessageBoxButton.OK, MessageBoxImage.Information);
return true;
//<----------
case ERROR_BAD_NETPATH:
message = $"Remote computer not found ({ remoteHostNameOrIp })";
break;
case ERROR_SHUTDOWN_IN_PROGRESS:
message = $"A shutdown is already in progress ({ remoteHostNameOrIp })";
break;
case RPC_S_UNKNOWN_IF:
message = $"Remote computer does not accept shutdown. Probably it is currently booting. ({ remoteHostNameOrIp })";
break;
default:
message = $"Could not shut down - errorcode: { proc.ExitCode } ({ remoteHostNameOrIp })";
break;
}
if (!silent) MessageBox.Show($"{ message }", "Reboot remote computer", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}

How to prevent WMI quotas from overflowing?

I am using a C# application to monitor the processes launched from a particular folder, and I am using WMI for monitoring. My WMI query is like
SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ExecutablePath LIKE '{0}%'
where I substitute the parameter with the path to the folder which I am intereseted. The WMI query is working fine and I am subscribing to the event notifications to do some additional processsing when a process from the particular folder comes up. The monitoring tool runs fine for hours after which I start getting a WMI QuotaViolation exception in my app. Once this happens I need to restart the Windows Management Instrumentation service to get the thing working.
I was initially using a
`SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'`
query and then checking the processes folder in the event notification, the modification in the query was done hoping it would reduce the result set and therefore prevent the Quota Violation.
Is there any way to flush the WMI quotas periodically or any other method whereby I can prevent the QuotaViolation? What is the best way to handle a QuotaViolation scenario?
Edit:
This is my process watcher object :
public class ProcessWatcher : ManagementEventWatcher
{
private string folder = "";
// Process Events
public event ProcessEventHandler ProcessCreated; //notifies process creation
//add any more event notifications required here
// WMI WQL process query strings
static readonly string WMI_OPER_EVENT_QUERY = #"SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'";
static readonly string WMI_OPER_EVENT_QUERY_WITH_PROC =
WMI_OPER_EVENT_QUERY + " and TargetInstance.Name = '{0}'";
public ProcessWatcher(string basepath)
{
folder = basepath;
Init(string.Empty);
}
public ProcessWatcher(string processName, string basepath)
{
folder = basepath;
Init(processName);
}
private void Init(string processName)
{
this.Query.QueryLanguage = "WQL";
if (string.IsNullOrEmpty(processName))
{
this.Query.QueryString = string.Format(WMI_OPER_EVENT_QUERY + #" AND TargetInstance.ExecutablePath LIKE '{0}%'", folder.Replace(#"\",#"\\")) ;
}
else
{
this.Query.QueryString =
string.Format(WMI_OPER_EVENT_QUERY_WITH_PROC, processName);
}
this.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
}
private void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
try
{
ManagementBaseObject mObj = e.NewEvent["TargetInstance"] as ManagementBaseObject;
if (mObj != null)
{
Win32_Process proc = new Win32_Process(mObj);
if (proc != null)
{
folder = folder.ToLower() ?? "";
string exepath = (string.IsNullOrEmpty(proc.ExecutablePath)) ? "" : proc.ExecutablePath.ToLower();
if (!string.IsNullOrEmpty(folder) && !string.IsNullOrEmpty(exepath) && exepath.Contains(folder))
{
if (ProcessCreated != null) ProcessCreated(proc);
}
}
proc.Dispose();
}
mObj.Dispose();
}
catch(Exception ex) { throw; }
finally
{
e.NewEvent.Dispose();
}
}
I create a ProcessWatcher object at app startup, in a viewmodel constructor like :
watch = new ProcessWatcher(BasePath);
watch.ProcessCreated += new ProcessEventHandler(procWatcher_ProcessCreated);
watch.Start();
The start call is where the QuotaViolation is raised if I try to start it a second time without restarting WMI.
At app exit, I am disposing off the ProcessWatcher object like :
watch.Stop();
watch.Dispose();
The Relevant Stack trace is :
Exception InnerException [System.Management.ManagementException: Quota violation
at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
at System.Management.ManagementEventWatcher.Start()
at App.ProcessTabViewModel1..ctor()
System.Management.ManagementException: Quota violation
Yes, that happens. I wrote a little test program, based on your snippet after adding the missing pieces:
static void Main(string[] args) {
for (int ix = 0; ix < 1000; ++ix) {
var obj = new ProcessWatcher("");
obj.ProcessCreated += obj_ProcessCreated;
obj.Start();
}
}
Kaboom! With the exact same stack trace as you quoted. It conked out at ix == 76. In other words, the WMI quota for this query is 75. Tested in Windows 8.1. Feels about right, this is a very expensive query, none too fast either.
You are going to have to do this fundamentally different, create only one query. One is enough, you probably got into trouble by doing this for many folders. Attack that differently, do your own filtering when you get the event. A rough example (I didn't quite get the filtering you want to do):
public class ProcessWatcher2 : IDisposable {
public delegate void ProcessCreateEvent(string name, string path);
public event ProcessCreateEvent ProcessCreated;
public ProcessWatcher2(string folder) {
this.folder = folder;
lock (locker) {
listeners.Add(this);
if (watcher == null) Initialize();
}
}
public void Dispose() {
lock (locker) {
listeners.Remove(this);
if (listeners.Count == 0) {
watcher.Stop();
watcher.Dispose();
watcher = null;
}
}
}
private static void Initialize() {
var query = new WqlEventQuery(#"SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'");
watcher = new ManagementEventWatcher(query);
watcher.EventArrived += watcher_EventArrived;
watcher.Start();
}
private static void watcher_EventArrived(object sender, EventArrivedEventArgs e) {
using (var proc = (ManagementBaseObject)e.NewEvent["TargetInstance"]) {
string name = (string)proc.Properties["Name"].Value;
string path = (string)proc.Properties["ExecutablePath"].Value;
lock (locker) {
foreach (var listener in listeners) {
bool filtered = false;
// Todo: implement your filtering
//...
var handler = listener.ProcessCreated;
if (!filtered && handler != null) {
handler(name, path);
}
}
}
}
}
private static ManagementEventWatcher watcher;
private static List<ProcessWatcher2> listeners = new List<ProcessWatcher2>();
private static object locker = new object();
private string folder;
}

C# Outlook Interop. Outlook Not Shutting Down

The following problem is driving me crazy. In C#, I am trying to ensure that outlook shuts down after I run some code to grab all calendar events, yet no matter what I try, it doesn't. Can anyone help me?
private void button1_Click(object sender, EventArgs e)
{
Microsoft.Office.Interop.Outlook.Application outlookApp = null;
Microsoft.Office.Interop.Outlook.NameSpace mapiNamespace = null;
Microsoft.Office.Interop.Outlook.MAPIFolder CalendarFolder = null;
Microsoft.Office.Interop.Outlook.Items outlookCalendarItems = null;
outlookApp = new Microsoft.Office.Interop.Outlook.Application();
mapiNamespace = outlookApp.GetNamespace("MAPI");
CalendarFolder = mapiNamespace.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderCalendar);
outlookCalendarItems = CalendarFolder.Items;
outlookCalendarItems.IncludeRecurrences = true;
foreach (Microsoft.Office.Interop.Outlook.AppointmentItem item in outlookCalendarItems)
{
if (item.IsRecurring)
{
Microsoft.Office.Interop.Outlook.RecurrencePattern rp = item.GetRecurrencePattern();
DateTime first = new DateTime(2008, 8, 31, item.Start.Hour, item.Start.Minute, 0);
DateTime last = new DateTime(2008, 10, 1);
Microsoft.Office.Interop.Outlook.AppointmentItem recur = null;
for (DateTime cur = first; cur <= last; cur = cur.AddDays(1))
{
try
{
recur = rp.GetOccurrence(cur);
MessageBox.Show(recur.Subject + " -> " + cur.ToLongDateString());
}
catch
{
}
}
}
else
{
MessageBox.Show(item.Subject + " -> " + item.Start.ToLongDateString());
break;
}
}
((Microsoft.Office.Interop.Outlook._Application)outlookApp).Quit();
//outlookApp.Quit();
//(outlookApp as Microsoft.Office.Interop.Outlook._Application).Quit();
outlookApp = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(outlookCalendarItems);
System.Runtime.InteropServices.Marshal.ReleaseComObject(CalendarFolder);
System.Runtime.InteropServices.Marshal.ReleaseComObject(mapiNamespace);
//System.Runtime.InteropServices.Marshal.ReleaseComObject(outlookApp);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(outlookCalendarItems);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(CalendarFolder);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(mapiNamespace);
//System.Runtime.InteropServices.Marshal.FinalReleaseComObject(outlookApp);
GC.Collect();
GC.WaitForPendingFinalizers();
mapiNamespace = null;
CalendarFolder = null;
outlookCalendarItems = null;
}
Temporary hardcore solution:
public static class OutlookKiller
{
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern long GetWindowThreadProcessId(long hWnd, out long lpdwProcessId);
public static void Kill(ref Microsoft.Office.Interop.Outlook.Application app)
{
long processId = 0;
long appHwnd = (long)app.Hwnd;
GetWindowThreadProcessId(appHwnd, out processId);
Process prc = Process.GetProcessById((int)processId);
prc.Kill();
}
}
Have you debugged your code to see if the application ever gets to the OutlookApp.Quit() command?
The only other way around this would be to crudley kill the process "Outlook.exe".
This should work for any and App by referencing the App Name and it will kill all instances of the application.
For instance if you have 5 instances of Notepad, it will kill them all...
using System.Management;
using System.Diagnostics;
public static class KillProcessByPID
{
public static void KillProcessByName(string ProcessName)
{
string OutlookProcessName = "";
foreach (Process otlk in Process.GetProcesses())
{
if (otlk.ProcessName.ToLower().Contains(ProcessName.ToLower())) //OUTLOOK is the one I am seeking - yours may vary
{
OutlookProcessName = otlk.ProcessName;
}
}
//Get process ID by Name
var processes = Process.GetProcessesByName(OutlookProcessName);
foreach (var process in processes)
{
Console.WriteLine("PID={0}", process.Id);
Console.WriteLine("Process Handle={0}", process.Handle);
KillProcessAndChildren(process.Id);
}
}
/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
public static void KillProcessAndChildren(int pid)
{
//Process[] remoteByName = Process.GetProcessesByName("notepad", "myComputer");
// Cannot close 'system idle process'.
if (pid == 0)
{
return;
}
ManagementObjectSearcher searcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection moc = searcher.Get();
foreach (ManagementObject mo in moc)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
}
try
{
Process proc = Process.GetProcessById(pid);
proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}
}
I call it at the beginning and end of my app with the following one Killer line of code:
KillProcessByPID.KillProcessByName("OUTLOOK");
I borrowed from many and I'm not sure where, but I thank you all and give you all credit... apologies for not referencing you directly, but this was instrumental.

Categories