I have a webservice with a couple of methods that attempt to run or message another .EXE on the system to do a specific task.
Either we can start the .EXE process with certain parameters or send it a WndProc message to make it do the desired operation. This works fine locally on my system calling the exe with parameters in cmd or sending a WndProc message from the webservice when debugging it in Visual Studio.
None of this works over a real environment however. I had the run .Exe with parameters method (DoSomething) write the exception to a file:
System.ComponentModel.Win32Exception (0x80004005): No Access
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at Someprogram.ProgramService.DoSomething(String text)
The other method for wndproc sendmessage I wrapped in a try/catch as well but no exception thrown. It actually locates the process though as I had it print a file:
public static void SendMessageToSomeProgram(string message) {
Process[] processes = Process.GetProcessesByName("SomeProgram");
if (processes.Length >= 1) {
//iterate through all running target applications
foreach (Process p in processes) {
//test write if process found
TextWriter tw = new StreamWriter(#"c:\wndprocfile.txt"); //this file is printed
tw.WriteLine(DateTime.Now.ToString());
tw.Close();
//do stuff
try {
byte[] sarr = System.Text.Encoding.Default.GetBytes(message);
int len = sarr.Length;
COPYDATASTRUCT cds;
cds.dwData = (IntPtr)100;
cds.lpData = message;
cds.cbData = len + 1;
SendMessage(p.MainWindowHandle, WM_COPYDATA, 0, ref cds);
} catch (Exception ex) {
TextWriter tw2 = new StreamWriter(#"c:\wndProc_errorfile.txt"); //not printed
tw2.WriteLine(DateTime.Now.ToString() + "Exception: " + ex.ToString());
tw2.Close();
}
}
}
Now I see why it's nice to have security like this in place but is there an easy way around this? Maybe just some settings in IIS?
UPDATED INFO: The server is running IIS 5.1 so no Application Pool feature.
The service inside IIS runs as the user connected to the app pool it's running as. So try to change the user of the app pool to a user that have access to start the application you want to start up.
To do this, check the applications app pool and then go to the app pool and change the user. To change the user you have to turn pass-through authentication off.
Related
I know this question was already asked, but I couldn't find the solution so far.
What I'm trying to do is uninstall a windows service and delete the folder with the windows service using C#.
Windows service uninstall
public static void Uninstall(string exeFilename)
{
var commandLineOptions = new string[1] { "/LogFile=uninstall.log" };
if (!Directory.Exists(exeFilename)) return;
var fileNames = Directory.GetFiles(exeFilename);
var serviceFile = fileNames.FirstOrDefault(f => f.EndsWith(".exe"));
var serviceFileName = Path.GetFileName(serviceFile);
var serviceName = Path.GetFileNameWithoutExtension(serviceFile);
var serviceExists = ServiceController.GetServices().Any(s => s.ServiceName == serviceName);
if (!serviceExists) return;
var installer =
new AssemblyInstaller($"{exeFilename}\\{serviceFileName}", commandLineOptions)
{
UseNewContext = true
};
installer.Uninstall(null);
installer.Dispose();
}
Folder and files delete
public static void DeleteFolder(string folderPath)
{
if(!Directory.Exists(folderPath)) return;
try
{
foreach (var folder in Directory.GetDirectories(folderPath))
{
DeleteFolder(folder);
}
foreach (var file in Directory.GetFiles(folderPath))
{
var pPath = Path.Combine(folderPath, file);
File.SetAttributes(pPath, FileAttributes.Normal);
File.Delete(file);
}
Directory.Delete(folderPath);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
Error that I get
Access to the path 'c:\services\x\x.exe' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalDelete(String path, Boolean checkHost)
at System.IO.File.Delete(String path)
This error occurs randomly. The .exe file is not readonly, sometimes the files get deleted.
Does anyone know what's wrong?
Stopping a Windows service does not equate to exiting a process. An executable can house multiple Windows services (it's essentially the physical shell for the services).
So what you're running into, it looks like, is that the service likely stopped just fine, and uninstall can proceed and the deletion of files can proceed, but only up until the point where it reaches the executable, which hasn't yet had a chance to exit.
Stopping, exiting, uninstalling are all asynchronous and need time to complete before moving to the next step.
What you have to do is follow this formula
Ensure your code is running with elevated privileges if you can; if you can't you may run into Access Denied. You can also try changing the ownership of the target executable.
Stop or ensure the service is stopped. If there are multiple services, stop all of them.
Wait for stop to actually occur. It's not always immediate.
Call the Uninstall().
Wait some amount of time. Check to see if the process is running. If it is you will call Process.Kill() (see an implementation below).
Finally, you can call the DeleteFolder() for which your implementation looks adequate to me.
Exiting the Process
Write a method that looks something like this. You might want to tweak it.
void Exit(Process p)
{
for (int i = 0; i <= 5; i++)
{
// doesn't block
if (p.HasExited) return;
// doesn't block; pass true to terminate children (if any)
p.Kill(true);
// wait 5 seconds then try again
Thread.Sleep(5000);
}
}
After all that you should be good to go.
Is the service stopped / executable stopped and does the program have Administrator access? If you don't have administrator access, refer to:
How do I force my .NET application to run as administrator?
to run as administrator. When uninstalling a program, administrator permissions are almost always required. If this still fails, you are not the owner of the file and you must change the owner to yourself or Administrators. You can do this programatically here: Getting / setting file owner in C#
I think you have to stop the service first.
try using
sc stop <service name>
to stop the service.
Then uninstall should work.
try using installutil.exe if your uninstall code does not work. It can give your error output also.
The exe cannot be deleted if it is currently executing so make sure exe is not executing when you try to delete it.
You can use setup project, it generates the uninstaller automatically and gives you a range of possibilities with it.
Example of the service installer
You must download the addon in the market place
I am working on a ASP.NET web service that'll run on a AWS EC2 Windows Server instance. What it will do is receive some files and start a process (Marmoset Toolbag, a 3D tool) with some arguments. This program converts files to another format and closes afterwards. The code that runs the process:
using (Process p = new Process())
{
p.StartInfo.FileName = path;
p.StartInfo.Arguments = _args;
bool result = p.Start();
int timeOut = 15000;
int intInterval = 100;
while (!p.HasExited)
{
System.Threading.Thread.Sleep(intInterval);
timeOut -= intInterval;
if (timeOut <= 0) break;
}
if (timeOut <= 0)
{
//timeout happened
p.Kill();
return false;
}
else
{
return true;
}
}
}
catch (Exception e)
{
AddToRespondBody(CustomResponse.Conversion_failed, false, e.ToString());
return false;
}
This all works fine locally in debug mode.
But when I run this on IIS on the server instance, the process does open but is in background and stays idle.
After some research I found some information that this doesn't really work on server because of permission problems:
ASP.NET Web page and server control code executes in the context of the ASP.NET worker process on the Web server. If you use the Start method in an ASP.NET Web page or server control, the new process executes on the Web server with restricted permissions. The process does not start in the same context as the client browser, and does not have access to the user desktop.
I also tried a way with ProcessStartInfo but again without success. If I understand this problem correctly I need to run the process with higher priviliges, but even this didn't help. Is there even a way to pull this off with running a user program from a process?
Any help is appreciated!
I'm writing a GUI for a third-party console application and I wanted it to capture the output of the console window and add it to a text box in the GUI. This seemed like it was simple, all I had to do was to redirect the output stream of the target process.
But, when I do that the console application throws the error:
CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents
The current code I have which causes this error is this:
// This gets called once after the application has initialized.
private void StartServer()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = srcdsExeFile;
processStartInfo.UseShellExecute = false;
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
serverProcess = Process.Start(processStartInfo);
serverProcess.EnableRaisingEvents = true;
serverProcess.Exited += new EventHandler(Server_Exited);
serverProcess.OutputDataReceived += ServerProcess_OutputDataReceived;
serverProcess.ErrorDataReceived += ServerProcess_ErrorDataReceived;
serverProcess.BeginOutputReadLine();
serverProcess.BeginErrorReadLine();
}
// This is (like seen above) an event handler for serverProcess.ErrorDataReceived.
private void ServerProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine("\n\nServer Error: " + e.Data + "\n\n");
}
// This is (like seen above) an event handler for serverProcess.OutputDataReceived.
private void ServerProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine(e.Data);
}
The above code does work for a minute or so while the external application is doing its initialization, but crashes after a specific point in the initialization process.
After doing some research it turns out that the third-party console application relies on the output stream to be a console, which is why it crashes when I try to redirect it. Trying to access the output stream without redirecting it also causes an error saying I have to redirect it first.
Which brings me to my actual question:
Is it possible to read the output of the console application without redirecting the output stream?
So this has been asked several times in the past couple of years.
I just run into the same issue and solved it in C++ but the same technique should apply to any other programming language since this is a WinAPI specific problem.
I've describe a solution for anyone that wishes to create an srcds server using CreateProcess and redirect input & output on windows.
This github repo put together how Console Handls & Standard Handles work together inside windows.
https://github.com/rprichard/win32-console-docs
Also the microsoft documentation of
https://learn.microsoft.com/en-us/windows/console/creation-of-a-console
I highly recommend to read about this as this makes it very obvious why srcds fails when redirecting the standard input.
The problem
a) Windows Console Handle is not equal to StandardInput and Output Handles.
b) And in windows there is no way to redirect Console handles.
c) GetNumberOfConsoleInputEvents requires a valid console handle with an input handle that is not a file, pipe. It must be a ConsoleHandle!
Since no one actually looks at why GetNumberOfConsoleInputEvents fails while it should be obvious after reading the documentation.
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
It clearly states:
hConsoleInput [in]
A handle to the console input buffer. The handle must have the
GENERIC_READ access right. For more information, see Console Buffer
Security and Access Rights.
But here
https://github.com/rprichard/win32-console-docs#allocconsole-attachconsole-modern
it's explained that when you redirect the pipe it pretty much breaks the console input buffer. So you have to actually work with the console input buffer and not with the StdHandles.
The solution
Luckily the WinAPI provides several options for us to get access to an existing process's std handles. It's just very tricky and not well documented!
You can attach to a Console and grab the STDHandles. Duplicate them and do whatever you like.
Note that AttachConsole(ProcessId) requires you that the current process has no console attached so you must call FreeConsole();
Here's a code how to send a single letter to an another application's console using WinAPI. You can also grab the console handle and write to it any time Using GetStdHandle.
int ProcessId = GetProcessId(ProcessInfo.hProcess);
if (ProcessId <= 0)
{
printf("Process terminated.\n");
break;
}
printf("Process Id: %d\n", ProcessId);
FreeConsole();
if (!AttachConsole(ProcessId))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
INPUT_RECORD ir[2];
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = FALSE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[1].Event.KeyEvent.wRepeatCount = 1;
ir[1].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
DWORD dwTmp = 0;
WriteConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &dwTmp);
FreeConsole();
if (!AttachConsole(ATTACH_PARENT_PROCESS))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
So the solution is simply to to write to the Console Input Buffer by attaching to the console of an SRCDS process.
Simply call AttachConsole and FreeConsole. and WriteConsoleInput.
To read the ouptut you can just call ReadConsoleOutput
For further reading visit the documentation:
GetNumberOfConsoleInputEvents
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
AttachConsole
https://learn.microsoft.com/en-us/windows/console/attachconsole
FreeConsole
https://learn.microsoft.com/en-us/windows/console/freeconsole
WriteConsoleInput
https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
ReadConsoleOutput
https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
I have a .net Windows Service running as Local System. I have another .net process that needs to wait for the service to terminate. That process does not know the service name so it can't query the service control manager. It does know the service process Id. I can modify sources of both the windows service and the other process.
When I do:
process = Process.GetProcessById(processId);
process.WaitForExit();
from the other process, I'm getting:
System.ComponentModel.Win32Exception: Access is denied
Stack trace:
at System.Diagnostics.Process.GetProcessHandle(Int32 access, Boolean throwIfExited)
at System.Diagnostics.Process.WaitForExit(Int32 milliseconds)
GetProcessHandle calls OpenProcess. Apparently the target process should allow SYNCHRONIZE bit for the above to work, which in theory can be set by SetSecurityInfo from the Windows Service. However it does not appear that there is an easy way of doing this in .NET short of calling several pinvokes to elevate, enable privilege and finally change the security.
Am I overlooking a simple way of waiting on another (system) process from a user process?
If you know the service process ID, Why can't you get the service name and check for the service status from process as follows ?
Process process = Process.GetProcessById(1);
ServiceController windowsService = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName == process.ProcessName);
//Wait until the windows service stops running
while (windowsService.Status == ServiceControllerStatus.Running)
{
Thread.Sleep(1000);
}
Try use the same user account to start the service and your program, Or use the built in Administrator account to run your program(Windows Vista and after).
If it still throw the exception, try set your computer's DEP(Data Execution Prevention) setting.
Updated:
Maybe you could use this stupid code.. It's trouble for getting the Service Name, since it may not the same as Process.Name.
static ServiceController GetServiceControllerByPId(int processId)
{
string serviceName = "";
string qry = "SELECT NAME FROM WIN32_SERVICE WHERE PROCESSID = " + processId.ToString();
System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher(qry);
System.Management.ManagementObjectCollection mngntObjs = searcher.Get();
foreach (System.Management.ManagementObject mngntObj in mngntObjs)
{
serviceName = (string)mngntObj["NAME"];
}
if (serviceName == "")
{
return null;
}
return System.ServiceProcess.ServiceController.GetServices().FirstOrDefault(x => x.ServiceName == serviceName);
}
Call this function, and monitor the ServiceController.Status
I'm trying to run dot net console application via Java:
process = Runtime.getRuntime().exec(commandLine);
I get the following output:
Detecting
The handle is invalid.
when running it directly via the console (windows) there is no problem:
Detecting
100%
Done.
100%
I'm running more applications in this form but have no problem .
Got this stack trace:
Detecting at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
at System.Console.get_CursorTop()
at AutomaticImageOrientation.HelperClasses.General.WriteProgressToConsole(Int32 lastIndex, Int32 totalImages)
at AutomaticImageOrientation.MainManager.DetectImage(String[] files, String outputPath, String& globalErrorMessage, Dictionary`2& foundRotations)
The problem is when the .net app trying to write to the console What is the solution?
found the line that cause the problem:
Console.CursorLeft = 0;
Do you know why?
The console application is trying to set the cursor position for a console. This isn't possible, since there is in fact no console. All operations which don't result in a simple read or write are likely to cause errors when there is no console (since most of them require a console output buffer to work).
It is a bad idea to do stuff like setting the cursor position or clearing the screen in console applications you wish to automate. A basic workaround is to just put the offending statement in a try-catch, and discard the exception. From the MSDN page on System.Console:
You should not use the Console class
to display output in unattended
applications, such as server
applications. Similarly, calls to
methods such as Write and WriteLine
have no effect in Windows
applications.
Console class members that work
normally when the underlying stream is
directed to a console might throw an
exception if the stream is redirected,
for example, to a file. Consequently,
program your application to catch
System.IO.IOException if you redirect
a standard stream.
I ran into the same problem, only it was from running a c# console application via the SQL task scheduler.
I believe the problem is that some console methods and properties (Console.WindowWidth, Console.CursorLeft) are attempting to manipulate the console output, which isn't possible when the console is redirected.
I wrapped the section of the code in a simple try catch block, and it works fine now.
//This causes the output to update on the same line, rather than "spamming" the output down the screen.
//This is not compatible with redirected output, so try/catch is needed.
try
{
int lineLength = Console.WindowWidth - 1;
if (message.Length > lineLength)
{
message = message.Substring(0, lineLength);
}
Console.CursorLeft = 0;
Console.Write(message);
}
catch
{
Console.WriteLine(message);
}
Hard to diagnose without more detail - perhaps permissions... a little bit of exception handling (perhaps writing stack-trace to stderr) would help enormously. but not much help if you don't own the app.
If you don't get anywhere, you could try using reflector to see what the .NET app is doing during "Detecting" - it might help identify the cause.
I don't think there is a problem with your friend's program. You probably need to get the output stream of the process object you receive from Runtime.getRuntime().exec(commandLine), and call a read() method or something. It might work.
Try this to get the output stream of the call command
Runtime r = Runtime.getRuntime();
mStartProcess = r.exec(applicationName, null, fileToExecute);
StreamLogger outputGobbler = new StreamLogger(mStartProcess.getInputStream());
outputGobbler.start();
int returnCode = mStartProcess.waitFor();
class StreamLogger extends Thread{
private InputStream mInputStream;
public StreamLogger(InputStream is) {
this.mInputStream = is;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(mInputStream);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
That can depend on how your Java application is being run. If your java application doesn't have console, when there can be problems when console presence is necessary for your inner process. Read this.