DEV Community

Xiao Ling
Xiao Ling

Posted on • Originally published at dynamsoft.com

How to Build .NET 6 Barcode and QR Code SDK for Windows, Linux & macOS

Microsoft .NET 6 SDK empowers C# developers to build cross-platform DotNet applications for Windows, Linux, and macOS from one codebase. This article describes the steps to build a .NET 6 barcode and QR code decoding library based on Dynamsoft C/C++ Barcode SDK, as well as how to pack the library into a NuGet package.

.NET 6 SDK Installation

Dynamsoft Barcode Reader

  1. Download C/C++ SDK v9.0.
  2. Get a valid license key for activating the SDK.

Steps to Develop and Build .NET 6 Barcode and QR Code SDK

  1. Create a new library project:

    dotnet new classlib -o BarcodeQRCodeSDK
    
  2. Copy shared library files from the C/C++ SDK package to the project root directory. For different platforms, the minimum required shared library files are:

- Windows: `DynamsoftBarcodeReader.dll`, `vcomp110.dll`
- Linux: `libDynamsoftBarcodeReader.so`
- macOS: `libDynamsoftBarcodeReader.dylib`    
Enter fullscreen mode Exit fullscreen mode
  1. Rename Class1.cs to BarcodeQRCodeReader.cs.
  2. P/Invoke is the technology used for bridging C/C++ and .NET. In the BarcodeQRCodeReader.cs file, we use DllImport to load the unmanaged shared library (e.g. *.dll, *.so, *.dylib) and define some managed methods to communicate with the native component.

    [DllImport("DynamsoftBarcodeReader")]
    static extern IntPtr DBR_CreateInstance();
    
    [DllImport("DynamsoftBarcodeReader")]
    static extern void DBR_DestroyInstance(IntPtr hBarcode);
    
    [DllImport("DynamsoftBarcodeReader")]
    static extern int DBR_InitLicense(string license, [Out] byte[] errorMsg, int errorMsgSize);
    
    [DllImport("DynamsoftBarcodeReader")]
    static extern int DBR_DecodeFile(IntPtr hBarcode, string filename, string template);
    
    [DllImport("DynamsoftBarcodeReader")]
    static extern int DBR_FreeTextResults(ref IntPtr pTextResultArray);
    
    [DllImport("DynamsoftBarcodeReader")]
    static extern void DBR_GetAllTextResults(IntPtr hBarcode, ref IntPtr pTextResultArray);
    
    [DllImport("DynamsoftBarcodeReader")]
    static extern int DBR_DecodeBuffer(IntPtr hBarcode, IntPtr pBufferBytes, int width, int height, int stride, ImagePixelFormat format, string template);
    
    [DllImport("DynamsoftBarcodeReader")]
    static extern int DBR_DecodeBase64String(IntPtr hBarcode, string base64string, string template);
    
  3. In addition, we need to define some native structs in C#:

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    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;
    }
    
  4. The memory operation between the managed structure and the unmanaged pointer is a little bit tricky. We need to use Marshal to convert data:

    IntPtr pTextResultArray = IntPtr.Zero;
    
    DBR_GetAllTextResults(hBarcode, ref pTextResultArray);
    
    if (pTextResultArray != IntPtr.Zero)
    {
        string[]? resultArray = null;
        TextResultArray? results = (TextResultArray?)Marshal.PtrToStructure(pTextResultArray, typeof(TextResultArray));
        if (results != null)
        {
            int count = results.Value.resultsCount;
            if (count > 0)
            {
                IntPtr[] barcodes = new IntPtr[count];
                Marshal.Copy(results.Value.results, barcodes, 0, count);
                resultArray = new string[count];
    
                for (int i = 0; i < count; i++)
                {
                    PTextResult? result = (PTextResult?)Marshal.PtrToStructure(barcodes[i], typeof(PTextResult));
                    if (result != null)
                    {
                        resultArray[i] = result.Value.barcodeText;
                    }
                }
            }
        }
    
        DBR_FreeTextResults(ref pTextResultArray);
    
        return resultArray;
    }
    
  5. Once the communication problem between managed and unmanaged code is solved, we can define some high-level C# methods:

    public class BarcodeQRCodeReader
    {
        private IntPtr hBarcode;
        private static string? licenseKey;
    
        public static void InitLicense(string license) {
            byte[] errorMsg = new byte[512];
            licenseKey = license;
            DBR_InitLicense(license, errorMsg, 512);
            Console.WriteLine(Encoding.ASCII.GetString(errorMsg) + "\n");
        }
    
        private BarcodeQRCodeReader()
        {
            hBarcode = DBR_CreateInstance();
        }
    
        public static BarcodeQRCodeReader Create()
        {
            if (licenseKey == null)
            {
                throw new Exception("Please call InitLicense first.");
            }
            return new BarcodeQRCodeReader();
        }
    
        ~BarcodeQRCodeReader()
        {
            if (hBarcode != IntPtr.Zero)
            {
                DBR_DestroyInstance(hBarcode);
                hBarcode = IntPtr.Zero;
            }
        }
    
        public void Destroy()
        {
            if (hBarcode != IntPtr.Zero)
            {
                DBR_DestroyInstance(hBarcode);
                hBarcode = IntPtr.Zero;
            }
        }
    
        public string[]? DecodeFile(string filename)
        {
            if (hBarcode == IntPtr.Zero) return null;
    
            int ret = DBR_DecodeFile(hBarcode, filename, "");
            return OutputResults();
        }
    }
    
  6. Build the source code to generate the *.dll file.

    dotnet build --configuration Release
    

