Pos .Net doesn’t natively support sharing devices across a network. The only networking feature available in Pos .Net is the ability to use a device connected to the local machine when the software is being run under a terminal sessions via Microsoft Terminal Server. While this is useful in some situations, many people want to be able to share a device attached to one machine so it can be used from others, in much the same way as Windows allows sharing of printers.
While Pos .Net doesn’t provide this feature itself, you can implement your own device sharing architecture. Doing this properly is not for the faint hearted though. I’ve posted some sample code for sharing a PosPrinter object between multiple PC’s here. The sample uses .Net Remoting to handle the network requests, you may prefer to use a more modern technology such WCF, but I used remoting in the sample because it was one of the simplest mechanisms available and required less code to make it work.
The sample I’ve provided is obviously not intended for production use. It only demonstrates sharing a a PosPrinter, and not any other types of device. Sharing other ‘output’ devices could be done easily by extending the code, but input devices (such as Barcode Scanners and MSRs) would require significantly more work as the server must also become a client, and the client also a server to enable the two-way communication required. The architecture for an input device would be very similar to the WS-POS specification which uses web services to share devices across the network.The sample code also doesn’t contain any error handling and so isn’t robust enough for a real world implementation. Finally, the sample also supports only a limited set of printing functionality. To fully implement a shared printer you would need to build your own PML to allow single-call printing of entire documents with a full feature set. Trying to implement the device any other way will likely result in a system that is too slow and not robust enough, as it will involve too many network calls.
The code in the sample is heavily commented, with some comments explaining the general architecture and others explaining what the code does. While comments that explain what code does are typically bad, since this is a sample designed to be used as a learning tool and everyone who views it will have a different level of knowledge, I felt it was appropriate to provide as many comments of both sorts as possible.
You will need to properly configure or disable firewalls on both the client and server, and ensure both machines can see each other on the network, in order for the sample to work correctly. This will be a problem with pretty much any networking implementation.
The sample code is split into four projects. First, there is a ‘common’ library which contains interfaces and base classes that are required on both the server and the client machines to enable the remote communication. There is a server library which contains a DeviceManager class, which an be instantiated in any host process that can act as a remoting server (i.e a windows service, windows application, ASP .Net application etc). This class is responsible for locating and opening the locally attached Pos .Net devices and setting up the code that listens for and processes incoming requests to use the devices. There is also a very simple Windows Forms application that loads the device manager, for the purposes of the demo. Finally there is a client application that communicates with the server, and allows the user to select a printer and then print simple text to it. Technically you can also print text containing escape codes using the sample if you can embed them in the sting sent to the remote server, but there is no support for printing barcodes, bitmaps and so on.
So, the server application depends on the server library and the common library. The client application depends only on the common library.
Some pieces of the architecture may seem a bit odd at first. For example, the DeviceManager class stores and exposes via a property a static collection of Pos .Net devices. The remote device classes which handle requests for specific device classes obtain a reference to a device from this static collection on every request, and then use that reference for their work. The static collection is used so the devices do not have to be located, instantiated and opened on every remote call, and this helps to improve the performance of the system. It would seem, however, to have been simpler to just have the remote device class to use a field to keep a reference to the particular Pos .Net device object it is associated with. Unfortunately this is not possible, for an object to be accessible it must be serialisable but the Pos .Net objects aren’t and so any class that has a field or public member that exposes a Pos .Net object cannot be serialised. Therefore, it is not possible to refer to a Pos .Net object inside the remote device classes except within the local scope of a method, and therefore the static collection on DeviceManager is used.
Another issue I had with the remoting was that some of the .Net enumeration types could not be exposed on public interfaces due to security issues (or so .Net claimed), therefore the printer station parameter on the print method of the remote pos printer class accepts an integer for the station and not the enum value. You can however cast the enum value to an int when passing it, so you can continue to use the enumeration values for convenience.
The way a client communicates with the server is relatively simple. First, the client obtains a list of available devices by name using the GetDeviceList method. In the sample, these names are loaded into a combobox where the user can select a device to work with. The client may then request a ‘remote device object’ using the GetDevice call and passing in the name of the device it wants to work with. The object retuned must then be cast to the interface of the remote device type being worked with, which in the case of the sample is always IRemotePosPrinter since only PosPrinters have been implemented. Once a remote device object reference has been obtained, the methods and properties of the interface can be called just like any other object, and these calls will be sent to the server via the .Net Remoting system and the actual code will then execute on the server side.
So, check out the sample and if you’re game feel free to base your own system on it’s design.