Introducing BrokeredPointOfService Brokered Windows Runtime Component wrapper library for POS for .NET 1.14


This blog article provides instructions for building, testing and extending the Open Source BrokeredPointOfService library available at http://github.com/apulliam/BrokeredPointOfService.

While the information in this article is about BrokeredPointOfService, POS for .NET, and writing Windows Modern Point Of Service (POS) applications with BrokeredPointOfService, most the concepts are applicable to any application using Brokered Windows Runtime Components.

What is BrokeredPointOfService?

BrokeredPointOfService is a Brokered Windows Runtime Component library which wraps Microsoft Point of Service for .NET (POS for .NET) v1.14 classes with a set of .NET classes that can be marshaled to the Windows Runtime, making POS for .NET 1.14 functionality available to Windows Modern (aka Windows 8, aka Metro) applications.

Why did I develop BrokeredPointOfService?

The Windows Runtime already provides some Point of Service (POS) device support for Modern applications, but the Windows.Devices.PointOfService API’s in the Windows Runtime only handle two of thirty six POS device types supported by POS for .NET 1.14 – barcode scanners and Magnetic Stripe Readers (MSR’s).  And even the support for scanners and MSR’s in Windows.Devices.PointOfService API’s is limited because these API’s only work with certain barcode scanners and MSR’s.  Specifically,  the Windows.Devices.PointOfService.BarcodeScanner class only supports barcode scanners that implement the USB Human Interface Device (HID) Barcode Scanner specification:

HID Barcode Scanner usage page (0x8C) and Bar Code Scanner usage ID (0x0002)

And the Windows.Devices.PointOfService.MagneticStripeReader class only supports a specific set of MSR devices:

  • MagTek MagneSafe HID USB reader (VID 0801, PID 0011)
  • MagTek SureSwipe HID USB reader ( VID 0801, PID 0002)
  • MagTek BulleT Bluetooth/USB reader, when connected via USB (VID 0801, PID 0011)
  • ID TECH SecureMag HID USB reader (VID 0ACD, PID 2010)
  • ID TECH MiniMag HID USB reader (VID 0ACD, PID 0500)

Barcode scanners which don’t implement the USB HID Barcode Scanner specification and MSR devices not listed above are not recognized by the built-in Windows 8.1 drivers, and thus don’t work with the Windows.Devices.PointOfService BarcodeScanner and MagneticStripeReader classes.  And, of course, there are a wide range of POS devices that simply aren’t supported in Windows.Devices.PointOfService.

As a result, the only way to get full POS device support in a Windows 8.1 Modern application is to use an interop technology to leverage POS libraries and drivers built for the Windows Desktop. Windows Modern applications originally didn’t have the ability to interop with code on the Windows Desktop, and this proved to be a major limitation for certain categories of applications – such as POS applications!  Microsoft recognized this limitation and introduced two technologies at the Build 2014 conference which allow Windows Modern applications to interact with the Windows Desktop code – network communication via the Loopback Adapter and Brokered Windows Runtime components.

I chose to use the Brokered Windows Runtime Component technology to access POS for .NET on the desktop because this technology was a more natural fit for the POS for .NET architecture, which instantiates stateful objects for POS devices, all of which have chatty communication semantics.

In a later blog post, I’ll demonstrate using the Loopback Adapter to communicate from a Windows 8.1 Modern application to a web service hosted by a Windows Service.

I will also update this blog as information about the POS support in Windows 10 becomes available.

Prerequisites for building BrokeredPointOfService code

To work with the BrokeredPointOfService library, you’ll need Visual Studio 2013 with Update 2 (or later) running on the Windows 8.1 Update. The target machine will also need Windows 8.1 Update.

The Brokered WinRT Component Project Templates were used to build the brokered Windows Runtime component and the proxy stub projects. These templates aren’t required to build and use the code, but you’ll likely need them if you decide to build other Brokered Windows Runtime components, as they make the process a lot easier!

You’ll also need to install POS for .NET 1.14. Technically, only the POS for .NET runtime is required, but you’ll want to install the POS for .NET SDK on your development machine in order to get the POS for .NET SDK help files and samples.  Additionally, the POS for .NET SDK also provides a set of POS simulator service objects which come in handy if you want to develop on a machine without physical POS devices.  Since I’ve tried to preserve the programming semantics of POS for .NET (as much as possible) in BrokeredPointOfService, the POS for .NET documentation and sample code can be used when writing Windows Modern applications with BrokeredPointOfService.   There are some differences between POS for .NET and BrokeredPointOfService which are explained in the BrokeredPointOfService Architecture section of this article.

Getting the BrokeredPointOfService code

The first step to working with the BrokeredPointOfService library is to clone the GIT repository, or “repo” in GIT jargon.  Visual Studio 2013 now has built-in support for GIT.  In the Team Explorer window in Visual Studio, simply select Clone Repository and type in the GitHub address of the BrokeredPointOfService project repository – https://github.com/apulliam/BrokeredPointOfService.git.  I’ll try to maintain a development branch for bug fixes and accepting GIT pull requests, so you’ll probably want to clone the release branch unless you see from the release notes that there is specific code in the development branch you need.

