How to print files to client rollpaper printer from MVC Web Application? - c#

In my project I came across this problem that I have been struggling for so much hours. Hope somebody could help me.
My client needs to print many tickets from the web (with one click) with some info from the database, let's say Name, Date and Price. All the tickets will be printed on a Rollpaper Printer, so after every ticket it should autocut each one.
Which would be an appropriate way to achieve this task?
I have a working code, that uses the PrintDocument Class (code below), the problem is that I need to have access to the printer to use it.
Scenario:
I'm using ASP.NET MVC3 on Visual Studio 2010.
The app is on a Shared Server.
Need to generate many tickets that will print all at once.
I have access to the Client PC, so I can install everything there and touch whatever is neccesary.
The app is used with Firefox and/or Chrome.
The printer is an Epson TMT82.
Thanks in advance.
public static bool PrintTicket(ref string Msg, string Impresora)
{
bool result = false;
try
{
System.Drawing.Printing.PrintDocument Pd = new System.Drawing.Printing.PrintDocument();
//If empty, Default
if(!String.IsNullOrEmpty(Impresora))
Pd.PrinterSettings.PrinterName = Impresora;
Margins margins = new Margins(10, 0, 0, 0);
Pd.DefaultPageSettings.Margins = margins;
Pd.PrintController = new System.Drawing.Printing.StandardPrintController();
Pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(printTicket_PrintPage);
Pd.Print();
PrintServer ps = new PrintServer();
PrintQueue printerQueue = LocalPrintServer.GetDefaultPrintQueue();
SpotTroubleUsingProperties(ref Msg, printerQueue);
result = (Msg == String.Empty);
if (!result)
{
var jobs = printerQueue.GetPrintJobInfoCollection();
foreach (var job in jobs)
{
job.Cancel();
}
}
}
catch (Exception ex)
{
Msg = ex.Message;
}
return result;
}

Related

Microsoft OPOS hangs on Claim after PC restart

I am using 'Microsoft Point of Service for .NET v1.14.' dlls to connect with Posiflex Thermal Printer (PP8800 model).
Code snippet which I am using is also pasted below. I am running this code from WPF application.
But I am facing one strange issue. If printer is powered on and machine on which the printer is connected is restarted, below pasted code is not able to claim the printer and it also does not come out of the Claim() statement. Whereas if I power off the printer and then power it on before running the below code snippet then it is able to claim the printer.
PosExplorer posExplorer = new PosExplorer();
var printerList = posExplorer.GetDevices(DeviceType.PosPrinter);
foreach (DeviceInfo item in printerList)
{
if (item.ServiceObjectName != printerName)
continue;
posPrinter = (PosPrinter)posExplorer.CreateInstance(item);
posPrinter.Open();
posPrinter.PowerNotify = PowerNotification.Enabled;
posPrinter.FlagWhenIdle = true;
posPrinter.StatusUpdateEvent += PosPrinter_StatusUpdateEvent;
posPrinter.DirectIOEvent += PosPrinter_DirectIOEvent;
posPrinter.ErrorEvent += PosPrinter_ErrorEvent;
posPrinter.OutputCompleteEvent += PosPrinter_OutputCompleteEvent;
try
{
posPrinter.Claim(100);
if (posPrinter.Claimed)
{
isPrinterClaimed = true;
}
else
{
isPrinterClaimed = false;
}
}
catch (Exception ex)
{
isPrinterClaimed = false;
}
break;
}
Has anybody faced the same issue while using OPOS libraries?

CEFSharp app displays blank screen and then responds on it's own after a minute

