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
  3. Rename Class1.cs to BarcodeQRCodeReader.cs.
  4. 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);
    
  5. 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;
     }
    
  6. 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;
     }
    
  7. 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();
         }
     }
    
  8. 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>

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>

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

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>

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>

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("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==");
                 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