How to start a process from an IIS hosted WCF service? - c#

I would like to run a process from an intranet client on the WCF service side. In my case a client asks a server to create a new process on the server's machine under the provided credentials. WCF service is hosted on IIS 7.5 and I start a process using this code
var processInfo = new ProcessStartInfo("C:\\Windows\\System32\\notepad.exe")
{
UserName = "some user",
Password = MakeSecureString("some password"),
UseShellExecute = false,
LoadUserProfile = true
};
Process process = Process.Start(processInfo);
This code works if I host WCF service as a self-hosted console application running under admin user and I see the notepad started under another user. It fails on IIS with no exception, but process is immediately terminated
process.HasExited = true;
process.ExitCode = -1073741502;
On IIS WCF application is running under the user with admin rights and has got full trust defined in web.config. I cannot use self hosted application as it doesn't support easy continuous delivery (like WebDeploy with IIS web farms).
Q: How can I start a process on a server side from WCF service hosted on IIS?
EDIT:
I stumbled upon this post, with similar issues and I tried all the methods there, including all possible variations for Process.Start and P/Invoke with CreateProcessWithLogonW and CreateProcessAsUser I also tried granting additional permissions to users. Non of this would work with the error messages identical to the ones the guy had posted.

Oleksii, the point is that if you host the WCF service in a console application, there is a windows session (a user logged in and Windows Explorer loaded) for that user and the notepad is opened and shown for that user, so you see it in the UI.
when you host your WCF service in IIS, being a server, IIS requires and allows no user interaction and works also if no user is logged in; in that context there is no UI to host your notepad or other UI enabled applications, you could execute a process for elaboration or other batch jobs but not render a windows UI application, because Windows Explorer is not loaded for you and there is no place to render your process's UI.

here is what I use to call GnuPGP to do encryption. How does your setup compare?
private int ExecuteCommand(string arguments, string passPhrase, int timeout)
{
Process processObject;
ProcessStartInfo pInfo = new ProcessStartInfo(_executablePath, arguments);
pInfo.CreateNoWindow = true;
pInfo.UseShellExecute = false;
pInfo.RedirectStandardInput = true;
pInfo.RedirectStandardOutput = true;
pInfo.RedirectStandardError = true;
processObject = Process.Start(pInfo);
if (!string.IsNullOrEmpty(passPhrase))
{
processObject.StandardInput.WriteLine(passPhrase);
processObject.StandardInput.Flush();
}
string result = processObject.StandardOutput.ReadToEnd();
string error = processObject.StandardError.ReadToEnd();
if (!processObject.WaitForExit(timeout))
{
throw new TimeoutException("GnuPG operation timeout. Waited " + timeout + " milliseconds ");
}
int exitcode = processObject.ExitCode;
Error = error;
Output = result;
return exitcode;
}

There's an apppool setting to make sure it loads the user profile.
loadUserProfile Optional Boolean attribute.
Specifies whether IIS loads the user profile for the application pool identity. Setting
this value to false causes IIS to revert to IIS 6.0 behavior. IIS 6.0 does not load the
user profile for an application pool identity.
The default value is false.
That along with being a domain user as the identity with enough permissions might work?? I know that at a minimum the user will need a user profile.
That said, it's a little bit of an odd architecture. It seems like a better arch would be to have a persistent process like a windows service that the site communicates with but I'm not sure what your constraints are.
Hope that helps.

Related

Process.Start() working but no window is popping up

