如何为桌面和移动开发创建Nuget软件包
#dotnet #android #ios #maui

在我的previous article中,我演示了如何将C ++库包装到Nuget软件包中。本周,我将将包装扩展到包括Android和iOS绑定库,从而在 Windows Linux macos 中启用多平台开发, android ios 。一个多合一的Nuget软件包的优点是,您可以使用Unified .NET API为台式机和移动平台创建条形码和QR代码扫描应用程序,从而消除了对平台特定代码的需求。

下载barcodeqrcodesdk

https://www.nuget.org/packages/BarcodeQRCodeSDK

多合一的Nuget软件包是什么样的?

All-in-one NuGet package

  • BarcodeQRCodeSDK.targets文件包含视觉C ++项目的构建指令。
  • net6.0文件夹包含Windows,Linux和MacOS的.NET绑定库。 runtimes文件夹包含C ++库(*.dll*.so*.dylib),由.NET绑定库调用。
  • net6.0-android31.0文件夹包含.NET绑定库和Android的*.aar文件。
  • net6.0-ios16.1文件夹包含.net绑定库和iOS的.xcframework软件包。

如何为Android和iOS绑定库创建统一的.NET API

要将新的Android和iOS绑定库项目添加到Visual Studio中的BarcodeQRCodeSDK解决方案中,只需单击解决方案上的右键,然后选择“ add”。有关构建Android和iOS库的详细步骤,请参考https://www.dynamsoft.com/codepool/dotnet-android-ios-nuget-package.html

.NET Android and iOS binding projects

Bar Codeqrcodesdk解决方案现在包括三个子项目。您需要在Windows上构建iOS绑定库之前运行Pair to Mac。在尝试在MACOS上构建解决方案的同时,将项目导入Mac的Visual Studio时会遇到以下错误。解决方法是在MacOS上独立构建iOS绑定项目。

Visual Studio macOS error

由于我们的目标是创建一个多合一的Nuget软件包,因此最好确保Android和iOS绑定库的输出名称与.NET绑定库一致。为此,我们可以将AssemblyName属性添加到Android和iOS绑定库的*.csproj文件中。

<AssemblyName>BarcodeQRCodeSDK</AssemblyName>