After you clone the BrokeredPointOfService repository, double click on BrokeredPointOfService in your repository list and you should then see the main solution in the Team Explorer window, BrokeredPointOfService.sln.

image

After opening the BrokeredPointOfService solution, you’ll see three projects in the Solution Explorer window - BrokeredPointOfService, BrokeredPointOfServiceProxyStub and ModernPosExplorer.

image

Building the BrokeredPointOfService code

Its easiest to build BrokeredPointOfService if you run Visual Studio as Administrator.  This isn’t requirement, but it allows automatic COM registration of the Brokered Windows Runtime Component DLL.  If you opt not to run Visual Studio as Administrator, you will still need Administrative privileges to manually register this DLL with the RegSvr32 command line utility.

As long as you have the POS for .NET 1.14 assemblies installed on your machine, the BrokeredPointOfService solution should build as-is in your cloned repository. There are three projects include in the solution – BrokeredPointOfService, BrokeredPointOfServiceProxyStub, and ModernPosExplorer.

The first project, BrokeredPointOfService was built from the Brokered Windows Runtime Component project template.  You can think of this project as a cross between a Windows Modern class library project and .NET class library project.    This project builds the library of the Brokered Windows Runtime Components you’ll access via your Windows Modern application.  Once you look through the code, you’ll quickly realize that the code is simply a set of facades over the POS for .NET classes.  That’s precisely the intent – to provide POS for .NET functionality in Windows Modern applications.  if you look closer, however, you’ll discover there are changes from the POS for .NET class hierarchy, changes in a few API signatures and a slightly different exception handling model, to name a few.  These changes are explained in detail in the BrokeredPointOfService Architecture section of this article.

If you examine the output of the BrokeredPointOfService project, you’ll see its creates an implementation winmd (Windows Metadata) file.  However, this winmd is not used by Windows Modern applications directly, but will instead by hosted by the Windows COM+ subsystem on the Windows Desktop.  This is where the BrokeredPointOfServiceProxyStub project comes in. 

Remarkably, you’ll discover there is source code checked in for the BrokeredPointOfServiceProxyStub project.  This project was built from the Brokered Windows Runtime ProxyStub project template.  This project template automatically generates code on the fly for all public sealed classes and interfaces in referenced Windows Runtime libraries. 

The BrokeredPointOfServiceProxyStub project builds a second reference winmd file that is used as a proxy by the Modern application, and BrokeredPointOfServiceProxyStub.dll binary that will load the implementation windmd file at runtime.  The BrokeredPointOfServiceProxy.dll file is loaded by Windows and hosted under a DllHost.exe COM+ host process whenever an “activatable” Brokered Windows Runtime Component is created. 

This brings us to the third project in the solution, ModernPosExplorer.  This a test/sample application for BrokeredPointOfService classes.  It is similar to the TestApp.exe application built by the SampleApplication project in the PosSamples solution in the POS for .NET SDK.  However, the ModernPosExplorer doesn’t attempt to provide full test coverage for device properties and methods, and only includes device-specific implementations for barcode scanner and MSR device types.

To use a Brokered Windows Runtime component a Modern application, needs to have a project reference to the proxy/stub project to access the reference winmd file, and also needs to know about “activatable” Brokered Windows Runtime Component and the location of the stub DLL and implementation winmd file.  This information is configured in an extension section of the Modern application project’s manifest file (package.appmanifest). Below is the section at the end of the ModernPosExplorer application manifest file which provide access to BrokeredPointOfService classes:

  <Extensions>
    <Extension Category="windows.activatableClass.inProcessServer">
      <InProcessServer>
        <Path>clrhost.dll</Path>
        <ActivatableClass ActivatableClassId="BrokeredPointOfService.PosExplorer" ThreadingModel="MTA">
          <ActivatableClassAttribute Name="DesktopApplicationPath" Type="string" Value="%BrokeredPointOfService%" />
        </ActivatableClass>
        <ActivatableClass ActivatableClassId="BrokeredPointOfService.DeviceCompatibilities" ThreadingModel="MTA">
          <ActivatableClassAttribute Name="DesktopApplicationPath" Type="string" Value="%BrokeredPointOfService%" />
        </ActivatableClass>
        <ActivatableClass ActivatableClassId="BrokeredPointOfService.EncryptionAlgorithm" ThreadingModel="MTA">
          <ActivatableClassAttribute Name="DesktopApplicationPath" Type="string" Value="%BrokeredPointOfService%" />
        </ActivatableClass>
        <ActivatableClass ActivatableClassId="BrokeredPointOfService.HealthCheckLevel" ThreadingModel="MTA">
          <ActivatableClassAttribute Name="DesktopApplicationPath" Type="string" Value="%BrokeredPointOfService%" />
        </ActivatableClass>
      </InProcessServer>
    </Extension>
  </Extensions>

