C# Mutex how to stop my console app running multiple times - c#

I have a console program, not that complicated but at the same time's not a hello world one. I have just two projects, first one has several classes. It is not multithreaded, and is all about calling restful APIs etc.
The thing is this: I am trying to make my application check if it runs twice (it is very important). I thought that it would be somehow feasible, but turns out to be extremely complicated.
Unfortunately, the only example I found, doesn't shed any light on how to use this technology. Below the code, which, is quite complicated for me, and I don't know how to plug it in my Main. Hence, the question.
Below the code (is not mine, I found it here: https://www.codeproject.com/Articles/3014/Single-Process-Instance-Object)
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;
namespace SpecialServices
{
//SingleProgamInstance uses a mutex synchronization
//object to ensure that only one copy of process is running
//at a particular time. It also allows for UI identification
// of the intial process by bringing that window to the foreground.
public class SingleProgramInstance : IDisposable
{
//Win32 API calls necesary to raise an unowned processs main window
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
private const int SW_RESTORE = 9;
//private members
private Mutex _processSync;
private bool _owned = false;
public SingleProgramInstance()
{
//Initialize a named mutex and attempt to
// get ownership immediately
_processSync = new Mutex(
true, // desire intial ownership
Assembly.GetExecutingAssembly().GetName().Name,
out _owned);
}
public SingleProgramInstance(string identifier)
{
//Initialize a named mutex and attempt to
// get ownership immediately.
//Use an addtional identifier to lower
// our chances of another process creating
// a mutex with the same name.
_processSync = new Mutex(
true, // desire intial ownership
Assembly.GetExecutingAssembly().GetName().Name + identifier,
out _owned);
}
~SingleProgramInstance()
{
//Release mutex (if necessary)
//This should have been accomplished using Dispose()
Release();
}
public bool IsSingleInstance
{
//If we don't own the mutex than
// we are not the first instance.
get {return _owned;}
}
public void RaiseOtherProcess()
{
Process proc = Process.GetCurrentProcess();
// Using Process.ProcessName does not function properly when
// the actual name exceeds 15 characters. Using the assembly
// name takes care of this quirk and is more accruate than
// other work arounds.
string assemblyName =
Assembly.GetExecutingAssembly().GetName().Name;
foreach (Process otherProc in
Process.GetProcessesByName(assemblyName))
{
//ignore "this" process
if (proc.Id != otherProc.Id)
{
// Found a "same named process".
// Assume it is the one we want brought to the foreground.
// Use the Win32 API to bring it to the foreground.
IntPtr hWnd = otherProc.MainWindowHandle;
if (IsIconic(hWnd))
{
ShowWindowAsync(hWnd,SW_RESTORE);
}
SetForegroundWindow(hWnd);
break;
}
}
}
private void Release()
{
if (_owned)
{
//If we own the mutex than release it so that
// other "same" processes can now start.
_processSync.ReleaseMutex();
_owned = false;
}
}
#region Implementation of IDisposable
public void Dispose()
{
//release mutex (if necessary) and notify
// the garbage collector to ignore the destructor
Release();
GC.SuppressFinalize(this);
}
#endregion
}
}
Any help using the above would be GREATLY appreciated; thank you so much.

how to use the sample code.
take the sample code (its one file) and add it to your project (as a separate .cs file)
now at the startup of your program main add
using(var spi = new SpecialServices.SingleProgramInstance("x5k6yz"))
{
if (!spi.IsSingleInstance){
Console.WriteLine("another copy is running");
return;
}
}
caveat, I have not tried the code from the sample, I assume it works.
EDIT. Ok tested, it works fine

Try this:
private void CloseDuplicateApplications()
{
string ProgramTitle = System.Diagnostics.Process.GetCurrentProcess().MainWindowTitle;
System.Diagnostics.Process[] Processes = System.Diagnostics.Process.GetProcesses();
for (int i = 0; i < Processes.Length; i++)
{
if (Processes[i].MainWindowTitle == ProgramTitle)
{
Processes[i].CloseMainWindow();
}
}
}
Currently the procedure simply closes any duplicate programs, however if you need to kill the duplicate program, replace this line:
Processes[i].CloseMainWindow();
With:
Processes[i].Kill();

Related

Can I programmatically attach a running .NET 6 process to a running instance of Visual Studio debugger?

I'm debugging an IPC logic where one .NET 6 processes is launching another, and I can't get to debug both within the same Visual Studio 2022 instance.
When the child process hits DebugBreak(true) as below:
[Conditional("ATTACH_DEBUGGER")]
public static void DebugBreak(bool condition)
{
if (condition)
{
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
System.Diagnostics.Debugger.Break();
}
}
}
I get prompted to launch a new instance of VS2022 to debug it:
However, I want to debug it within the existing VS instance, were I'm already debugging the parent process.
Is that possible at all? Is there a VS config setting I might be missing?
Current workaround: attaching manually, while putting the child process in the waiting state using WaitForDebugger() below:
[Conditional("ATTACH_DEBUGGER")]
public static void WaitForDebugger()
{
using var process = Process.GetCurrentProcess();
Console.WriteLine($"Waiting for debugger, process: {process.ProcessName}, id: {process.Id}, Assembly: {Assembly.GetExecutingAssembly().Location}");
while (!System.Diagnostics.Debugger.IsAttached)
{
// hit Esc to continue without debugger
if (Console.KeyAvailable && Console.ReadKey(intercept: true).KeyChar == 27)
{
break;
}
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.Beep(frequency: 1000, duration: 50);
}
}
I have a very similar setup where the following steps achieve this. However, this is with .NET Framework so I cannot guarantee it will work with .NET Core. (UPDATE: see below for .NET Core approach; see end for link to complete code samples on GitHub.)
First, identify a point in your first process (say, FirstProcess) where you want to attach the debugger to the second process (SecondProcess). Obviously SecondProcess will need to be running by this point (as SecondProcess.exe, say).
Open Solution Explorer and navigate to References for the relevant project in FirstProcess. Right-click, search for "env" and add two references, EnvDTE (v.8.0.0.0) and EnvDTE80 (v.8.0.0.0).
Add the following methods to the class in FirstProcess from where you want to attach:
private static void Attach(DTE2 dte)
{
var processName = "SecondProcess.exe";
var processes = dte.Debugger.LocalProcesses;
// Note: Depending on your setup, consider whether an exact match is required instead of using .IndexOf()
foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf(processName) != -1))
{
proc.Attach();
}
}
private static DTE2 GetCurrent()
{
// Note: "16.0" is for Visual Studio 2019; you might need to tweak this for VS2022.
var dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.16.0");
return dte2;
}
Visual Studio should now prompt you to add the following references to your class:
using System.Runtime.InteropServices;
using EnvDTE80;
Finally, insert the following line at the point in FirstProcess where you wish to attach the debugger to SecondProcess:
Attach(GetCurrent());
If you manage to get this working, please feel free to edit this answer with any changes needed for the .NET Core environment.
UPDATE - for .NET Core:
For .NET Core there are two problems to overcome:
The DTE references are not available in .NET Core. However, they can
be added as NuGet packages. You will need to add two NuGet
packages, envdte and envdte80 (both by Microsoft with 2M+
downloads), to FirstProcess.
The method Marshal.GetActiveObject() is not available in .NET
Core. To resolve this you can get the source code from Microsoft
(here) and add it manually; this has already been done in
the code sample below.
What follows is a complete working .NET Core code sample for FirstProcess. This correctly attaches programmatically to SecondProcess after startup.
namespace FirstProcess
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Threading;
using EnvDTE80;
class Program
{
private const string FileName = "C:\\Users\\YourUserName\\source\\repos\\TwoProcessesSolution\\SecondProcess\\bin\\Debug\\net5.0\\SecondProcess.exe";
private const string ProcessName = "SecondProcess";
static void Main(string[] args)
{
var childProcess = Process.Start(new ProcessStartInfo(FileName));
Attach(GetCurrent());
while (true)
{
// Your code here.
Thread.Sleep(1000);
}
childProcess.Kill();
}
private static void Attach(DTE2 dte)
{
var processes = dte.Debugger.LocalProcesses;
// Note: Depending on your setup, consider whether an exact match is required instead of using .IndexOf()
foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf(ProcessName) != -1))
{
proc.Attach();
}
}
private static DTE2 GetCurrent()
{
// Note: "16.0" is for Visual Studio 2019; you might need to tweak this for VS2022.
var dte2 = (DTE2)Marshal2.GetActiveObject("VisualStudio.DTE.16.0");
return dte2;
}
public static class Marshal2
{
internal const String OLEAUT32 = "oleaut32.dll";
internal const String OLE32 = "ole32.dll";
[System.Security.SecurityCritical] // auto-generated_required
public static Object GetActiveObject(String progID)
{
Object obj = null;
Guid clsid;
// Call CLSIDFromProgIDEx first then fall back on CLSIDFromProgID if
// CLSIDFromProgIDEx doesn't exist.
try
{
CLSIDFromProgIDEx(progID, out clsid);
}
// catch
catch (Exception)
{
CLSIDFromProgID(progID, out clsid);
}
GetActiveObject(ref clsid, IntPtr.Zero, out obj);
return obj;
}
//[DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
[DllImport(OLE32, PreserveSig = false)]
[ResourceExposure(ResourceScope.None)]
[SuppressUnmanagedCodeSecurity]
[System.Security.SecurityCritical] // auto-generated
private static extern void CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);
//[DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
[DllImport(OLE32, PreserveSig = false)]
[ResourceExposure(ResourceScope.None)]
[SuppressUnmanagedCodeSecurity]
[System.Security.SecurityCritical] // auto-generated
private static extern void CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);
//[DllImport(Microsoft.Win32.Win32Native.OLEAUT32, PreserveSig = false)]
[DllImport(OLEAUT32, PreserveSig = false)]
[ResourceExposure(ResourceScope.None)]
[SuppressUnmanagedCodeSecurity]
[System.Security.SecurityCritical] // auto-generated
private static extern void GetActiveObject(ref Guid rclsid, IntPtr reserved, [MarshalAs(UnmanagedType.Interface)] out Object ppunk);
}
}
}
Note 1: This is for Visual Studio 2019 and .NET Core 5. I would expect this to work for VS2022 and .NET Core 6 with a single change to the Visual Studio version as annotated above.
Note 2: You will need to have only one instance of Visual Studio open (though if this isn't possible, the code is probably fixable quite easily).
Downloadable demo
Complete working examples for both .NET Framework (v4.7.2) and .NET Core (v5.0) are available on GitHub here: https://github.com/NeilTalbott/VisualStudioAutoAttacher

Prevent process seeing existing instance

Here's the situation.
I have an application which for all intents and purposes I have to treat like a black box.
I need to be able to open multiple instances of this application each with a set of files. The syntax for opening this is executable.exe file1.ext file2.ext.
If I run executable.exe x amount of times with no arguments, new instances open fine.
If I run executable.exe file1.ext followed by executable.exe file2.ext then the second call opens file 2 in the existing window rather than creating a new instance. This interferes with the rest of my solution and is the problem.
My solution wraps this application and performs various management operations on it, here's one of my wrapper classes:
public class myWrapper
{
public event EventHandler<IntPtr> SplashFinished;
public event EventHandler ProcessExited;
private const string aaTrendLocation = #"redacted";
//private const string aaTrendLocation = "notepad";
private readonly Process _process;
private readonly Logger _logger;
public myWrapper(string[] args, Logger logger =null)
{
_logger = logger;
_logger?.WriteLine("Intiialising new wrapper object...");
if (args == null || args.Length < 1) args = new[] {""};
ProcessStartInfo info = new ProcessStartInfo(aaTrendLocation,args.Aggregate((s,c)=>$"{s} {c}"));
_process = new Process{StartInfo = info};
}
public void Start()
{
_logger?.WriteLine("Starting process...");
_logger?.WriteLine($"Process: {_process.StartInfo.FileName} || Args: {_process.StartInfo.Arguments}");
_process.Start();
Task.Run(()=>MonitorSplash());
Task.Run(() => MonitorLifeTime());
}
private void MonitorLifeTime()
{
_logger?.WriteLine("Monitoring lifetime...");
while (!_process.HasExited)
{
_process.Refresh();
Thread.Sleep(50);
}
_logger?.WriteLine("Process exited!");
_logger?.WriteLine("Invoking!");
ProcessExited?.BeginInvoke(this, null, null, null);
}
private void MonitorSplash()
{
_logger?.WriteLine("Monitoring Splash...");
while (!_process.MainWindowTitle.Contains("Trend"))
{
_process.Refresh();
Thread.Sleep(500);
}
_logger?.WriteLine("Splash finished!");
_logger?.WriteLine("Invoking...");
SplashFinished?.BeginInvoke(this,_process.MainWindowHandle,null,null);
}
public void Stop()
{
_logger?.WriteLine("Killing trend...");
_process.Kill();
}
public IntPtr GetHandle()
{
_logger?.WriteLine("Fetching handle...");
_process.Refresh();
return _process.MainWindowHandle;
}
public string GetMainTitle()
{
_logger?.WriteLine("Fetching Title...");
_process.Refresh();
return _process.MainWindowTitle;
}
}
My wrapper class all works fine until I start providing file arguments and this unexpected instancing behaviour kicks in.
I can't modify the target application and nor do I have access to its source to determine whether this instancing is managed with Mutexes or through some other feature. Consequently, I need to determine if there is a way to prevent the new instance seeing the existing one. Would anyone have any suggestions?
TLDR: How do I prevent an application that is limited to a single instance determining that there is already an instance running
To clarify (following suspicious comments), my company's R&D team wrote executable.exe but I don't have time to wait for their help in this matter (I have days not months) and have permission to do whatever required to deliver the required functionality (there's a lot more to my solution than this question mentions) swiftly.
With some decompiling work I can see that the following is being used to find the existing instance.
Process[] processesByName = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);
Is there any way to mess with this short of creating multiple copies of the application with different names? I looked into renaming the Process on the fly but apparently this isn't possible short of writing kernel exploits...
I have solved this problem in the past by creating copies of the source executable. In your case, you could:
Save the 'original.exe' in a specific location.
Each time you need to call it, create a copy of original.exe and name it 'instance_xxxx.exe', where xxxx is a unique number.
Execute your new instance exe as required, and when it completes you can delete it
You could possibly even re-use the instances by creating a pool of them
Building on Dave Lucre's answer I solved it by creating new instances of the executable bound to my wrapper class. Initially, I inherited IDisposable and removed the temporary files in the Disposer but for some reason that was causing issues where the cleanup would block the application, so now my main program performs cleanup at the end.
My constructor now looks like:
public AaTrend(string[] args, ILogger logger = null)
{
_logger = logger;
_logger?.WriteLine("Initialising new aaTrend object...");
if (args == null || args.Length < 1) args = new[] { "" };
_tempFilePath = GenerateTempFileName();
CreateTempCopy(); //Needed to bypass lazy single instance checks
HideTempFile(); //Stops users worrying
ProcessStartInfo info = new ProcessStartInfo(_tempFilePath, args.Aggregate((s, c) => $"{s} {c}"));
_process = new Process { StartInfo = info };
}
With the two new methods:
private void CreateTempCopy()
{
_logger?.WriteLine("Creating temporary file...");
_logger?.WriteLine(_tempFilePath);
File.Copy(AaTrendLocation, _tempFilePath);
}
private string GenerateTempFileName(int increment = 0)
{
string directory = Path.GetDirectoryName(AaTrendLocation); //Obtain pass components.
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(AaTrendLocation);
string extension = Path.GetExtension(AaTrendLocation);
string tempName = $"{directory}\\{fileNameWithoutExtension}-{increment}{extension}"; //Re-assemble path with increment inserted.
return File.Exists(tempName) ? GenerateTempFileName(++increment) : tempName; //If this name is already used, increment an recurse otherwise return new path.
}
Then in my main program:
private static void DeleteTempFiles()
{
string dir = Path.GetDirectoryName(AaTrend.AaTrendLocation);
foreach (string file in Directory.GetFiles(dir, "aaTrend-*.exe", SearchOption.TopDirectoryOnly))
{
File.Delete(file);
}
}
As a side-note, this approach will only work for applications with (lazy) methods of determining instancing that rely on Process.GetProcessByName(); it won't work if a Mutex is used or if the executable name is explicitly set in the manifests.

C# COM events are not fired

I am trying to fetch events from the TaiPan Realtime COM server. I am able to extract other data from there. Accessing keys is working correctly.
But when i try to fetch events, the function is not fired somehow. Hopefully this is a small mistake. For better readability i made a small test code, which is easyer to read for you. After adding those ids to the Stream the Visual Basic debugger is working and it show cpu activity for com_test.
So i gues events are there but i made mistake in eventhandling somehow.
Thanks for help.
using System;
using System.Collections.Generic;
using TaiPanRTLib;
namespace com_test
{
class Program
{
static void Main(string[] args)
{
var handle = new handle();
handle.start();
Console.ReadLine();
}
}
public class handle
{
public int counter;
// this is a list contains the internal numbers of the taipan Software
public static List<int> numbercodes = new List<int>(new int[] { 78379670, 78379685, 78379692, 78379669, 78379729, 78379672, 78379674, 78379698, 78379682, 78379681, 78379704, 78379689, 78379694, 78379673, 78379697, 78379687, 78379702, 78379690, 78379668, 78379671, 78379715, 78379666, 78379706, 78379727, 78379679, 127289939, 78379677, 78379693, 78379676, 78379678, 78379680, 78379688, 78379726, 78379686, 78379696, 78379675, 78379667, 78379703, 78379691, 78379684, 78379700, 78379699, 78379705, 78379695, 78379701, 78379664, 78379716, 78379982, 78379665, 78379707, 78379728, 78379717, 78379719, 7837971 });
void TPRTDataStream_Bezahlt(int SymbolNr, float Kurs, float Volume, DateTime Zeit)
{
Console.WriteLine("peng"); // never see this in window - so not fired?
counter += 1;
}
public void start()
{
TaiPanRealtime TPRTObject = new TaiPanRealtime(); // connects to launches Application
DataStream TPRTDataStream = (DataStream)TPRTObject.DataStream; // attach to the DataStream Object.
foreach (int db_num in numbercodes)
{
TPRTDataStream.Add(db_num, 0); // This adds the internal dbnumber to the Stream
}
TPRTDataStream.Bezahlt+=new _IDataStreamEvents_BezahltEventHandler(TPRTDataStream_Bezahlt);
while (true)
{
Console.WriteLine(counter); // counter stays 0 all the time
System.Threading.Thread.Sleep(1000);
};
}
}
}
I want to post the solution TaiPan service found out. To handle the events correctly you need to set "Embed Interop Types" to false in the reference properties of visual basic. here is a screenshot:
screenshot properties
hope this helps others.

Lock text file during read and write or alternative

I have an application where I need to create files with a unique and sequential number as part of the file name. My first thought was to use (since this application does not have any other data storage) a text file that would contain a number and I would increment this number so then my application would always create a file with a unique id.
Then I thought that maybe at a time when there are more than one user submitting to this application at the same time, one process might be reading the txt file before it has been written by the previous process. So then I am looking for a way to read and write to a file (with try catch so then I can know when it's being used by another process and then wait and try to read from it a few other times) in the same 'process' without unlocking the file in between.
If what I am saying above sounds like a bad option, could you please give me an alternative to this? How would you then keep track of unique identification numbers for an application like my case?
Thanks.
If it's a single application then you can store the current number in your application settings. Load that number at startup. Then with each request you can safely increment it and use the result. Save the sequential number when the program shuts down. For example:
private int _fileNumber;
// at application startup
_fileNumber = LoadFileNumberFromSettings();
// to increment
public int GetNextFile()
{
return Interlocked.Increment(ref _fileNumber);
}
// at application shutdown
SaveFileNumberToSettings(_fileNumber);
Or, you might want to make sure that the file number is saved whenever it's incremented. If so, change your GetNextFile method:
private readonly object _fileLock = new object();
public int GetNextFile()
{
lock (_fileLock)
{
int result = ++_fileNumber;
SaveFileNumbertoSettings(_fileNumber);
return result;
}
}
Note also that it might be reasonable to use the registry for this, rather than a file.
Edit: As Alireza pointed in the comments, it is not a valid way to lock between multiple applications.
You can always lock the access to the file (so you won't need to rely on exceptions).
e.g:
// Create a lock in your class
private static object LockObject = new object();
// and then lock on this object when you access the file like this:
lock(LockObject)
{
... access to the file
}
Edit2: It seems that you can use Mutex to perform inter-application signalling.
private static System.Threading.Mutex m = new System.Threading.Mutex(false, "LockMutex");
void AccessMethod()
{
try
{
m.WaitOne();
// Access the file
}
finally
{
m.ReleaseMutex();
}
}
But it's not the best pattern to generate unique ids. Maybe a sequence in a database would be better ? If you don't have a database, you can use Guids or a local database (even Access would be better I think)
I would prefer a complex and universal solution with the global mutex. It uses a mutex with name prefixed with "Global\" which makes it system-wide i.e. one mutex instance is shared across all processes. if your program runs in friendly environment or you can specify strict permissions limited to a user account you can trust then it works well.
Keep in mind that this solution is not transactional and is not protected against thread-abortion/process-termination.
Not transactional means that if your process/thread is caught in the middle of storage file modification and is terminated/aborted then the storage file will be left in unknown state. For instance it can be left empty. You can protect yourself against loss of data (loss of last used index) by writing the new value first, saving the file and only then removing the previous value. Reading procedure should expect a file with multiple numbers and should take the greatest.
Not protected against thread-abortion means that if a thread which obtained the mutex is aborted unexpectedly and/or you do not have proper exception handling then the mutex could stay locked for the life of the process that created that thread. In order to make solution abort-protected you will have to implement timeouts on obtaining the lock i.e. replace the following line which waits forever
blnResult = iLock.Mutex.WaitOne();
with something with timeout.
Summing this up I try to say that if you are looking for a really robust solution you will come to utilizing some kind of a transactional database or write a kind of such a database yourself :)
Here is the working code without timeout handling (I do not need it in my solution). It is robust enough to begin with.
using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
namespace ConsoleApplication31
{
class Program
{
//You only need one instance of that Mutex for each application domain (commonly each process).
private static SMutex mclsIOLock;
static void Main(string[] args)
{
//Initialize the mutex. Here you need to know the path to the file you use to store application data.
string strEnumStorageFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MyAppEnumStorage.txt");
mclsIOLock = IOMutexGet(strEnumStorageFilePath);
}
//Template for the main processing routine.
public static void RequestProcess()
{
//This flag is used to protect against unwanted lock releases in case of recursive routines.
bool blnLockIsSet = false;
try
{
//Obtain the lock.
blnLockIsSet = IOLockSet(mclsIOLock);
//Read file data, update file data. Do not put much of long-running code here.
//Other processes may be waiting for the lock release.
}
finally
{
//Release the lock if it was obtained in this particular call stack frame.
IOLockRelease(mclsIOLock, blnLockIsSet);
}
//Put your long-running code here.
}
private static SMutex IOMutexGet(string iMutexNameBase)
{
SMutex clsResult = null;
clsResult = new SMutex();
string strSystemObjectName = #"Global\" + iMutexNameBase.Replace('\\', '_');
//Give permissions to all authenticated users.
SecurityIdentifier clsAuthenticatedUsers = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
MutexSecurity clsMutexSecurity = new MutexSecurity();
MutexAccessRule clsMutexAccessRule = new MutexAccessRule(
clsAuthenticatedUsers,
MutexRights.FullControl,
AccessControlType.Allow);
clsMutexSecurity.AddAccessRule(clsMutexAccessRule);
//Create the mutex or open an existing one.
bool blnCreatedNew;
clsResult.Mutex = new Mutex(
false,
strSystemObjectName,
out blnCreatedNew,
clsMutexSecurity);
clsResult.IsMutexHeldByCurrentAppDomain = false;
return clsResult;
}
//Release IO lock.
private static void IOLockRelease(
SMutex iLock,
bool? iLockIsSetInCurrentStackFrame = null)
{
if (iLock != null)
{
lock (iLock)
{
if (iLock.IsMutexHeldByCurrentAppDomain &&
(!iLockIsSetInCurrentStackFrame.HasValue ||
iLockIsSetInCurrentStackFrame.Value))
{
iLock.MutexOwnerThread = null;
iLock.IsMutexHeldByCurrentAppDomain = false;
iLock.Mutex.ReleaseMutex();
}
}
}
}
//Set the IO lock.
private static bool IOLockSet(SMutex iLock)
{
bool blnResult = false;
try
{
if (iLock != null)
{
if (iLock.MutexOwnerThread != Thread.CurrentThread)
{
blnResult = iLock.Mutex.WaitOne();
iLock.IsMutexHeldByCurrentAppDomain = blnResult;
if (blnResult)
{
iLock.MutexOwnerThread = Thread.CurrentThread;
}
else
{
throw new ApplicationException("Failed to obtain the IO lock.");
}
}
}
}
catch (AbandonedMutexException iMutexAbandonedException)
{
blnResult = true;
iLock.IsMutexHeldByCurrentAppDomain = true;
iLock.MutexOwnerThread = Thread.CurrentThread;
}
return blnResult;
}
}
internal class SMutex
{
public Mutex Mutex;
public bool IsMutexHeldByCurrentAppDomain;
public Thread MutexOwnerThread;
}
}

Can a C# Multithreaded Application use separate WorkingDirectories per thread?

In C# (.NET), can two threads running in the same application have DIFFERENT "WorkingFolders"??
As best I can tell, the answer would be "NO". I think the WORKING DIR is set by the PROCESS in Win32.. Am I wrong here?
According to the following test code, (as well the Win32 SetCurrentDirectory API call), this is NOT possible, but has anyone figured out a way to MAKE it possible?
using System;
using System.Threading;
public class TestClass {
public ManualResetEvent _ThreadDone = new ManualResetEvent(false);
public static void Main() {
Console.WriteLine(Environment.CurrentDirectory);
Thread _Thread = new Thread(new ParameterizedThreadStart(Go));
TestClass test = new TestClass();
_Thread.Start(test);
if(test._ThreadDone.WaitOne()) {
Console.WriteLine("Thread done. Checking Working Dir...");
Console.WriteLine(Environment.CurrentDirectory);
}
}
public static void Go(object instance) {
TestClass m_Test = instance as TestClass;
Console.WriteLine(Environment.CurrentDirectory);
System.IO.Directory.SetCurrentDirectory("L:\\Projects\\");
Console.WriteLine(Environment.CurrentDirectory);
m_Test._ThreadDone.Set();
}
}
I know SOMEONE out there has to have ran across this before!
I'm going to guess what you're trying to do is to make code such as File.Open("Foo.txt") behave differently on different threads. Can you do this? The short answer is No - nor should you be trying to do this. On Windows, the current working directory is set at the process level. The .NET framework does not violate that rule.
A better approach would be to create an abstraction on top of Environment.CurrentDirectory that is thread specific. Something like:
public static class ThreadEnvironment
{
[ThreadStatic]
static string _currentDir;
public static string CurrentDirectory
{
get
{
if (_currentDir == null) // If Current Directory has not been set on this thread yet, set it to the process default
{
_currentDir = Environment.CurrentDirectory;
}
return _currentDir;
}
set
{
if (value == null)
throw new ArgumentException("Cannot set Current Directory to null.");
_currentDir = value;
}
}
}
You can then refer to ThreadEnvironment.CurrentDirectory to get that thread's current directory, which will default to the process directory if it has not been set on that thread. For example:
static void Main(string[] args)
{
(new Thread(Thread1)).Start();
(new Thread(Thread2)).Start();
}
static void Thread1()
{
Console.WriteLine("Thread1 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
ThreadEnvironment.CurrentDirectory = #"C:\";
Console.WriteLine("Thread1 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
}
static void Thread2()
{
Console.WriteLine("Thread2 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
ThreadEnvironment.CurrentDirectory = #"C:\Windows";
Console.WriteLine("Thread2 Working Dir is: {0}", ThreadEnvironment.CurrentDirectory);
}
You would, of course, then need to qualify that path whenever dealing with file IO, however this is arguably a safer design anyway.
has anyone figured out a way to MAKE it possible?
It's simply not possible. You can't even have different working directories per App Domain.
The windows rule is: one Environment set per Process. Running in .NET won't change the basic rules.
Instead of that, if you experienced problem in loading assemblies, consider adding the corresponding folder to the PATH environment variable.

Categories