I am trying to print a pdf file from a WebAPI Web Service by calling GhostScript using Process()
This works perfectly when 'Debugging' an ASP.NET application on my local machine, but when I use the exact same code to try and print through a Web Service - also on my local machine - it doesn't work.
I don't get any kind of exception in VS - so far as the application is concerned, the application is working fine.
Here's the code I'm using to call the GhostScript printer:
Process process = new Process();
process.StartInfo.Arguments = #" -dPrinted -dBATCH -dNOPROMPT -dNOPAUSE -dNOSAFER -q -dNumCopies=1 -sDEVICE=mswinpr2 -dNoCancel -sPAPERSIZE=a4 -sOutputFile=\\spool\\\printserver\printer c:\\test\test.pdf";
process.StartInfo.FileName = #"C:\Program Files (x86)\GPLGS\gswin32c.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
process.OutputDataReceived += (sender, e) => {
if (e.Data == null)
{
outputWaitHandle.Set();
}
else
{
Console.Write(e.Data);
output.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
{
errorWaitHandle.Set();
}
else
{
Console.Write(e.Data);
error.AppendLine(e.Data);
}
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
if (process.HasExited == false) process.Kill();
return process.ExitCode == 0;
I thought this might have something to do with printers not being installed for All Users so have added them for all users. The only other thing that I can see it being is Permissions - but can't figure out a solution.
Does anyone have any ideas?
Thanks in advance for your help. :)
As suspected, this turned out to be a Permissions issue. After publishing the Web Service to IIS, I changed the Default Application Pool to use a User Identity, the printing suddenly started working correctly.
I will set up a user account specifically for printing - with no write permissions - and use this for now.
Related
So I am writing a C# program to manage a web server on Windows. I have it so the web server software starts with the users typed in configuration and starts the web server successfully. However, I am struggling with getting the IP addresses that the web server software is connecting to. I am attempting to show incoming and outgoing connection IPs of a certain exe but am only able to show all incoming outgoing connections of the whole computer not just one specific process exe like I am trying to. Here is the code where I am attempting to do this:
private void ipmonitor_Click(object sender, EventArgs e)
{
IPAddress[] addrList = Dns.GetHostByName(Dns.GetHostName()).AddressList;
string IP = addrList[0].ToString();
richTextBox2.Text = IP;
}
}
}
Is there a way to do this in C# code? Thanks.
One option is to run netstat, and parse the result. see a great example here for inspiration.
using (Process p = new Process())
{
ProcessStartInfo ps = new ProcessStartInfo();
ps.Arguments = "-a -n -o";
ps.FileName = "netstat.exe";
ps.UseShellExecute = false;
ps.WindowStyle = ProcessWindowStyle.Hidden;
ps.RedirectStandardInput = true;
ps.RedirectStandardOutput = true;
ps.RedirectStandardError = true;
p.StartInfo = ps;
p.Start();
StreamReader stdOutput = p.StandardOutput;
StreamReader stdError = p.StandardError;
string content = stdOutput.ReadToEnd() + stdError.ReadToEnd();
string exitStatus = p.ExitCode.ToString();
// inpect and parse content and exitStatus for errors
}
protected void Button1_Click(object sender, EventArgs e)
{
try
{
System.Diagnostics.Process process1 = new System.Diagnostics.Process();
process1.StartInfo.FileName = Server.MapPath(#"~\\bin\\HelloApp.exe");
process1.StartInfo.Arguments = "";
process1.Start();
process1.WaitForExit();
process1.Close();
}
catch (Exception ex)
{
Response.Write(ex);
}
}
try this code and see if you get any error. if you get an error check whether the published code bin has the HelloApp.exe.
protected void Button1_Click(object sender, EventArgs e)
{
string fileNameWithPath = Path.GetFullPath(#"~\\bin\\HelloApp.exe");
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Arguments = "";
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = true;
startInfo.FileName = fileNameWithPath;
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
StreamReader outputReader = exeProcess.StandardOutput;
StreamReader errorReader = exeProcess.StandardError;
string errors = errorReader.ReadToEnd();
if (!string.IsNullOrEmpty(errors))
throw new Exception(errors);
}
}
It is impossible to call another UI application by the application hosted in IIS. Due to the fact that there is no user session to render the UI. Since Windows Vista, there is a mechanic called Session Isolation, which separates the application and the service.
However, it will work when we start the application by Visual Studio locally since the current session is inherited with the account running Visual Studio.
For details, please refer to the below links.
How to start a process from an IIS hosted WCF service?
IIS Session isolation problem
I have a WebAPI that works great but need to add the ability to call to an EXE on the server to run some tasks with Video, the server is our machine and running IIS to host the WebAPI.
I have tried it working with Process() and the calls make it to the cmd.exe file I have written but the issue is that the user is IUSER and this won't work as the Video processing needs to use system hardware so needs to be the current logged in Windows User.
I don't want to give the IUSER this privilege for obvious security reasons so I am looking for another way to call and pass data to the an EXE or background task (Short running <3seconds) and for that process to reply with the results.
It's all on the server which we have full control over.
Current Code:
string exeLoc = Environment.ExpandEnvironmentVariables(baseEXELocation);
using var process = new Process();
process.StartInfo.FileName = exeLoc;
process.StartInfo.Arguments = $"{command}";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, data) =>
{
if (!String.IsNullOrEmpty(data.Data))
{
Console.WriteLine(data.Data);
consoleData += data.Data;
}
};
process.Start();
process.BeginOutputReadLine();
var exited = process.WaitForExit(1000 * 10); // (optional) wait up to 10 seconds
Thanks
You could try to use the impersonation in iis:
<identity impersonate="true" />
https://support.microsoft.com/en-us/help/306158/how-to-implement-impersonation-in-an-asp-net-application
or assign the administrator permission to the application pool by using application pool advance setting -> identity to the custom account.
or You can try using the verb runas in Process.Start to execute the exe file as an Administrator.
ProcessStartInfo proc = new ProcessStartInfo();
proc.WindowStyle = ProcessWindowStyle.Normal;
proc.FileName = myExePath;
proc.CreateNoWindow = false;
proc.UseShellExecute = false;
proc.Verb = "runas"; //this is how you pass this verb
I am trying to reach one simple requirement. I would like to create a C# library that talks to the git executable. I am writing a version control tool for my team that will allow access to git commands for non tech-savvy individuals. Unfortunately, I can not use any third party DLL's (I am using Unity and I do not want to push the requirement for Unity pro due to plugins), otherwise I would use GitSharp or something along those lines.
Currently, I have a function called RunGitCommand that is meant to do all my bidding. This snippet is as follows:
private void RunGitCommand(string executablePath, string arguments, int maxCommandDurationMilliseconds)
{
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
try
{
CommandOutput = string.Empty;
CommandError = string.Empty;
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.FileName = executablePath;
processStartInfo.Arguments = arguments;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
int processedTime = 0;
using (Process process = new Process())
{
StringBuilder outputData = new StringBuilder();
StringBuilder errorData = new StringBuilder();
process.StartInfo = processStartInfo;
process.OutputDataReceived += (sender, e) => {
outputWaitHandle.Set();
if (e.Data == null)
{
//outputWaitHandle.Set();
}
else
{
outputData.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
errorWaitHandle.Set();
if (e.Data == null)
{
//errorWaitHandle.Set();
}
else
{
errorData.AppendLine(e.Data);
}
};
process.Start();
if(process.Id == 0)
{
Environment.LogError("Process id is 0. Aborting.");
return;
}
RunningProcessDescriptor processDescriptor = new RunningProcessDescriptor(maxCommandDurationMilliseconds, process.Id);
ProcessIds.Add(processDescriptor);
Thread.Sleep(200);
int newMaxTime = maxCommandDurationMilliseconds - 100;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (process.WaitForExit(newMaxTime) && outputWaitHandle.WaitOne(newMaxTime) && errorWaitHandle.WaitOne(newMaxTime))
{
process.CancelOutputRead();
process.CancelErrorRead();
CommandOutput = outputData.ToString();
CommandError = errorData.ToString();
string combinedOutput = string.Join(System.Environment.NewLine, new string[]{ CommandError, CommandOutput }).Trim();
BatchOutput = string.Join(System.Environment.NewLine, new string[]{ BatchOutput, string.Format("----------// {0} {1} //----------", executablePath, arguments), CommandOutput }).Trim();
BatchError = string.Join(System.Environment.NewLine, new string[]{ BatchError, string.Format("----------// {0} {1} //----------", executablePath, arguments), CommandError }).Trim();
InterpretErrorsAndAddToLists(combinedOutput);
}
else
{
process.Close();
process.WaitForExit();
}
ProcessIds.Remove(processDescriptor);
}
}
catch(Exception genericException)
{
Environment.LogError(genericException.Message);
Environment.LogError(genericException.StackTrace);
}
}
}
Another thing to note is that I'm using this to run these commands so that they don't execute on the main thread:
ThreadPool.QueueUserWorkItem
As you should be able to identify by my commenting and layout, this function is the result of hours of troubleshooting and I am willing to try anything at this point to make it work. I've already moved to using asynchronous calls for receiving the output/error streams, and even added AutoResetEvent objects to cause my threads to wait for each other (although I am not completely familiar with them and might be doing something wrong).
No matter what I try, it seems to randomly hang and not allow the process to exit. When I manually kill the git process, the output is spit out (and is usually right) and the process exits and everything works as normal.
I'm at the point in troubleshooting and frustration where I need professional input. Here are my questions:
Is there something blatantly or obviously wrong with my code below? If I'm doing it wrong, please advise me how to properly execute this code.
Is there another solution where I do not need to include third party DLL files and can just use raw .NET to grab the git console process and interact with it (on mac and pc)?
Other alternatives to these approaches, such as one i've been considering, that uses a "client/server" architecture. I can use third party dll's and whatnot in a separate downloadable program that communicates to the git plugin via TCP to send and receive output/input to the git process. This one is much more work, but would potentially take less time than troubleshooting the git only version.
Just let me know what your professional opinions are so that I can rest at night :).
I look forward to talking with everyone!
-Zack
I tend to use a very simple piece of code for executing a non-interactive command, and getting the standard output from the result. I would suggest starting from something like this, and checking it doesn't hang. Then build any additional logic from there.
private static string ExecuteCommand(string command, string arguments)
{
command = System.Environment.ExpandEnvironmentVariables(command);
var process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
FileName = command,
Arguments = arguments
}
};
process.Start();
process.WaitForExit();
return process.StandardOutput.ReadToEnd();
}
It's often hard to isolate the problem when there's so much 'dead wood' in the code. Strip it back to the bare bones, and see if you can isolate the problem from there.
I am having trouble invoking a batch file with impersonation (.NET 4.0)
I have a web application that invokes a test batch file residing on the local file system of the server. When I run it without impersonation it runs fine. But with impersonation the batch file doesnt produce any output, nor does it returns any error.
Here is the code for executing the batch file that i use -
public static bool ExecuteBatchFile(string fileLocationPath, string filePath, string arguments, TextBox textBox)
{
try
{
ProcessStartInfo procStartInfo = new ProcessStartInfo(filePath);
procStartInfo.UseShellExecute = false;
procStartInfo.Arguments = arguments;
procStartInfo.WorkingDirectory = fileLocationPath;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.RedirectStandardInput = true;
procStartInfo.UserName = "XYZ";
procStartInfo.Domain = "ABC";
System.Security.SecureString pwd = new System.Security.SecureString();
foreach (char c in "PWD")
pwd.AppendChar(c);
procStartInfo.Password = pwd;
procStartInfo.LoadUserProfile = true;
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
textBox.Text += "output rcvd\r\n";
textBox.Text += e.Data;
};
proc.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
textBox.Text += "error rcvd\r\n";
textBox.Text += e.Data;
};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
proc.Close();
return true;
}
catch (Exception e)
{
textBox.Text += e.Message;
return false;
}
}
Without impersonation i can see the output of the batch file. I get Output received with output of batch file and then an empty OutputReceived, and then an empty ErrorReceived.
But with impersonation, i can see nothing! Just one event for OutputReceived with no data, and one event for ErrorReceived with no data.
I have set impersonation in the web config file as follows:
<identity impersonate="true" userName="ABC\XYZ" password="PWD"/>
I experienced similar problem with Windows 2003 Server where my service hosted in IIS was executing application.
Things i discovered:
1. You must ajust permissions on service and configure it to output stack trace (including folder where .Net puts built service).
2. Starting app with loading of user profile cannot be done from service. You can create wrapper application which you will execute without loading profile, and that app can in its turn execute app with loading of user profile. Wrapper must be built in Release mode and mustn't call Trace functions.
3. On service change you must
- restart application pool in IIS snapp-in
- restart site
- execute iisreset.