Friday, November 2, 2018

D365 Modern POS DLLHost.exe file detailed description

Everyone knows that Modern POS is being running by application that will be Windows, Android or IOS. Means that it will be install with Operating System.
But the question is how MPOS will read its configuration like:

1) On which terminal and device it is running.
2) Where Retail Server URL will be stored.
3) From where MPOS will be reading proxy dll or extension for offline mode.
4) From where MPOS will be reading config files and dll paths.
5) From where MPOS will be loading its all required configurations.

Now the Answer is "DLLHost.exe.config" which will be placed in below path when you install Modern POS:

"C:\Program Files (x86)\Microsoft Dynamics 365\70\Retail Modern POS\ClientBroker"



If you will open this file in any text file tool then you can see all Modern POS configurations. The main configurations are:

1) Terminal Id
2) Device Id
3) Retail Server URL
4) Installed App Version
5) Retail Proxy Assembly


Remember that above diagram has empty fields just for understanding otherwise all fields data will be automatically filled during Activation Process. If any field (mentioned above) data is missed then MPOS will not work correctly. It means that you missed some steps in configuration or MPOS not installed correctly or someone upgrade MPOS incorrectly.  

When you will do development then you can also change data in "DLLHost.exe.config" file fir testing. But if you want to apply new changes in development environment then follow below steps:

1) Close Modern POS
2) Go to Task Manager > Details. Now search for "dllhost.exe" process and stop it. If you see multiple "dllhost.exe" processes then kill all.
3) Now Start MPOS. Now your latest changes will be affecting in MPOS.



D365 Apply new changes in DLLHost.exe file for MPOS

When you will do development then you can also change data in "DLLHost.exe.config" file for testing. But if you want to apply new changes in development environment then follow below steps:

1) Close Modern POS
2) Go to Task Manager > Details. Now search for "dllhost.exe" process and stop it. If you see multiple "dllhost.exe" processes then kill all.
3) Now Start MPOS. Now your latest changes will be affecting in MPOS.


D365 Register Dedicated Hardware Station Extension for Modern POS

In this blog, you can find how to register dedicated hardware station for Modern POS when you doing development or deployment.

First of all, create a  new dll "Test.HardwareStation.Extension.dll" of Hardware Station.
Go to the folder "C:\Program Files (x86)\Microsoft Dynamics 365\70\Retail Modern POS\ClientBroker" and place your newly created dll "Test.HardwareStation.Extension.dll" in this folder.


Now you need to register this dll in config file to apply new changes in Modern POS Hardware Station.
Go to the folder "C:\Program Files (x86)\Microsoft Dynamics 365\70\Retail Modern POS\ClientBroker" again and open file "HardwareStation.Dedicated.config". Now you will see something like:


Now Register your dll in this config file on the first line of "Composition" section if you want to run your extension first. Otherwise you can also register your extension is middle or last in "Composition" section.


Now your extension has been registered in Dedicated Hardware Station for Modern POS.
If you want to generate Retail Deployment Package then create package simply but if you want to apply new changes in development environment then follow below steps:

1) Close Modern POS
2) Go to Task Manager > Details. Now search for "dllhost.exe" process and stop it. If you see multiple "dllhost.exe" processes then kill all.
3) Now Start MPOS. Now it will read new Hardware Changes from config file.


D365 Extend Windows Printer Hardware Station

Open Hardware Station sample from Retail SDK. Path of Retail SDK will be something like below:

C:\RetailSDK\Code\SampleExtensions\HardwareStation\SampleHardwareStation.sln




Create new project in this solution "MyNewWindowsPrinterExtension".
Right click on "SampleHardwareStation" Solution and select "Class Library (Portable)":



Now select following targets and click OK:
  • .NET Framework 4.5
  • Windows 8
  • ASP.NET Core 1.0
  • Windows Phone 8.1

Now you can see your new project in this solution:


Now add new class "WindowsPrinter".


Add below references in your newly created project:


Now add below code in your class:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.Dynamics.Commerce.HardwareStation;
using Microsoft.Dynamics.Commerce.HardwareStation.Peripherals;
using Microsoft.Dynamics.Commerce.Runtime.Handlers;
using Microsoft.Dynamics.Commerce.Runtime.Messages;
using Microsoft.Dynamics.Retail.Diagnostics;