Currently, there is no application manifest UI support for Brokered Windows Runtime Components in the application manifest, so the application manifest must be opened with the XML editor to update this section.  The InProcessServer element needs to have an ActivatableClass element for each Brokered Windows Runtime component that is directly instantiated by the Modern application. 

The ModernPosExplorer has support for the BrokeredScanner via the Scanner interface and the BrokeredMsr class via the Msr interface, but they’re not listed under InProcessServer.  This is because these classes are not created directly from the Modern application.  Instead they are created indirectly via CreateInstance method of the PosExplorer class.  Only the PosExplorer and a few enum wrapper classes, which are used for enum value comparisons, are created directly by the Modern application.

The ActivableClass element has an ActivatableClassId attribute which contains the namespace-qualified Brokered Windows Runtime component class name.  This element has a ActivatableClassAttribute child element with the Name attribute of “DesktopApplicationPath” and Value attribute set to the location where the stub DLL and implementation winmd file are located.

Because, you likely won’t deploy the Brokered Window Runtime component to the same location on a production machine as your development machine, its useful to take advantage of the support for environment variables in the Value for the DesktopApplicationPath

The BrokeredPointOfService library uses a system level environment variable named BrokeredPointOfService.  For development, this should point to the output directory of BrokeredPointOfServiceProxyStub project.  For example, my GIT repos are stored under D:\repos on my development machine, therefore I have the following system level environment variable defined:

BrokeredPointOfService=D:\repos\BrokeredPointOfService\Debug\BrokeredPointOfServiceProxyStub

If you choose not to run Visual Studio as Administrator, you will also need to run RegSvr32 on the BrokeredPointOfServiceProxyStub.dll

Debugging and Testing BrokeredPointOfService

You can debug ModernPosExplorer  in Visual Studio just as you would any other Modern application, but you can’t step into the BrokeredPointOfService code.  However, you can attach a second Visual Studio Instance to the DllHost.exe COM+ host process running BrokeredPointOfServiceProxyStub.dll and simultaneously debug the BrokeredPointOfService there.  Simply open a second Visual Studio instance with the same BrokeredPointOfService solution.  You will receive a warning that Visual C++ browsing database can’t be stored at the default location, due to a conflict with first Visual Studio instance, but you can safely ignore this warning since you don’t want to make any code changes in this second instance.   After a BrokeredPointOfService object is created in the first Visual Studio instance, attach the debugger of the second Visual Studio instance to the DllHost.exe instance which shows a type of Managed and x86.

You can also debug ModernPosExplorer on a remote machine.  The remote debugging support for Windows Modern application is improved from traditional remote debugging of desktop applications.  Simply download and run the version Remote Tools for Visual Studio 2013 that matches your develop Visual Studio update level.  Visual Studio will automatically find any instances of the Visual Studio remote debugger on the same network subnet. 

I developed the BrokeredPointOfService library on my laptop computer using the POS device simulator implementations from the POS for .NET 1.14 SDK.   Additionally, I test on my target machine- a tablet with a retail jacket containing a barcode scanner and MSR.  On this device, I copy BrokeredPointOfServiceProxyStub.dll and the implementation winmd file BrokeredPointOfService.winmd  from the release output directory of the BrokeredPointOfServiceProxyStub project to C:\BrokeredPointOfService on my target machine. On this target machine I have the BrokeredPointOfService system environment variable set as follows:

BrokeredPointOfService=C:\BrokeredPointOfService

I discovered that RegSvr32 will fail to register the debug version of BrokeredPointOfServiceProxyStub.dll, unless you have Visual Studio installed, due to the lack of Visual C++ debug runtime libraries.  If you don’t want to install Visual Studio on your target machine, just use the release version for remote debugging.   After running RegSvr32 on BrokeredPointOfServiceProxyStub.dll, there’s one more step you have to do manually that’s handled by the BrokeredPointOfServiceProxyStub project on your development machine.  In the directory where you copied the DLL and winmd files on the target machine, run the following command to grant the COM+ process rights to the files in this folder:

icacls . /T /grant *S-1-15-2-1:RX

BrokeredPointOfService Architecture

 

Class Structure

The BrokeredPointOfService library architecture is designed to provide the same programming semantics as the original POS for .NET library – as much as possible. The UML class diagram below shows the POS for .NET class hierarchy (with only Scanner and Msr concrete classes shown).

image

All concrete POS for .NET device classes inherit from both the abstract PosCommon and PosDevice classes. The PosExplorer class provides several methods that allow the caller to enumerate all available POS devices. These methods return a special collection type which contains DeviceInfo objects.  The DeviceInfo object which provide high level information about the POS device, such as the service object name, a description of the hardware and the manufacturer name.  The actual POS device objects are created from the CreateInstance abstract factory method of the PosExplorer class which accepts a DeviceInfo object and returns POS device object via pointer to its PosDevice base class. The user already knows how to cast the PosDevice device object to the appropriate POS Device class based on the DeviceInfo class.