此时,两个绑定库都可以使用。但是,API尚未统一。为了实现这一目标,您需要在Android和iOS绑定库项目中创建一个BarcodeQRCodeReader.cs文件以实现其他功能。参考.NET绑定库项目中现有的BarcodeQRCodeReader.cs文件,您可以为Android和iOS绑定库编写以下代码。

  1. 导入因名称空间。

    android

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;
    using Android.Hardware.Lights;
    using Com.Dynamsoft.Dbr;
    using Java.Nio;
    using Java.Nio.FileNio.Attributes;
    

    ios

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    using com.Dynamsoft.Dbr;
    using System.Runtime.InteropServices;
    using Foundation;
    
  2. 创建一个BarcodeQRCodeReader类,一个Result类和ImagePixelFormat枚举:

    android

    namespace Dynamsoft
    {
        public class BarcodeQRCodeReader
        {
            private BarcodeReader? reader;
    
            public class Result
            {
                public string? Text { get; set; }
                public int[]? Points { get; set; }
                public string? Format1 { get; set; }
                public string? Format2 { get; set; }
            }
    
            public enum ImagePixelFormat
            {
                IPF_BINARY,
                IPF_BINARYINVERTED,
                IPF_GRAYSCALED,
                IPF_NV21,
                IPF_RGB_565,
                IPF_RGB_555,
                IPF_RGB_888,
                IPF_ARGB_8888,
                IPF_RGB_161616,
                IPF_ARGB_16161616,
                IPF_ABGR_8888,
                IPF_ABGR_16161616,
                IPF_BGR_888
            }
        }
    }
    

    ios

    namespace Dynamsoft
    {
        public class BarcodeQRCodeReader
        {
            private DynamsoftBarcodeReader? reader;
    
            public class Result
            {
                public string? Text { get; set; }
                public int[]? Points { get; set; }
                public string? Format1 { get; set; }
                public string? Format2 { get; set; }
            }
    
            public enum ImagePixelFormat
            {
                IPF_BINARY,
                IPF_BINARYINVERTED,
                IPF_GRAYSCALED,
                IPF_NV21,
                IPF_RGB_565,
                IPF_RGB_555,
                IPF_RGB_888,
                IPF_ARGB_8888,
                IPF_RGB_161616,
                IPF_ARGB_16161616,
                IPF_ABGR_8888,
                IPF_ABGR_16161616,
                IPF_BGR_888
            }
        }
    }
    
  3. 实施InitLicense()Create()GetVersionInfo()DecodeFile()DecodeBuffer()DecodeBase64()SetParameters方法:

    android

    public class DBRLicenseVerificationListener : Java.Lang.Object, IDBRLicenseVerificationListener
    {
        public void DBRLicenseVerificationCallback(bool isSuccess, Java.Lang.Exception error)
        {
            if (!isSuccess)
            {
                System.Console.WriteLine(error.Message);
            }
        }
    }
    
    public static void InitLicense(string license)
    {
        BarcodeReader.InitLicense(license, new DBRLicenseVerificationListener());
    }
    
    private BarcodeQRCodeReader()
    {
        reader = new BarcodeReader();
    }
    
    public static BarcodeQRCodeReader Create()
    {
        return new BarcodeQRCodeReader();
    }
    
    ~BarcodeQRCodeReader()
    {
        Destroy();
    }
    
    public void Destroy()
    {
        reader = null;
    }
    
    public static string? GetVersionInfo()
    {
        return BarcodeReader.Version;
    }
    
    private Result[]? OutputResults(TextResult[]? results)
    {
        Result[]? output = null;
        if (results != null && results.Length > 0)
        {
            output = new Result[results.Length];
            for (int i = 0; i < results.Length; ++i)
            {
                TextResult tmp = results[i];
                Result r = new Result();
                output[i] = r;
                r.Text = tmp.BarcodeText;
                r.Format1 = tmp.BarcodeFormatString;
                r.Format2 = tmp.BarcodeFormatString;
                if (tmp.LocalizationResult != null && tmp.LocalizationResult.ResultPoints != null)
                {
                    IList<Point> points = tmp.LocalizationResult.ResultPoints;
                    r.Points = new int[8] { points[0].X, points[0].Y, points[1].X, points[1].Y, points[2].X, points[2].Y, points[3].X, points[3].Y };
                }
                else
                    r.Points = null;
            }
        }
    
        return output;
    }
    
    public Result[]? DecodeFile(string filename)
    {
        if (reader == null) { return null; }
    
        TextResult[]? results = reader.DecodeFile(filename);
        return OutputResults(results);
    }
    
    public Result[]? DecodeBuffer(byte[] buffer, int width, int height, int stride, ImagePixelFormat format)
    {
        if (reader == null) { return null; }
    
        TextResult[]? results = reader.DecodeBuffer(buffer, width, height, stride, (int)format);
    
        return OutputResults(results);
    }
    
    public Result[]? DecodeBase64(string base64string)
    {
        if (reader == null) { return null; }
        TextResult[]? results = reader.DecodeBase64String(base64string);
    
        return OutputResults(results);
    }
    
    public void SetParameters(string parameters)
    {
        if (reader == null) { return; }
    
        reader.InitRuntimeSettingsWithString(parameters, EnumConflictMode.CmOverwrite);
    }
    

    ios

    public class Listener : DBRLicenseVerificationListener
    {
        public void DBRLicenseVerificationCallback(bool isSuccess, NSError error)
        {
            if (error != null)
            {
                System.Console.WriteLine(error.UserInfo);
            }
        }
    }
    
    public static void InitLicense(string license)
    {
        DynamsoftBarcodeReader.InitLicense(license, new Listener());
    }
    
    private BarcodeQRCodeReader()
    {
        reader = new DynamsoftBarcodeReader();
    }
    
    public static BarcodeQRCodeReader Create()
    {
        return new BarcodeQRCodeReader();
    }
    
    ~BarcodeQRCodeReader()
    {
        Destroy();
    }
    
    public void Destroy()
    {
        reader = null;
    }
    
    public static string? GetVersionInfo()
    {
        return DynamsoftBarcodeReader.Version;
    }
    
    private Result[]? OutputResults(iTextResult[]? results)
    {
        Result[]? output = null;
        if (results != null && results.Length > 0)
        {
            output = new Result[results.Length];
            for (int i = 0; i < results.Length; ++i)
            {
                iTextResult tmp = results[i];
                Result r = new Result();
                output[i] = r;
                r.Text = tmp.BarcodeText;
                r.Format1 = tmp.BarcodeFormatString;
                r.Format2 = tmp.BarcodeFormatString;
                if (tmp.LocalizationResult != null && tmp.LocalizationResult.ResultPoints != null)
                {
                    NSObject[] points = tmp.LocalizationResult.ResultPoints;
                    r.Points = new int[8] { (int)((NSValue)points[0]).CGPointValue.X, (int)((NSValue)points[0]).CGPointValue.Y, (int)((NSValue)points[1]).CGPointValue.X, (int)((NSValue)points[1]).CGPointValue.Y, (int)((NSValue)points[2]).CGPointValue.X, (int)((NSValue)points[2]).CGPointValue.Y, (int)((NSValue)points[3]).CGPointValue.X, (int)((NSValue)points[3]).CGPointValue.Y };
                }
                else
                    r.Points = null;
            }
        }
    
        return output;
    }
    
    public Result[]? DecodeFile(string filename)
    {
        if (reader == null) { return null; }
    
        NSError error;
        iTextResult[]? results = reader.DecodeFileWithName(filename, out error);
        return OutputResults(results);
    }
    
    public Result[]? DecodeBuffer(byte[] myBytes, int width, int height, int stride, ImagePixelFormat format)
    {
        if (reader == null) { return null; }
    
        NSError error;
        IntPtr buffer = Marshal.AllocHGlobal(myBytes.Length);
        Marshal.Copy(myBytes, 0, buffer, myBytes.Length);
        NSData data = NSData.FromBytes(buffer, (nuint)myBytes.Length);
    
        iTextResult[]? results = reader.DecodeBuffer(data, width, height, stride, (EnumImagePixelFormat)format, out error);
        Marshal.FreeHGlobal(buffer);
    
        return OutputResults(results);
    }
    
    public Result[]? DecodeBase64(string base64string)
    {
        if (reader == null) { return null; }
    
        NSError error;
        iTextResult[]? results = reader.DecodeBase64(base64string, out error);
    
        return OutputResults(results);
    }
    
    public void SetParameters(string parameters)
    {
        if (reader == null) { return; }
    
        NSError error;
        reader.InitRuntimeSettingsWithString(parameters, EnumConflictMode.Overwrite, out error);
    }
    