namespace MyNewWindowsPrinterExtension
{
    public class WindowsPrinter : INamedRequestHandler
    {
        private const string BarCodeRegEx = "<B: (.+?)>";
        private const string NormalTextMarker = "|1C";
        private const string BoldTextMarker = "|2C";
        private const string DoubleSizeNormalTextMarker = "|3C";
        private const string DoubleSizeBoldTextMarker = "|4C";

        private const string ExceptNormalMarkersRegEx = @"\|2C|\|3C|\|4C";
        private const string AllMarkersForSplitRegEx = @"(\|1C|\|2C|\|3C|\|4C)";

        private List<TextPart> parts;
        private int printLine;
        private Barcode barCode = new BarcodeCode39();
        private DefaultLogo defaultLogo = new DefaultLogo();

        /// <summary>
        /// Gets the esc marker.
        /// </summary>
        public static string EscMarker
        {
            get
            {
                return "&#x1B;";
            }
        }

        /// <summary>
        /// Gets the unique name for this request handler.
        /// </summary>
        public string HandlerName
        {
            get { return PeripheralType.Windows; }
        }

        /// <summary>
        /// Gets the collection of supported request types by this handler.
        /// </summary>
        public IEnumerable<Type> SupportedRequestTypes
        {
            get
            {
                return new[]
                {
                        typeof(OpenPrinterDeviceRequest),
                        typeof(PrintPrinterDeviceRequest),
                        typeof(ClosePrinterDeviceRequest)
                    };
            }
        }

        /// <summary>
        /// Gets or sets the printer name.
        /// </summary>
        protected string PrinterName { get; set; }

        /// <summary>
        /// Represents the entry point for the printer device request handler.
        /// </summary>
        /// <param name="request">The incoming request message.</param>
        /// <returns>The outgoing response message.</returns>
        public Response Execute(Request request)
        {
            ThrowIf.Null(request, "request");

            Type requestType = request.GetType();

            if (requestType == typeof(OpenPrinterDeviceRequest))
            {
                var openRequest = (OpenPrinterDeviceRequest)request;

                this.Open(openRequest.DeviceName);
            }
            else if (requestType == typeof(PrintPrinterDeviceRequest))
            {
                var printRequest = (PrintPrinterDeviceRequest)request;

                this.Print(
                    printRequest.Header,
                    printRequest.Lines,
                    printRequest.Footer);
            }
            else if (requestType == typeof(ClosePrinterDeviceRequest))
            {
                // Do nothing. Just for support of the close request type.
            }
            else
            {
                throw new NotSupportedException(string.Format("Request '{0}' is not supported.", requestType));
            }

            return new NullResponse();
        }

        private static bool DrawBitmapImage(PrintPageEventArgs e, byte[] defaultLogoBytes, float contentWidth, ref float y)
        {
            using (MemoryStream ms = new MemoryStream(defaultLogoBytes))
            {
                var image = Image.FromStream(ms);

                if (y + image.Height >= e.MarginBounds.Height)
                {
                    // No more room - advance to next page
                    e.HasMorePages = true;
                    return false;
                }

                float center = ((contentWidth - image.Width) / 2.0f) + e.MarginBounds.Left;
                if (center < 0)
                {
                    center = 0;
                }

                float top = e.MarginBounds.Top + y;

                e.Graphics.DrawImage(image, center, top); 

                y += image.Height;

                return true;
            }
        }

        /// <summary>
        /// Opens a peripheral.
        /// </summary>
        /// <param name="peripheralName">Name of the peripheral.</param>
        private void Open(string peripheralName)
        {
            this.PrinterName = peripheralName;
        }