The BrokeredPointOfService library wraps POS for .NET classes with .NET classes and interfaces that can be marshaled to the Windows Runtime. Only public sealed .NET classes can be exposed to the Windows Runtime, so no base classes can be exposed to the Windows Runtime. Luckily, interfaces can also be returned, even when the implementation class is not public. The following diagram shows the class architecture of BrokeredPointOfService.

image

Like the original POS for .NET architecture, BrokeredPointOfService provides a PosExplorer class which can be instantiated by the caller.  It also has methods to return a collection of available POS devices via DeviceInfo objects.  In order to preserve the inheritance hierarchy of POS for .NET, the PosDevice, PosCommon and device classes in POS for.NET are mapped to interfaces in BrokeredPointOfService.  For example, when the CreateInstance method of PosExplorer is passed the DeviceInfo of a barcode scanner, it internally instantiates a BrokeredScanner object.  This object is returned via a reference to its PosDevice interface. The caller can cast it to its derived PosCommon interface or Scanner interface. Note, that the object cannot be cast to BrokeredScanner, BrokeredPosCommon or BrokeredPosDevice, since these are internal classes, which are not exposed to the Windows Runtime.

For the most part, the code for BrokeredPointOfService should be very similar to code for POS for .NET.  The BrokeredPointOfService solution includes a ModernPosExplorer Windows Modern application to show how the use the BrokeredPointOfService library.  ModernPosExplorer is based on the TestApp.exe sample application included in the POS for .NET SDK.

Changes to Properties and Method Signatures

All classes in the BrokeredPointOfService library that can be instantiated directed or returned via properties or methods to the caller have to be marshaled from .NET code running under a COM+ process on the Desktop to the Windows Runtime.  Only a limited subset of .NET primitives,  .NET types defined within the Brokered Windows Runtime project, and, of course, Windows Runtime types can be used.  As a result, none of the original POS for.NET classes are returned directly.

The Visual Studio C# Compiler will (usually) provide the following error message if finds an exposed type that can’t be marshaled to the Windows Runtime:

Method 'BrokeredWindowsRuntimeComponent.BrokeredTest.BaseMethod2(System.String)' returns 'System.DateTime', which is not a valid Windows Runtime type. Methods exposed to Windows Runtime must return only Windows Runtime types. D:\source\BrokeredWindowsRuntimeComponent\BrokeredWindowsRuntimeComponent\BrokeredTest.cs 34 25 BrokeredWindowsRuntimeComponent

Most of the signatures in BrokeredPointOfService look the same because the POS for .NET classes have been replaced by BrokeredPointOfService classes (or interfaces) with the same name.  However, there are few places where the signatures look slightly different.  For example the GetDevices methods on the POS for .NET PosExplorer class return a DeviceCollection object containing DeviceInfo objects.  The POS for .NET DeviceCollection object is based on the System.Collections .NET library, which has no direct Windows Runtime equivalent.  Therefore the BrokeredPointOfService PosExplorer class returns a simple array of BrokeredPointOfService DeviceInfo objects.

The POS for .NET PosExplorer.GetDevices method has four overloaded signatures. The Windows Runtime allows overloaded methods as long as one of the signatures is declared as the default signature.  However, this limits visibility of the other non-default methods to Windows Modern applications written in C#.  To permit BrokeredPointOfService to be used with all Windows Modern development languages, all overloaded POS for .NET methods were renamed with unique method names in BrokeredPointOfService – based on the parameter types.

Finally the POS for .NET PosCommon Claim and Release methods were renamed as ClaimDevice and ReleaseDevice.  In this case, there wasn’t a problem with overloaded method signatures.  Instead, the PosCommon.Release method conflicted with the Release method on the COM IUnknown interface, which is present in the BrokeredPointOfServiceProxyStub project.

Enumerations

As noted above, the compiler error will usually ensure that you use Window Runtime friendly types. However, the compiler fails to warn you about C# enum types. The compiler will happily allow you to return C# enum’s from your brokered Windows Runtime component, however you’ll get a nasty surprise when you run the application.

System.BadImageFormatException = {"An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)"}

Luckily, I didn’t spend much time trying to track down this cryptic exception. In his blog poste entitled “Create a WinRT brokered component : Feedback from a “real life” development”, Sebastien Pertus explained the cause of this exception and the workaround – saving me a lot of grief!

Unfortunately, the answer is (currently) you can’t return enum’s from Windows Runtime components authored in managed code. There are two available workarounds – return enum’s as integers or wrap enum’s in public sealed classes – which can be marshaled to the Windows Runtime.  Casting enum’s to integers is the easiest solution, but all enum type information is lost in client code.  Since enums are such an essential part of POS for .NET, the BrokeredPointOfService library wraps enum’s with public sealed classes.

