.NET Core Barcode Reader for Windows, Linux & macOS

.NET Core empowers C# developers to build DotNet applications for Windows, Linux, and macOS using one codebase. In this article, I want to share how to create a cross-platform .NET Core barcode app with Dynamsoft Barcode Reader SDK.

.NET Core barcode reader

.NET Core Installation

The latest .NET Core does not support the project.json files anymore. Instead, it uses MSBuild/csproj files for project configuration. If you have an old version installed and want to upgrade, you’d better read the article: A mapping between project.json and csproj properties.

C/C++ Barcode Libraries for Windows, Linux, and macOS

Although Dynamsoft Barcode SDK supports all platforms, the version numbers are not consistent. What you should know is the library for Windows is version 5.x, whereas the libraries for Linux and macOS are version 4.x.

Get Dynamsoft C/C++ Barcode libraries now.

.NET Core Barcode Reader

Create a new console project:

dotnet new console –o DynamsoftBarcode

The command line will generate two files: DynamsoftBarcode.csproj and Program.cs.

Copy shared libraries to the project root folder. To run the app, you have to copy *.dll, *.so, and *.dylib files to the output directory after building the project. Therefore, create an item group in DynamsoftBarcode.csproj file:

<ItemGroup>
    <None Update="DynamsoftBarcodeReader.dll">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
    <None Update="libDynamsoftBarcodeReader.dylib">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
    <None Update="libDynamsoftBarcodeReader.so">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
</ItemGroup>

It is time to write some C# code in Program.cs.

To learn how to interoperate native libraries, you can read the article Native Interoperability. The technology used for accessing native structs and functions is called P/Invoke. Use namespace System.Runtime.InteropServices to import shared libraries and define the corresponding native methods. From Dynamsoft Barcode Reader 4.x to 5.x, the API definitions changed a lot.

For Dynamsoft Barcode Reader 5.x:

        [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_InitLicenseEx(IntPtr hBarcode, string license);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_DecodeFileEx(IntPtr hBarcode, string filename, ref IntPtr pBarcodeResultArray);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_SetBarcodeFormats(IntPtr hBarcode, int iFormat);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_SetMaxBarcodesNumPerPage(IntPtr hBarcode, int iMaxCount);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_FreeBarcodeResults(ref IntPtr pBarcodeResultArray);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern void DBR_SetBarcodeTextEncoding(IntPtr hBarcode, BarcodeTextEncoding emEncoding);

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct BarcodeResult
        {
            public int emBarcodeFormat;
            public string pBarcodeData;
            public int iBarcodeDataLength;
            public int iLeft;
            public int iTop;
            public int iWidth;
            public int iHeight;
            public int iX1;
            public int iY1;
            public int iX2;
            public int iY2;
            public int iX3;
            public int iY3;
            public int iX4;
            public int iY4;
            public int iPageNum;
            public IntPtr pBarcodeText;
            public int iAngle;
            public int iModuleSize;
            public int bIsUnrecognized;
            public string pBarcodeFormatString;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct BarcodeResultArray
        {
            public int iBarcodeCount;
            public IntPtr ppBarcodes;
        }

For Dynamsoft Barcode Reader 4.x:

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_InitLicense(string license);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_DecodeFile(string filename, IntPtr opt, ref IntPtr pBarcodeResultArray);

        [DllImport("DynamsoftBarcodeReader")]
        public static extern int DBR_FreeBarcodeResults(ref IntPtr pBarcodeResultArray);

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct BarcodeResult
        {
            public Int64 llFormat;
            public string pBarcodeData;
            public int iBarcodeDataLength;
            public int iLeft;
            public int iTop;
            public int iWidth;
            public int iHeight;
            public int iX1;
            public int iY1;
            public int iX2;
            public int iY2;
            public int iX3;
            public int iY3;
            public int iX4;
            public int iY4;
            public int iPageNum;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct BarcodeResultArray
        {
            public int iBarcodeCount;
            public IntPtr ppBarcodes;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct ReaderOptions
        {
            public int iMaxBarcodesNumPerPage;
            public long llBarcodeFormat;
        }

To use these APIs correctly, you have to check operating system in runtime and set a valid license:

            if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                iOS = BarcodeManager.OS_WIN;
                license = "t0260NQAAALGw+aCAePXdOS3p1xkqT5hesExKVpEe7NiIhkdlUz/Jvx8km3ItI0ykUcmeP67BYVlJ2PDW++bjSYmDLmyMgOmmvc0mdvhlSy500kqnLoBAL+TybcdAP42b5p5WehK9Gsmweqi+ydK6B0KaUNQMDJZ1DrnhDXZ209pfpJoVybPk/CMcDKXaF2oRLKEOYVscXTF6mbiWUnMP5lj4OdTvFa0eVRcE0q9BckiqYgUZLK4L6DVgRXWRL5nRPtvEtd+qZe6psu0JZ7HEPhsbodfAVH2G436z1QahLGJXdQCoQv8UQ/quGQP2wCWemfueeKJ4Y6WsvEvmkUpizbTOE3Njjaw=";
            }
            else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                iOS = BarcodeManager.OS_LINUX;
                license = "30771C7C2299A4271A84011B981A3901";
            }
            else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                iOS = BarcodeManager.OS_MAC;
                license = "30771C7C2299A427B5765EB4250FC51B";
            }

Invoking barcode APIs is not a big deal. The key point is memory management – how to convert managed structure to an unmanaged pointer and vice versa.

You need to pass a managed structure to unmanaged pointer when using Dynamsoft Barcode 4.x:

LinuxMacBarcodeManager.ReaderOptions ro = new LinuxMacBarcodeManager.ReaderOptions();
ro.llBarcodeFormat = iFormat;
ro.iMaxBarcodesNumPerPage = iMaxCount;

// Copy the struct to unmanaged memory.
IntPtr opt = Marshal.AllocHGlobal(Marshal.SizeOf(ro));
Marshal.StructureToPtr(ro, opt, false);

// Read barcodes
int ret = LinuxMacBarcodeManager.DBR_DecodeFile(filename, opt, ref pBarcodeResultArray);

To get the final results, use Marshal.PtrToStructure:

// Print barcode results
if (pBarcodeResultArray != IntPtr.Zero)
{
    LinuxMacBarcodeManager.BarcodeResultArray results = (LinuxMacBarcodeManager.BarcodeResultArray)Marshal.PtrToStructure(pBarcodeResultArray, typeof(LinuxMacBarcodeManager.BarcodeResultArray));
    int count = results.iBarcodeCount;
    IntPtr[] barcodes = new IntPtr[count];

    Marshal.Copy(results.ppBarcodes, barcodes, 0, count);

    for (int i = 0; i < count; i++)
    {
        LinuxMacBarcodeManager.BarcodeResult result = (LinuxMacBarcodeManager.BarcodeResult)Marshal.PtrToStructure(barcodes[i], typeof(LinuxMacBarcodeManager.BarcodeResult));
        Console.WriteLine("Value: " + result.pBarcodeData);
        Console.WriteLine("Format: " + LinuxMacBarcodeManager.GetFormatStr(result.llFormat));
        Console.WriteLine("-----------------------------");
    }

    // Release memory of barcode results
    LinuxMacBarcodeManager.DBR_FreeBarcodeResults(ref pBarcodeResultArray);

}

That’s it. Try the sample code on Windows, Linux, and macOS.

dotnet restore
dotnet run

.NET Core bin
.NET Core barcode reader

Source Code

https://github.com/dynamsoft-dbr/dotnet-core-barcode