        /// <summary>
        /// Print the data on printer.
        /// </summary>
        /// <param name="header">The header.</param>
        /// <param name="lines">The lines.</param>
        /// <param name="footer">The footer.</param>
        private void Print(string header, string lines, string footer)
        {
            string textToPrint = header + lines + footer;

            if (!string.IsNullOrWhiteSpace(textToPrint))
            {
                using (PrintDocument printDoc = new PrintDocument())
                {
                    printDoc.PrinterSettings.PrinterName = this.PrinterName;
                    string subString = textToPrint.Replace(EscMarker, string.Empty);
                    var printText = subString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

                    this.parts = new List<TextPart>();
                    foreach (var line in printText)
                    {
                        var lineParts = TextLogoParser.Parse(line);
                        if (null != lineParts)
                        {
                            this.parts.AddRange(lineParts);
                        }
                    }

                    this.printLine = 0;

                    printDoc.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(this.PrintPageHandler);

#if DEBUG
                    if ("Microsoft XPS Document Writer" == this.PrinterName)
                    {
                        printDoc.PrinterSettings.PrintFileName = Path.Combine(Path.GetTempPath(), "HardwareStation_Print_Result.xps");
                        printDoc.PrinterSettings.PrintToFile = true;
                        NetTracer.Information(string.Format(CultureInfo.InvariantCulture, "Look for XPS file here: {0}", printDoc.PrinterSettings.PrintFileName));
                    }
#endif
                    try
                    {
                        printDoc.Print();
                    }
                    catch (InvalidPrinterException)
                    {
                        throw new PeripheralException(PeripheralException.PeripheralNotFound);
                    }
                }
            }
        }

        /// <summary>
        /// Prints the page.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="PrintPageEventArgs" /> instance containing the event data.</param>
        private void PrintPageHandler(object sender, PrintPageEventArgs e)
        {
            const int LineHeight = 10;

            const string TextFontName = "Courier New";

            const int DoubleSizeTextFontSize = 14;

            e.HasMorePages = false;
            using (Font textFont = new Font(TextFontName, TextFontSize, FontStyle.Regular))
            using (Font textFontBold = new Font(TextFontName, TextFontSize, FontStyle.Bold))
            using (Font doubleSizeTextFont = new Font(TextFontName, DoubleSizeTextFontSize, FontStyle.Regular))
            using (Font doubleSizeTextFontBold = new Font(TextFontName, DoubleSizeTextFontSize, FontStyle.Bold))
            {
                float y = 0;
                float dpiXRatio = e.Graphics.DpiX / 96f; // 96dpi = 100%
                float dpiYRatio = e.Graphics.DpiY / 96f; // 96dpi = 100%

                // This calculation isn't exactly the width of the rendered text.
                // All the calculations occurring in the rendering code of PrintTextLine.  It almost needs to run that code and use e.Graphics.MeasureString()
                // the first time to get the true contentWidth, then re-run the same logic using the true contentWidth for rendering.
                //
                // For now, the rendering is close, but it's not 'exact' center due to the mismatch in estimated vs. true size
                float contentWidth = this.parts.Where(x => x.TextType == TextType.Text)
                                               .Select(p => p.Value)
                                               .Max(str => str.Replace(NormalTextMarker, string.Empty)
                                                              .Replace(BoldTextMarker, string.Empty)
                                                              .Replace(DoubleSizeNormalTextMarker, string.Empty)
                                                              .Replace(DoubleSizeBoldTextMarker, string.Empty)
                                                              .Length)
                                     * dpiXRatio; // Line with max length = content width

                for (; this.printLine < this.parts.Count; this.printLine++)
                {
                    var part = this.parts[this.printLine];

                    if (part.TextType == TextType.Text)
                    {
                        if (!this.PrintTextLine(
                                                e,
                                                LineHeight,
                                                textFont,
                                                textFontBold,
                                                doubleSizeTextFont,
                                                doubleSizeTextFontBold,
                                                dpiYRatio,
                                                contentWidth,
                                                dpiXRatio,
                                                part.Value,
                                                ref y))
                        {
                            return;
                        }
                    }
                    else if (part.TextType == TextType.LegacyLogo)
                    {
                        byte[] defaultLogoBytes = this.defaultLogo.GetBytes();
                        if (!DrawBitmapImage(e, defaultLogoBytes, contentWidth, ref y))
                        {
                            return;
                        }
                    }
                    else if (part.TextType == TextType.LogoWithBytes)
                    {
                        byte[] image = TextLogoParser.GetLogoImageBytes(part.Value);
                        if (!DrawBitmapImage(e, image, contentWidth, ref y))
                        {
                            return;
                        }
                    }
                }
            }
        }

