This question already has an answer here:
How to run console application which is send pdf to printer in server?
(1 answer)
Closed 2 years ago.
I download .zip file from outlook then send pdf files to printer. It works on my local machine while compiling, However I setup published app on server, it downloads .zip file from outlook and open it but it can not send to printer . How can I handle that?
This is my code:
static void Main(string[] args)
{
ExchangeService exchange = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
exchange.Credentials = new WebCredentials("mail#", "password");
exchange.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
if (exchange != null)
{
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
FindItemsResults<Item> result = exchange.FindItems(WellKnownFolderName.Inbox, sf, new ItemView(1));
foreach (Item item in result)
{
EmailMessage message = EmailMessage.Bind(exchange, item.Id);
message.IsRead = true;
message.Update(ConflictResolutionMode.AutoResolve);
List<Attachment> zipList = message.Attachments.AsEnumerable().Where(x => x.Name.Contains(".zip")).ToList();
foreach (Attachment Attachment in zipList)
{
if (Attachment is FileAttachment)
{
FileAttachment f = Attachment as FileAttachment;
f.Load("C:\\TEST\\" + f.Name);
string zipPath = #"C:\TEST\" + f.Name;
string extractPath = #"C:\TEST\" + Path.GetRandomFileName();
ZipFile.ExtractToDirectory(zipPath, extractPath);
string[] filePaths = Directory.GetFiles(extractPath, "*.pdf",
SearchOption.TopDirectoryOnly);
foreach (string path in filePaths)
{
SendToPrinter(path);
}
}
}
}
}
}
static void SendToPrinter(string path)
{
try
{
var printerName = "EPSON L310 Series";
ProcessStartInfo info = new ProcessStartInfo(path);
info.Verb = "PrintTo";
info.UseShellExecute = false;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
info.Arguments = "\"" + printerName + "\"";
Process p = new Process();
p.StartInfo = info;
p.Start();
p.WaitForInputIdle();
System.Threading.Thread.Sleep(3000);
if (false == p.CloseMainWindow())
p.Kill();
}
catch (Exception ex)
{
Console.WriteLine("error:", ex.Message);
Console.ReadLine();
}
}
As I said, everything is work fine but printer not working. I can print pdf manually I mean machine works. Also this app works on my local machine
You need to change:
info.UseShellExecute = false;
to:
info.UseShellExecute = true;
since the path is a path to a PDF file, not an executable.
As per the docs:
When you use the operating system shell to start processes, you can
start any document (which is any registered file type associated with
an executable that has a default open action) and perform operations
on the file, such as printing, by using the Process object. When
UseShellExecute is false, you can start only executables by using the
Process object.
Additionally:
If you set the WindowStyle to ProcessWindowStyle.Hidden,
UseShellExecute must be set to true.
You are using Hidden - thus UseShellExecute must be true.
Related
My console Application is working when double clicking application and it downloads .zip files from mail then send pdfs to printer. However, Problem is that when I use task scheduled to run this app, it reads mail then download .zip files but it doesnt send pdf files to printer. I didnt Understand what problem is that. How can I handle this ?
static void Main(string[] args)
{
ExchangeService exchange = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
exchange.Credentials = new WebCredentials("mail", "password);
exchange.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
if (exchange != null)
{
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
FindItemsResults<Item> result = exchange.FindItems(WellKnownFolderName.Inbox, sf, new ItemView(20));
foreach (Item item in result)
{
EmailMessage message = EmailMessage.Bind(exchange, item.Id);
message.IsRead = true;
message.Update(ConflictResolutionMode.AutoResolve);
List<Attachment> zipList = message.Attachments.AsEnumerable().Where(x => x.Name.Contains(".zip")).ToList();
foreach (Attachment Attachment in zipList)
{
if (Attachment is FileAttachment)
{
FileAttachment f = Attachment as FileAttachment;
f.Load("C:\\pdfFiles\\" + f.Name);
string zipPath = #"C:\pdfFiles\" + f.Name;
string extractPath = #"C:\pdfFiles\" + Path.GetRandomFileName();
ZipFile.ExtractToDirectory(zipPath, extractPath);
string[] filePaths = Directory.GetFiles(extractPath, "*.pdf",
SearchOption.TopDirectoryOnly);
foreach (string path in filePaths)
{
SendToPrinter(path);
}
}
}
}
}
}
static void SendToPrinter(string path)
{
var printerName = "EPSON L310 Series";
ProcessStartInfo info = new ProcessStartInfo();
info.Verb = "PrintTo";
info.FileName = filePath;
info.CreateNoWindow = true;
info.Arguments = "\"" + printerName + "\"";
info.WindowStyle = ProcessWindowStyle.Hidden;
Process p = new Process();
p.StartInfo = info;
p.Start();
System.Threading.Thread.Sleep(3000);
}
By the way, code which is bottom work but I need to use above
static void SendToPrinter(string path)
{
PdfDocument pdf = new PdfDocument();
pdf.LoadFromFile(path);
pdf.Print();
pdf.Dispose();
}
My console Application is working when double clicking application
It appears that taskscheduler is configured to run under a different account than currently logged in user.
Check the user-account that is configured in the task scheduler to trigger the console app and ensure that its the same account/user that you have logged in to the computer as.
Edit Task > General > Security Options > When running the task use the following account > Select *your* account
If your task is interactive (has a GUI) Checkmark Run only when user is logged on
i connected over WMI on a remote Machine.
username and password is correctly set.
var options = new ConnectionOptions();
servicePath = "\\\\Testserver\\root\\cimv2";
options.EnablePrivileges = true;
options.Username = username;
options.Password = pwd;
serviceScope = new ManagementScope(servicePath, options);
serviceScope.Connect();
this is the code sequence i want to run on the remote machine
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
Console.WriteLine($#"Volume {index} {label}:\");
}
}
if i am going to call the process over wmi like this...
object[] theProcessToRun = { "diskpart" };
using (var managementClass = new ManagementClass(serviceScope, new ManagementPath("Win32_Process"), new ObjectGetOptions()))
{
managementClass.InvokeMethod("Create", theProcessToRun);
}
i can call the process, but havent the possibilites to hand over the commands i would like to execute in this process...
How to solve this ?
You can use a script to be passed in as part of the command line arguments that will contain your command.
Reference: https://www.computerhope.com/diskpart.htm
Also you should redirect your output to a file using cmd as you should get your output directly either.
For script, make sure you use a unc that other machine/user has access to. For output file, make sure you use a unc that other machine/user can write to and you can read from via the program.
Is there a way to display the windows popup msg by using C#?
I mean by using the windows msg.exe program that can be used in cmd, for example:" msg * Hello "
PD: I know that i can use MessageBox.Show() instead. But i want to know if this is possible :(
I wrote 2 ways to do it but none worked:
Process.Start("cmd.exe","/C msg * Hello");
and...
Process cmd = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/C msg * Hello",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
}
};
cmd.Start();
Did you try adding msg.exe directly?
Process cmd = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"msg.exe",
Arguments = #"* /v Hello",
WorkingDirectory = Environment.SystemDirectory;
WindowStyle = ProcessWindowStyle.Normal
}
};
cmd.Start();
I encountered the same problem.
This was because the project was configured as "AnyCPU" but the "Prefer 32-bit" option was checked in the "Build" tab of the project configuration. Uncheck that option and the problem will disappear.
Edit: personnaly, I use the following function to locate the binary according to the executable and OS platform (32/64 bits):
public static bool LocateMsgExe(out string returnedMsgPath)
{
returnedMsgPath = null;
string[] msgPaths = new string[] { Environment.ExpandEnvironmentVariables(#"%windir%\system32\msg.exe"),
Environment.ExpandEnvironmentVariables(#"%windir%\sysnative\msg.exe") };
foreach (string msgPath in msgPaths)
{
if (File.Exists(msgPath))
{
returnedMsgPath = msgPath;
return true;
}
}
return false;
}
And for invoking it :
if (LocateMsgExe(out string strMsgPath))
{
Process.Start(strMsgPath, "* \"Hello world!\"");
}
Regards,
damien.
This is my solution. It consists of a webpage (.aspx) with a listbox (lstComputers), a textbox (txtMessageToSend), a dropdownlist to select the OU that contains the computers that will receive the message and a button (btnSendMessage).
This is the code for btnSendMessage on the aspx.cs
protected void btnSendMessage_Click(object sender, EventArgs e)
{
string searchbase = ddlZaal.SelectedItem.Text; //This is a dropdownlist to select an OU
DirectoryEntry entry = new DirectoryEntry("LDAP://OU=" + searchbase + ",OU=YourOU,OU=YourSubOU," + Variables.Domain + ""); //Variables.Domain is specified in the Web.config
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = ("(objectClass=computer)");
foreach (SearchResult result in mySearcher.FindAll())
{
DirectoryEntry directoryObject = result.GetDirectoryEntry();
string computernaam = directoryObject.Properties["Name"].Value.ToString();
lstComputers.Items.Add(computernaam); //This is a listbox that shows the computernames. To each computer a message is sent.
string pingnaam = computernaam + "dns.suffix"; //Might be necessary for connecting to the computes in the domain
string MessageToSend = txtMessageToSend.Text; //The text in this textbox will be the messagetext
Process process = new Process();
ProcessStartInfo psi = new ProcessStartInfo(#"C:\inetpub\wwwroot\PsExec.exe"); //Location of PsExec.exe on the webserver that hosts the web-application.
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.RedirectStandardInput = true;
psi.WindowStyle = ProcessWindowStyle.Minimized;
psi.CreateNoWindow = true;
psi.Arguments = "/accepteula -s -i \\\\" + pingnaam + " cmd /c msg.exe * " + MessageToSend;
process.StartInfo = psi;
process.Start();
}
}
i am genrating pdf from html code.Here i find code for generating using wkhtmltopdf
private void WritePDF(string HTML)
{
string inFileName,
outFileName,
tempPath;
Process p;
System.IO.StreamWriter stdin;
ProcessStartInfo psi = new ProcessStartInfo();
tempPath = Request.PhysicalApplicationPath + "ExcelFiles\\";
inFileName = Session.SessionID + ".htm";
outFileName = Session.SessionID + ".pdf";
// run the conversion utility
psi.UseShellExecute = false;
psi.FileName = "E:\\wkhtmltopdf";
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
// note that we tell wkhtmltopdf to be quiet and not run scripts
// NOTE: I couldn't figure out a way to get both stdin and stdout redirected so we have to write to a file and then clean up afterwards
psi.Arguments = "-q -n - " + tempPath + outFileName;
p = Process.Start(psi);
try
{
stdin = p.StandardInput;
stdin.AutoFlush = true;
stdin.Write(HTML);
stdin.Close();
if (p.WaitForExit(15000))
{
// NOTE: the application hangs when we use WriteFile (due to the Delete below?); this works
Response.BinaryWrite(System.IO.File.ReadAllBytes(tempPath + outFileName));
//Response.WriteFile(tempPath + outFileName);
}
}
finally
{
p.Close();
p.Dispose();
}
// delete the pdf
System.IO.File.Delete(tempPath + outFileName);
}
I found this answer from here
how to pass html as a string using wkhtmltopdf?
Here i get error Could not find file on
if (p.WaitForExit(15000))
{
// NOTE: the application hangs when we use WriteFile (due to the Delete below?); this works
Response.BinaryWrite(System.IO.File.ReadAllBytes(tempPath + outFileName));
//Response.WriteFile(tempPath + outFileName);
}
how i can create file for this now?
It gets Created on stdin.Close();
Please check if the html in stdin.Write(HTML); has some content in it
I run a Web Role on Windows Azure to receive AAC audio files (uploaded by base64 string) and store them into blob. it works fine by now.
Next, I also have to convert them into MP3 and store the MP3s into blob too. I decided to use something like ffmpeg.exe -i path.aac path.mp3.
The problems are that:
How to call external ffmpeg.exe inside a web service of a web role?
what would be the path?
Please help me if you know. Thank you in advance.
I suggest that you use a Local Storage Resource for your webrole where you can download the AAC files from the blob storage, and have them converted to MP3. Then upload back to blob storage.
Side note is that you can also use the Path.GetTempFileName() to get a temporary file name for your AAC / MP3 files, but I don't encourage to do so (even if I've done it before).
As for the actuall ffmpeg running, you might want to browse the code for AzureVideoConv, which I've built some time ago. You will find a lot of useful code there.
Here is a sample of the actual ffmpeg call (note that I download the exe from a blob storage, to avoid bloating my azure package with external exe files, and to easily update the ffmpeg.exe when required):
internal void ConvertFile(string inputFileName, Guid taskID)
{
string tmpName = string.Format(
"{0}\\{1}.flv",
Path.GetTempPath(), inputFileName.Substring(inputFileName.LastIndexOf("\\")+1));
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = this._processorExecutable;
psi.Arguments = string.Format(#"-i ""{0}"" -y ""{1}""", inputFileName, tmpName);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardError = true;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(psi))
{
exeProcess.PriorityClass = ProcessPriorityClass.High;
string outString = string.Empty;
// use ansynchronous reading for at least one of the streams
// to avoid deadlock
exeProcess.OutputDataReceived += (s, e) => {
outString += e.Data;
};
exeProcess.BeginOutputReadLine();
// now read the StandardError stream to the end
// this will cause our main thread to wait for the
// stream to close (which is when ffmpeg quits)
string errString = exeProcess.StandardError.ReadToEnd();
Trace.WriteLine(outString);
Trace.TraceError(errString);
byte[] fileBytes = File.ReadAllBytes(tmpName);
if (fileBytes.Length > 0)
{
this._sSystem.SaveOutputFile(
fileBytes,
tmpName.Substring(tmpName.LastIndexOf("\\")+1),
taskID
);
}
}
}
catch (Exception e)
{
Trace.TraceError(e.Message);
}
}
NOTE the last check in of the project is using Windows Azure SDK 1.3
Thank you a lot #astaykov. You did a good job. Though It's not specific for my case(I need a specific piece of code instead of a whole large project), but it really inspired me. For specifying into my case, I am going to answer this question by my own - note that I did this based on #astaykov's code with somewhere directly copy&paste.
Firstly, configure the role with a Local Storage Resource. Then get its path by these code:
LocalResource converter_path =
RoleEnvironment.GetLocalResource("AudioConvertSpace");
string rootPathName = converter_path.RootPath;
get the path of ffmpeg.exe, xxx.aac and xxx.mp3 in the local storage:
string aac_path = rootPathName + "\\" + "fmwa-" + guidguid + ".aac";
string mp3_path = rootPathName + "\\" + "fmwa-" + guidguid + ".mp3";
string exe_path = rootPathName + "\\" + "ffmpeg.exe";
write the .aac file to local storage:
System.IO.File.WriteAllBytes(aac_path, decoded_audio_byte_array);
keep in mind that the local storage is not guaranteed to be stable or durable, so check the existence of the ffmpeg.exe -- if it doesn't exist, download it from blob.
if (System.IO.File.Exists(exe_path) == false)
{
var exeblob = _BlobContainer.GetBlobReference("ffmpeg.exe");
exeblob.DownloadToFile(exe_path, null);
}
initial and run the ffmpeg.exe process:
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = exe_path;
psi.Arguments = string.Format(#"-i ""{0}"" -y ""{1}""",
aac_path, mp3_path);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardError = true;
Process exeProcess = Process.Start(psi);
exeProcess.PriorityClass = ProcessPriorityClass.High;
string outString = string.Empty;
exeProcess.OutputDataReceived += (s, e) => {
outString += e.Data;
};
exeProcess.BeginOutputReadLine();
string errString = exeProcess.StandardError.ReadToEnd();
Trace.WriteLine(outString);
Trace.TraceError(errString);
exeProcess.WaitForExit();
upload the output of ffmpeg.exe into the blob storage:
byte[] mp3_audio_byte_array = System.IO.File.ReadAllBytes(mp3_path);
var mp3blob = _BlobContainer.GetBlobReference("fmwa-"+guidguid+".mp3");
mp3blob.Properties.ContentType = "audio/mp3";
mp3blob.UploadByteArray(mp3_audio_byte_array);
clean the temp files:
System.IO.File.Delete(aac_path);
System.IO.File.Delete(mp3_path);