Wrapping enum’s is fairly easy, although given the number of enum’s in POS for .NET, it can be quite tedious. The code sample below shows the ControlState enum from POS for .NET:

// Summary:
//     Enumerates the valid states of a device.
[SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "UPOS specification does not include 0.")]
public enum ControlState
{
    // Summary:
    //     The device is closed. The service object is not initialized.
    Closed = 1,
    //
    // Summary:
    //     The device is in a good state and is not busy.
    Idle = 2,
    //
    // Summary:
    //     The device is in a good state and is busy performing output.
    Busy = 3,
    //
    // Summary:
    //     An error has been reported, and the application must recover the Control
    //     to a good state before normal I/O can resume.
    Error = 4,
}

The basic pattern to expose enum’s is to wrap them with public sealed classes, with each enum value defined as a static property of the class. 

public sealed class ControlState : Enumeration
{
    internal Microsoft.PointOfService.ControlState InternalControlState { get; set; }

    internal ControlState(Microsoft.PointOfService.ControlState controlState)
    {
        this.InternalControlState = controlState;
    }

    // Summary:
    //     The device is closed. The service object is not initialized.
    public static ControlState Closed
    {
        get
        {
            return new ControlState(Microsoft.PointOfService.ControlState.Closed);
        }
    }

    //
    // Summary:
    //     The device is in a good state and is not busy.
    public static ControlState Idle
    {
        get
        {
            return new ControlState(Microsoft.PointOfService.ControlState.Idle);
        }
    }

    //
    // Summary:
    //     The device is in a good state and is busy performing output.
    public static ControlState Busy
    {
        get
        {
            return new ControlState(Microsoft.PointOfService.ControlState.Busy);
        }
    }
    //
    // Summary:
    //     An error has been reported, and the application must recover the Control
    //     to a good state before normal I/O can resume.
    public static ControlState Error
    {
        get
        {
            return new ControlState(Microsoft.PointOfService.ControlState.Error);
        }
    }

     public override bool Equals(object obj)
     {
         if (obj == null)
             return false;

         ControlState other = (ControlState)obj;
         return this.InternalControlState.Equals(other.InternalControlState);
     }

     public override int GetHashCode()
     {
         return InternalControlState.GetHashCode();
     }

     public override string ToString()
     {
         return InternalControlState.ToString();
     }

     public int Value
     {
         get
         {
             return (int)InternalControlState;
         }
     }
  
}

Each enum wrapper class overrides the Equals, GetHashCode and ToString virtual methods of the base Object class.  Also, all enum wrappers derive from an Enumeration interface.  Currently, this only defines the signature for the Value property, but may be used in the future to test for Enumeration implementation in order to add static methods similar to those provide on the C# Enum class. 

There is also one special case for enumerations, which requires additional effort – C# enum’s with the flag attribute.   In this case, every possible bit combination must be defined as a unique static property.  Below is a simple example, with only 3 bit options.

[Flags]
public enum LineDirection
{
    None = 0,
    Horizontal = 1,
    Vertical = 2,
}

In addition to defining static constants for the None, Horizontal and Vertical enum values, the BrokeredPointOfService wrapper LineDirection class defines a static constant HorizonatlVertical for the combination of Horizontal and Veritical bits being set:

public static LineDirection HorizonatlVertical
{
    get
    {
        return new LineDirection(Microsoft.PointOfService.LineDirection.Horizontal |
                                 Microsoft.PointOfService.LineDirection.Vertical);
    }
}

Asynchronous Methods

While its always good practice to make long running or blocking calls asynchronous, this is especially important with Brokered Windows Runtime components, since the calls cross process boundaries.  I didn’t have any prior experience with POS for .NET, so I just relied on the POS for .NET method signatures to determine which calls may block.  I found that blocking methods include a timeout parameter.  There are currently only two asynchronous methods in BrokeredPointOfService– the ClaimDevice (equivalent to Claim in POS for .NET) method on PosCommon and the WriteTracks method on Msr.

For both methods, I simply wrapped the native implementation in a Task-Based Asynchronous Programming (TAP) model Task with Task.Run

public IAsyncOperation<int> ClaimDevice(int timeout)
{
    return Task.Run(() =>
    {
        return ExecuteFunction(() =>
            {
                InternalPosCommon.Claim(timeout);
                return 0;
            });
    }).AsAsyncOperation<int>();
}

The .NET Task, however, cannot be marshaled to the Windows Runtime.  It needs to be converted to a Windows Runtime IAsyncAction (for Tasks without a return value) or IAsyncOperation (for Tasks with a return value).

The POS for .NET PosCommon.Claim method does not return a value.  I had to use IAsyncOperation with dummy integer return value is used because of a bug in the Brokered Windows Runtime ProxyStub project template – which results in compile errors with IAsyncAction.  I’ve reported this to the team which owns the Brokered Windows Runtime component Visual Studio templates, and I will update the code and this blog if the bug is fixed.