        private bool PrintTextLine(
                        PrintPageEventArgs e,
                        int lineHeight,
                        Font textFont,
                        Font textFontBold,
                        Font doubleSizeTextFont,
                        Font doubleSizeTextFontBold,
                        float dpiYRatio,
                        float contentWidth,
                        float dpiXRatio,
                        string line,
                        ref float y)
        {
            // it always use one line height
            // because the Report designer use a col and a line to set position for all conrtols
            if (y + lineHeight >= e.MarginBounds.Height)
            {
                // No more room - advance to next page
                e.HasMorePages = true;
                return false;
            }

            bool hasFontModificator = (line.Length > 0) && Regex.IsMatch(line, ExceptNormalMarkersRegEx, RegexOptions.Compiled);
            if (hasFontModificator)
            {
                // Text line printing with bold Text in it.
                float x = 0;
                Font currentFont = textFont;
                int sizeFactor = 1;

                string[] subStrings = Regex.Split(line, AllMarkersForSplitRegEx, RegexOptions.Compiled);
                foreach (string subString in subStrings)
                {
                    switch (subString)
                    {
                        case NormalTextMarker:
                            currentFont = textFont;
                            sizeFactor = 1;
                            break;
                        case BoldTextMarker:
                            currentFont = textFontBold;
                            sizeFactor = 2;
                            break;
                        case DoubleSizeNormalTextMarker:
                            currentFont = doubleSizeTextFont;
                            sizeFactor = 1;
                            break;
                        case DoubleSizeBoldTextMarker:
                            currentFont = doubleSizeTextFontBold;
                            sizeFactor = 2;
                            break;
                        default:
                            if (!string.IsNullOrEmpty(subString))
                            {
                                e.Graphics.DrawString(subString, currentFont, Brushes.Black, x + e.MarginBounds.Left, y + e.MarginBounds.Top);
                                x = x + (subString.Length * sizeFactor * 6);
                            }

                            break;
                    }
                }
            }
            else
            {
                // Text line printing with no bold Text and no double size Text in it.
                string subString = line.Replace(NormalTextMarker, string.Empty);

                Match barCodeMarkerMatch = Regex.Match(subString, BarCodeRegEx, RegexOptions.Compiled | RegexOptions.IgnoreCase);

                if (barCodeMarkerMatch.Success)
                {
                    // Get the receiptId
                    subString = barCodeMarkerMatch.Groups[1].ToString();

                    using (Image barcodeImage = barCode.Create(subString, e.Graphics.DpiX, e.Graphics.DpiY))
                    {
                        if (barcodeImage != null)
                        {
                            float barcodeHeight = barcodeImage.Height / dpiYRatio;

                            if (y + barcodeHeight >= e.MarginBounds.Height)
                            {
                                // No more room - advance to next page
                                e.HasMorePages = true;
                                return true;
                            }

                            // Render barcode in the center of receipt.
                            e.Graphics.DrawImage(barcodeImage, ((contentWidth - (barcodeImage.Width / dpiXRatio)) / 2) + e.MarginBounds.Left, y + e.MarginBounds.Top);
                            y += barcodeHeight;
                        }
                    }
                }
                else
                {
                    e.Graphics.DrawString(subString, textFont, Brushes.Black, e.MarginBounds.Left, y + e.MarginBounds.Top);
                }
            }

            y = y + lineHeight;
            return true;
        }
    }
}

D365 Extend OPOS MSR Hardware Station

Open Hardware Station sample from Retail SDK. Path of Retail SDK will be something like below:

C:\RetailSDK\Code\SampleExtensions\HardwareStation\SampleHardwareStation.sln



Create new project in this solution "MyNewMSRExtension".
Right click on "SampleHardwareStation" Solution and select "Class Library (Portable)":



Now select following targets and click OK:
  • .NET Framework 4.5
  • Windows 8
  • ASP.NET Core 1.0
  • Windows Phone 8.1

Now you can see your new project in this solution:


Now add new class "OposMagneticSwipeReader".


Add below references in your newly created project:


Now add below code in your class:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Interop.OposMSR;
using Microsoft.Dynamics.Commerce.HardwareStation;
using Microsoft.Dynamics.Commerce.HardwareStation.CardPayment;
using Microsoft.Dynamics.Commerce.HardwareStation.Peripherals;
using Microsoft.Dynamics.Commerce.HardwareStation.Peripherals.Entities;
using Microsoft.Dynamics.Commerce.Runtime.Handlers;
using Microsoft.Dynamics.Commerce.Runtime.Messages;
using Microsoft.Dynamics.Retail.Diagnostics;

