We try to access a directory which is within a network directory but get wrong results (C#/Windows):
var exists = Directory.Exists("Z:\\Sessions\\Data1");
"Z" is the network directory, "Sessions" is a directory where a recording software constantly creates directories (e.g. "Data1") and puts some data in it.
It seems that Windows caches the wrong state about Data1: The method returns false. But when I access the directory via Explorer it's here. When I run the method (Directory.Exists) after accessing the directory with Explorer, it returns true. Of course I can guarantee that the directory actually exists at the first attempt.
What is the reason for this behaviour? What can I do about it?
Edit:
It seems that windows could not connect the network drive to the remote computer. When I try to navigate into the directory with Explorer it automatically tries to connect the drive.
So the question changes:
Is there a way to force windows to try a reconnect via .NET?
Solution:
Reconnecting a disconnected network drive
I'm not sure which version of mpr.dll the solution link above works with, but I am using Win7 and have a slightly different version (although similar). This entry point is:
[DllImport("mpr.dll", SetLastError = true, EntryPoint = "WNetRestoreSingleConnectionW", CharSet = CharSet.Unicode)]
internal static extern int WNetRestoreSingleConnection( IntPtr windowHandle,
[MarshalAs(UnmanagedType.LPWStr)] string localDrive,
[MarshalAs(UnmanagedType.Bool)] bool useUI);
then:
IntPtr hWnd = new IntPtr(0);
int res = WNetRestoreSingleConnection(hWnd, <your drive path>, false);
You'll have to add your own error checking / handling.
I too use a remote drive and it takes a few seconds to connect, so I just wait and works every time. If it does not connect, it will send an email and I will check it.
logger.Info("Create Z: drive ");
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = #"/C net use z: \\" + Tools.servername + #"\SHARE_NAME_HERE /user:USER_NAME_HERE PASSWORD_HERE";
process.StartInfo = startInfo;
process.Start();
logger.Info("Z: drive created ");
// wait get Z:
int xtimestimeout = 5;
while (!Directory.Exists(#"Z:\") & (xtimestimeout > 0))
{
Application.DoEvents();
SetBalloonTip(#"Program", #"connecting... please wait");
ShowBalloon();
logger.Info("Creating Z:... waiting...");
Application.DoEvents();
System.Threading.Thread.Sleep(3000);
xtimestimeout -= 1;
}
// check for sucessfull creation of Z: in server Z:\somedirectory
if (!Directory.Exists(#"Z:\"))
{
SendEmail2("Oh my... help!", "drive Z: not created <<< CHECK!");
logger.Info("Z: email sent because of not created");
}
logger.Info("Z: drive created successfully.");
What is the reason for this behaviour?
Quote from the documentation:
If you do not have at a minimum read-only permission to the directory,
the Exists method will return false.
What can I do about it?
Ensure that you are running your .NET application under an account that has at least read-only permission to access this folder. Notice that if you are writing this in an ASP.NET application this probably won't be the case so depending on which account you configured your web server to execute your application under, take the necessary steps to grant permissions to this account.
Related
I am using gpg(GnuPG) to encrypt .csv file to .gpg file.
The below code is generate encrypted file in debug mode. When I Install under windows service it’s throwing exception. “gpg: <>C:\emp.csv: skipped: No public key
gpg: [stdin]: encryption failed: No public key”.
Its working when I run service in debug mode like “consoleapp.exe -c”
string arguments = string.Format(" --yes --quiet --always-trust -e -o {0} -r \"{1}\" {2}", "C:\\emp.gpg", "KeyName", "C:\\emp.csv");
ProcessStartInfo pInfo = new ProcessStartInfo( #"C:\Program Files (x86)\GNU\GnuPG\gpg2", arguments );
pInfo.WorkingDirectory = #"C:\Program Files (x86)\GNU\GnuPG\";
pInfo.CreateNoWindow = false;
pInfo.UseShellExecute = false;
pInfo.RedirectStandardInput = true;
pInfo.RedirectStandardOutput = true;
pInfo.RedirectStandardError = true;
Process process = new Process()
{
StartInfo = pInfo,
EnableRaisingEvents = true
};
process.Start();
error = process.StandardError.ReadToEnd();
agent.LogConsole(process.StandardOutput.ReadToEnd());
GnuPG manages per-user GnuPG home directories. If you import keys as a local user (while developing/debugging), they will be imported to your local user's home directory. If you later run it as system service, the user defined as service owner will possibly be another one and has no access to your local user's home directory.
Possible solutions:
Logon as the service's user and import the keys.
If you're only performing encryption: import the key when starting up the application.
Define a fixed home directory for your application using the GNUPGHOME environment variable or --homedir parameter. Be aware that GnuPG is by default rather picky on the folder's permissions, and if you're not very sure about the implications, don't change anything about this.
I have written a simple custom shell application for Windows 8.1 systems in WPF. It functions fine, but I need to start some of the applications in the Run section of the registry. Fine and good, however, no matter what I try, they don't launch, and I receive an error: "The system cannot find the file specified."
This is designed for 64 bit systems, so I've heard that using C:\Windows\Sysnative\ for the path rather than C:\Windows\System32\ is a fix, but it didn't work. My code is as follows:
Process processToStart = new Process
{
StartInfo =
{
FileName = #"C:\Windows\Sysnative\hkcmd.exe",
WorkingDirectory = #"C:\Windows\Sysnative\")
}
};
processToStart.Start();
The way I found to get this to work was to disable WOW64 file system redirection. Nothing else seemed to work.
From this link:
http://tcamilli.blogspot.co.uk/2005/07/disabling-wow64-file-system.html
[DllImport("Kernel32.Dll", EntryPoint="Wow64EnableWow64FsRedirection")]
public static extern bool EnableWow64FSRedirection(bool enable);
Wow64Interop.EnableWow64FSRedirection(false)
Process processToStart = new Process
{
StartInfo =
{
FileName = #"C:\Windows\Sysnative\hkcmd.exe",
WorkingDirectory = #"C:\Windows\Sysnative\")
}
};
processToStart.Start();
Wow64Interop.EnableWow64FSRedirection(true)
Not sure if these might be the cause of your issue but from your example posted in the question, note these few points:
StartInfo.FileName should only contain the fileName, not the path.
If you're trying to execute hkcmd.exe, you wrote it as hkcmnd.exe (extra N).
In the example above I believe it effectively looks like specifying the filename and workingdirectory repeatedly causes a file to not be found. See this link I used to check and this link as well
Sysnative was not present on my machine (Win 7 x64), it might be in Windows 8.
I couldn't get hkcmd.exe to execute, it threw an error you had as well, however, executing cmd.exe and notepad.exe is fine.
Sample code:
System.Diagnostics.Process processToStart = new System.Diagnostics.Process();
processToStart.StartInfo.FileName = "cmd.exe"; //or notepad.exe
processToStart.StartInfo.WorkingDirectory = #"C:\Windows\System32\";
processToStart.Start();
I have Wcf Service hosted in IIS, Windows Server 2008 R2, using an AppPool .NET 4.0 with NETWORK SERVICE Identity.
My Wcf Service has a method that calls an command EXE using Process.Start.
I need use an different user as credentials for execute command EXE, an domain user account.
I try execute it but it doesn't works for me: it seems not execute the command EXE.
update: process exited, but not execute code
I get errors like:
exit code -1073741502
and eventvwr:
Process Information:
Process ID: 0xc50
Process Name: C:\DeployTools\DeployTools.Commands.Ejecutar.exe
Exit Status: 0xc0000142
The appplication was unable to start correctly (0xC0000142). Click OK
to close the application
Any suggestions?
Code:
StreamReader sr = null;
StreamReader serr = null;
try
{
var psi = new ProcessStartInfo(MY_COMMAND_EXE);
psi.WorkingDirectory = Path.GetDirectoryName(MY_COMMAND_EXE);
psi.Arguments = arguments;
psi.Domain = DOMAIN;
psi.UserName = USER_IN_DOMAIN;
psi.Password = SecureStringHelper.ToSecureString(pwd);
psi.LoadUserProfile = true;
psi.UseShellExecute = false;
psi.ErrorDialog = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
psi.CreateNoWindow = true;
psi.WindowStyle = ProcessWindowStyle.Minimized;
using (Process pr = Process.Start(psi))
{
sr = pr.StandardOutput;
serr = pr.StandardError;
if (!pr.HasExited)
{
pr.WaitForExit(300000);
}
output = pr.StandardOutput.ReadToEnd();
errors = pr.StandardError.ReadToEnd();
exitCode = pr.ExitCode;
return output;
}
}
catch (Exception exc)
{
return "EXCEPCIÓN: " + exc.Message;
}
finally
{
if (sr != null)
{
sr.Close();
sr.Dispose();
sr = null;
}
if (serr != null)
{
serr.Close();
serr.Dispose();
serr = null;
}
}
I had to add a reference to AsproLock.dll and associated code in order to permit the user account to have access to the running resource.
//The following security adjustments are necessary to give the new
//process sufficient permission to run in the service's window station
//and desktop. This uses classes from the AsproLock library also from
//Asprosys.
IntPtr hWinSta = NativeMethods.GetProcessWindowStation();
WindowStationSecurity ws = new WindowStationSecurity(hWinSta,
System.Security.AccessControl.AccessControlSections.Access);
ws.AddAccessRule(new WindowStationAccessRule(userPassDto.Usuario,
WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
ws.AcceptChanges();
IntPtr hDesk = NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId());
DesktopSecurity ds = new DesktopSecurity(hDesk,
System.Security.AccessControl.AccessControlSections.Access);
ds.AddAccessRule(new DesktopAccessRule(userPassDto.Usuario,
DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
ds.AcceptChanges();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetProcessWindowStation();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetThreadDesktop(int dwThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int GetCurrentThreadId();
The Perils and Pitfalls of Launching a Process Under New Credentials by Asprosys
This isn't a common need but it also isn't that rare, so I thought I had better post this step by step guide to troubleshooting the launching of a process under impersonated credentials. This is based on using the Start method of the .Net Process class but it is also applicable to the underlying API calls: CreateProcessWithLogonW and CreateProcessWithTokenW.
Access Denied - The first attempt and an access denied exception right off the bat. This is the most common initial problem and is caused by the fact that the service is running under the LOCAL SYSTEM account. Strangely, the SYSTEM account is the most powerful account on the computer but one of the few things it cannot do is launch a process using CreateProcessWithLogonW which is the API underlying the call to Process.Start. So change your service account to Local Service, it's probably the more appropriate account anyway.
Access Denied Again - Aargh, I thought we solved this. Oops, double check the permissions on the application that you are trying to launch. Remember that the system tries to access the application file as the user account that the process will be running under, not the service account.
Invalid Directory Error - What? All the paths are correct. All the directories are spelled correctly, no invalid characters. This is an incredibly annoying error and is not very consistent. Usually when we run a process we don't bother setting the WorkingDirectory property and just accept the default from the parent process. When starting a process with new credentials you can't do that, you must explicitly set a path for the WorkingDirectory or you'll get a "The directory name is invalid." Win32Exception.
Failure: No Error? - Process.Start handles the creation of the Environment block for the new process for you quite well. So this is a problem only if you are using the underlying API. When calling one of the CreateProcess* APIs it is normal to leave the lpEnvironment parameter as NULL and have the system use the default of copying the block from the parent process. But when launching under new credentials you must create an environment block explicitly, either manually or using CreateEnvironmentBlock. What makes this worse is, if you leave this out the CreateProcess* call will fail but GetLastError will return ERROR_SUCCESS and if you make an error creating your environment block there will be no error but the process may just not run at all.
Application Failed to Initialize Properly - No more exceptions, you've solved all the problems and the process has been launched. Oops again, where is the process? Check the event log (or you may have received an Application Error pop-up). There should be an entry for Application Error that says that your process was the faulting application, either user32.dll or kernel32.dll was the faulting module and the exception was: 0xC0000142. There may be some minor variation in this but basically it is saying that your application could not initialize. The reason for this is that on initialization, before any application code is run, all processes are attached to a Window Station and all threads are attached to a Desktop but the user you are launching under does not have permission to access the Window Station and Desktop in which your process is being launched, ergo it can't initialize. The security descriptors for the Window Station and Desktop must be adjusted to give AllAccess permission to the user the process is being launched under. This is a devil to do directly in .Net, so you might find the security wrapper classes here useful.
No More Errors - Really, no more errors, your process should be running smoothly now. There may be some variations in what you need to do based on who the user is (for instance an administrator will already have the correct permissions in some cases) or what kind of session you are launching in. But following these steps should make your life smooth and easy (well maybe not your whole life).
References:
The Perils and Pitfalls of Launching a Process Under New Credentials
Aspro Lock - Access Control
Code Samples
Creating New Process Under Alternate Credentials (createprocessasuser)
processstart-hangs
I'm trying to run the Windows System Assessment Tool (winsat.exe) using the following code:
System.Diagnostics.Process WinSPro =
new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo WinSSInfo =
new System.Diagnostics.ProcessStartInfo();
WinSSInfo.FileName = "cmd.exe";
WinSSInfo.Arguments = "/k winsat.exe";
WinSPro.StartInfo = WinSSInfo;
WinSPro.Start();
This code works if I only call cmd.exe, and even if I call regedit.exe it still works.
However, when I try to call winsat.exe as a argument of cmd.exe, it fails.
The command prompt shows this:
'winsat.exe' is not recognized as an internal or external command,
operable program or batch file.
I tried several ways to call winsat.exe:
Call it directly by assigning "winsat.exe" to ProcessStartInfo.FileName. It fails with a Win32Exception: The system cannot find the file specified
As above, using the full path - #"c:\windows\system32\winsat.exe". It fails with the same error.
Run the code as the System Administrator. It still fails.
Call winsat.exe as in the coded example. It failed as I explained earlier.
It's interesting that the command prompt launched from the code can only see .dll files in c:\windows\system32.
Does anyone have any idea why winsat.exe cannot be launched through System.Diagnostics.Process? Are there any limitations which I've misunderstood?
Thanks,
Rex
winsat.exe is redirected using Windows-on Windows 64-bit redirection. What's happening is that your launch request (from a 32-bit process) is being redirected to %windir%\SysWOW64\winsat.exe. Since there's no 32-bit version of this particular executable on 64-bit installs, the launch fails. To bypass this process and allow your 32-bit process to access the native (64-bit) path, you can reference %windir%\sysnative instead:
Process WinSPro = new Process();
ProcessStartInfo WinSSInfo = new ProcessStartInfo();
WinSSInfo.FileName = #"c:\windows\sysnative\winsat.exe";
WinSPro.StartInfo = WinSSInfo;
WinSPro.Start();
Alternatively, if you build your program as x64, you can leave the path as c:\windows\system32.
Note that it's best to use Environment.GetFolderPath to get the path to the windows directory, just in case the OS is installed in a non-standard location:
WinSSInfo.FileName = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Windows),
#"sysnative\winsat.exe");
Based on Simon MᶜKenzie's answer, and the link he provided (thanks to soyuz for his comment) I wrote method that should work in either cases (to just copy/paste the code):
public static string GetSystem32DirectoryPath()
{
string winDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
string system32Directory = Path.Combine(winDir, "system32");
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
// For 32-bit processes on 64-bit systems, %windir%\system32 folder
// can only be accessed by specifying %windir%\sysnative folder.
system32Directory = Path.Combine(winDir, "sysnative");
}
return system32Directory;
}
and code to launch the process:
var pi = new ProcessStartInfo
{
FileName = Path.Combine(GetSystem32DirectoryPath(), "winsat.exe"),
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(pi);
The action I need help about, is to execute a EXE file on own servers disk from a intranet-webpage, which IIS are on same server-installation. The webpage use a business layer to execute a ProcessStart together with given parameters.
When I perform the execution from web, the taskmanager show me that the application are starting up with the IIS AppPool of webpage as user. Few seconds later it's killed. In my database logs, I can see;
The Microsoft Jet database engine cannot open the file '\\computer\pathfile.ext'. It is already opened exclusively by another user, or you need permission to view its data.
That's correct. The EXE tool are, in turn, loading files from other computers. This is a special behavior which are well studied and well working while using the tool from desktop.
My goal/question,
I want this web-function-call behave with desktop rights. Is it possible at all?
The IIS AppPool have a regular setup with account ApplicationPoolIdentity. I appeared to be "lucky unwise", without knowledge about how much IIS 7.5 and Windows Server 2008 R2 raised the security model since <=IIS6.
I tried to change the app-pool user to NetworkService, Administrator.
I tried to set the application with app-pool as exec/read right
I even tried to let webapp to run a batch-file with a call to application inside..
Then I was begin to change the ProcessStart-behavior. And here, I
don't know much of what to do. I tried to add VERB runas. Force a
password prompt is not a solution here. I tried to simulate a
username/password. No luck there. I also tried to add runas /user:
blabla as parameters with ProcessStart, after used /savecred in a
desktop command window once. No luck there.
Maybe this should work but I just don't understand the correct setup of properties. I add the ProcessStart code snippet below, also added some commented code to let you see what I tried.
public string RunProcess(ApplicationType type, int param)
{
currentSelection = GetApplicationType(type);
ProcessStartInfo info = new ProcessStartInfo(currentSelection.Path);
info.CreateNoWindow = false;
info.UseShellExecute = true;
//info.UseShellExecute = false;
//info.ErrorDialog = false;
//info.UserName = "dummyUsEr";
//info.Password = this.SecurePwd("DummyPWd");
info.WindowStyle = ProcessWindowStyle.Normal;
info.Arguments = string.Format(" {0}", param.ToString());
using (Process exec = Process.Start(info))
{
try
{
exec.WaitForExit();
}
catch
{
}
}
return output;
}
EDIT
Just to be clear, and perhaps help some another guy/girl browsing to this question, I attach the snippet of Password-generation,
protected System.Security.SecureString SecurePwd(string pwd)
{
SecureString securePwd = new SecureString();
foreach (char ch in pwd.ToCharArray())
securePwd.AppendChar(ch);
return securePwd;
}
I see that you've tried putting in a specific username and password for the process start impersonation, but you say that the process accesses files on another computer and I don't see any mention of specifying a domain name which presumably you would need to access remote files?
So like this:
info.Domain = "domainname";
info.UserName = "dummyUsEr";
info.Password = "DummyPWd";
Also, what does this.SecurePwd() do and have you tried it with just the straight password string that you're passing into it?