Events

At first glance the events exposed by BrokeredPointOfService classes may look like standard .NET events.  However, if you look closely, you’ll notice that they use the Windows.Foundation.TypedEventHandler event class from the Windows Runtime rather that the System.EventHandler .NET event class.  The arguments for TypedEventHandler have the same type restrictions as property and method arguments, so all BrokeredPointOfService events return wrapper event arguments classes. 

Within the BrokeredPointOfService classes which expose events, the POS for .NET events are handled internally and forwarded as Windows Runtime event types.  Currently the BrokeredPointOfService classes subscribe to all wrapped POS for .NET events in the constructor and unsubscribe in the Dispose method.  This liberal event handling model was chosen to monitor and learn how POS for .NET events work during debugging and may be optimized at a later time.

Thread Synchronization

The POS for .NET PosExplorer class has a constructor which accepts a .NET Framework ISynchronizeInvoke parameter, which is used by POS for .NET to marshal event callbacks to a particular client thread.  Since the BrokeredPointOfService components run in a different process from the Windows Modern application, this feature is not supported.  Instead, event callbacks will always occur on worker thread and the application should marshal the events back to the primary UI thread as appropriate.  The code below from the ModernPosExplorer sample demonstrates this technique.

async void Scanner_DataEvent(object sender, DataEventArgs e)
{
    var coreWindowDispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
    await coreWindowDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        UpdateSettings();
        DeviceViewModel.UpdateSettings();
    }).AsTask();
}

Exception Handling

While .NET Exceptions can be marshaled to the Windows Runtime, almost all of the Exception information is lost – with the exception of the HRESULT.  For this reason, BrokeredPointOfService includes custom Exception marshaling code. 

Based on observed behavior of POS for .NET, I made the assumption that only operations that change the state of POS for .NET objects typically throw an exception.  For now, I have all property setters and methods are wrapped with one of two protected methods implemented in BrokeredPosCommon, depending on whether they have return value or not.   The methods, along with the LastException property, are implemented in the BrokeredPosCommon base device class.

public NetfxException LastException
{
    get;
    protected set;
}

internal void ExecuteAction(Action action)
{
    try
    {
        action();
    }

    catch (Exception exception)
    {
        LastException = BrokeredNetfxExceptionFactory.CreateBrokeredException(exception);
        throw exception;  // doesn't hurt to rethrow here.  All stack gets lost anyway.
    }
}

    
internal TResult ExecuteFunction<TResult>(Func<TResult> function)
{
    try
    {
        return function();
    }
    catch (Exception exception)
    {
        LastException = BrokeredNetfxExceptionFactory.CreateBrokeredException(exception);
        throw exception;  // doesn't hurt to rethrow here.  All stack gets lost anyway.
    }
}

Below is an example of property setter wrapped with ExecuteAction and method wrapped with ExecuteFunction.

public bool DataEventEnabled
{
    get
    {
        return InternalScanner.DataEventEnabled;
    }
    set
    {
        ExecuteAction(() =>
            {
                InternalScanner.DataEventEnabled = value;
            });
    }
}
public DirectIOData DirectIO(int command, int data, object obj)
{
    return ExecuteFunction(() =>
        {
            return new DirectIOData(InternalPosCommon.DirectIO(command, data, obj));
        });
}

The client needs to be aware of the LastException project and know to retrieve exception details there, rather from the caught exception.  This technique is demonstrated in the ModernPosExplorer sample.

public void CheckHealth()
{
    try
    {
        PosCommon.CheckHealth(HealthCheckLevel.Internal);
        RaisePropertyChanged("CheckHealthText");
    }
    catch (Exception ex)
    {
        ErrorOutput = PosCommon.LastException.ToString();
    }

}

Extending BrokeredPointOfService

 

Adding Additional Device Class Wrappers

The BrokeredPointOfService library currently only provides full wrapper classes two POS for .NET classes – Scanner and Msr.  This is because these are the two devices I was tasked with helping support for a partner project.  More practically, these were the only devices to which I had access. 

Most of the wrapper classes for PosPrinter are also in-place, with the exception of the PrinterColor enum wrapper class, which needs to be completed.  Also, there is no PosPrinter support in the ModernPosExplorer sample.  At one point, I thought I would have access to a  PosPrinter compatible printer.   However, I never got a hold of PosPrinter device,  so I never finished this implementation. 

