I have two windows application, one is a windows service which create EventWaitHandle and wait for it. Second application is a windows gui which open it by calling EventWaitHandle.OpenExisting() and try to Set the event. But I am getting an exception in OpenExisting. The Exception is "Access to the path is denied".
windows Service code
EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset, "MyEventName");
wh.WaitOne();
Windows GUI code
try
{
EventWaitHandle wh = EventWaitHandle.OpenExisting("MyEventName");
wh.Set();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
I tried the same code with two sample console application, it was working fine.
You need to use the version of the EventWaitHandle constructor that takes an EventWaitHandleSecurity instance. For example, the following code should work (it's not tested, but hopefully will get you started):
// create a rule that allows anybody in the "Users" group to synchronise with us
var users = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
var rule = new EventWaitHandleAccessRule(users, EventWaitHandleRights.Synchronize | EventWaitHandleRights.Modify,
AccessControlType.Allow);
var security = new EventWaitHandleSecurity();
security.AddAccessRule(rule);
bool created;
var wh = new EventWaitHandle(false, EventResetMode.AutoReset, "MyEventName", out created, security);
...
Also, if you're running on Vista or later, you need to create the event in the global namespace (that is, prefix the name with "Global\"). You'd also have to do this on Windows XP if you use the "Fast User Switching" feature.
This might be caused by the service process running at an elevated privilege level, but the GUI process is not. If you put the same code into two console apps, they'll both be running at user level and won't have any trouble accessing each other's named shared objects.
Try running the GUI app with the "Run as administrator" flag from the Windows start menu. If that solves the issue, you need to read up on how to request elevation within your code. (I haven't done that)
Related
Collaborators have built a prototype using Processing that connects to a Sparkfun RFID reader, I think using a serial connection over USB. We've deployed the prototype into a number of trialists' homes and one common usage scenario I foolishly overlooked was user switching. Hence I am writing a wrapper that ensures only one instance of the prototype application is running across all users on the machine.
I’m testing out my first stab at this as a simple console app. Here’s the code:
static void Main(string[] args)
{
// http://stackoverflow.com/a/2590446/575530
var users = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
var rule = new EventWaitHandleAccessRule(users, EventWaitHandleRights.FullControl, AccessControlType.Allow);
var security = new EventWaitHandleSecurity();
security.AddAccessRule(rule);
bool createdStartup;
using (var whStartup = new EventWaitHandle(false, EventResetMode.AutoReset, "Global/AaltoTokensAppStartupEvent", out createdStartup, security))
{
bool createdShutdown;
using (var whShutdown = new EventWaitHandle(false, EventResetMode.AutoReset, "Global/AaltoTokensAppShutdownEvent", out createdShutdown, security))
{
Console.WriteLine("Let other instances shutdown");
whStartup.Set();
Console.WriteLine("If other instances exist wait for them to shutdown");
if (!createdShutdown)
{
whShutdown.WaitOne();
}
whShutdown.Reset();
Console.WriteLine("Start tray app");
var tokenProc = Process.Start(#"C:\Temp\FAMILY3_WIN\TokensApp.exe");
Console.WriteLine(tokenProc.ProcessName);
Console.WriteLine("Wait to see if another instance to tries to start");
whStartup.Reset();
whStartup.WaitOne();
Console.WriteLine("Shutdown if another instance starts");
//if (tokenProc != null) tokenProc.Kill();
foreach (var process in Process.GetProcesses())
{
if (process.ProcessName.StartsWith("javaw"))
{
process.Kill();
}
}
whShutdown.Set();
}
}
Console.WriteLine("Done...");
Console.ReadLine();
}
(N.B. I know there are issues with this code around (1) killing Java processes that are not the running prototype and (2) there’s no code to respond to lots of instances starting simultaneously, just two at a time. But that’s not what my question is about.)
Testing this under a single user account works fine. I can start my app, it in turn starts the prototype, and if I start a second instance of my app the first one kills the initial instance of the prototype before the second one starts another instance of the prototype afresh.
But if I try doing this from two different user accounts it fails (silently). If I
Start an instance of my application it starts the prototype
Switch user
Start an instance of my application then it starts the
prototype without my app from step 1 first shutting down the existing
instance.
Can anyone see what’s wrong with my code? How should I use EventWaitHandle across several simultaneous user sessions on the same machine?
Isn't it always the way, minutes after writing a long question the answer leaps to mind!
I got the slash the wrong way around in the name of the EventWaitHandle. For example replacing the constructor call:
new EventWaitHandle(false, EventResetMode.AutoReset, "Global/AaltoTokensAppShutdownEvent", out createdShutdown, security)
with this one:
new EventWaitHandle(false, EventResetMode.AutoReset, #"Global\AaltoTokensAppShutdownEvent", out createdShutdown, security)
fixes my problem.
My scenerio is connection to remote machine with C#.Net, and listing all processes with that remote computer. I can kill a process, or start a new process at remote. The problem is, when I execute a new process on remote, I can see the process on task manager, but it doesnt apeear on windows screen. Any idea why its not appearing on windows, but appearing on task manager/ process. Here is my execution code
private void btnStartNew_Click(object sender, EventArgs e)
{
object[] arrParams = { txtNewProcess.Text.Trim()};
try
{
manageClass = new ManagementClass(myScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
manageClass.InvokeMethod("Create", arrParams);
btnConnect_Click(sender, e);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
My Scope is :
myScope = new ManagementScope(#"\ROOT\CIMV2", connOptions);
the problem is about administrator permissions
Shouldn't the ManagementPath be something like \ComputerName\Root\CIMV2 instead of just \ROOT\CIMV2 ?
If you have trouble with authentication, then you need to check the DCOM configuration on the target machine.
On the target machine, run dcomcnfg from the command prompt.
Expand Component Services\Computers\My Computer\DCOM Config
Find Windows Management Instruction, identified with GUID 8BC3F05E-D86B-11D0-A075-00C04FB68820 (you can see this in the details view).
Edit the properties and then add the username you are trying to login with under the permissions tab.
You may need to reboot the service for the changes to take effect.
i have been trying the code using mutex but im unable to open my exe after button click
im successful in not making the multiple entries of the application on the taskbar at button click but my application is launched only when i close my form..
i want to launch my application on button click and if the application is already launched then i need to focus on the previous running application..
how could i able to resolve my need to launch as well as focusin and reopening that application again..
im sending u my code that im using on button click event and plz modify my errors...
coding at program.cs
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
System.Diagnostics.Process.Start("filename.exe");
}
:
coding done at form1.cs
private void button1_Click(object sender, EventArgs e)
{
try
{
bool createdNew;
Mutex m = new Mutex(true, "e-Recording", out createdNew);
System.Diagnostics.ProcessStartInfo f = new System.Diagnostics.ProcessStartInfo("C:\\windows\\system32\\rundll32.exe", "C:\\windows\\system32\\shimgvw.dll,ImageView_Fullscreen " + "filename.exe".TrimEnd(null));
if (createdNew) Launch();
else
{
MessageBox.Show("e-Recording is already running!", "Multiple Instances");
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
This is be able to find and switch to an already running process that matches what you are trying to start.
[DllImport( "user32.dll" )]
public static extern bool ShowWindowAsync( HandleRef hWnd, int nCmdShow );
public const int SW_RESTORE = 9;
public void SwitchToCurrent() {
IntPtr hWnd = IntPtr.Zero;
Process process = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName( process.ProcessName );
foreach ( Process _process in processes ) {
// Get the first instance that is not this instance, has the
// same process name and was started from the same file name
// and location. Also check that the process has a valid
// window handle in this session to filter out other user's
// processes.
if ( _process.Id != process.Id &&
_process.MainModule.FileName == process.MainModule.FileName &&
_process.MainWindowHandle != IntPtr.Zero ) {
hWnd = _process.MainWindowHandle;
ShowWindowAsync( NativeMethods.HRef( hWnd ), SW_RESTORE );
break;
}
}
}
I posted an answer a while back to a question about Delphi. I explained that I didn't have a background in Delphi but I described at a high level what I did in C# to build a component that uses InterProcess Communication (IPC) with .NET remoting to not only activate a running instance, but also forward the command line parameters from the second instance into the first instance. I linked to a pretty simple to use component that wraps all this functionality up. It may be useful to you.
Hers's my answer from the other question:
The best way to do this is actually in
the the startup code of your exe. In
other words, let Explorer launch a
second copy of the exe which then
proceeds to detect that it is already
running and have it send a message to
the running instance.
Personally, I have practically no
experience with Delphi, but the way I
did this in a .NET application was
using a mutex and an interprocess
communication channel.
The general idea was that the first
instance of the application would
start, and begin listening on an IPC
channel. It would also create a named
interprocess mutex. When the second
instance launched, it would be unable
to create the mutex of the same name
which meant that a previous instance
was running and listening for calls on
the IPC channel. The second instance
then sent the command line arguments
to the first instance over IPC and the
first instance took action on them.
The second instance then exits without
showing any UI.
I've uploaded the code for this
component (C#) and the link is below.
I don't believe it has any external
dependencies and I don't know what the
equivalent communication mechanism in
Delphi would be - but hopefully this
gives you some ideas.
InstanceManager Component (C#)
Note that usage of named mutexes is discouraged for security reasons. Any process (even one running under guest account) can create a mutex with the same name before your process was started. Solving these security problems is usually harder than just not using named mutex at all.
To solve your problem, you just need to store process handler or process ID and then look for a window with that process ID. This is similar to the way task manager works.
I want to kill a process programmatically in vista/windows 7 (I'm not sure if there's significant problems in the implementation of the UAC between the two to make a difference).
Right now, my code looks like:
if(killProcess){
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
myProcess = System.Diagnostics.Process.Start(psi);
}
I have to do this because I need to make sure that if the user crashes the program or exits abruptly, this secondary process is restarted when the application is restarted, or if the user wants to change the parameters for this secondary process.
The code works fine in XP, but fails in Windows 7 (and I assume in Vista) with an 'access is denied' message. From what the Almighty Google has told me, I need to run my killing program as administrator to get around this problem, but that's just weak sauce. The other potential answer is to use LinkDemand, but I don't understand the msdn page for LinkDemand as it pertains to processes.
I could move the code into a thread, but that has a whole host of other difficulties inherent to it that I really don't want to discover.
You are correct in that it's because you don't have administrative priveleges. You can solve this by installing a service under the local system user and running a custom command against it as needed.
In your windows form app:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "SERVICE_NAME");
scp.Assert();
System.ServiceProcess.ServiceController serviceCon = new System.ServiceProcess.ServiceController("SERVICE_NAME", Environment.MachineName);
serviceCon.ExecuteCommand((int)SimpleServiceCustomCommands.KillProcess);
myProcess = System.Diagnostics.Process.Start(psi);
In your service:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
protected override void OnCustomCommand(int command)
{
switch (command)
{
case (int)SimpleServiceCustomCommands.KillProcess:
if(killProcess)
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
}
break;
default:
break;
}
}
I'll add the code for Simon Buchan's suggestion. It makes sense and should work as well, assuming your windows form is what launched the process in the first place.
Here's where you create the process. Notice the variable myProc. That's your handle on it:
System.Diagnostics.Process myProc = new System.Diagnostics.Process();
myProc.EnableRaisingEvents=false;
myProc.StartInfo.FileName="PATH_TO_EXE";
myProc.Start();
Later, just kill it with:
myProc.Kill();
I am using a global named mutex for file access synchronization between an ASP.NET application and a console application.
While running the ASP.NET application, the console application fails to acquire mutex - as expected. While running the console application, the ASP.NET application throws UnauthorizedAccessException: Access to the path 'Global\TheNameOfTheMutex' is denied.
I will try to catch the exception and treat it like it failed to acquire the mutex, but I want to know why is it behaving like this? The ASP.NET application runs as expected if it is accessed from two different browsers and the console applications also runs as expected when running multiple instances.
Update: on Windows XP the exception is also thrown when the ASP.NET application is running and I try to start the console application.
The code used for synchronization is in a common assembly:
using (Mutex m = new Mutex(false, "Global\\TheNameOfTheMutex")) // exception thrown
{
try
{
lock = m.WaitOne(0, false);
}
catch (AbandonedMutexException)
{
// ...
}
if(lock)
{
// ...
m.ReleaseMutex();
}
}
Environment: Windows Server 2008, IIS 7, ASP.NET 2.0
do you have the right user set up to access to the resources? using
MutexSecurity and MutexAccessRule ?
try looking at this on MSDN http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity.aspx
and http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexaccessrule.aspx
p.s. I am awaiting a Jon Skeet answer to show my ignorance in the matter...=>
Here the sample from How to determine if a previous instance of my application is running? (see the romkyns' answer)
var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
var mutexsecurity = new MutexSecurity();
mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow));
mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny));
mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny));
_mutex = new Mutex(false, "Global\\YourAppName-{add-your-random-chars}", out created, mutexsecurity);