GUI Barcode Reader: Porting .NET Framework to .NET Core

Last month, Microsoft released .NET Core 3.0 that allows developers to port Windows Forms and Windows Presentation Foundation (WPF) projects to the .NET Core projects. However, the feature is still Windows-only, which may disappoint someone who wants to create cross-platform GUI apps based on .NET Core. In this article, I will share how to create a simple Windows GUI barcode reader app on .NET Framework, and then port it to .NET Core.

Visual Studio 2017 and Visual Studio Code

I use Visual Studio 2017 for the .NET Framework project and Visual Studio Code for .NET Core project.

Visual Studio 2017 does not support creating Windows Forms App on .NET Core. If you want to create the .NET Core GUI app in Visual Studio, please install Visual Studio 2019 and then follow the video tutorial to create your first WinForms app on .NET Core.

Visual Studio Code is like a light-weighted Visual Studio. It is convenient for running and debugging the .NET Core project.

Creating a Windows Barcode Reader on .NET Framework

Create a new Windows Forms App project. Drag a PictureBox, a Button, and a TextBox from the toolbox to form designer.

windows form designer

Open NuGet to install Dynamsoft Barcode Reader.

nuget barcode sdk

Use system file dialog to load image files:

            using (OpenFileDialog dlg = new OpenFileDialog())
            {
                dlg.Title = "Open Image";
                dlg.Filter = "Image files (*.bmp, *.jpg, *.png) | *.bmp; *.jpg; *.png";

                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    Bitmap bitmap = null;
                    
                    try
                    {
                        bitmap = new Bitmap(dlg.FileName);
                    }
                    catch (Exception exception)
                    {
                        MessageBox.Show(exception.ToString());
                        return;
                    }

                    pictureBox1.Image = bitmap;
                    textBox1.Clear();
                }
            } 

Pass a bitmap to the barcode decoding function:

                    this.Invoke((MethodInvoker)delegate
                    {
                        string[] results = mBarcodeReaderManager.ReadBarcode(bitmap);
                        if (results != null)
                        {
                            foreach (string result in results)
                            {
                                textBox1.AppendText(result);
                                textBox1.AppendText(Environment.NewLine);
                            }
                        }
                        else
                        {
                            textBox1.AppendText("No barcode detected!");
                        }
                        
                    });

Get a free trial license from Dynamsoft website to make the barcode reader object work:

        public BarcodeReaderManager()
        {
            mBarcodeReader = new BarcodeReader("LICENSE-KEY");
        }

        public string[] ReadBarcode(Bitmap bitmap)
        {
            TextResult[] textResults = mBarcodeReader.DecodeBitmap(bitmap, "");
            
            if (textResults != null)
            {
                string[] results = new string[textResults.Length];
                int index = 0;
                foreach (TextResult result in textResults)
                {
                    results[index++] = result.BarcodeText;
                }
                return results;
            }

            return null;
        }

Run the app.

Converting .NET Framework Project to .NET Core Project

The C# code above is pretty simple. Now let’s try to port the .NET Framework project to .NET Core project.

Microsoft provided some tools to help developers quickly port their projects. Download and run PortabilityAnalyzer to check the portability of our .NET Framework project.

portability analyzer
portability analyzer report

It seems the conversion will be perfect.

Download and run try-convert with the following commands:

> cd <.NET Framework Project>
> try-convert -w .\
Conversion complete!

Let’s check the changes.

try convert

The tool only modified the *.csproj file.

So far, you may think the porting has succeeded. Run the app to see what will happen.

.NET Core crash

Oops! It crashed! We can use Visual Studio Code to debug where the error occurs.

.NET Core conversion failure

The exception occurred in Dynamsoft.BarcodeReader.dll. Although I’ve used the portability analyzer tool to analyze the project, the compatibility issue still exists. It turns out the analyzer tool is not 100% reliable.

Fortunately, there is a C/C++ dll file available in the SDK. We can interoperate C# code with unmanaged C/C++ code to make the app work on .NET Core.

Use DllImport to load relevant C/C++ functions:

        [DllImport("DynamsoftBarcodeReader")]
        public static extern IntPtr DBR_CreateInstance();

        [DllImport("DynamsoftBarcodeReader")]
        public static extern void DBR_DestroyInstance(IntPtr hBarcode);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_InitLicense(IntPtr hBarcode, string license);
        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_FreeTextResults(ref IntPtr pTextResultArray);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern void DBR_GetAllTextResults(IntPtr hBarcode, ref IntPtr pTextResultArray);
        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_DecodeBuffer(IntPtr hBarcode, IntPtr pBufferBytes, int width, int height, int stride, ImagePixelFormat format, string template);

Define relevant structures for converting unmanaged memory to managed memory:

        internal struct PTextResult
        {
            BarcodeFormat emBarcodeFormat;
            public string barcodeFormatString;
            BarcodeFormat_2 barcodeFormat_2;
            string barcodeFormatString_2;
            public string barcodeText;
            IntPtr barcodeBytes;
            int barcodeBytesLength;
            IntPtr localizationResult;
            IntPtr detailedResult;
            int resultsCount;
            IntPtr results;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 56)]
            char[] reserved;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct TextResultArray
        {
            public int resultsCount;
            public IntPtr results;
        }

Get the pointer of the bitmap pixel data and then decode barcodes:

            BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
            ImageLockMode.ReadWrite, bitmap.PixelFormat);
            ImagePixelFormat format = ImagePixelFormat.IPF_ARGB_8888;

            switch (bitmap.PixelFormat)
            {
                case PixelFormat.Format24bppRgb:
                    format = ImagePixelFormat.IPF_RGB_888;
                    break;
                case PixelFormat.Format32bppArgb:
                    format = ImagePixelFormat.IPF_ARGB_8888;
                    break;
                case PixelFormat.Format16bppRgb565:
                    format = ImagePixelFormat.IPF_RGB_565;
                    break;
                case PixelFormat.Format16bppRgb555:
                    format = ImagePixelFormat.IPF_RGB_555;
                    break;
                case PixelFormat.Format8bppIndexed:
                    format = ImagePixelFormat.IPF_GRAYSCALED;
                    break;
            }

            int ret = DBR_DecodeBuffer(hBarcode, bmpData.Scan0, bitmap.Width, bitmap.Height, bmpData.Stride, format, "");
            //Unlock the pixels
            bitmap.UnlockBits(bmpData);

Try again.

.NET Core barcode reader

I finally ported the .NET Framework barcode reader to .NET Core barcode reader successfully.

Related Article

.NET Core Barcode Reader for Windows, Linux & macOS

Source Code

https://github.com/yushulx/net-barcode-reader