It should be very simple to complete the PosPrinter wrapper implementation, and hopefully it should be easy to add additional POS device class wrappers by using the Scanner and Msr wrapper implementations as a reference.  All new classes should be implemented as internal classes, which inherit from BrokeredPosCommon and implement an interface that corresponds to the wrapped POS for .NET class.  This interface should also inherit from PosCommon.   The CreateInstance method of PosExplorer will need be updated to return the new internal class via a pointer to its PosDevice base interface.  The code below shows this pattern.

    public static PosDevice CreatedBrokeredPosDevice(Microsoft.PointOfService.PosExplorer posExplorer,
                                                     Microsoft.PointOfService.DeviceInfo deviceInfo)
    {
        var internalPosDevice =posExplorer.CreateInstance(deviceInfo);
        if (internalPosDevice == null)
            return null;

        PosDevice brokeredPosDevice = null;

        if (internalPosDevice is Microsoft.PointOfService.Scanner)
            brokeredPosDevice = new BrokeredScanner((Microsoft.PointOfService.Scanner)internalPosDevice);
        else if (internalPosDevice  is Microsoft.PointOfService.Msr)
            brokeredPosDevice = new BrokeredMsr((Microsoft.PointOfService.Msr)internalPosDevice);
        else if (internalPosDevice is Microsoft.PointOfService.PosPrinter)
            brokeredPosDevice = new BrokeredPosPrinter((Microsoft.PointOfService.PosPrinter)internalPosDevice);

        // Fallback to BrokeredPosCommon/BrokeredPosDevice for POS device types that don't have
        // brokered component wrapper implemented
        else if (internalPosDevice is Microsoft.PointOfService.PosCommon)
            brokeredPosDevice = new BrokeredPosCommon((Microsoft.PointOfService.PosCommon)internalPosDevice);
        else
            brokeredPosDevice = new BrokeredPosDevice(internalPosDevice);
        return brokeredPosDevice;
    }

Extending ModernPosExplorer

The ModernPosExplorer sample is based on the TestApp sample from the POS for .NET SDK.  Unlike the POS for .NET TestApp.exe sample, the ModernPosExplorer sample makes no attempt to provide full coverage for either common or device specific properties and methods.  The reason is simple.  My main goal in developing BrokeredPointOfService was to deliver support for a particular tablet barcode scanner and MSR.  I simply didn’t have time to develop an exhaustive test application.  And, if you look at the ModernPosExplorer coder, you’ll quickly realize I’m not a particularly good UI developer. 🙂 I found it hard enough to squeeze as many test options as I did onto the UI!

If you find yourself wondering why I implemented a test UI for one property or method and neglected another, its because ModernPosExplorer was developed as a way for me to test my POS device wrapper implementations.  Not knowing anything about the POS devices or POS for .NET, I just chose the properties and methods that appeared on the surface to be useful for basic testing.  If anyone wants to expand the test coverage for PosCommon, Scanner or Msr, please feel free to do so and submit a GIT pull request.

ModernPosExplorer was built with a modular XAML control and view model architecture to allow it to be extended as additional POS device wrappers are implemented. The common device UI (DevicePage) and view model (DeviceViewModel)are separate from the device specific UI and view models.  The Scanner and Msr implementations can be used as a guide for other device UI’s.

To add additional device support in ModernPosExplorer,  a new XAML UserControl and corresponding view model must be created.

The XAML fragment below from ScannerControl.xaml, along with the code-behind from ScannerControl.xamls.cs, shows the pattern for setting up the data binding between the UserControl and view model.

<UserControl
   x:Class="ModernPosExplorer.Views.ScannerControl"
   DataContext="{Binding ScannerViewModel, RelativeSource={RelativeSource Self}}"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="using:ModernPosExplorer.Views"
   xmlns:common="using:ModernPosExplorer.Common"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d">
public sealed partial class ScannerControl : UserControl
{
    private ScannerViewModel scannerViewModel = new ScannerViewModel();

    public ScannerViewModel ScannerViewModel
    {
        get { return this.scannerViewModel; }
    }

    public ScannerControl()
    {
        this.InitializeComponent();
    }
}

The view model implementation class should derive from ViewModelBase and implement the LoadData method accepting a reference to the common device UI via DeviceViewModel.

public class ScannerViewModel : ViewModelBase
{
    private DeviceViewModel DeviceViewModel { get; set; }

    private Scanner Scanner
    {
        get
        {
            return DeviceViewModel.PosCommon as Scanner;
        }
    }
. . .
public void LoadData(DeviceViewModel deviceViewModel)
{
    this.DeviceViewModel = deviceViewModel;
    Scanner.DataEvent += Scanner_DataEvent;
    Scanner.ErrorEvent += Scanner_ErrorEvent;
}

Finally, to hook the new device specific UI, additional code needs also to be added to nagivationHelper_LoadState method of the DevicePage code-behind using the pattern shown below.

private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
    PageProgressRing.IsActive = true;

    if (e.NavigationParameter != null)
    {
        // Use e.NavigationParameter here, because App.CurrentCustomerId is set to 0
        // when you create a new instance of CustomerViewModel above.
        deviceViewModel.LoadData((DeviceInfo)e.NavigationParameter);

        if (deviceViewModel.PosCommon is Scanner)
        {
            var scanner = new ScannerControl();
            deviceSpecificViewModel = scanner.ScannerViewModel;
            scanner.ScannerViewModel.LoadData(deviceViewModel);
            this.DeviceSpecificFields.Children.Add(scanner);
        }
        else if (deviceViewModel.PosCommon is Msr)
        {
            var msr = new MsrControl();
            deviceSpecificViewModel = msr.MsrViewModel;
            msr.MsrViewModel.LoadData(deviceViewModel);
            this.DeviceSpecificFields.Children.Add(msr);
        }
        else if (deviceViewModel.PosCommon is PosPrinter)
        {
        }


    }