I've been working on a app which uses CEFSharp (version 83.4.20) to load my companies VOIP platform (engage.ringcentral.com). The source is up on Github https://github.com/dylanlangston/EngageRC
The app has been working for about a month now and after putting it into production I've received reports of an odd issue. People are seeing a blank screen and unable to interact with the webpage (
Example of issue ). After a few minutes the issue seems to resolve itself and their able to interact with the webpage again. I haven't been able to reproduce the issue myself.
I thought this might be a rendering issue. So far I've tried to adjust the following in my app:
Remove the lines below.
settings.CefCommandLineArgs.Add("disable-gpu");
settings.CefCommandLineArgs.Add("disable-gpu-shader-disk-cache", "1");
Replace them with.
settings.CefCommandLineArgs.Add("disable-gpu-compositing");
Unfortunately this hasn't resolved the issues and I'm unsure what I'm missing.
The relevant code for CEF Initialization is located in https://github.com/dylanlangston/EngageRC/blob/master/Windows/MainWindow.Designer.cs under the InitializeChromium method. See Below
//
// Chrome
//
private CefSharp.WinForms.ChromiumWebBrowser chromeBrowser;
public ChromiumWebBrowser InitializeChromium(string URL, string Name)
{
// Check if already Initialized
if (Cef.IsInitialized == false)
{
CefSettings settings = new CefSettings();
// Enable Logging if debugging or console window
if (!this.debug || !this.console)
{
settings.LogSeverity = LogSeverity.Disable;
}
else
{
settings.LogSeverity = LogSeverity.Verbose;
}
// Enable Microphone settings
settings.CefCommandLineArgs.Add("enable-media-stream", "1");
// Set Custom Browser Paths
settings.CefCommandLineArgs.Add("disable-gpu-shader-disk-cache", "1");
// Disable GPU to fix rendering issues on some machines.
settings.CefCommandLineArgs.Add("disable-gpu");
// Disable CORS protection (cross site scripting) which the engage.ringcentral.com site doesn't seem to like/respect, disabled as the website doesn't seem to improve with this turned on.
//settings.CefCommandLineArgs.Add("disable-web-security");
// Enable session Cookie persistence, disabled as it's unneeded.
//settings.PersistSessionCookies = true;
// Custom Browser paths
settings.BrowserSubprocessPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\CefSharp.BrowserSubprocess.exe");
settings.LocalesDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\locales\");
settings.ResourcesDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\");
// Check if Resources folder is writable. If it isn't then write to application data.
DirectoryInfo di = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\"));
if ((di.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
settings.RootCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"EngageRC\CEFSharp\cache");
settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"EngageRC\CEFSharp\cache");
settings.LogFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"EngageRC\CEFSharp\debug.log");
}
else
{
settings.RootCachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\cache");
settings.CachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\cache");
settings.LogFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\debug.log");
}
// Initialize cef with the provided settings or add new tab
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
}
// Create a browser component
chromeBrowser = new ChromiumWebBrowser(URL);
// Set Name
chromeBrowser.Name = Name;
// Adjust size and scaling
this.chromeBrowser.Dock = DockStyle.Fill;
this.chromeBrowser.Width = this.Width;
this.chromeBrowser.Height = this.Height;
// Add control
this.Controls.Add(chromeBrowser);
// Bring to front
this.chromeBrowser.BringToFront();
// Set label to Goodbye.
this.label1.Text = "Goodbye";
// Return control
return chromeBrowser;
}
I wasn't able to find any previous questions with my Google foo so it seems this isn't something other's are seeing their applications. I'm still learning the ropes when it comes to C# so it's 100% possible this is my own fault and something I'm doing wrong.
I was able to get verbose logging from a machine that experienced the issue. I'm mostly seeing the following error. Full logs at https://gist.github.com/dylanlangston/194e389ead437e6c6fe08b2d4746bf43
[0729/083616.491:ERROR:paint_controller.cc(646)] PaintController::FinishCycle() completed
Any ideas or recommendations to help troubleshoot this is appreciated!
Updated: Based on #amaitland's helpful comments it seems this behavior may be caused by using Thread.Sleep on a thread on I'm not supposed to.
After reviewing my code it looks like I have this in two locations.
First I call thread.sleep to wait until the browsers zoom level matches the value I've saved to the windows isolatedstorage. This shouldn't be running except when the application first starts. I don't think this is the problem but I am posting it here anyways to be safe. (https://github.com/dylanlangston/EngageRC/blob/master/CEFSharpHandlers.cs)
public void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
ChromiumWebBrowser chrome = (ChromiumWebBrowser)sender;
if (!args.IsLoading) // Loading finished.
{
// If first load set zoom level to previous level
if (firstLoad < 3)
{
try
{
double zoom = double.Parse(ConfigReader.ReadIsolatedStorage("z"), System.Globalization.CultureInfo.InvariantCulture);
while (args.Browser.GetZoomLevelAsync().Result != zoom) { chrome.SetZoomLevel(zoom); Thread.Sleep(50); }
}
catch { }
firstLoad++;
}
// These JS and CSS modifications built into EngageRC
string hotfixJS = "";
string hotfixCSS = "/* Fix for dropdowns */ ul.dropdown-menu[style=\\\"opacity: 1;\\\"] {display: block !important; } /* End fix for dropdowns */";
// Inject custom javascript and css code on page load
chrome.ExecuteScriptAsync(hotfixJS);
chrome.ExecuteScriptAsync("var engageRCSS = document.getElementById('EngageRCSS'); if (!engageRCSS) { var node = document.createElement('style'); node.setAttribute('id', 'EngageRCSS'); node.innerHTML = \"" + hotfixCSS +"\"; document.body.appendChild(node); }");
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.js"))) { chrome.ExecuteScriptAsync(System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.js"))); }
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.css"))) { chrome.ExecuteScriptAsync("var customCSS = document.getElementById('customcss'); if (!customCSS) { var node = document.createElement('style'); node.setAttribute('id', 'customcss'); node.innerHTML = \"" + System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.css")).Replace("\n", String.Empty).Replace("\r", String.Empty).Replace("\t", String.Empty).Replace("\"", "\\\"") + "\"; document.body.appendChild(node); }"); }
}
}
I also have it when displaying a notification. This is most likely my problem as this is running in the main application thread... (https://github.com/dylanlangston/EngageRC/blob/master/NotificationSupport.cs)
public void displayNotification(string Title = "EngageRC", string Message = "")
{
// Display notification for 5 seconds
Notify notification = MainWindow.notification;
try
{
if (!IsActive(hWnd)) // Check if application is already active.
{
string config = ConfigReader.GetConfigValue("NotificationsDisabled").ToLower();
if (!(config == "true" || config == "1"))
{
notification.NewNotification(Title, Message, 5000);
}
config = ConfigReader.GetConfigValue("GetFocusOnCallDisabled").ToLower();
if (!(config == "true" || config == "1") && (Message == "You have incoming call"))
{
// Minimize window
ShowWindow(hWnd, 0x02);
// Restore window to previous state.
ShowWindow(hWnd, 0x09);
}
config = ConfigReader.GetConfigValue("GetFocusOnPendingDispositionDisabled").ToLower();
if (!(config == "true" || config == "1") && (Message == "You have a disposition pending"))
{
// Minimize window
ShowWindow(hWnd, 0x02);
// Restore window to previous state.
ShowWindow(hWnd, 0x09);
}
}
}
catch { notification.SetIconVisible(false); }
finally
{
Thread.Sleep(4999);
notification.SetIconVisible(false);
}
}
Based on what I've heard I should make the Notification Form run on a different thread from the main application then? Or am I way off base?
Update:
I was able to move the notification into it's own thread. I'm closing this issue and will have the user's test to see if the blank screen persists.
Thanks for the help again!!
#amaitland's comments were helpful in narrowing this issue down.

PrintQueue.Refresh don't show the status of printer

I need to run a Windows Service of server X to show the status of all printers: Out of paper, no toner, etc.
The service is running on a machine but of course not all printers are installed on it. Even when the printers are installed on the machine, we do NOT have the status of the printers!
The only thing I was able to do is to remove the paper, print a test page (notepad), and now I can see that I'm missing paper with the code below, but as you might thing, this is not doable: I don't want to send a test page to every printers of the network every 10 minutes or so!
I try to query PrintQueue.Refresh but status is not updating, I don't see that the printer tray is open (or missing paper, or no toner, whatever I do with the printer.)
BTW, Win32_printer don't show me a better result.
NOTE:
MonitoringWS is the web service that can access the database.
Printers is the list of printers that we want to query.
This is what I try to do.
var printServers = GetListOfPrinterServers();
var listPrinters = printers as List<Printer> ?? printers.ToList();
foreach (
var printServer in
printServers.Select(
server => new PrintServer(server, PrintSystemDesiredAccess.EnumerateServer)))
{
printServer.Refresh();
var printQueues = printServer.GetPrintQueues();
foreach (var printQueue in printQueues)
{
var queue = printQueue;
var printersFound = listPrinters.Where(p =>
string.Equals(p.PrinterName, queue.FullName,
StringComparison.OrdinalIgnoreCase));
foreach (var printer in printersFound)
{
printQueue.Refresh();
Debug.WriteLine(string.Format("{0} {1}", printQueue.FullName, printQueue.HostingPrintServer.Name) );
var pm = new MonitoringWS.PrinterMonitoring
{
FkPrinter = printer.PkPrinter,
QueueStatus = printQueue.QueueStatus,
DriverName = printQueue.QueueDriver.Name,
MonitoringDateTime = DateTime.Now
};
printerMonitorings.Add(pm);
}
}
}
I found a way: SNMP. I use library SNMP# at http://www.snmpsharpnet.com/ and I implement RFC 2790: https://www.rfc-editor.org/rfc/rfc2790 .
With that, when the printer support that standard and when SNMP is active, I got the status of the printer (no toner, no paper, paper jam, etc.)
Thanks everybody for your help.

PrintQueue.AddJob hangs when printing to non-xps based printers

I am trying to print an xps document to printers (network printer, some virtual local printers, xps and non xps based) with the following code.
C# Source:
static void Main(string[] args)
{
PrintServer printServer = new PrintServer(#"\\printserver.csez.zohocorpin.com");
foreach (PrintQueue queue in printServer.GetPrintQueues())
{
Console.WriteLine("Printer: {0}, Port: {1}, ShareName: {2}, status: {3}, PrintingIsCancelled: {4}",
queue.Name, queue.QueuePort.Name, queue.ShareName, queue.QueueStatus, queue.PrintingIsCancelled);
Program program = new Program();
Thread printingThread = new Thread(() => program.Print_XPXFile(queue, #"D:\Assist\RemotePrint\Spool\Donalduck.xps"));
// Set the thread that will use PrintQueue.AddJob to single threading.
printingThread.SetApartmentState(ApartmentState.STA);
printingThread.Start();
printingThread.Join();
}
}
public void Print_XPXFile(PrintQueue pQueue, String FilePath)
{
// Create print server and print queue.
bool fastCopy = pQueue.IsXpsDevice;
FileInfo file = new FileInfo(FilePath);
if (!file.Exists)
{
Console.WriteLine("There is no such file.");
}
else
{
Console.WriteLine("Adding {0} to {1} queue. share name : {2}", FilePath, pQueue.Name, pQueue.ShareName);
try
{
// Print the Xps file while providing XPS validation and progress notifications.
PrintSystemJobInfo xpsPrintJob = pQueue.AddJob(file.Name, FilePath, fastCopy);
Console.WriteLine("Done adding.");
}
catch (PrintJobException e)
{
Console.WriteLine("\n\t{0} could not be added to the print queue.", file.Name);
if (e.InnerException.Message == "File contains corrupted data.")
{
Console.WriteLine("\tIt is not a valid XPS file."); // Use the isXPS Conformance Tool to debug it.
}
else
{
Console.WriteLine("\tmessage : {0}", e.InnerException.Message);
}
}
}
}
When printing to Microsoft XPS Document Writer, Microsoft Print to PDF, etc it works fine.
I found that it is working fine with all XPS based printers. I even installed a XPS sample printer driver and added a virtual local printer to confirm this claim and as expected it worked.
For non-xps based printers, it actually gets stuck in the AddJob function. It neither throws any exception, nor it moves to the next statement.
I developed the code based on this msdn resource.
What is the cause and solution?
All thoughts are welcome.
I can't seem to find the problem. But here is a more promising way to print XPS files:
public static void PrintXPSToDefaultPrinter(string FilePath)
{
try
{
// Create the print dialog object and set options
PrintDialog pDialog = new PrintDialog();
pDialog.PageRangeSelection = PageRangeSelection.AllPages;
pDialog.UserPageRangeEnabled = true;
FileInfo file = new FileInfo(FilePath);
XpsDocument xpsDocument = new XpsDocument(FilePath, FileAccess.ReadWrite);
FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence();
pDialog.PrintDocument(fixedDocSeq.DocumentPaginator, file.Name);
}
catch (System.IO.IOException ex)
{
Console.WriteLine("The file is being used by some other process.");
}
catch (Exception ex)
{
Console.WriteLine("Exception occured : {0}", ex.Message);
}
}
You should call this in STA mode just like you have used:
static void Main(string[] args)
{
Thread printingThread = new Thread(() => PrintXPSToDefaultPrinter(#"D:\Assist\RemotePrint\Spool\Donalduck.xps"));
printingThread.SetApartmentState(ApartmentState.STA);
printingThread.Start();
}
You can also mention any printqueue u want in the pDialog.PrintQueue property.
Hope this helps. Enjoy man !!!
I, too, got stuck in the AddJob() method. The problem seemed to be more prevalent as Windows 10 platforms became involved.
Breaking the execution in debug, the stack trace showed the STA thread was blocked on a call to
MS.Internal.PrintWin32Thunk.XpsCompatiblePrinter.JobIdentifier.get
and this was further blocking on a low-level WaitOne() on some unknown synchronization object.
Even though details on this problem are thin (this is the only post I have found on the topic), THANKFULLY the accepted solution works extremely well (and I have been trying all kinds of things to print in WPF for years).
AddJob() is fully replaced in all respects. One can even control the PrintTicket more easily through the PrintDialog (to set paper size, orientation, one/two sided printing, etc.)

Enumerate available WiFi Printers in my Windows Phone app

I know similar questions have been asked a lot, ie "can I print from my wp8?". Most folks seem content with the simple "no" response. I am looking to add this functionality into my app and I know it can be accomplished at some level - even if I can only support a very minimum printer set for now.
I've looked at the app PrintHand, and it appears to have the capabilities I need: print to wireless and Bluetooth printers.
I've been looking through the Bluetooth scenarios document a bit and I think this might help discover a Bluetooth printer. That's a start. Perhaps it will help in identifying wireless printers also.
I realize I need to start very small with this project and I want to first try to enumerate any wireless printers (I don't have a Bluetooth one yet) available on the current network. Would anybody happen to have a pointer in the right direction on how to get started or better yet, some related sample code?
Thanks so much!
Best I can give is my Github Repo which isn't related to Wifi printers, but Google cloud printing.
There isn't really a .net reference for using Google Cloud Print in their documentation, but the code works in Mono, and should be easy to move over to .NET and hence, Windows Phone.
MonoGCP
I was able to detect a nearby wireless printer using Simple Service Discovery Protocol.
Here is my sample function:
private const string SSDP_IP = "239.255.255.250";
private const string SSDP_PORT = "1900";
public async static void DiscoverAsync2()
{
var multicastIP = new HostName(SSDP_IP);
var found = false;
using (var socket = new DatagramSocket())
{
socket.MessageReceived += (sender, e) =>
{
var reader = e.GetDataReader();
var bytesRemaining = reader.UnconsumedBufferLength;
var receivedString = reader.ReadString(bytesRemaining);
// TODO: something useful with this new info
found = true;
};
await socket.BindEndpointAsync(null, string.Empty);
socket.JoinMulticastGroup(multicastIP);
while (true)
{
found = false;
using (var stream = await socket.GetOutputStreamAsync(multicastIP, SSDP_PORT))
{
var request = new StringBuilder();
request.AppendLine("M-SEARCH * HTTP/1.1");
request.AppendLine("HOST: " + SSDP_IP + ":" + SSDP_PORT);
request.AppendLine("MAN: \"ssdp:discover\"");
request.AppendLine("MX: 3");
request.AppendLine("ST: urn:schemas-upnp-org:device:Printer:1"); // use ssdp:all to get everything
request.AppendLine(); // without this extra blank line, query won't run properly
var buff = Encoding.UTF8.GetBytes(request.ToString());
await stream.WriteAsync(buff.AsBuffer());
await Task.Delay(5000);
if (!found)
break;
}
}
}

Categories