I'm trying to get half-duplex ssh connection in C#/.NET 5 with SSH.NET for sending protobuf-formatted packets. (Although full-duplex would be nice, I can emulate that using two streams)
So far I'm able to get data back and forth, however with a few caveats:
I need to send a newline in order to actually commit any info.
I'm only able to send plain text, sending protobuf-formatted binary data creates weird deserialization errors, probably related to encoding-errors.
Currently I'm trying to use SSH.NETs ShellStream on the client-side and deserializing using ProtoBufs non-generic deserializer, mapping types to indexes with a dict:
using SshClient client = new(connectionInfo);
var stream = client.CreateShellStream("", 80, 24, 800, 600, 1024, new Dictionary<TerminalModes, uint>());
stream.WriteLine("./bin/myProcess");
stream.Expect("Running");
// The stream is now at the point where the packets are supposed to be sent
// Two dicts for mapping types to indicies and back
// Just send strings as packets for now
Dictionary<int, Type> typeByIndex = new { [0] = typeof(string) };
Dictionary<Type, int> indexByType = new { [typeof(string)] = 0 };
// Send a packet over
Serializer.SerializeWithLengthPrefix(
stream, "Hello World!",
PrefixStyle.Base128, indexByType[typeof(string)]);
stream.WriteLine(""); // Write newline to commit and use SSH.NET writing method to directly flush
while (true)
{
if (Serializer.NonGeneric.TryDeserializeWithLengthPrefix(
stream, PrefixStyle.Base128,
field => typeByIndex[field-1], out var packet)) Console.WriteLine(packet);
}
With the server (./bin/myProcess) just reading from Console.OpenStandardInput() with the same while-loop.
This produces deserialization errors (Unexpected wire-type usually). My guess is that this has to do with encoding problems.
Given the not to great documentation of SSH.NET I don't really know what to try here.
To sum up:
How would I configure SSH.NET to allow sending arbitrary protobuf-formatted binary data back and forth (without any kind of encoding conversions or control characters causing issues) and how would I get around having to send a newline with every packet?
It sounds like the Stream it is giving you here is really a wrapper over the text-based console, and doesn't really support arbitrary binary. This is something you should probably ask the package creator - there may be some toggle to enable "binary mode" or similar, but without that - honestly I'm a little surprised it doesn't give you a text-writer/text-reader API instead of a stream.
Worst case, you can base-64 encode the protobuf data, and send that down a text protocol, and reverse it at the other end.
In .Net core, I have huge text files that need to be converted from Unix to Windows.
Since I can't load the file completly in memory (the files are too big), I read each byte one after the other, and when I encounter a LF, I output a LF+CR. This process works, but it takes a long time for huge files. Is there a more efficently way to do?
I thought about using a StreamReader, but the problem I'm having is that we don't know the source file encoding.
Any idea?
Thank you
Without knowing more about the specific files you're trying to process, I'd probably start off with something like the below and see if that gets me the results I want.
Depending on the specifics of your situation you may be able to do something more efficient, but if you're handling truly large datasets with unstructured text then it's usually a matter of throwing more powerful hardware at the problem if speed is still an issue.
You don't have to specify the Encoding to make use of the StreamReader class. Was there a specific problem with the reader you encountered?
const string inputFilePath = "";
const string outputFilePath = "";
using var sr = new StreamReader(inputFilePath);
using var sw = new StreamWriter(outputFilePath);
string line;
// Buffers each line into memory, but not the newline characters.
while ((line = await sr.ReadLineAsync()) != null)
{
// Write the contents of the string out to the "fixed" file (manually
// specifying the line ending you want).
await sw.WriteAsync(line + "\r\n");
}
I need to stream the standard output from ffmpeg.exe directly to a web response stream on the fly. I have no problem setting up the command line with Process/RedirectStandardOutput/etc to pipe the output stream.
However, the problem seems to be the output from Process.StandardOutput is not in the correct raw format. Something like this (pseudocode):
var ffpsi = new ProcessStartInfo("ffmpeg.exe", "-i input.mp3 -acodec copy -f mp3 -")
ffpsi.UseShellExecute = false;
ffpsi.RedirectStandardOutput = true;
var ffmpegProcess = new Process();
ffpmpegProcess.StartInfo = ffpsi;
ffmpegProcess.Start();
var outputFile = new FileStream("out.mp3");
ffmpegProcess.StandardOutput.BaseStream.CopyTo(outputFile);
creates a file a little larger than the original and it's clearly not a valid MP3.
I've played with various encodings copying strategies with the base streams and get data from the async callbacks, but nothing seems to work.
Any ideas here? I guess this comes down to how to get the raw binary output from ffmpeg stdout into a .NET stream I can pass to a response stream?
I´ve trying to solve this problem for nearly 2 days. There are a lot of more or fewer good solutions on the net, but not a single one fits my task perfectly.
Task:
Print a PDF programmatically
Do it with a fixed printer
Don´t let the user do more than one Button_Click
Do it silent - the more, the better
Do it client side
First Solutions:
Do it with a Forms.WebBrowser
If we have Adobe Reader installed, there is a plugin to show PDF´s in the webbrowser. With this solution we have a nice preview and with webbrowserControlName.Print() we can trigger the control to print its content.
Problem - we still have a PrintDialog.
Start the AcroRd32.exe with start arguments
The following CMD command let us use Adobe Reader to print our PDF.
InsertPathTo..\AcroRd32.exe /t "C:\sample.pdf" "\printerNetwork\printerName"
Problems - we need the absolute path to AcroRd32.exe | there is an Adobe Reader Window opening and it has to be opened until the print task is ready.
Use windows presets
Process process = new Process();
process.StartInfo.FileName = pathToPdf;
process.StartInfo.Verb = "printto";
process.StartInfo.Arguments = "\"" + printerName + "\"";
process.Start();
process.WaitForInputIdle();
process.Kill();
Problem - there is still an Adobe Reader window popping up, but after the printing is done it closes itself usually.
Solution - convince the client to use Foxit Reader (don´t use last two lines of code).
Convert PDF pages to Drawing.Image
I´ve no idea how to do it with code, but when I get this to work the rest is just a piece of cake. Printing.PrintDocument can fulfill all demands.
Anyone an idea to get some Drawing.Image´s out of those PDF´s or another approach how to do it?
Best Regards,
Max
The most flexible, easiest and best performing method I could find was using GhostScript. It can print to windows printers directly by printer name.
"C:\Program Files\gs\gs9.07\bin\gswin64c.exe" -dPrinted -dBATCH -dNOPAUSE -sDEVICE=mswinpr2 -dNoCancel -sOutputFile="%printer%printer name" "pdfdocument.pdf"
Add these switches to shrink the document to an A4 page.
-sPAPERSIZE=a4 -dPDFFitPage
If a commercial library is an option, you can try with Amyuni PDF Creator. Net.
Printing directly with the library:
For opening a PDF file and send it to print directly you can use the method IacDocument.Print. The code in C# will look like this:
// Open PDF document from file<br>
FileStream file1 = new FileStream ("test.pdf", FileMode.Open, FileAccess.Read);
IacDocument doc1 = new IacDocument (null);
doc1.Open (file1, "" );
// print document to a specified printer with no prompt
doc1.Print ("My Laser Printer", false);
Exporting to images (then printing if needed):
Choice 1: You can use the method IacDocument.ExportToJPeg for converting all pages in a PDF to JPG images that you can print or display using Drawing.Image
Choice 2: You can draw each page into a bitmap using the method IacDocument.DrawCurrentPage with the method System.Drawing.Graphics.FromImage. The code in C# should look like this:
FileStream myFile = new FileStream ("test.pdf", FileMode.Open, FileAccess.Read);
IacDocument doc = new IacDocument(null);
doc.Open(myFile);
doc.CurrentPage = 1;
Image img = new Bitmap(100,100);
Graphics gph = Graphics.FromImage(img);
IntPtr hdc = gph.GetHDC();
doc.DrawCurrentPage(hdc, false);
gph.ReleaseHdc( hdc );
Disclaimer: I work for Amyuni Technologies
I tried many things and the one that worked best for me was launching a SumatraPDF from the command line:
// Launch SumatraPDF Reader to print
String arguments = "-print-to-default -silent \"" + fileName + "\"";
System.Diagnostics.Process.Start("SumatraPDF.exe", arguments);
There are so many advantages to this:
SumatraPDF is much much faster than Adobe Acrobat Reader.
The UI doesn't load. It just prints.
You can use SumatraPDF as a standalone application so you can include it with your application so you can use your own pa. Note that I did not read the license agreement; you should probably check it out yourself.
Another approach would to use spooler function in .NET to send the pre-formatted printer data to a printer. But unfortunately you need to work with win32 spooler API
you can look at How to send raw data to a printer by using Visual C# .NET
you only can use this approach when the printer support PDF document natively.
My company offers Docotic.Pdf library that can render and print PDF documents. The article behind the link contains detailed information about the following topics:
printing PDFs in Windows Forms or WPF application directly
printing PDFs via an intermediate image
rendering PDFs on a Graphics
There are links to sample code, too.
I work for the company, so please read the article and try suggested solutions yourselves.
Process proc = new Process();
proc.StartInfo.FileName = #"C:\Program Files\Adobe\Acrobat 7.0\Reader\AcroRd32.exe";
proc.StartInfo.Arguments = #"/p /h C:\Documents and Settings\brendal\Desktop\Test.pdf";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
for (int i = 0; i < 5; i++)
{
if (!proc.HasExited)
{
proc.Refresh();
Thread.Sleep(2000);
}
else
break;
}
if (!proc.HasExited)
{
proc.CloseMainWindow();
}
You can use ghostscript to convert PDF into image formats.
The following example converts a single PDF into a sequence of PNG-Files:
private static void ExecuteGhostscript(string input, string tempDirectory)
{
// %d will be replaced by ghostscript with a number for each page
string filename = Path.GetFileNameWithoutExtension(input) + "-%d.png";
string output = Path.Combine(tempDirectory, filename);
Process ghostscript = new Process();
ghostscript.StartInfo.FileName = _pathToGhostscript;
ghostscript.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
ghostscript.StartInfo.Arguments = string.Format(
"-dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -r300 -sOutputFile=\"{0}\" \"{1}\"", output, input);
ghostscript.Start();
ghostscript.WaitForExit();
}
If you prefer to use Adobe Reader instead you can hide its window:
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
I found a slightly different version of your code that uses the printto verb. I didn't try it, but maybe it helps you:
http://vbcity.com/forums/t/149141.aspx
If you're interested in commercial solutions which do exactly what you require then there are quite a few options. My company provides one of those options in a developer toolkit called Debenu Quick PDF Library.
Here is a code sample (key functions are PrintOptions and PrintDocument):
/* Print a document */
// Load a local sample file from the input folder
DPL.LoadFromFile("Test.pdf", "");
// Configure print options
iPrintOptions = DPL.PrintOptions(0, 0, "Printing Sample")
// Print the current document to the default
// printing using the options as configured above.
// You can also specify the specific printer.
DPL.PrintDocument(DPL.GetDefaultPrinterName(), 1, 1, iPrintOptions);
I know that the tag has Windows Forms; however, due to the general title, some people might be wondering if they may use that namespace with a WPF application -- they may.
Here's code:
var file = File.ReadAllBytes(pdfFilePath);
var printQueue = LocalPrintServer.GetDefaultPrintQueue();
using (var job = printQueue.AddJob())
using (var stream = job.JobStream)
{
stream.Write(file, 0, file.Length);
}
Now, this namespace must be used with a WPF application. It does not play well with ASP.NET or Windows Service. It should not be used with Windows Forms, as it has System.Drawing.Printing. I don't have a single issue with my PDF printing using the above code.
Note that if your printer does not support Direct Printing for PDF files, this won't work.
What about using the PrintDocument class?
http://msdn.microsoft.com/en-us/library/system.drawing.printing.printdocument.aspx
You just need to pass the filename of the file you want to print (based on the example).
HTH
As of July 2018, there is still no answer for the OP. There is no free way to 1) silently print your pdf for a 2) closed source project.
1) You can most certainly use a process i.e. Adobe Acrobat or Foxit Reader
2) Free solutions have a GPL (GNU's General Public License). This means you must open your source code if giving the software, even for free, to anyone outside your company.
As the OP says, if you can get a PDF to Drawing.Image, you can print it with .NET methods. Sadly, software to do this also requires payment or a GPL.
Been pulling my hair out over what should have been a quick and easy task.
I have a self-hosted WCF service in which I need to implement real-time video transcoding, the transcoding isn't a problem as such, using FFMpeg to a local temp file.
Quick sample of what my code looks like;
public Stream StreamMedia(int a)
{
String input = #"\media\" + a + ".mkv";
String output = #"\temp\transcoded\" + a + DateTime.Now.Ticks.ToString() + ".wmv";
ProcessStartInfo pi = new ProcessStartInfo("ffmpeg.exe");
pi.Arguments = "-i " + input + " -y -ab 64k -vcodec wmv2 -b 800k -mbd 2 -cmp 2 -subcmp 2 -s 320x180 -f asf " + output;
Process p = new Process;
p.StartInfo = pi;
p.Start();
Thread.Sleep(2500);
return new FileStream(output, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
The problem I am facing is that the returned Stream only gives me what was written to the file when it is returned - resulting in a rather short video file :)
I've played around with the obvious here, but no matter what I do it will only return what's available there and then.
What I need to happen is for the Stream to be returned with no respect to the actual current lenght of the output file - there is other code involved which makes sure the data is never sent to the client faster than what FFMpeg manages to encode, so basically I just need an open-ended stream.
Any takers?
One solution would be to create your custom Stream class which would wrap around the file from disk; BUT, there's also the concurrency issue, meaning that you need some locking mechanism as for the writing process (video transcoder) to properly share the file with your FileStream.
Is it possible for your transcoder to create multi-volume output? If so, then your lucky and this would work with (almost) no pain at all, just do the streaming of the volume N, then the transcoder writes the volume N + 1, and you'll not have any file access concurrency issues.
happy coding!
- Adrian
The simplest may be to use the Streaming Media service that is built into the operating system. See: http://technet.microsoft.com/en-us/windowsserver/dd448620
The other way to do it would be not to read from the file, but send the stream that is writing to the file, straight out to the client.
What is obvious is, this cannot be done via file system. You need a dynamic solution.
You can do it via your own made media service. In your case it could be a WCF or windows service.
This service should be responsible for both writing to the file (as data receives) and streaming.