namespace MyNewMSRExtension
{
    public sealed class OposMagneticSwipeReader : INamedRequestHandler
    {
        private const string MsrInstanceNameTemplate = "OposMsr_{0}";
        private OPOSMSR oposMsr;
        private string msrInstanceName;

        /// <summary>
        /// Occurs when card is swiped.
        /// </summary>
        private event EventHandler<MagneticCardSwipeInfo> MsrSwipeEvent;

        /// <summary>
        /// Gets the unique name for this request handler.
        /// </summary>
        public string HandlerName
        {
            get { return PeripheralType.Opos; }
        }

        /// <summary>
        /// Gets the collection of supported request types by this handler.
        /// </summary>
        public IEnumerable<Type> SupportedRequestTypes
        {
            get
            {
                return new[]
                {
                        typeof(OpenMagneticSwipeReaderDeviceRequest),
                        typeof(SwipeMagneticSwipeReaderDeviceRequest),
                        typeof(CloseMagneticSwipeReaderDeviceRequest)
                    };
            }
        }

        /// <summary>
        /// Represents the entry point for the magnetic swipe reader device request handler.
        /// </summary>
        /// <param name="request">The incoming request message.</param>
        /// <returns>The outgoing response message.</returns>
        public Response Execute(Request request)
        {
            ThrowIf.Null(request, "request");

            Type requestType = request.GetType();

            if (requestType == typeof(OpenMagneticSwipeReaderDeviceRequest))
            {
                var openRequest = (OpenMagneticSwipeReaderDeviceRequest)request;

                this.Open(openRequest.DeviceName);
            }
            else if (requestType == typeof(SwipeMagneticSwipeReaderDeviceRequest))
            {
                var swipeRequest = (SwipeMagneticSwipeReaderDeviceRequest)request;

                MagneticCardSwipeInfo swipeInfo = this.WaitForSwipe(swipeRequest.TimeoutInSeconds);

                return new SwipeMagneticSwipeReaderDeviceResponse(swipeInfo);
            }
            else if (requestType == typeof(CloseMagneticSwipeReaderDeviceRequest))
            {
                this.Close();
            }
            else
            {
                throw new NotSupportedException(string.Format("Request '{0}' is not supported.", requestType));
            }

            return new NullResponse();
        }

        /// <summary>
        /// Opens a peripheral.
        /// </summary>
        /// <param name="peripheralName">Name of the peripheral.</param>
        private void Open(string peripheralName)
        {
            this.msrInstanceName = string.Format(MsrInstanceNameTemplate, peripheralName);

            this.oposMsr = OPOSDeviceManager<OPOSMSR>.Instance.AcquireDeviceHandle<OPOSMSRClass>();

            if (!this.oposMsr.DeviceEnabled)
            {
                // Open the device only if the device is not claimed.
                if (!this.oposMsr.Claimed)
                {
                    // Open
                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Open");
                    this.oposMsr.Open(peripheralName);
                    OposHelper.CheckResultCode(this, this.oposMsr.ResultCode);

                    // Claim
                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Claimed");
                    this.oposMsr.ClaimDevice(OposHelper.ClaimTimeOut);
                    OposHelper.CheckResultCode(this, this.oposMsr.ResultCode);

                    // Enable/Configure
                    this.oposMsr.DeviceEnabled = true;

                    // Set the decode data - so that the device decodes the scanned data
                    this.oposMsr.DecodeData = true;

                    // Note: there are two properties that look similar
                    // ParseDecodedData and ParseDecodeData
                    // Both do the same as per the OPOS spec.
                    // Setting this property makes the device return data
                    // in individual fields.
                    this.oposMsr.ParseDecodedData = true;

                    // Set Transmit Sentinels to true
                    // so that when the data is sent, we can get the sentinels
                    // and can parse the data of the tracks.
                    this.oposMsr.TransmitSentinels = true;

                    // Plug in handlers for data events
                    this.oposMsr.DataEvent += this.OnMsrDataEvent;
                    this.oposMsr.ErrorEvent += this.OnMsrErrorEvent;

                    // Set autodisable to false
                    this.oposMsr.AutoDisable = false;

                    // Enable data events
                    this.oposMsr.DataEventEnabled = true;
                }
                else
                {
                    RetailLogger.Log.HardwareStationPeripheralInteraction(this.msrInstanceName, "Prevent MSR device open call, as device is already claimed.");
                }
            }
        }