I have a web API project that has a call that allows the user to basically start a separate application on the server.
Basically my web API is a gateway to remotely call this application from an MVC project.
Problem:
The problem I am facing is that the Process.Start() method is working perfectly (as in I can see the process starting on the server's task manager) but no window is popping up? I can run the application directly and see it start in its own window.
Web API Code:
public void ReconnectEPLAN()
{
if (CheckEplanConnection() == false)
{
Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = #"C:\Program Files\EPLAN\Pro Panel\2.8.3\Bin\W3u.exe"; //works but no ui poopup
process.StartInfo.CreateNoWindow = false;
process.Start();
}
}
What can I do to force the started process's app window to appear as well?
On your server, IIS runs as a service (unlike IIS Express, which runs in the user space).
Since Windows Vista, services can no longer interact with the user's desktop directly.
See:
How to run console application from Windows Service?
https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/f8f91e8f-5954-43a7-8bc4-80ed2ff1e3b1/quotallow-service-to-interact-with-desktopquot-does-not-work-on-vista?forum=windowssdk
https://www.codeproject.com/Questions/1239551/Why-does-process-start-goes-to-background-when-sta
Services cannot interact directly with the user at all: this is because services can run on a machine that doesn't have a user logged in at all, so there is no way to interact with them.

How to display application normally

I am creating a complex service on Windows 7, which starts off by loading an .exe application. The only way to achieve this was to enable to "Interactive Services Detection" service in services.msc.
As a test application, I added the following code which simply opens the system calculator. It works, however not as intended. I first get the following message when starting the service:
When clicking "View this message", it loads the calculator as it is supposed to, but opens a blue full screen mode, and contains the calculator within it.
My question is basically the following: How can I make the application display automatically, and not have to prompt the user to "view the message", and more importantly, how can I get the application to display normally, rather than in this "interactive services detection" sandbox?
This is the code of my sample service:
public partial class OpenCalculator : ServiceBase
{
public Process process;
public OpenCalculator()
{
this.ServiceName = "Open Calculator";
InitializeComponent();
}
protected override void OnStart(string[] args)
{
start_calc();
}
protected override void OnStop()
{
process.Kill();
}
protected void start_calc()
{
try
{
process = new Process();
process.StartInfo.FileName = #"C:\Windows\system32\calc.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.ErrorDialog = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.Start();
using (StreamWriter writer = File.AppendText("C:\\Users\\scaruana\\Desktop\\test.txt"))
{
writer.WriteLine(process);
}
}
catch (Exception ex)
{
using (StreamWriter writer = File.AppendText("C:\\Users\\scaruana\\Desktop\\test.txt"))
{
writer.WriteLine(ex.Message);
}
}
}
}
Services run in session 0, whose desktop you cannot see. Interactive desktops are hosted in other sessions. So, for instance, the first interactive logon runs in session 1. This means that services cannot directly show UI on an interactive desktop.
In older versions of Windows (XP and earlier), the first interactive logon shared session 0 with services. And so when you enabled the allow service to interact with desktop option in the service control manager, the service could show UI on an interactive desktop. However, all that ended with Vista because it was a security risk.
So, you need to start a new process and specifically force it onto the desktop of an logged in interactive user. That is not easy to do. This MSDN blog article covers your options: Launching an interactive process from Windows Service in Windows Vista and later.
Now, that's how you go about starting a new process from a service, and putting the new process onto an interactive desktop. But that is almost certainly the wrong way to solve your problem. As you can see by reading the linked article, doing it that way is complex. The standard solution to your problem is to run an interactive process on the logged in user's desktop. This remains hidden until your service needs to show UI. The service then communicates with the desktop app using your preferred IPC mechanism, and then the desktop app shows the UI.
This question has been the cause of great frustration, and I have finally solved my problem. Yes, I have managed to make a service load a GUI application, even though everyone says that it is impossible. There is a warning though - the "fix" can be considered as exploiting a loophole in Windows, as the code which I used basically passes the Vista and Windows 7 UAC. Essentially, the application is always executed with full rights and bypasses the UAC.
If anyone has this same problem, what you need to do is iterate through a list of logged in users on the PC, and choose to open the UI application in this session (which has the user's desktop), rather than in session 0 where the service is supposed to be running (and which does not have a desktop).
For some people, this might not be a solution as it is not secure. But in my case, security is not a main concern, and I just needed it to work (had to be a service by force).
Hope this helps anyone who has the same problem that I had.

Recycle IIS7 Application Pool with C#

I have read through all of the threads on here on how to do this programmatically but I can not get it to work. A little background on how my code works.
You log in to the site and choose App Pool monitor. There is a drop down that allows you to choose the server you are looking for. I have a stored procedure that will pull the app pools from that server and display them in a grid. I then have this code that I am testing:
while (rdr.Read())
{
string appPool = rdr["AppPoolName"].ToString();
string permission = rdr["Permission"].ToString();
string serverIP = rdr["ServerIP"].ToString();
if (permission == "E")
{
lblErrorStart.Visible = false;
using (var serverManager = ServerManager.OpenRemote(serverIP))
{
ApplicationPool poolName = serverManager.ApplicationPools[appPool];
poolName.Stop();
lblSuccess.Visible = true;
}
}
else
{
lblErrorStart.Visible = true;
}
}
The reader I have created pools the AppPoolName, ServerIP, and Permission from SQL.
I use a DirectoryEntry for IIS6 and it works great but it will not work for IIS7.
using (DirectoryEntry w3svc = new DirectoryEntry(string.Format("IIS://" + serverIP + "/W3SVC/AppPools/" + appPool)))
{
w3svc.Invoke("Recycle", null);
lblSuccess.Visible = true;
}
Any help would be appreciated.
EDIT: The exception I get with the Directory Services is System.Runtime.InteropServices.COMException
How do you know it's not working? How would you really know if the app pool has been recycled or not? We had this issue several months ago and tried to figure out a way to see if it's really recycling or not. We turned on tracing in IIS but it didn't log any messages, which we thought meant it wasn't working. What we did then, was create a dummy service and a console app that would ping that service every 0.5 a second. Then, we recycled the AppPool that was hosting the service, and oddly enough, we saw a delay in the ping response, which meant it actually did recycle.
Try changing w3svc.Invoke("Recycle", null); to w3svc.Invoke("Stop", null);. If that stops the AppPool, then you can be almost sure that Recycle will work just fine. Note that invoking a Recycle operation does not block, so if your code depends on the AppPool to be up and running after a recycle, you need to check for its state before continuing.
Edit:
Give this a shot:
In the Windows Features window, make sure IIS 6 Management Compatibility is enabled.
Open up the Start menu and type in "services", then press Enter. Make sure IIS Admin Service is running. Now try to invoke the command and see if it works.

Triggering an application from a windows service cannot find file?

Background:
I created a service that will trigger the execution of an application when certain conditions have been met. This service is setup to run under the same windows user account that is used to log on to the system via RDP. I also created the .NET application that is trigger via this service. This application looks for a configuration file on disk (found in the ProgramData folder for the application) uses the settings found in the configuration file to affect the output of this application.
Problem:
When the application is ran by the user interactively the application runs great. However when the service triggers the application to run it appears that the application is not loading the correct values from the configuration files. It's almost as though the application when ran from a service has its own configuration file, and is not using the one found in ProgramData.
I'm just looking for some insight to why this may be happening. I have seem some odd behavior from Windows 7 and Windows 2008 R2 when running applications via scheduled tasks or as a service. It's almost like interactive applications and service applications have different environments on the same system running as the same user...
Note: The service executable is also found in the same folder as the triggered application. I would expect that the working directory by default would be the services running directory.
public int ExecRun()
{
Process proc = new Process();
proc.StartInfo = new ProcessStartInfo
{
FileName = "C:\\Program Files\\TEST\\runme.exe",
Arguments = "/DS:TEMP"
};
proc.Start();
proc.WaitForExit();
return proc.ExitCode;
}
Try adding the working directory info:
Process proc = new Process();
proc.StartInfo = new ProcessStartInfo
{
FileName = "C:\\Program Files\\TEST\\runme.exe",
WorkingDirectory="C:\\Program Files\\TEST",
Arguments = "/DS:TEMP"
};
It sounds like the service that triggers the execution of the application also needs to set the working directory. If you're using the Process class, you'll need to set the StartInfo.WorkingDirectory property the path where your application resides.
This has been solved.
Unfortunately, I think I wasted all your time with this question. The users is running this service on a second system (other than the one they claimed was having this issue). They copied the same configuration to both systems which would've been fine if they had setup both systems the same way, but alas they did not. The file did not exist on the system throwing the error, but both systems were setup to log exceptions to the same location.
The user had to disable the second service, or setup the configuration file correctly.

How to start the bat-file at Asp.net web server side?

I need to call a console application to load data into another desktop application on the remote server that located within the corporate domain.
Users will enter the web page and upload data to asp.net web server, which after transformation should call that console application. Users are located remotely and do not have any other access except the web server.
I decided to lower the security web application context and let the asp.net working process to start the console application on the current IIS 6.0 web server
What I have done:
I changed the security account for the application pool for Local System;
I added ASPNET Account and IIS_WPG IIS Process Account to Administrators group;
I added “Allow service to interact with desctop” for “IIS Admin Service” and “World Wide Web Publishing Service” processes and restarted the machine;
I tried to start BAT-file at server side through the test page code-behind, but failed:
protected void btnStart_Click(object sender, EventArgs e)
{
Process process = new Process();
process.StartInfo.FileName = #”C:\run.bat”;
process.StartInfo.UseShellExecute = false;
process.Start();
process.WaitForExit();
}
The error was access denied.
Please help me to find any workable idea how to start the bat-file at web server side.
Thanks
Try setting UseShellExecute to true instead of false. After all, batch files run in a shell - so you need a shell to execute it. (Another option is to run cmd.exe and pass the name of the batch file in as an argument, e.g. "cmd.exe /k c:\run.bat")
You might also want to try creating a simple .NET app which just (say) creates a file with a timestamp in. That way you can test the "can I start another process" bit separately from the "can I get the batch file to work" bit.
Put that particular batch file in your application itself.
string str_Path = Server.MapPath(".") + "\\run.bat";
ProcessStartInfo processInfo = new ProcessStartInfo(str_Path);
processInfo.UseShellExecute = false;
Process batchProcess = new Process();
batchProcess.StartInfo = processInfo;
batchProcess.Start();
Take a look at this example: Run Interactive Command Shell or Batch Files From ASP.NET
It uses little different approach. They suggest running cmd.exe and executing command line by line.

Categories

Resources