How to Generate and Publish NuGet Package

To generate the *.nupkg file, the easiest way is to add <GeneratePackageOnBuild>true</GeneratePackageOnBuild> to the *.csproj file:

<PropertyGroup>
  ...
  <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  ...
</PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

Then the *.nupkg file will be generated automatically when you build the project.

Our package contains some native library files. To pack them into the *.nupkg file correctly, we set the corresponding PackagePath in *.csproj file according to the Runtime Identifier:

<ItemGroup>
  <None CopyToOutputDirectory="Always" Include="DynamsoftBarcodeReader.dll" Pack="true" PackagePath="runtimes/win-x64/native/DynamsoftBarcodeReader.dll" />
  <None CopyToOutputDirectory="Always" Include="vcomp110.dll" Pack="true" PackagePath="runtimes/win-x64/native/vcomp110.dll" />
  <None CopyToOutputDirectory="Always" Include="libDynamsoftBarcodeReader.dylib" Pack="true" PackagePath="runtimes/osx-x64/native/libDynamsoftBarcodeReader.dylib" />
  <None CopyToOutputDirectory="Always" Include="libDynamsoftBarcodeReader.so" Pack="true" PackagePath="runtimes/linux-x64/native/libDynamsoftBarcodeReader.so" />
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

As the *.nupkg file is ready, we can publish it to the NuGet Gallery either via the dotnet command:

dotnet nuget push *.nupkg -k <api-key> -s https://api.nuget.org/v3/index.json
Enter fullscreen mode Exit fullscreen mode

or the NuGet online page.

nuget package upload

Here is the final page of BarcodeQRCodeSDK:
https://www.nuget.org/packages/BarcodeQRCodeSDK/

How to Add a .NET Library Project as a Reference Locally

For source code, add <ProjectReference> in *.csproj file:

<ItemGroup>
    <ProjectReference Include="..\..\BarcodeQRCodeSDK.csproj" />
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

For generated *.nupkg file, add the package directory to NuGet source list and then install the package via dotnet add package:

dotnet nuget add source <package directory>
dotnet add package <package name>
Enter fullscreen mode Exit fullscreen mode

A Simple .NET 6 Command-line Example

  1. Create a new .NET console app:

    dotnet new console -o Test
    
  2. Install the .NET Barcode and QR Code SDK:

    dotnet add package BarcodeQRCodeSDK
    
  3. Use the following code to decode barcode and QR code from an image file:

    using System;
    using System.Runtime.InteropServices;
    using Dynamsoft;
    
    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                BarcodeQRCodeReader.InitLicense("LICENSE-KEY");
                BarcodeQRCodeReader? reader = null;
                try {
                    reader = BarcodeQRCodeReader.Create();
                    Console.WriteLine("Please enter an image file: ");
                    string? filename = Console.ReadLine();
                    if (filename != null) {
                        string[]? results = reader.DecodeFile(filename);
                        if (results != null) {
                            foreach (string result in results) {
                                Console.WriteLine(result);
                            }
                        }
                        else {
                            Console.WriteLine("No barcode found.");
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                finally
                {
                    if (reader != null)
                    {
                        reader.Destroy();
                    }
                }
            }
        }
    }
    
  4. Run the application in Windows, Linux or macOS:

    dotnet run
    

    command-line .NET barcode and QR code reader

Source Code

https://github.com/yushulx/dotnet-barcode-qr-code-sdk

Top comments (0)