构建Android绑定库不会提出任何问题。但是,当尝试使用MacOS上的Visual Studio构建iOS绑定库时,可能会出现红色小脚架。

.NET ios binding issue

不要惊慌。您可以安全地忽略这些红色的尖叫声并成功地构建项目。

如何配置 *.nuspec文件以包含Android和iOS绑定库

如果您已经成功构建了Android和iOS绑定库,则下一步是配置*.nuspec文件以将它们包含在Nuget软件包中。

<!-- Android -->
<file src="android\bin\Release\net6.0-android\**\*.*" target="lib\net6.0-android31.0" />

<!-- iOS -->
<file src="ios/manifest" target="lib/net6.0-ios16.1/BarcodeQRCodeSDK.resources" />
<file src="ios\bin\Release\net6.0-ios\BarcodeQRCodeSDK.dll" target="lib\net6.0-ios16.1" />
<file src="ios\bin\Release\net6.0-ios\BarcodeQRCodeSDK.resources\**\*.*" target="lib\net6.0-ios16.1\BarcodeQRCodeSDK.resources" />
<file src="ios/bin/Release/net6.0-ios/BarcodeQRCodeSDK.resources/DynamsoftBarcodeReader.xcframework/ios-arm64/DynamsoftBarcodeReader.framework/DynamsoftBarcodeReader" target="lib/net6.0-ios16.1/BarcodeQRCodeSDK.resources/DynamsoftBarcodeReader.xcframework/ios-arm64/DynamsoftBarcodeReader.framework" />
<file src="ios/bin/Release/net6.0-ios/BarcodeQRCodeSDK.resources/DynamsoftBarcodeReader.xcframework/ios-arm64_x86_64-simulator/DynamsoftBarcodeReader.framework/DynamsoftBarcodeReader" target="lib/net6.0-ios16.1/BarcodeQRCodeSDK.resources/DynamsoftBarcodeReader.xcframework/ios-arm64_x86_64-simulator/DynamsoftBarcodeReader.framework" />

请注意,虽然Wildcard **\*.*包括指定文件夹中的所有文件和子文件夹,但它将无法说明DynamsoftBarcodeReader汇编文件。因此,您必须手动添加此文件。

如何使用多合一的Nuget软件包更新MAUI条形码QR代码扫描仪

要尝试使用多合一的Nuget软件包,您可以下载https://github.com/yushulx/maui-barcode-qrcode-scanner项目。该项目是使用DynamSoft条形码读取器的Android和iOS绑定库开发的。

这是使用多合一的Nuget软件包更新项目的步骤:

  1. BarcodeQRCodeSDK替换Barcode.NET.Mobile

    NuGet .NET barcode QR SDK

  2. 修改特定于平台的代码如下:

    - using BarcodeQrScanner.Services;
    + using Dynamsoft;
    
    - BarcodeQRCodeService _barcodeQRCodeService;
    + BarcodeQRCodeReader _reader;
    
    - data = _barcodeQRCodeService.DecodeFile(path);
    + BarcodeQRCodeReader.Result[] results = _reader.DecodeFile(imagepath);
    
    - TextResult[] results = barcodeReader.DecodeBuffer(image.GetYuvData(), previewWidth, previewHeight, stridelist[0], EnumImagePixelFormat.IpfNv21);
    + BarcodeQRCodeReader.Result[] results = barcodeReader.DecodeBuffer(image.GetYuvData(), previewWidth, previewHeight, stridelist[0], BarcodeQRCodeReader.ImagePixelFormat.IPF_NV21);
    
    - results = reader.DecodeBuffer(buffer, width, height, bpr, EnumImagePixelFormat.Argb8888, out errorr);
    + byte[] bytearray = new byte[buffer.Length];
    + System.Runtime.InteropServices.Marshal.Copy(buffer.Bytes, bytearray, 0, Convert.ToInt32(buffer.Length));
    + results = reader.DecodeBuffer(bytearray, (int)width, (int)height, (int)bpr, BarcodeQRCodeReader.ImagePixelFormat.IPF_ARGB_8888);
    
  3. 在Android和iOS上运行MAUI条形码QR代码扫描仪。

    MAUI barcode QR code scanner

修改后的MAUI条形码QR代码扫描仪项目可在https://github.com/yushulx/dotnet-barcode-qr-code-sdk/tree/main/example/maui上找到。

源代码

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