Introduction
Often, there are requirements by clients to make a web application that can handle hardware installed on a client machine. It sounds completely logical to a user to fire a print/scan command from his/her own browser and the scanner/printer attached to the machine should start working. It looks so logical to users but programmers know that it is not logical actually. Browser applications are not designed to access system resources.
Browser is an application to work in a client - server scenario and browsers cannot gain access to system resources and manipulate them. This would be a security breach if allowed and a remote code can manipulate local systems.
There are limitations inside a browser, you can print what you are seeing in a browser but you cannot access any document from the local computer, manipulate it, and print it with a desired printer silently. Some user intervention is always required, which means pop up windows. Worse is the case with scanners and other hardware devices where you can't even talk to them via client side scripts.
Background
Recently, I also got a requirement for client side printing via browser and I spent several days searching all kinds of possible ways. I found that using ActiveX controls, you can control printing to an extent in Internet Explorer, but it is not a solution anybody will like to use. I went further to use a Windows Service for accessing the printer but a Windows Service too has limitations with other hardware like scanners mainly because the drivers of some hardware devices want to interact with the user and the Windows Service creates problems as they are not designed to have user interaction.
In cases where a scanner wanted to display a sample scanned image to user or in case of a bar-code scanner, we can't even use windows services. In a last attempt, I tried to use JavaScript and tried to consume a self hosted WCF service using JavaScript. I used a new feature in WCF which allows cross browser access of service. But I was not able to make it work. If somebody has worked on this idea, please share.
Finally I admitted that, if all of the devices are installed on the client machine, a Windows Forms application is the way to talk to all devices seamlessly. So finally, I decided to make a formless notify icon application and host a WCF service on it and it worked for me.
The hardware access scenario is a bit indirect in this case, means when the user fires a print or scan command, it goes to the server first and then the server will call the client machine's WCF service to print/scan. Please note that the "WCF service" box inside the NotifyIcon
application box shows that the WCF service is hosted on the NotifyIcon
app.
Using the Code
The WCF Service
public interface IService1
{
[OperationContract]
Boolean TestPrint();
[OperationContract]
Boolean TestScan();
}
public class Service1 : IService1
{
public bool TestPrint()
{
WindowsFormsApplication1.TestPrint f = new WindowsFormsApplication1.TestPrint();
f.Show();
return true;
}
public bool TestScan()
{
WindowsFormsApplication1.TestScan f = new WindowsFormsApplication1.TestScan();
f.Show();
return true;
}
}
The service includes two methods only, one for print and one for scanning. We can have as many devices as we want to control using this service. Also, we can change the print settings before printing. This gives us complete control on the hardware as the service is hosted on a form application. A basic knowledge of WCF is expected here. Refer to the link for WCF application hosting basics.
The Formless NotifyIcon Application
ServiceHost sHost;
private void Form1_Load(object sender, EventArgs e)
{
Use_Notify();
sHost = new ServiceHost(typeof(Service1));
sHost.Open();
}
private void Use_Notify()
{
MyNotify.ContextMenuStrip = contextMenuStrip1;
MyNotify.BalloonTipText = "Print/Scan App";
MyNotify.BalloonTipTitle = "Print/Scan App";
MyNotify.ShowBalloonTip(10000);
}
private void Form1_Activated(object sender, EventArgs e)
{
Hide();
}
private void TestPrint_Load(object sender, EventArgs e)
{
WindowsFormsApplication1.PrintDocumentMethod printDoc =
new WindowsFormsApplication1.PrintDocumentMethod();
printDoc.Printing("Name_of_Printer");
}
private void TestScan_Load(object sender, EventArgs e)
{
Twain tw = new Twain();
tw.Init(this.Handle);
tw.Acquire();
}
The notify icon application is actually the key to this solution. It looks like a service and it works like a form application as it is a form application actually. Refer to the link for more details. There are two forms, one for printing and one for scanning which are displayed when the user gives the print /scan command, though these forms are not needed as such and can be skipped and you can make this work completely silently.
The ASP.NET Web Application (to Test the Printing/Scanning)
protected void Button1_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client sc = new ServiceReference1.Service1Client();
sc.TestPrint();
}
protected void Button2_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client sc = new ServiceReference1.Service1Client();
sc.TestScan();
}
The web application page is quite simple. I included only two ASP.NET buttons which call the WCF service methods. A service reference is needed to generate a proxy for the WCF service. Please refer to the attached code.