Hi Friends,
I have a windows application where i controls single instance.
When this application updates using clickonce, application will restart after applying updates.
The application restarts but fails to continue as, IsFirstInstance = false condition.
But Application.Restart() documentation says it shutdowns running app and creates new instance.
Single instance class is given below:
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Threading;
using agent_common_plugin_sdk;
/// <summary>
/// Application Instance Manager
/// </summary>
public static class ApplicationInstanceManager
{
/// <summary>
/// Creates the single instance.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="callback">The callback.</param>
/// <returns></returns>
public static bool CreateSingleInstance(string name, EventHandler<InstanceCallbackEventArgs> callback)
{
EventWaitHandle eventWaitHandle = null;
string eventName = string.Format("{0}-{1}", Environment.MachineName, name);
InstanceProxy.IsFirstInstance = false;
InstanceProxy.CommandLineArgs = Environment.GetCommandLineArgs();
try
{
// Try opening existing wait handle
eventWaitHandle = EventWaitHandle.OpenExisting(eventName);
}
catch(Exception ex)
{
// Got exception = handle wasn't created yet
InstanceProxy.IsFirstInstance = true;
}
if (InstanceProxy.IsFirstInstance)
{
// init handle
eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
// register wait handle for this instance (process)
ThreadPool.RegisterWaitForSingleObject(eventWaitHandle, WaitOrTimerCallback, callback, Timeout.Infinite, false);
eventWaitHandle.Close();
// register shared type (used to pass data between processes)
RegisterRemoteType(name);
}
else
{
// pass console arguments to shared object
UpdateRemoteObject(name);
// invoke (signal) wait handle on other process
if (eventWaitHandle != null) eventWaitHandle.Set();
// kill current process
Environment.Exit(0);
}
return InstanceProxy.IsFirstInstance;
}
/// <summary>
/// Updates the remote object.
/// </summary>
/// <param name="uri">The remote URI.</param>
private static void UpdateRemoteObject(string uri)
{
// register net-pipe channel
IpcClientChannel clientChannel = new IpcClientChannel();
ChannelServices.RegisterChannel(clientChannel, true);
// get shared object from other process
InstanceProxy proxy =
Activator.GetObject(typeof(InstanceProxy),
string.Format("ipc://{0}{1}/{1}", Environment.MachineName, uri)) as InstanceProxy;
// pass current command line args to proxy
if (proxy != null)
proxy.SetCommandLineArgs(InstanceProxy.IsFirstInstance, InstanceProxy.CommandLineArgs);
// close current client channel
ChannelServices.UnregisterChannel(clientChannel);
}
/// <summary>
/// Registers the remote type.
/// </summary>
/// <param name="uri">The URI.</param>
private static void RegisterRemoteType(string uri)
{
// register remote channel (net-pipes)
IpcServerChannel serverChannel = new IpcServerChannel(Environment.MachineName + uri);
ChannelServices.RegisterChannel(serverChannel, true);
// register shared type
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(InstanceProxy), uri, WellKnownObjectMode.Singleton);
// close channel, on process exit
Process process = Process.GetCurrentProcess();
process.Exited += delegate { ChannelServices.UnregisterChannel(serverChannel); };
}
/// <summary>
/// Wait Or Timer Callback Handler
/// </summary>
/// <param name="state">The state.</param>
/// <param name="timedOut">if set to <c>true</c> [timed out].</param>
private static void WaitOrTimerCallback(object state, bool timedOut)
{
// cast to event handler
EventHandler<InstanceCallbackEventArgs> callback = state as EventHandler<InstanceCallbackEventArgs>;
if (callback == null) return;
// invoke event handler on other process
callback(state,
new InstanceCallbackEventArgs(InstanceProxy.IsFirstInstance,
InstanceProxy.CommandLineArgs));
}
}
}
here:
try
{
// Try opening existing wait handle
eventWaitHandle = EventWaitHandle.OpenExisting(eventName);
}
catch(Exception ex)
{
// Got exception = handle wasn't created yet
InstanceProxy.IsFirstInstance = true;
}
no exception is being thrown. Hence as you can see in the code, it will exit from Environment.Exit(0).
Related
I am trying to create a game in Unity3D which connects to bluetooth low energy device that supports the heart rate service and collects the HR data. I have created a WPF library that has been tested on a console application to connect to a BLE device and read the data which works perfectly. However, I do not know how to use this in Unity.
I transferred the library to Unity and had to disable it for the editor as the code uses Windows namespaces which are not supported in the editor. My problem now is how do I debug the code in Unity to check if the library code is working when I run the game. I tried wrapping the code for calling the library namespace and the functions from the library in #if NETFX_CORE, #if ENABLE_WINMD_SUPPORT, #if WINDOWS_UWP and many more but, never got any of the debug logs that I wrote.
Is there any possible solution to this?
Any help would be appreciated, thank you!
This is the code for the bluetooth LE connection library:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Storage.Streams;
namespace BLEHR
{
/// <summary>
/// Wraps and makes use if the <see cref="BluetoothLeAdvertisementWatcher"/>
/// for easier consumption
///
/// </summary>
public class BLEAdvertisementWatcher
{
#region Private Members
/// <summary>
/// The underlying bluetooth watcher class
/// </summary>
private readonly BluetoothLEAdvertisementWatcher mWatcher;
/// <summary>
/// a list of discovered devices
/// </summary>
private readonly Dictionary<string, BLEDevice> mDiscoveredDevices = new Dictionary<string, BLEDevice>();
/// <summary>
/// The details about Gatt services
/// </summary>
private readonly GattServiceIDs mGattServiceIds;
/// <summary>
/// a thread lock object for this class
/// </summary>
private readonly object mThreadLock = new object();
#endregion
#region Public Properties
/// <summary>
/// indicates is this watcher is listening for advertisements
/// </summary>
public bool Listening => mWatcher.Status == BluetoothLEAdvertisementWatcherStatus.Started;
/// <summary>
/// a list of discovered devices
/// </summary>
public IReadOnlyCollection<BLEDevice> DiscoveredDevices
{
get
{
//Clena up any Timeouts
CleanupTimeouts();
//Practice Thread safety
lock (mThreadLock)
{
//Convert to readonly list
return mDiscoveredDevices.Values.ToList().AsReadOnly();
}
}
}
/// <summary>
/// The timeout in seconds that a device is removed from the <see cref="DiscoveredDevices"/>
/// list if it is not re-advertised within this time
/// </summary>
public int TimeoutRemoval { get; set; } = 20;
public int HRValue { get; set; }
#endregion
#region Constructor
/// <summary>
/// The default constructor
/// </summary>
public BLEAdvertisementWatcher(GattServiceIDs gattIds)
{
//Null guard
mGattServiceIds = gattIds ?? throw new ArgumentNullException(nameof(gattIds));
//Create bluetooth listener
mWatcher = new BluetoothLEAdvertisementWatcher
{
ScanningMode = BluetoothLEScanningMode.Active
};
//Listen out for new advertisements
mWatcher.Received += WatcherAdvertisementReceivedAsync;
//Listen out for when the watcher stops listening
mWatcher.Stopped += (watcher, e) =>
{
//Inform listeners
StoppedListening();
};
}
#endregion
#region Private Methods
/// <summary>
/// Listens out for watcher advertisements
/// </summary>
/// <param name="sender"> The Watcher </param>
/// <param name="args">The Arguments </param>
private async void WatcherAdvertisementReceivedAsync(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
//cleanup timeouts
CleanupTimeouts();
//Get BLE device info
var device = await GetBLEDeviceAsync(args.BluetoothAddress, args.Timestamp, args.RawSignalStrengthInDBm);
//Null guard
if(device == null)
{
return;
}
//is new discovery?
var newDiscovery = false;
var existingName = default(string);
//Lock your doors
lock (mThreadLock)
{
//Check if this is a new discovery
newDiscovery= !mDiscoveredDevices.ContainsKey(device.DeviceID);
//If this is not new...
if (!newDiscovery)
{
//Store the old name
existingName = mDiscoveredDevices[device.DeviceID].Name;
}
}
//Name changed?
var nameChanged =
//if it already exists
!newDiscovery &&
//And is not a blank name
!string.IsNullOrEmpty(device.Name) &&
//And the name is different
existingName != device.Name;
lock (mThreadLock)
{
//add/update the device in the dictionary
mDiscoveredDevices[device.DeviceID] = device;
}
//Inform listeners
DeviceDiscovered(device);
//if new discovery...
if (newDiscovery)
{
//Inform listeners
NewDeviceDiscovered(device);
}
}
/// <summary>
/// Connects to the BLE device and extracts more information from the
/// <seealso cref="https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice"/>
/// </summary>
/// <param name="address">The BT address of the device to connect to</param>
/// <param name="broadcastTime">The time the broadcast message was received</param>
/// <param name="rssi">The signal strength in dB</param>
/// <returns></returns>
private async Task<BLEDevice> GetBLEDeviceAsync(ulong address, DateTimeOffset broadcastTime, short rssi)
{
//Get bluetooth device info
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(address).AsTask();
//Null guard
if(device == null)
{
return null;
}
//Get GATT services that are available
var gatt = await device.GetGattServicesAsync().AsTask();
//if we have any services..
if(gatt.Status == GattCommunicationStatus.Success)
{
//loop each gatt service
foreach(var service in gatt.Services)
{
//This ID contains the GATT profile assigned number we want!
//TODO: Get more info and connect
var gattProfileId = service.Uuid;
}
}
//Return the new device information
return new BLEDevice
(
//Device ID
deviceID: device.DeviceId,
//Bluetooth address
address: device.BluetoothAddress,
//Device name
name: device.Name,
//Broadcast time
broadcastTime: broadcastTime,
//Signal strength
rssi: rssi,
//Is connected
connected: device.ConnectionStatus== BluetoothConnectionStatus.Connected,
//Can Pair?
canPair: device.DeviceInformation.Pairing.CanPair,
//Is Paired?
isPaired: device.DeviceInformation.Pairing.IsPaired
);
}
/// <summary>
/// Prune any timed out devices that we have not heard off
/// </summary>
private void CleanupTimeouts()
{
lock (mThreadLock)
{
//The date in time that if less than means a device has timed out
var threshold = DateTime.UtcNow - TimeSpan.FromSeconds(TimeoutRemoval);
//any devices that have not sent a new broadcast within the time
mDiscoveredDevices.Where(f => f.Value.BroadcastTime < threshold).ToList().ForEach(device =>
{
//remove device
mDiscoveredDevices.Remove(device.Key);
//Inform listeners
DeviceTimeout(device.Value);
});
}
}
#endregion
#region Public events
/// <summary>
/// Fired when the bluetooth watcher stops listening
/// </summary>
public event Action StoppedListening = () => { };
/// <summary>
/// Fired when the bluetooth watcher start listening
/// </summary>
public event Action StartedListening = () => { };
/// <summary>
/// fired when a new device is discovered
/// </summary>
public event Action<BLEDevice> NewDeviceDiscovered = (device) => {};
/// <summary>
/// fired when a device is discovered
/// </summary>
public event Action<BLEDevice> DeviceDiscovered = (device) => { };
/// <summary>
/// Fired when a device is removed for timing out
/// </summary>
public event Action<BLEDevice> DeviceTimeout = (device) => { };
#endregion
#region Public Methods
/// <summary>
/// Starts listening for advertisements
/// </summary>
public void StartListening()
{
lock (mThreadLock)
{
//if already listening...
if (Listening)
{
//DO nothing more
return;
}
//Start the underlying watcher
mWatcher.Start();
}
//inform listeners
StartedListening();
}
/// <summary>
/// Stops listening for advertisements
/// </summary>
public void StopListening()
{
lock (mThreadLock)
{
//if we are not currently listening
if (!Listening)
{
//Do nothing more
return;
}
//Stop listening
mWatcher.Stop();
//clear any devices
mDiscoveredDevices.Clear();
}
}
/// <summary>
/// Attempts to pair to a BLE device, by ID
/// </summary>
/// <param name="deviceID"> The BLE device ID</param>
/// <returns></returns>
public async Task PairToDeviceAsync(string deviceID)
{
//Get bluetooth device info
var device = await BluetoothLEDevice.FromIdAsync(deviceID).AsTask();
//Null guard
if (device == null)
{
//TODO: localize
throw new ArgumentNullException("");
}
//if we are already paired...
if (device.DeviceInformation.Pairing.IsPaired)
{
//un-pair the device
await device.DeviceInformation.Pairing.UnpairAsync().AsTask();
return;
}
//Try and pair to the device
device.DeviceInformation.Pairing.Custom.PairingRequested += (sender, args) =>
{
//Accept all attempts
args.Accept(); // <-- could enter a pin in here to accept
};
var result = await device.DeviceInformation.Pairing.Custom.PairAsync(DevicePairingKinds.ConfirmOnly).AsTask();
//Get GATT services that are available
var gatt = await device.GetGattServicesAsync().AsTask();
GattDeviceService serviceReq = null;
GattCharacteristic characteristicReq = null;
//if we have any services..
if (gatt.Status == GattCommunicationStatus.Success)
{
//loop each gatt service
foreach (var service in gatt.Services)
{
if (service.Uuid == GattServiceUuids.HeartRate)
{
serviceReq = service;
}
//This ID contains the GATT profile assigned number we want!
//TODO: Get more info and connect
var gattProfileId = service.Uuid;
}
var charResults = await serviceReq.GetCharacteristicsAsync().AsTask();
if(charResults.Status == GattCommunicationStatus.Success)
{
foreach (var chars in charResults.Characteristics)
{
if(chars.Uuid == GattCharacteristicUuids.HeartRateMeasurement)
{
characteristicReq = chars;
}
}
GattCharacteristicProperties properties = characteristicReq.CharacteristicProperties;
if (properties.HasFlag(GattCharacteristicProperties.Read))
{
GattReadResult readVal = await characteristicReq.ReadValueAsync().AsTask();
if(readVal.Status == GattCommunicationStatus.Success)
{
var reader = DataReader.FromBuffer(readVal.Value);
byte[] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(input);
HRValue = BitConverter.ToInt32(input, 0);
}
}
}
}
////Log the result
//if(result.Status == DevicePairingResultStatus.Paired)
//{
// Console.WriteLine("Pairing successful");
//}
//else
//{
// Console.WriteLine($"Pairing failed: {result.Status}");
//}
}
#endregion
}
}
And here is the code I am trying in Unity:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if NETFX_CORE
using BLEHR
#endif
public class HRTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
#if NETFX_CORE
var watcher = new BLEAdvertisementWatcher(new GattServiceIDs());
Debug.Log("Working?");
#endif
}
// Update is called once per frame
void Update()
{
#if WINDOWS_UWP
Debug.Log("Connecting");
#endif
}
}
Debugging with UWP/.Net-Core-only features in Unity seems like an infeasible workflow to me because of the long edit-compile-run cycles. The Unity Editor runs with .Net-Framework and thus can't directly access .Net-Core features. For non-Unity projects there is the possibility to access UWP-API from .Net-Framework via wrappers provided by Windows. But that doesn't work for Unity because of some Unity-specifics1.
BUT there is the possibility to wrap the UWP-calls in a C++ winrt dll and copy the dll to Unity. This way the UWP-API can be also used inside the Unity Editor (e.g. when pushing the play button). My use case also requires BLE and I uploaded my wrapper in this repo. You're free to use it as a starting point if you want.
One last thing: With .Net version 5 which is also announced for Unity, I guess this should become obsolete as version 5 is said to merge .Net-Framework and .Net-Core.
[1] Much information for this answer came from this blog-post by Mike Taulty.
When I run the following code :
static void Main(string[] args)
{
if (!System.Diagnostics.EventLog.SourceExists("eventSource"))
{
System.Diagnostics.EventLog.CreateEventSource("eventSource", "");
}
System.Diagnostics.EventLog eventLog = new System.Diagnostics.EventLog();
eventLog.Source = "eventSource";
eventLog.Log = "";
for (int i = 0; i < 10000; i++)
{
Thread.Sleep(100);
eventLog.WriteEntry("test", EventLogEntryType.Information);
//I also tried the static method, the program still leaks
//System.Diagnostics.EventLog.WriteEntry("eventSource", "test", EventLogEntryType.Information);
}
Console.ReadKey();
}
The memory usage starts around 1MB, but rise up very quickly and doesn't stop. Why ?
The code I'm using to install windows service with a designated event log:
[RunInstaller(true)]
public partial class SampleServiceInstaller : System.Configuration.Install.Installer
{
private string SampleServiceName = string.Empty;
private string SampleLogName = string.Empty;
public SampleServiceInstaller()
{
this.SampleServiceName = "SampleService";
this.SampleLogName = this.SampleServiceName + "Log";
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
serviceProcessInstaller.Password = null;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
ServiceInstaller serviceInstaller = new ServiceInstaller();
//This must be identical to the WindowsService.ServiceBase name
// set in the constructor of WindowsService.cs
serviceInstaller.ServiceName = this.SampleServiceName;
serviceInstaller.DisplayName = this.SampleServiceName;
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "Sample Windows Service";
// kill the default event log installer
serviceInstaller.Installers.Clear();
// Create Event Source and Event Log
// This recreates the log every time the service is reinstaled (old messages are lost)
if (EventLog.SourceExists(this.SampleServiceName)) EventLog.DeleteEventSource(this.SampleServiceName);
System.Diagnostics.EventLogInstaller logInstaller = new System.Diagnostics.EventLogInstaller();
logInstaller.Source = this.SampleServiceName; // use same as ServiceName
logInstaller.Log = this.SampleLogName; //can't be the same as service name
// Add all installers
this.Installers.AddRange(new Installer[] {
serviceProcessInstaller, serviceInstaller, logInstaller
});
}
public override void Install(System.Collections.IDictionary savedState)
{
base.Install(savedState);
}
protected override void OnBeforeUninstall(System.Collections.IDictionary savedState)
{
base.OnBeforeUninstall(savedState);
}
service code:
public partial class SampleService : ServiceBase
{
/// <summary>
/// Designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Event log for the service
/// </summary>
EventLog serviceLog;
/// <summary>
/// Public Constructor for WindowsService.
/// - Initialization code here.
/// </summary>
public SampleService()
{
InitializeComponent();
}
/// <summary>
/// The Main Thread: list of services to run.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new SampleService() };
ServiceBase.Run(ServicesToRun);
}
/// <summary>
/// Startup code
/// </summary>
/// <param name="args"></param>
protected override void OnStart(string[] args)
{
base.OnStart(args);
//
// run your own start code here
//
serviceLog.WriteEntry("My service started", EventLogEntryType.Information, 0);
}
/// <summary>
/// Stop code
/// </summary>
protected override void OnStop()
{
//
// run your own stop code here
//
serviceLog.WriteEntry("My service stopped", EventLogEntryType.Information, 0);
base.OnStop();
}
/// <summary>
/// Cleanup code
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
//
// do disposing here
//
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// Pause code
/// </summary>
protected override void OnPause()
{
base.OnPause();
//
// code to run if service pauses
//
}
/// <summary>
/// Continue code
/// </summary>
protected override void OnContinue()
{
base.OnContinue();
//
// code tu run when service continues after being paused
//
}
/// <summary>
/// Called when the System is shutting down
/// - when special handling
/// of code that deals with a system shutdown, such
/// as saving special data before shutdown is needed.
/// </summary>
protected override void OnShutdown()
{
//
// code tu run when system is shut down
//
base.OnShutdown();
}
/// <summary>
/// If sending a command to the service is needed
/// without the need for Remoting or Sockets,
/// this method is used to do custom methods.
/// int command = 128; //Some Arbitrary number between 128 & 256
/// ServiceController sc = new ServiceController("NameOfService");
/// sc.ExecuteCommand(command);
/// </summary>
/// <param name="command">Arbitrary Integer between 128 & 256</param>
protected override void OnCustomCommand(int command)
{
base.OnCustomCommand(command);
//
// handle custom code here
//
}
/// <summary>
/// Useful for detecting power status changes,
/// such as going into Suspend mode or Low Battery for laptops.
/// </summary>
/// <param name="powerStatus">The Power Broadcast Status
/// (BatteryLow, Suspend, etc.)</param>
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
{
//
// handle power events here
//
return base.OnPowerEvent(powerStatus);
}
/// <summary>
/// To handle a change event
/// from a Terminal Server session.
/// Useful if determining
/// when a user logs in remotely or logs off,
/// or when someone logs into the console is needed.
/// </summary>
/// <param name="changeDescription">The Session Change
/// Event that occured.</param>
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
//
// handle session change here
//
base.OnSessionChange(changeDescription);
}
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
// first 8 letters should be unique
this.ServiceName = "SampleService";
// if you want to log service event to log registered while installing the service
string newLogName = this.ServiceName + "Log";
string newSourceName = this.ServiceName;
if (!EventLog.SourceExists(newSourceName))
{
EventLog.CreateEventSource(newSourceName, newLogName);
}
serviceLog = new EventLog();
serviceLog.Source = newSourceName;
serviceLog.Log = newLogName;
// Causes log to be disposed when the service is disposed
components.Add(serviceLog);
// Flags set whether or not to handle that specific type of event.
this.CanHandlePowerEvent = true;
this.CanHandleSessionChangeEvent = true;
this.CanPauseAndContinue = true;
this.CanShutdown = true;
this.CanStop = true;
}
}
I have an application that uses a mutex to stop multiple instances running at the same time, and to accept command line inputs in to the running instance.
I have a function in the app that asks the user if they want to restart as admin when required. For example the features they have enabled might require admin rights.
The mutex class looks like this:
namespace SingleInstanceClassLibrary
{
/// <summary>
/// Enforces single instance for an application.
/// </summary>
public class SingleInstance : IDisposable
{
private Mutex mutex = null;
private Boolean ownsMutex = false;
private Guid identifier = Guid.Empty;
/// <summary>
/// Enforces single instance for an application.
/// </summary>
/// <param name="identifier">An identifier unique to this application. </param>
public SingleInstance(Guid identifier)
{
this.identifier = identifier;
mutex = new Mutex(true, identifier.ToString(), out ownsMutex);
}
/// <summary>
/// Indicates whether this is the first instance of this application.
/// </summary>
public Boolean IsFirstInstance
{ get { return ownsMutex; } }
/// <summary>
/// Passes the given arguments to the first running instance of the application.
/// </summary>
/// <param name="arguments">The arguments to pass.</param>
/// <returns>Return true if the operation succeded, false otherwise. </returns>
public Boolean PassArgumentsToFirstInstance(String[] arguments)
{
if (IsFirstInstance)
throw new InvalidOperationException("This is the first instance.");
try
{
using (NamedPipeClientStream client = new NamedPipeClientStream(identifier.ToString()))
using (StreamWriter writer = new StreamWriter(client))
{
client.Connect(200);
foreach (String argument in arguments)
writer.WriteLine(argument);
}
return true;
}
catch (TimeoutException)
{ } //Couldn't connect to server
catch (IOException)
{ } //Pipe was broken
return false;
}
/// <summary>
/// Listens for arguments being passed from successive instances of the applicaiton.
/// </summary>
public void ListenForArgumentsFromSuccessiveInstances()
{
if (!IsFirstInstance)
throw new InvalidOperationException("This is not the first instance.");
ThreadPool.QueueUserWorkItem(new WaitCallback(ListenForArguments));
}
/// <summary>
/// Listens for arguments on a named pipe.
/// </summary>
/// <param name="state">State object required by WaitCallback delegate.</param>
private void ListenForArguments(Object state)
{
try
{
using (NamedPipeServerStream server = new NamedPipeServerStream(identifier.ToString()))
using (StreamReader reader = new StreamReader(server))
{
server.WaitForConnection();
List<String> arguments = new List<String>();
while (server.IsConnected)
arguments.Add(reader.ReadLine());
ThreadPool.QueueUserWorkItem(new WaitCallback(CallOnArgumentsReceived), arguments.ToArray());
}
}
catch (IOException)
{ } //Pipe was broken
finally
{
ListenForArguments(null);
}
}
/// <summary>
/// Calls the OnArgumentsReceived method casting the state Object to String[].
/// </summary>
/// <param name="state">The arguments to pass.</param>
private void CallOnArgumentsReceived(Object state)
{
OnArgumentsReceived((String[])state);
}
/// <summary>
/// Event raised when arguments are received from successive instances.
/// </summary>
public event EventHandler<ArgumentsReceivedEventArgs> ArgumentsReceived;
/// <summary>
/// Fires the ArgumentsReceived event.
/// </summary>
/// <param name="arguments">The arguments to pass with the ArgumentsReceivedEventArgs.</param>
private void OnArgumentsReceived(String[] arguments)
{
if (ArgumentsReceived != null)
ArgumentsReceived(this, new ArgumentsReceivedEventArgs() { Args = arguments });
}
#region IDisposable
private Boolean disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (mutex != null && ownsMutex)
{
mutex.ReleaseMutex();
mutex = null;
}
disposed = true;
}
}
~SingleInstance()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
I call it like this:
private static void Main()
{
Guid guid = new Guid("{6EAE2E61-E7EE-42bf-8EBE-BAB890C5410F}");
//SingleInstance ensures only 1 instance of the app runs at one time. If another instance is started
//it will be closed. If the 2nd instance included arguments these will be passed to
//the singleInstance_ArgumentsReceived event for originally running process
using (SingleInstance singleInstance = new SingleInstance(guid))
{
//MessageBox.Show(Environment.GetCommandLineArgs().ToString());
//if (Environment.GetCommandLineArgs().Contains("RunAsAdmin"))
//MessageBox.Show("YES");
if (singleInstance.IsFirstInstance || Environment.GetCommandLineArgs().Contains("RunAsAdmin"))
{
singleInstance.ArgumentsReceived += singleInstance_ArgumentsReceived;
singleInstance.ListenForArgumentsFromSuccessiveInstances();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Show the system tray icon.
//using (ProcessIcon pi = new ProcessIcon())
using (ProcessIcon pi = new ProcessIcon())
{
//Use to pass instance of ProcessIcon to LyncPresenceSwitcher
lyncPresenceSwitcher.processIcon = pi;
//Pass Lync instance
pi.lync = lyncClientController;
pi.Display();
// Make sure the application runs!
Application.Run();
}
}
else
singleInstance.PassArgumentsToFirstInstance(Environment.GetCommandLineArgs());
}
}
//Process arguments past with app execution
private static void singleInstance_ArgumentsReceived(object sender, ArgumentsReceivedEventArgs e)
{
if (settingsBox == null)
return;
foreach (String arg in e.Args)
{
//if arguments include OpenSettings open SettingsBox
if (arg == "OpenSettings")
{
settingsBox.ShowDialog();
}
if (arg == "RunAsAdmin")
{
//singleInstance.Dispose();
}
}
}
The app checks whether settings require admin access and prompts user to restart as admin when required. To restart the app I run this:
if (!IsRunAsAdmin())
{
// Launch itself as administrator
ProcessStartInfo proc = new ProcessStartInfo();
proc.UseShellExecute = true;
proc.WorkingDirectory = Environment.CurrentDirectory;
proc.FileName = Application.ExecutablePath;
proc.Verb = "runas";
proc.Arguments = "RunAsAdmin";
try
{
Process.Start(proc);
System.Environment.Exit(2);
return true;
}
catch
{
// The user refused the elevation.
// Do nothing and return directly ...
return false;
}
The problem is a new instance starts but is closed because of the mutex. Then when I close the original instance I get this:
So I was thinking I could pass an argument to the running instance to tell it to allow a new one to spawn, then close the original. Trouble is I cant figure out how to make this work.
Any help would be very much appreciated :)
p.s. I am a C# novice
Thanks!
The solution is to simply destroy your mutex prior to restarting your application:
mutex.Dispose();
Overview of the problem:
I try to use a thread (while..loop) to listen a command from user. If user send a command, it will assign new value in the global variable which is in the class (LoopingWorkerThread).
I don't understand if I don't put the thread sleep value lower than 10 milliseconds, and I wouldn't get any response (it is in the ListenCommand method) . Look like the global parameter is being overwritten "_CommandReceived" in the method, probably the processor run to fast and ignore the value of the parameter had changed ("_CommandReceived").
Kindly comment if there is any better mechanism. I had lock it in the ListenCommand while loop.
The following are the codes:
public class LoopingWorkerThread
{
/// <summary>
/// Local main thread for LoopingWorkerThread
/// </summary>
private Thread t;
/// <summary>
/// Local parameter to identify the LoopingWorkerThread Is On
/// </summary>
private bool _IsOn;
/// <summary>
/// Local parameter to store command received from user
/// </summary>
private int _CommandReceived;
/// <summary>
/// Local object to use for locking the LoopingWorker Thread
/// </summary>
private object _LockListenCommand = new object();
/// <summary>
/// Properties of LoopingWorker Thread Is On
/// </summary>
public bool IsOn
{
get { return _IsOn; }
set { _IsOn = value; }
}
/// <summary>
/// Property of storing the command received from user
/// </summary>
public int CommandReceived
{
get { return _CommandReceived; }
set { _CommandReceived = value; }
}
/// <summary>
/// Delegate for OnResponse Event Handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void OnResponseHandler(object sender, ResponseArg e);
/// <summary>
/// Event of OnResponse
/// </summary>
public event OnResponseHandler OnResponse;
/// <summary>
/// Constructor of LoopingWorkerThread Class
/// </summary>
public LoopingWorkerThread()
{
_IsOn = false;
}
/// <summary>
/// Method of LoopingWorkerThread Function
/// </summary>
private void ListenCommand()
{
lock (_LockListenCommand)
while (_IsOn)
{
switch (_CommandReceived)
{
case 0:
// Ignore default command
break;
case 1:
FireOnResponse("Received cmd 1, response [Hello One]");
break;
case 2:
FireOnResponse("Received cmd 2, response [Hello Two]");
break;
default:
FireOnResponse("Error. Received unidentified command - " + _CommandReceived.ToString());
break;
}
//Console.WriteLine("ThreadProc: Cmd:[{0}] - Response:{1}", _CommandReceived.ToString(), ReaderResponse);
// Reset or Clear the Command Received
_CommandReceived = 0;
// If the sleep less than 10 millisecond, it always don't catch the
// command received which assigned to 1 or 2. Don't understand, or is there
// any better method.
**Thread.Sleep(10);**
}
}
/// <summary>
/// Function of firing response event back to user
/// </summary>
/// <param name="message"></param>
private void FireOnResponse(string message)
{
ResponseArg myarg = new ResponseArg(message);
if (OnResponse != null)
OnResponse(this, myarg);
}
/// <summary>
/// Method of starting the LoopingWorkerThread
/// </summary>
public void Start()
{
_IsOn = true;
FireOnResponse("Main thread: Started.");
// The constructor for the Thread class requires a ThreadStart
// delegate that represents the method to be executed on the
// thread. C# simplifies the creation of this delegate.
t = new Thread(new ThreadStart(ListenCommand));
// Start ThreadProc. Note that on a uniprocessor, the new
// thread does not get any processor time until the main thread
// is preempted or yields. Uncomment the Thread.Sleep that
// follows t.Start() to see the difference.
t.Start();
//Thread.Sleep(0);
FireOnResponse("Main thread: Call Start().");
}
/// <summary>
/// Method of stopping the LoopingWorkerThread
/// </summary>
public void Stop()
{
_IsOn = false;
t.Join();
//t.Abort();
FireOnResponse("LoopingWorker Thread is stopped.");
}
/// <summary>
/// Method of sending command to the LoopingWorkerThread
/// </summary>
/// <param name="readercmd"></param>
public void SendCommand(int readercmd)
{
_CommandReceived = readercmd;
}
}
Your code works because Thread.Sleep produces the necessary memory barrier required to read _commandReceived correctly. If you remove the Thread.Sleep call then you also remove the implicit memory barrier. Obviously, this is not a good mechanism to rely on though.
More importantly you are going about this the wrong way. What you should be using is the producer-consumer pattern. This is pretty easy with the BlockingCollection class since it blocks the consumer on Take while the queue is empty.
public class Example
{
private BlockingCollection<int> commands = new BlockingCollection<int>();
public Example()
{
var thread = new Thread(Run);
thread.IsBackground = true;
thread.Start();
}
public void SendCommmand(int command)
{
commands.Add(command);
}
private void Run()
{
while (true)
{
int command = commands.Take();
ProcessCommand(command);
}
}
private void ProcessCommand(int command)
{
// Process the command here.
}
}
BlockingCollection is available for 3.5 as part of the Reactive Extensions download.
Try declaring the variable volatile. More about this on http://msdn.microsoft.com/en-us/library/x13ttww7.aspx
WatiN seems to not handle repeated download dialogs consistently:
foreach (string file in lstFiles)
{
// continue if no download link
if (!ie.Element([search criteria]).Exists) continue;
var btnDownload = ie.Element([search criteria]);
string fullFilename = workingDir + "\\" + file;
FileDownloadHandler download = new FileDownloadHandler(fullFilename);
using (new UseDialogOnce(ie.DialogWatcher, download))
{
btnDownload.ClickNoWait();
download.WaitUntilFileDownloadDialogIsHandled(30);
download.WaitUntilDownloadCompleted(150);
ie.RemoveDialogHandler(download);
}
}
Basically, I loop through a list of filenames that I expect to be available and click the download button. This usually works, but after so many downloads (it varies, sometimes everything that's available downloads, sometimes nothing) it will hang while waiting to handle the dialog. The button's identified correctly, the download dialog appears, it just isn't detected and handled. It isn't site-specific as similar methods on other sites are also met with variable success. Anyone encounter this before and know of a resolution?
edit: Repeated downloads do not work whatsoever in Server 2008. In Win7, this happens randomly after one or more successful repeated downloads.
The problem appears because IE File Download Dialog consist of 2 windows. WatiN DialogWatcher gets all the system windows and tries to handle them in foreach loop. After handling first correct dialog window DialogWatcher gets the next window wich has the same properties and is a valid Download Dialog. DialogWatcher starts waiting until this window is visible but it closes immediately after previos window is handled.
My solution is to return from foreach loop after any dialog is handled:
#region WatiN Copyright (C) 2006-2011 Jeroen van Menen
//Copyright 2006-2011 Jeroen van Menen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion Copyright
using System;
using System.Collections.Generic;
using System.Threading;
using WatiN.Core.Exceptions;
using WatiN.Core.Interfaces;
using WatiN.Core.Logging;
using WatiN.Core.Native.InternetExplorer;
using WatiN.Core.Native.Windows;
using WatiN.Core.UtilityClasses;
namespace WatiN.Core.DialogHandlers
{
/// <summary>
/// This class handles alert/popup dialogs. Every second it checks if a dialog
/// is shown. If so, it stores it's message in the alertQueue and closses the dialog
/// by clicking the close button in the title bar.
/// </summary>
public class DialogWatcher : IDisposable
{
private static IList<DialogWatcher> dialogWatchers = new List<DialogWatcher>();
private bool _keepRunning = true;
private readonly IList<IDialogHandler> _handlers;
private readonly Thread _watcherThread;
private bool _closeUnhandledDialogs = Settings.AutoCloseDialogs;
public Window MainWindow { get; private set; }
/// <summary>
/// Gets the dialog watcher for the specified (main) internet explorer window.
/// It creates new instance if no dialog watcher for the specified window exists.
/// </summary>
/// <param name="mainWindowHwnd">The (main) internet explorer window.</param>
/// <returns></returns>
public static DialogWatcher GetDialogWatcher(IntPtr mainWindowHwnd)
{
var window = new Window(mainWindowHwnd);
Logger.LogDebug("GetDialogWatcher mainhwnd: " + window.Hwnd + ", " + window.Title);
var toplevelWindow = window.ToplevelWindow;
Logger.LogDebug("GetDialogWatcher mainhwnd: " + toplevelWindow.Hwnd + ", " + toplevelWindow.Title);
CleanupDialogWatcherCache();
var dialogWatcher = GetDialogWatcherFromCache(toplevelWindow);
// If no dialogwatcher exists for the ieprocessid then
// create a new one, store it and return it.
if (dialogWatcher == null)
{
dialogWatcher = new DialogWatcher(toplevelWindow);
dialogWatchers.Add(dialogWatcher);
}
return dialogWatcher;
}
public static DialogWatcher GetDialogWatcherFromCache(Window mainWindow)
{
// Loop through already created dialogwatchers and
// return a dialogWatcher if one exists for the given processid
foreach (var dialogWatcher in dialogWatchers)
{
if (dialogWatcher.MainWindow.Equals(mainWindow))
{
return dialogWatcher;
}
}
return null;
}
public static void CleanupDialogWatcherCache()
{
var cleanedupDialogWatcherCache = new List<DialogWatcher>();
foreach (var dialogWatcher in dialogWatchers)
{
if (!dialogWatcher.IsRunning)
{
dialogWatcher.Dispose();
}
else
{
cleanedupDialogWatcherCache.Add(dialogWatcher);
}
}
dialogWatchers = cleanedupDialogWatcherCache;
}
/// <summary>
/// Initializes a new instance of the <see cref="DialogWatcher"/> class.
/// You are encouraged to use the Factory method <see cref="DialogWatcher.GetDialogWatcherFromCache"/>
/// instead.
/// </summary>
/// <param name="mainWindow">The main window handle of internet explorer.</param>
public DialogWatcher(Window mainWindow)
{
MainWindow = mainWindow;
_handlers = new List<IDialogHandler>();
// Create thread to watch windows
_watcherThread = new Thread(Start);
// Start the thread.
_watcherThread.Start();
}
/// <summary>
/// Increases the reference count of this DialogWatcher instance with 1.
/// </summary>
public void IncreaseReferenceCount()
{
ReferenceCount++;
}
/// <summary>
/// Decreases the reference count of this DialogWatcher instance with 1.
/// When reference count becomes zero, the Dispose method will be
/// automatically called. This method will throw an <see cref="ReferenceCountException"/>
/// if the reference count is zero.
/// </summary>
public void DecreaseReferenceCount()
{
if (ReferenceCount > 0)
{
ReferenceCount--;
}
else
{
throw new ReferenceCountException();
}
if (ReferenceCount == 0)
{
Dispose();
}
}
/// <summary>
/// Adds the specified handler.
/// </summary>
/// <param name="handler">The handler.</param>
public void Add(IDialogHandler handler)
{
lock (this)
{
_handlers.Add(handler);
}
}
/// <summary>
/// Removes the specified handler.
/// </summary>
/// <param name="handler">The handler.</param>
public void Remove(IDialogHandler handler)
{
lock (this)
{
_handlers.Remove(handler);
}
}
/// <summary>
/// Removes all instances that match <paramref name="handler"/>.
/// This method determines equality by calling Object.Equals.
/// </summary>
/// <param name="handler">The object implementing IDialogHandler.</param>
/// <example>
/// If you want to use RemoveAll with your custom dialog handler to
/// remove all instances of your dialog handler from a DialogWatcher instance,
/// you should override the Equals method in your custom dialog handler class
/// like this:
/// <code>
/// public override bool Equals(object obj)
/// {
/// if (obj == null) return false;
///
/// return (obj is YourDialogHandlerClassNameGoesHere);
/// }
/// </code>
/// You could also inherit from <see cref="BaseDialogHandler"/> instead of implementing
/// <see cref="IDialogHandler"/> in your custom dialog handler. <see cref="BaseDialogHandler"/> provides
/// overrides for Equals and GetHashCode that work with RemoveAll.
/// </example>
public void RemoveAll(IDialogHandler handler)
{
while (Contains(handler))
{
Remove(handler);
}
}
/// <summary>
/// Removes all registered dialog handlers.
/// </summary>
public void Clear()
{
lock (this)
{
_handlers.Clear();
}
}
/// <summary>
/// Determines whether this <see cref="DialogWatcher"/> contains the specified dialog handler.
/// </summary>
/// <param name="handler">The dialog handler.</param>
/// <returns>
/// <c>true</c> if [contains] [the specified handler]; otherwise, <c>false</c>.
/// </returns>
public bool Contains(IDialogHandler handler)
{
lock (this)
{
return _handlers.Contains(handler);
}
}
/// <summary>
/// Gets the count of registered dialog handlers.
/// </summary>
/// <value>The count.</value>
public int Count
{
get
{
lock (this)
{
return _handlers.Count;
}
}
}
/// <summary>
/// Gets or sets a value indicating whether unhandled dialogs should be closed automaticaly.
/// The initial value is set to the value of <cref name="Settings.AutoCloseDialogs" />.
/// </summary>
/// <value>
/// <c>true</c> if unhandled dialogs should be closed automaticaly; otherwise, <c>false</c>.
/// </value>
public bool CloseUnhandledDialogs
{
get
{
lock (this)
{
return _closeUnhandledDialogs;
}
}
set
{
lock (this)
{
_closeUnhandledDialogs = value;
}
}
}
/// <summary>
/// Gets the (main) internet explorer window handle this dialog watcher watches.
/// </summary>
/// <value>The process id.</value>
public IntPtr MainWindowHwnd
{
get { return MainWindow.Hwnd; }
}
/// <summary>
/// Called by the constructor to start watching popups
/// on a separate thread.
/// </summary>
private void Start()
{
while (_keepRunning)
{
if (MainWindow.Exists())
{
var winEnumerator = new WindowsEnumerator();
var windows = winEnumerator.GetWindows(win => true);
foreach (var window in windows)
{
if (!_keepRunning) return;
if(HandleWindow(window))
break;
}
// Keep DialogWatcher responsive during 1 second sleep period
var count = 0;
while (_keepRunning && count < 5)
{
Thread.Sleep(200);
count++;
}
}
else
{
_keepRunning = false;
}
}
}
public bool IsRunning
{
get { return _watcherThread.IsAlive; }
}
public int ReferenceCount { get; private set; }
/// <summary>
/// Get the last stored exception thrown by a dialog handler while
/// calling the <see cref="IDialogHandler.HandleDialog"/> method of the
/// dialog handler.
/// </summary>
/// <value>The last exception.</value>
public Exception LastException { get; private set; }
/// <summary>
/// If the window is a dialog and visible, it will be passed to
/// the registered dialog handlers. I none if these can handle
/// it, it will be closed if <see cref="CloseUnhandledDialogs"/>
/// is <c>true</c>.
/// </summary>
/// <param name="window">The window.</param>
/// <returns>
/// <c>true</c> if dialog is handled by one of handlers; otherwise, <c>false</c>.
/// </returns>
public bool HandleWindow(Window window)
{
if (!window.IsDialog()) return false;
if (!HasDialogSameProcessNameAsBrowserWindow(window)) return false;
// This is needed otherwise the window Style will return a "wrong" result.
WaitUntilVisibleOrTimeOut(window);
// Lock the thread and see if a handler will handle
// this dialog window
lock (this)
{
foreach (var dialogHandler in _handlers)
{
try
{
if (dialogHandler.CanHandleDialog(window, MainWindow.Hwnd))
{
if (dialogHandler.HandleDialog(window)) return true;
}
}
catch (Exception e)
{
LastException = e;
Logger.LogAction((LogFunction log) => { log("Exception was thrown while DialogWatcher called HandleDialog: {0}",e.ToString()); });
}
}
// If no handler handled the dialog, see if the dialog
// should be closed automatically.
if (!CloseUnhandledDialogs || !MainWindow.Equals(window.ToplevelWindow)) return false;
Logger.LogAction((LogFunction log) => { log("Auto closing dialog with title: '{0}', text: {1}, style: ", window.Title, window.Message, window.StyleInHex); });
window.ForceClose();
}
return false;
}
private bool HasDialogSameProcessNameAsBrowserWindow(Window window)
{
var comparer = new Comparers.StringComparer(window.ProcessName, true);
return comparer.Compare(MainWindow.ProcessName);
}
private static void WaitUntilVisibleOrTimeOut(Window window)
{
// Wait untill window is visible so all properties
// of the window class (like Style and StyleInHex)
// will return valid values.
var tryActionUntilTimeOut = new TryFuncUntilTimeOut(TimeSpan.FromSeconds(Settings.WaitForCompleteTimeOut));
var success = tryActionUntilTimeOut.Try(() => window.Visible);
if (!success)
{
Logger.LogAction((LogFunction log) => { log("Dialog with title '{0}' not visible after {1} seconds.", window.Title, Settings.WaitForCompleteTimeOut); });
}
}
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or
/// resetting unmanaged resources.
/// </summary>
public void Dispose()
{
lock (this)
{
_keepRunning = false;
}
if (IsRunning)
{
_watcherThread.Join();
}
Clear();
}
#endregion
}
}
My team ran into this as well while automating IE8 with WatiN. The problem seems to be with IE, possibly doing some time consuming house-cleaning. The work-around we ultimately used was to invoke a new instance of IE within the outer loop, disposing of it each iteration and then waiting for 5 seconds for whatever was going on in the background to resolve. It was a hack but got the job done.
foreach (var file in lstFiles)
{
string fullFilename = workingDir + "\\" + file;
using (var browser = new IE(fullFilename))
{
//page manipulations...
FileDownloadHandler download = new FileDownloadHandler(fullFilename);
using (new UseDialogOnce(browser.DialogWatcher, download))
{ //lnkFile.ClickNoWait();
download.WaitUntilFileDownloadDialogIsHandled(15);
download.WaitUntilDownloadCompleted(150);
}
}
Thread.Sleep(5000);
}