        /// <summary>
        /// Waits for a swipe during the specified <paramref name="timeoutInSeconds"/>.
        /// </summary>
        /// <param name="timeoutInSeconds">The timeout in seconds to wait for a swipe to occur.</param>
        /// <returns>Swipe info.</returns>
        private MagneticCardSwipeInfo WaitForSwipe(int timeoutInSeconds)
        {
            var eventCompletionSource = new TaskCompletionSource<MagneticCardSwipeInfo>();
            EventHandler<MagneticCardSwipeInfo> handler = null;

            handler = (sender, args) =>
            {
                eventCompletionSource.SetResult(args);

                this.MsrSwipeEvent -= handler;
            };

            this.MsrSwipeEvent += handler;

            var timeout = TimeSpan.FromSeconds(timeoutInSeconds);

            var completeTaskIndex = Task.WaitAny(new[] { (Task)eventCompletionSource.Task }, timeout);

            var isSwipeOccurred = completeTaskIndex == 0;

            return isSwipeOccurred
                ? eventCompletionSource.Task.Result
                : null;
        }

        /// <summary>
        /// Closes a connection with MSR.
        /// </summary>
        private void Close()
        {
            try
            {
                this.oposMsr = OPOSDeviceManager<OPOSMSR>.Instance.AcquireDeviceHandle<OPOSMSRClass>();

                if (this.MsrSwipeEvent != null)
                {
                    this.MsrSwipeEvent(this, null);
                }

                if (this.oposMsr != null)
                {
                    this.oposMsr.DataEvent -= this.OnMsrDataEvent;
                    this.oposMsr.ErrorEvent -= this.OnMsrErrorEvent;
                    this.oposMsr.DeviceEnabled = false;

                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Released");
                    this.oposMsr.ReleaseDevice();

                    RetailLogger.Log.HardwareStationOposMethodCall(this.msrInstanceName, "Closed");
                    this.oposMsr.Close();
                }
            }
            finally
            {
                if (this.oposMsr != null)
                {
                    OPOSDeviceManager<OPOSMSR>.Instance.ReleaseDeviceHandle(this.oposMsr);
                }
            }
        }

        private void OnMsrErrorEvent(int resultCode, int resultCodeExtended, int errorLocus, ref int errorResponse)
        {
            var ex = new PeripheralException(PeripheralException.PeripheralEventError, "Error Event from Peripheral with error code {0}", resultCode);
            RetailLogger.Log.HardwareStationPerpheralError("Magnetic Swipe Reader (MSR)", resultCode, resultCodeExtended, ex);

            throw ex;
        }

        private void OnMsrDataEvent(int status)
        {
            if (this.MsrSwipeEvent != null)
            {
                var swipeInfo = new MagneticCardSwipeInfo();

                //Do your work here. Here you can find MSR scanned data

                // Store the account number in the temporary storage and mask the accout number only if it's the payment card.
                if (swipeInfo.IsPaymentCard)
                {
                    // Store the swipe info into the temporary memory storage.
                    var temporaryMemoryManager = new TemporaryMemoryManager<string, string>();
                    temporaryMemoryManager.AddCardInfoToMemory(MagneticCardSwipeInfo.MsrCardKey, swipeInfo.AccountNumber, DateTime.UtcNow, swipeInfo.AccountNumber);

                    // Mask the account number using mask chars.
                    string maskedAccountNumber = Utilities.GetMaskedCardNumber(swipeInfo.AccountNumber);
                    swipeInfo.AccountNumber = maskedAccountNumber;
                }

                this.MsrSwipeEvent(this, swipeInfo);

            }

            // This should not be required since the auto disable is set to false
            // but apparently some Msrs require this explictly enabled
            // every time.
            this.oposMsr.DataEventEnabled = true;
        }
    }
}

You can also write your custom logic in above code after comments "Do your work here. Here you can find MSR scanned data".


D365 Modern POS DLLHost.exe file detailed description

Everyone knows that Modern POS is being running by application that will be Windows, Android or IOS. Means that it will be install with Ope...