    PageProgressRing.IsActive = false;
}

Known Issues and Developer Notes

  • Currently, asynchronous methods with no return value are implemented with IAsyncOperation and a dummy int return value, instead of IAsyncAction, due to a bug in the Brokered Windows Runtime ProxyStub project template.   See the notes in the Asynchronous Methods section of BrokeredPointOfService Architecture..
  • Events for wrapped POS for .NET classes are handled even when not subscribed by the BrokeredPointOfService wrapper class.  See the notes in the Events section of BrokeredPointOfService Architecture.
  • The PrinterColor  enum wrapper class to support PosPrinter is not complete and there is no PosPrinter UI in ModernPosExplorer. See the notes in the Extending BrokeredPointOfService section.
  • The Brokered Windows Runtime Component technology only works on the Intel x86 processor architecture.  This should not be a problem, however, as POS for .NET itself wraps OPOS COM components, which also are targeted to the same Intel x86 processor architecture.
  • BrokeredPointOfService developers should be aware that the POS for .NET components are hosted in a COM+ DllHost.exe process separate from the Window Modern application process.  This process and the Brokered Windows Runtime components should be included in the security thread model, along with the Windows Modern application process.

Comments (9)

  1. ErikEJ says:

    Great articke, but Images are broken

  2. Alan Pulliam says:

    Apologies for the broken images.  Our image server replication is broken.  Hopefully will be fixed shortly.

  3. Anonymous says:

    Thanks Alan – this very thorough post answered a few questions that have been in the back of my mind.

  4. Anonymous says:

    Great article. I follow all your steps to run ModernPosExplorer project but I'm getting the following run time exception:

     System.TypeLoadException was unhandled by user code

     HResult=-2146233054

     Message=Could not find or load a type. (Exception from HRESULT: 0x80131522)

     Source=mscorlib

     TypeName=""

     StackTrace:

          at System.StubHelpers.StubHelpers.GetWinRTFactoryObject(IntPtr pCPCMD)

          at BrokeredPointOfService.PosExplorer..ctor()

          at ModernPosExplorer.ViewModels.MainViewModel.<LoadData>d__0.MoveNext()

     InnerException:

    it is thrown at line 42 (MainViewModel file): PosExplorer = new PosExplorer();

    it seems that the type PosExplorer cannot be instanciated.

    I wonder if you can point what could be the cause of that error. thanks.

  5. JGO -JorgeGO says:

    Thanks so much for this great article and for give the opportunity to explore a new POS apps using the Universal windows app guidelines.

    Let me ask a question difficult to answer, I could understand...

    Next year I must suggest to my client different decisions, one is strategy to build custom POS apps for retail and I will build a modern windows apps, based on Universal windows apps, where I could use it on tablet and mobile using classical devices barcode scanner, RFID readers, printer, etc.. My idea is use POS for .NET 1.14 and your excellent component to permit it, initially I will build based on Windows embedded 8.1 OS but based on windows 10 roadmap as soon as windows 10 will be ready and if plans from Microsoft permit tu upgrade to windows 10 I want to rebuild and deploy it to new OS... What do you think based on your experience and information, transition will be easy or not?

    I wait for your next post:

    1- demonstrate using the Loopback Adapter to communicate from a Windows 8.1 Modern application to a web service hosted by a Windows Service.

    2-Information about the POS support in Windows 10 becomes available

  6. Anonymous says:

    Hi,

    Great article and very in-depth! I was able to get everything building as you described without changing anything. However, when running ModernPosExplorer, the app errors out with a FileNotFoundException on Microsoft.PointOfService. Not sure why it's failing to find this; I installed it in the default location (C:Program Files (x86)Microsoft Point Of Service). Any ideas?

    Could not load file or assembly 'Microsoft.PointOfService, Version=1.14.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified.

      at ModernPosExplorer.ViewModels.MainViewModel.LoadData()

      at ModernPosExplorer.Views.MainPage.navigationHelper_LoadState(Object sender, LoadStateEventArgs e)

      at ModernPosExplorer.Common.NavigationHelper.OnNavigatedTo(NavigationEventArgs e)

      at ModernPosExplorer.Views.MainPage.OnNavigatedTo(NavigationEventArgs e)

  7. Anonymous says:

    I have the same problem. Could you fix it? Thanks

  8. Sean Liming says:

    Nice article. Opens the POS world to Universal Apps.

  9. Tim Wallace says:

    So, will this wrapper will allow one to use ANY mag stripe reader, or still only those few "special" MSRs?

Skip to main content