Aracılığıyla paylaş


C/C++ kitaplıklarını Xamarin ile kullanma

Genel bakış

Xamarin, geliştiricilerin Visual Studio ile platformlar arası yerel mobil uygulamalar oluşturmasını sağlar. C# bağlamaları genellikle mevcut platform bileşenlerini geliştiricilere göstermek için kullanılır. Ancak, Xamarin uygulamalarının mevcut kod temelleriyle çalışması gereken zamanlar vardır. Bazen ekiplerin büyük, iyi test edilmiş ve yüksek oranda iyileştirilmiş bir kod tabanını C# ile taşıması için yeterli zamanı, bütçeyi veya kaynakları yoktur.

Platformlar arası mobil geliştirme için Visual C++, C/C++ ve C# kodunun aynı çözümün bir parçası olarak derlenmesini sağlar ve birleşik hata ayıklama deneyimi de dahil olmak üzere birçok avantaj sunar. Microsoft, Hyperlapse Mobile ve Pix Kamera gibi uygulamaları sunmak için C/C++ ve Xamarin'i bu şekilde kullanmıştır.

Ancak bazı durumlarda, mevcut C/C++ araçlarını ve işlemlerini yerinde tutma ve kitaplık kodunu uygulamadan ayrı tutma ve kitaplığı üçüncü taraf bir bileşene benzermiş gibi ele alma isteği (veya gereksinimi) vardır. Bu gibi durumlarda, zorluk yalnızca ilgili üyeleri C# ile açığa çıkarmakla kalmaz, aynı zamanda kitaplığı bağımlılık olarak yönetmektir. Ve tabii ki, bu sürecin mümkün olduğunca çoğunu otomatikleştirmek.

Bu gönderide bu senaryo için üst düzey bir yaklaşım özetlenmiştir ve basit bir örnekte yol gösterilir.

Background

C/C++ platformlar arası bir dil olarak kabul edilir, ancak kaynak kodun yalnızca tüm hedef derleyiciler tarafından desteklenen ve koşullu olarak dahil edilen platform veya derleyiciye özgü kodun az veya hiç içermediği C/C++ kullanılarak platformlar arası olduğundan emin olmak için büyük özen gösterilmelidir.

Sonuçta kod tüm hedef platformlarda başarıyla derlenmeli ve çalıştırılmalıdır, bu nedenle bu hedeflenen platformlar (ve derleyiciler) genelindeki ortaklığa iner. Sorunlar hala derleyiciler arasındaki küçük farklardan kaynaklanabilir ve bu nedenle her bir hedef platformda kapsamlı test (tercihen otomatikleştirilmiş) giderek daha önemli hale gelir.

Üst düzey yaklaşım

Aşağıdaki çizim, C/C++ kaynak kodunu NuGet aracılığıyla paylaşılan ve ardından bir Xamarin.Forms uygulamasında kullanılan platformlar arası bir Xamarin kitaplığına dönüştürmek için kullanılan dört aşamalı yaklaşımı temsil eder.

Xamarin ile C/C++ kullanmak için üst düzey yaklaşım

4 aşama şunlardır:

  1. C/C++ kaynak kodunu platforma özgü yerel kitaplıklara derleme.
  2. Yerel kitaplıkları visual studio çözümüyle sarmalama.
  3. .NET sarmalayıcısı için bir NuGet paketini paketleme ve gönderme.
  4. Xamarin uygulamasından NuGet paketini kullanma.

1. Aşama: C/C++ kaynak kodunu platforma özgü yerel kitaplıklara derleme

Bu aşamanın amacı, C# sarmalayıcısı tarafından çağrılabilen yerel kitaplıklar oluşturmaktır. Bu durum, durumunuzla ilgili olabilir veya olmayabilir. Bu ortak senaryoda ele alınabilecek birçok araç ve işlem bu makalenin kapsamının dışındadır. Önemli noktalar C/C++ kod tabanını herhangi bir yerel sarmalayıcı kodu, yeterli birim testi ve derleme otomasyonu ile eşitlenmiş durumda tutmaktır.

Kılavuzdaki kitaplıklar, birlikte gelen bir kabuk betiğiyle Visual Studio Code kullanılarak oluşturulmuştur. Bu kılavuzun genişletilmiş bir sürümü, örneğin bu bölümünü daha ayrıntılı olarak ele alan Mobile CAT GitHub deposunda bulunabilir. Bu durumda yerel kitaplıklar üçüncü taraf bağımlılığı olarak ele alınmaktadır ancak bu aşama bağlam için gösterilmiştir.

Kolaylık olması için, izlenecek yol mimarilerin yalnızca bir alt kümesini hedefler. iOS için, bireysel mimariye özgü ikili dosyalardan tek bir yağ ikili dosyası oluşturmak için lipo yardımcı programını kullanır. Android, .so uzantılı dinamik ikili dosyaları, iOS ise .a uzantısına sahip statik bir yağ ikili dosyası kullanır.

2. Aşama: Yerel kitaplıkları Visual Studio çözümüyle sarmalama

Sonraki aşama, yerel kitaplıkları .NET'ten kolayca kullanılacak şekilde sarmalamadır. Bu işlem, dört proje içeren bir Visual Studio çözümüyle gerçekleştirilir. Paylaşılan proje ortak kodu içerir. Xamarin.Android, Xamarin.iOS ve .NET Standard'ın her birini hedefleyen projeler, kitaplığa platformdan bağımsız bir şekilde başvurulmasına olanak tanır.

Sarmalayıcı 'yem ve anahtar numarası' kullanır. Tek yol bu değildir, ancak kitaplığa başvurmayı kolaylaştırır ve kullanan uygulamanın kendi içinde platforma özgü uygulamaları açıkça yönetme gereksinimini ortadan kaldırır. İşin püf noktası, hedeflerin (.NET Standard, Android, iOS) aynı ad alanını, derleme adını ve sınıf yapısını paylaşmasını sağlamaktır. NuGet her zaman platforma özgü bir kitaplığı tercih edeceğinden, .NET Standard sürümü hiçbir zaman çalışma zamanında kullanılmaz.

Bu adımdaki çalışmaların çoğu, yerel kitaplık yöntemlerini çağırmak ve temel alınan nesnelere yapılan başvuruları yönetmek için P/Invoke kullanmaya odaklanacaktır. Amaç, kitaplığın işlevselliğini tüketiciye sunarken herhangi bir karmaşıklığı soyutlamadır. Xamarin.Forms geliştiricilerinin yönetilmeyen kitaplığın iç çalışmalarıyla ilgili çalışma bilgilerine sahip olması gerekmez. Başka bir yönetilen C# kitaplığı kullanıyormuş gibi hissetmelidir.

Sonuç olarak, bu aşamanın çıktısı bir sonraki adımda paketi oluşturmak için gereken bilgileri içeren nuspec belgesinin yanı sıra hedef başına bir .NET kitaplığı kümesidir.

3. Aşama: .NET sarmalayıcısı için bir NuGet paketini paketleme ve gönderme

Üçüncü aşama, önceki adımdaki derleme yapıtlarını kullanarak bir NuGet paketi oluşturmaktır. Bu adımın sonucu, Xamarin uygulamasından tüketilebilen bir NuGet paketidir. İzlenecek yol, NuGet akışı olarak hizmet vermek için yerel bir dizin kullanır. Üretimde, bu adım bir paketi genel veya özel bir NuGet akışına yayımlamalı ve tam olarak otomatik olmalıdır.

4. Aşama: Xamarin.Forms uygulamasından NuGet paketini kullanma

Son adım, Xamarin.Forms uygulamasından NuGet paketine başvurmak ve kullanmaktır. Bunun için Visual Studio'da NuGet akışının önceki adımda tanımlanan akışı kullanacak şekilde yapılandırılması gerekir.

Akış yapılandırıldıktan sonra, pakete platformlar arası Xamarin.Forms uygulamasındaki her projeden başvuru yapılması gerekir. 'Yem ve anahtar numarası' aynı arabirimler sağlar, bu nedenle yerel kitaplık işlevi tek bir konumda tanımlanan kod kullanılarak çağrılabilir.

Kaynak kod deposu, Azure DevOps'ta özel NuGet akışı ayarlama ve paketin bu akışa nasıl gönderildiğini gösteren makaleleri içeren daha fazla okuma listesini içerir. Yerel dizinden biraz daha fazla kurulum süresi gerektirse de, ekip geliştirme ortamında bu tür bir akış daha iyidir.

Gözden geçirme

Sağlanan adımlar Mac için Visual Studio özeldir, ancak yapı Visual Studio 2017'de de çalışır.

Önkoşullar

Bunu takip etmek için geliştiricinin şunları ihtiyacı vardır:

Not

Uygulamaları i Telefon dağıtmak için etkin bir Apple Geliştirici Hesabı gereklidir.

Yerel kitaplıkları oluşturma (1. Aşama)

Yerel kitaplık işlevselliği, İzlenecek Yol: Statik Kitaplık Oluşturma ve Kullanma (C++) örneğini temel alır.

Bu kılavuzda, kitaplık bu senaryoda üçüncü taraf bağımlılığı olarak sağlandığından, yerel kitaplıkları oluşturan ilk aşama atlanır. Önceden derlenmiş yerel kitaplıklar örnek koda eklenmiştir veya doğrudan indirilebilir.

Yerel kitaplıkla çalışma

Özgün MathFuncsLib örneği, aşağıdaki tanım ile adlı MyMathFuncs tek bir sınıf içerir:

namespace MathFuncs
{
    class MyMathFuncs
    {
    public:
        double Add(double a, double b);
        double Subtract(double a, double b);
        double Multiply(double a, double b);
        double Divide(double a, double b);
    };
}

Ek bir sınıf, .NET tüketicisinin temel alınan yerel MyMathFuncs sınıfı oluşturmasına, atmasına ve bunlarla etkileşim kurmasına olanak sağlayan sarmalayıcı işlevlerini tanımlar.

#include "MyMathFuncs.h"
using namespace MathFuncs;

extern "C" {
    MyMathFuncs* CreateMyMathFuncsClass();
    void DisposeMyMathFuncsClass(MyMathFuncs* ptr);
    double MyMathFuncsAdd(MyMathFuncs *ptr, double a, double b);
    double MyMathFuncsSubtract(MyMathFuncs *ptr, double a, double b);
    double MyMathFuncsMultiply(MyMathFuncs *ptr, double a, double b);
    double MyMathFuncsDivide(MyMathFuncs *ptr, double a, double b);
}

Bu, Xamarin tarafında kullanılan sarmalayıcı işlevleri olacaktır.

Yerel kitaplığı sarmalama (2. Aşama)

Bu aşama, önceki bölümde açıklanan önceden derlenmiş kitaplıkları gerektirir.

Visual Studio çözümü oluşturma

  1. Mac için Visual Studio'da, Yeni Proje'ye (Hoş Geldiniz Sayfasından) veya Yeni Çözüm'e (Dosya menüsünden) tıklayın.

  2. Yeni Proje penceresinde Paylaşılan Proje'yi seçin (Çok Platformlu > Kitaplık'ın içinden) ve ardından İleri'ye tıklayın.

  3. Aşağıdaki alanları güncelleştirin ve Oluştur'a tıklayın:

    • Proje Adı: MathFuncs.Shared
    • Çözüm Adı: MathFuncs
    • Konum: Varsayılan kaydetme konumunu kullanın (veya alternatif bir konum seçin)
    • Çözüm dizininde proje oluşturma: Bunu işaretli olarak ayarlayın
  4. Çözüm Gezgini MathFuncs.Shared projesine çift tıklayın ve Ana Ayarlar gidin.

  5. öğesini kaldırın. Varsayılan Ad Alanı'ndan paylaşıldığındanyalnızca MathFuncs olarak ayarlanır ve Tamam'a tıklayın.

  6. MyClass.cs açın (şablon tarafından oluşturulur), ardından hem sınıfı hem de dosya adını MyMathFuncsWrapper olarak yeniden adlandırın ve ad alanını MathFuncs olarak değiştirin.

  7. CONTROL + MathFuncs çözümüne tıklayın, ardından Ekle menüsünden Yeni Proje Ekle... öğesini seçin.

  8. Yeni Proje penceresinde .NET Standart Kitaplığı'nı (Çok Platformlu > Kitaplık'ın içinden) seçin ve İleri'ye tıklayın.

  9. .NET Standard 2.0'ı seçin ve İleri'ye tıklayın.

  10. Aşağıdaki alanları güncelleştirin ve Oluştur'a tıklayın:

    • Proje Adı: MathFuncs.Standard
    • Konum: Paylaşılan projeyle aynı kaydetme konumunu kullanın
  11. Çözüm Gezgini MathFuncs.Standard projesine çift tıklayın.

  12. Ana Ayarlar gidin, ardından Varsayılan Ad Alanını MathFuncs olarak güncelleştirin.

  13. Çıkış ayarlarına gidin, ardından Derleme adını MathFuncs olarak güncelleştirin.

  14. Derleyici ayarlarına gidin, Yapılandırma'yı Yayın olarak değiştirin ve Hata ayıklama bilgilerini Yalnızca Simgeler olarak ayarlayın ve Tamam'a tıklayın.

  15. projeden Class1.cs/Başlarken'i silin (bunlardan biri şablonun parçası olarak eklenmişse).

  16. CONTROL + Proje Bağımlılıkları/Başvurular klasörüne tıklayın ve ardından Başvuruları Düzenle'yi seçin.

  17. Projeler sekmesinde MathFuncs.Shared öğesini seçin ve tamam'a tıklayın.

  18. Aşağıdaki yapılandırmaları kullanarak 7-17 arası adımları (9. adımı yoksayarak) yineleyin:

    PROJE ADI ŞABLON ADı YENI PROJE MENÜSÜ
    MathFuncs.Android Sınıf Kitaplığı Android > Kitaplığı
    MathFuncs.iOS Bağlama Kitaplığı iOS > Kitaplığı
  19. Çözüm Gezgini MathFuncs.Android projesine çift tıklayın ve derleyici ayarlarına gidin.

  20. Yapılandırma Hata Ayıklama olarak ayarlandığında Simge Tanımla'yı Android;'i içerecek şekilde düzenleyin.

  21. Yapılandırma'yı Yayın olarak değiştirin, ardından Simge Tanımla'yı Android'i de içerecek şekilde düzenleyin;

  22. MathFuncs.iOS için 19-20 arası adımları yineleyin ve Her iki durumda da Simgeleri Tanımla'yı Android yerine iOS'i içerecek şekilde düzenleme.

  23. Çözümü Yayın yapılandırmasında (CONTROL + COMMAND + B) derleyin ve üç çıkış derlemesinin de (Android, iOS, .NET Standard) (ilgili proje bölmesi klasörlerinde) MathFuncs.dll aynı adı paylaştığını doğrulayın.

Bu aşamada, çözümün biri Android, iOS ve .NET Standard için olmak üzere üç hedefi ve üç hedefin her biri tarafından başvurulan paylaşılan bir proje olmalıdır. Bunlar aynı ad alanını ve aynı ada sahip çıkış derlemelerini kullanacak şekilde yapılandırılmalıdır. Bu, daha önce bahsedilen 'yem ve anahtar' yaklaşımı için gereklidir.

Yerel kitaplıkları ekleme

Sarmalayıcı çözümüne yerel kitaplık ekleme işlemi Android ve iOS arasında biraz değişiklik gösterir.

MathFuncs.Android için yerel başvurular

  1. CONTROL + MATHFuncs.Android projesine TıKLAYıN, ardından Ekle menüsünden Yeni Klasör'e tıklayın.

  2. Her ABI için (Uygulama İkili Arabirimi), CONTROL + Lib klasörüne TıKLAYıN, ardından Ekle menüsünden Yeni Klasör'e tıklayın ve ilgili ABI'den sonra adlandırın. Bu durumda:

    • arm64-v8a
    • armeabi-v7a
    • x86
    • x86_64

    Not

    Daha ayrıntılı bir genel bakış için NDK geliştirici kılavuzundaki Mimariler ve CPU'lar konusuna, özellikle uygulama paketlerinde yerel kodu ele alma bölümüne bakın.

  3. Klasör yapısını doğrulayın:

    - lib
        - arm64-v8a
        - armeabi-v7a
        - x86
        - x86_64
    
  4. Aşağıdaki eşlemeye göre abi klasörlerinin her birine karşılık gelen .so kitaplıklarını ekleyin:

    arm64-v8a: lib/Android/arm64

    armeabi-v7a: lib/Android/arm

    x86: lib/Android/x86

    x86_64: lib/Android/x86_64

    Not

    Dosya eklemek için CONTROL + İlgili ABI'yi temsil eden klasöre TıKLAYıN, ardından Ekle menüsünden Dosya Ekle...öğesini seçin. Uygun kitaplığı seçin (PrecompiledLibs dizininden) Aç'a tıklayın ve ardından Tamam'a tıklayarak dosyayı dizine kopyala seçeneğini varsayılan olarak bırakın.

  5. .so dosyalarının her biri için CONTROL + CLICK tuşlarına basın ve ardından Derleme Eylemi menüsünden EmbeddedNativeLibrary seçeneğini belirleyin.

Şimdi lib klasörü aşağıdaki gibi görünmelidir:

- lib
    - arm64-v8a
        - libMathFuncs.so
    - armeabi-v7a
        - libMathFuncs.so
    - x86
        - libMathFuncs.so
    - x86_64
        - libMathFuncs.so

MathFuncs.iOS için yerel başvurular

  1. CONTROL + MathFuncs.iOS projesine tıklayın, ardından Ekle menüsünden Yerel Başvuru Ekle'yiseçin.

  2. libMathFuncs.a kitaplığını seçin (PrecompiledLibs dizini altındaki libs/ios'tan) ve ardından Aç'a tıklayın

  3. CONTROL + libMathFuncs dosyasına tıklayın (Yerel Başvurular klasörünün içinde, ardından menüden Özellikler seçeneğini belirleyin

  4. Yerel Başvuru özelliklerini, Özellikler Bölmesi'nde denetlenmeleri (bir onay işareti simgesi gösteriliyor) için yapılandırın:

    • Yüklemeye Zorla
    • C++ mı?
    • Akıllı Bağlantı

    Not

    Bağlama kitaplığı proje türünün yerel başvuruyla birlikte kullanılması statik kitaplığı ekler ve buna başvuran Xamarin.iOS uygulamasıyla (NuGet paketi aracılığıyla dahil edildiğinde bile) otomatik olarak bağlanmasına olanak tanır.

  5. ApiDefinition.cs açın, şablonlu açıklamalı kodu silin (yalnızca ad alanını bırakın) ve ardından Structs.cs için aynı adımı gerçekleştirin MathFuncs

    Not

    Bağlama kitaplığı projesi derlemek için bu dosyaları (ObjCBindingApiDefinition ve ObjCBindingCoreSource derleme eylemleriyle) gerektirir. Ancak, standart P/Invoke kullanılarak hem Android hem de iOS kitaplık hedefleri arasında paylaşılabilen bir şekilde bu dosyaların dışında yerel kitaplığımızı çağırmak için kodu yazacağız.

Yönetilen kitaplık kodunu yazma

Şimdi yerel kitaplığı çağırmak için C# kodunu yazın. Amaç, temel alınan karmaşıklıkları gizlemektir. Tüketicinin yerel kitaplık içleri veya P/Invoke kavramları hakkında herhangi bir çalışma bilgisine ihtiyacı olmamalıdır.

Kasa Handle oluşturma

  1. CONTROL + MATHFuncs.Shared projesine TıKLAYıN, ardından Ekle menüsünden Dosya Ekle... öğesini seçin.

  2. Yeni Dosya penceresinde Boş Sınıf'ıseçin, myMathFuncs Kasa Handle olarak adlandırın ve Yeni'ye tıklayın

  3. MyMathFuncs Kasa Handle sınıfını uygulayın:

    using System;
    using Microsoft.Win32.SafeHandles;
    
    namespace MathFuncs
    {
        internal class MyMathFuncsSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            public MyMathFuncsSafeHandle() : base(true) { }
    
            public IntPtr Ptr => handle;
    
            protected override bool ReleaseHandle()
            {
                // TODO: Release the handle here
                return true;
            }
        }
    }
    

    Not

    Kasa Handle, yönetilen koddaki yönetilmeyen kaynaklarla çalışmanın tercih edilen yoludur. Bu, kritik sonlandırma ve nesne yaşam döngüsüyle ilgili birçok ortak kodu soyutlar. Bu tanıtıcının sahibi daha sonra bunu başka bir yönetilen kaynak gibi değerlendirebilir ve tam Tek Kullanımlık düzeni uygulaması gerekmez.

İç sarmalayıcı sınıfı oluşturma

  1. MyMathFuncsWrapper.cs açın ve bunu bir iç statik sınıfa değiştirme

    namespace MathFuncs
    {
        internal static class MyMathFuncsWrapper
        {
        }
    }
    
  2. Aynı dosyada aşağıdaki koşullu deyimi sınıfına ekleyin:

    #if Android
        const string DllName = "libMathFuncs.so";
    #else
        const string DllName = "__Internal";
    #endif
    

    Not

    Bu, Kitaplığın Android veya iOS için derlenip derlenmediğine bağlı olarak DllName sabit değerini ayarlar. Bu, ilgili platformlar tarafından kullanılan farklı adlandırma kurallarının yanı sıra bu durumda kullanılan kitaplık türünü de ele almaktır. Android dinamik bir kitaplık kullanıyor ve bu nedenle uzantı da dahil olmak üzere bir dosya adı bekliyor. Statik kitaplık kullandığımız için iOS için '__Internal' gereklidir.

  3. MyMathFuncsWrapper.cs dosyasının en üstüne System.Runtime.InteropServices başvurusu ekleme

    using System.Runtime.InteropServices;
    
  4. MyMathFuncs sınıfının oluşturulmasını ve atılmasını işlemek için sarmalayıcı yöntemlerini ekleyin:

    [DllImport(DllName, EntryPoint = "CreateMyMathFuncsClass")]
    internal static extern MyMathFuncsSafeHandle CreateMyMathFuncs();
    
    [DllImport(DllName, EntryPoint = "DisposeMyMathFuncsClass")]
    internal static extern void DisposeMyMathFuncs(MyMathFuncsSafeHandle ptr);
    

    Not

    DllName sabitimizi, .NET çalışma zamanına bu kitaplık içinde çağrılacak işlevin adını açıkça bildiren EntryPoint ile birlikte DllImport özniteliğine geçiriyoruz. Teknik olarak yönetilen yöntem adlarımız yönetilmeyen adla aynıysa EntryPoint değerini sağlamamız gerekmez. Sağlanmadıysa, yönetilen yöntem adı bunun yerine EntryPoint olarak kullanılır. Ancak, açık olmak daha iyidir.

  5. Aşağıdaki kodu kullanarak MyMathFuncs sınıfıyla çalışmamızı sağlamak için sarmalayıcı yöntemlerini ekleyin:

    [DllImport(DllName, EntryPoint = "MyMathFuncsAdd")]
    internal static extern double Add(MyMathFuncsSafeHandle ptr, double a, double b);
    
    [DllImport(DllName, EntryPoint = "MyMathFuncsSubtract")]
    internal static extern double Subtract(MyMathFuncsSafeHandle ptr, double a, double b);
    
    [DllImport(DllName, EntryPoint = "MyMathFuncsMultiply")]
    internal static extern double Multiply(MyMathFuncsSafeHandle ptr, double a, double b);
    
    [DllImport(DllName, EntryPoint = "MyMathFuncsDivide")]
    internal static extern double Divide(MyMathFuncsSafeHandle ptr, double a, double b);
    

    Not

    Bu örnekteki parametreler için basit türler kullanıyoruz. Bu durumda, marshalling bit düzeyinde bir kopya olduğundan, bizim tarafımızda ek bir çalışma gerektirmez. Ayrıca standart IntPtr yerine MyMathFuncs Kasa Handle sınıfının kullanıldığına da dikkat edin. IntPtr, Kasa Handle ile otomatik olarak marshalling işleminin bir parçası olarak eşlenir.

  6. Tamamlanmış MyMathFuncsWrapper sınıfının aşağıdaki gibi göründüğünü doğrulayın:

    using System.Runtime.InteropServices;
    
    namespace MathFuncs
    {
        internal static class MyMathFuncsWrapper
        {
            #if Android
                const string DllName = "libMathFuncs.so";
            #else
                const string DllName = "__Internal";
            #endif
    
            [DllImport(DllName, EntryPoint = "CreateMyMathFuncsClass")]
            internal static extern MyMathFuncsSafeHandle CreateMyMathFuncs();
    
            [DllImport(DllName, EntryPoint = "DisposeMyMathFuncsClass")]
            internal static extern void DisposeMyMathFuncs(MyMathFuncsSafeHandle ptr);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsAdd")]
            internal static extern double Add(MyMathFuncsSafeHandle ptr, double a, double b);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsSubtract")]
            internal static extern double Subtract(MyMathFuncsSafeHandle ptr, double a, double b);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsMultiply")]
            internal static extern double Multiply(MyMathFuncsSafeHandle ptr, double a, double b);
    
            [DllImport(DllName, EntryPoint = "MyMathFuncsDivide")]
            internal static extern double Divide(MyMathFuncsSafeHandle ptr, double a, double b);
        }
    }
    

MyMathFuncs Kasa Handle sınıfı tamamlanıyor

  1. MyMathFuncs Kasa Handle sınıfını açın, ReleaseHandle yöntemindeki yer tutucu TODO açıklamasına gidin:

    // TODO: Release the handle here
    
  2. TODO satırını değiştirin:

    MyMathFuncsWrapper.DisposeMyMathFuncs(this);
    

MyMathFuncs sınıfını yazma

Sarmalayıcı tamamlandıktan sonra yönetilmeyen C++ MyMathFuncs nesnesine başvuruyu yönetecek bir MyMathFuncs sınıfı oluşturun.

  1. CONTROL + MATHFuncs.Shared projesine TıKLAYıN, ardından Ekle menüsünden Dosya Ekle... öğesini seçin.

  2. Yeni Dosya penceresinde Boş Sınıf'ıseçin, myMathFuncs olarak adlandırın ve ardından Yeni'ye tıklayın

  3. MyMathFuncs sınıfına aşağıdaki üyeleri ekleyin:

    readonly MyMathFuncsSafeHandle handle;
    
  4. Sınıf örneği oluşturulurken yerel MyMathFuncs nesnesine bir tanıtıcı oluşturup depolaması için sınıfın oluşturucusunu uygulayın:

    public MyMathFuncs()
    {
        handle = MyMathFuncsWrapper.CreateMyMathFuncs();
    }
    
  5. Aşağıdaki kodu kullanarak IDisposable arabirimini uygulayın:

    public class MyMathFuncs : IDisposable
    {
        ...
    
        protected virtual void Dispose(bool disposing)
        {
            if (handle != null && !handle.IsInvalid)
                handle.Dispose();
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        // ...
    }
    
  6. Depoladığımız işaretçiyi temel alınan yönetilmeyen nesneye geçirerek arka planda gerçek işi gerçekleştirmek için MyMathFuncsWrapper sınıfını kullanarak MyMathFuncs yöntemlerini uygulayın. Kod aşağıdaki gibi olmalıdır:

    public double Add(double a, double b)
    {
        return MyMathFuncsWrapper.Add(handle, a, b);
    }
    
    public double Subtract(double a, double b)
    {
        return MyMathFuncsWrapper.Subtract(handle, a, b);
    }
    
    public double Multiply(double a, double b)
    {
        return MyMathFuncsWrapper.Multiply(handle, a, b);
    }
    
    public double Divide(double a, double b)
    {
        return MyMathFuncsWrapper.Divide(handle, a, b);
    }
    

Nuspec oluşturma

Kitaplığın NuGet aracılığıyla paketlenmesi ve dağıtılması için çözüm için bir nuspec dosyası gerekir. Bu, desteklenen her platform için elde edilen derlemelerden hangisinin dahil olacağını belirler.

  1. CONTROL + ÇÖZÜME TıKLAYINMathFuncs, ardından Çözüm Klasörü Ekle menüsünden Çözüm Klasörü Ekle'yiseçin.

  2. CONTROL + SolutionItems klasörüne TıKLAYıN, ardından Ekle menüsünden Yeni Dosya...öğesini seçin.

  3. Yeni Dosya penceresinde Boş XML Dosyası'nı seçin, MathFuncs.nuspec olarak adlandırın ve Yeni'ye tıklayın.

  4. MathFuncs.nuspec dosyasını NuGet tüketicisine görüntülenecek temel paket meta verileriyle güncelleştirin. Örneğin:

    <?xml version="1.0"?>
    <package>
        <metadata>
            <id>MathFuncs</id>
            <version>$version$</version>
            <authors>Microsoft Mobile Customer Advisory Team</authors>
            <description>Sample C++ Wrapper Library</description>
            <requireLicenseAcceptance>false</requireLicenseAcceptance>
            <copyright>Copyright 2018</copyright>
        </metadata>
    </package>
    
  5. <files> Öğesini öğesinin <package> alt öğesi olarak ekleyin (hemen altında<metadata>), her dosyayı ayrı <file> bir öğeyle tanımlayın:

    <files>
    
        <!-- Android -->
    
        <!-- iOS -->
    
        <!-- netstandard2.0 -->
    
    </files>
    

    Not

    Bir paket bir projeye yüklendiğinde ve aynı adla belirtilen birden çok derleme bulunduğunda, NuGet belirli bir platforma en özel derlemeyi etkili bir şekilde seçer.

  6. <file> Android derlemeleri için öğeleri ekleyin:

    <file src="MathFuncs.Android/bin/Release/MathFuncs.dll" target="lib/MonoAndroid81/MathFuncs.dll" />
    <file src="MathFuncs.Android/bin/Release/MathFuncs.pdb" target="lib/MonoAndroid81/MathFuncs.pdb" />
    
  7. <file> iOS derlemeleri için öğeleri ekleyin:

    <file src="MathFuncs.iOS/bin/Release/MathFuncs.dll" target="lib/Xamarin.iOS10/MathFuncs.dll" />
    <file src="MathFuncs.iOS/bin/Release/MathFuncs.pdb" target="lib/Xamarin.iOS10/MathFuncs.pdb" />
    
  8. <file> netstandard2.0 derlemeleri için öğeleri ekleyin:

    <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.dll" target="lib/netstandard2.0/MathFuncs.dll" />
    <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.pdb" target="lib/netstandard2.0/MathFuncs.pdb" />
    
  9. nuspec bildirimini doğrulayın:

    <?xml version="1.0"?>
    <package>
    <metadata>
        <id>MathFuncs</id>
        <version>$version$</version>
        <authors>Microsoft Mobile Customer Advisory Team</authors>
        <description>Sample C++ Wrapper Library</description>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <copyright>Copyright 2018</copyright>
    </metadata>
    <files>
    
        <!-- Android -->
        <file src="MathFuncs.Android/bin/Release/MathFuncs.dll" target="lib/MonoAndroid81/MathFuncs.dll" />
        <file src="MathFuncs.Android/bin/Release/MathFuncs.pdb" target="lib/MonoAndroid81/MathFuncs.pdb" />
    
        <!-- iOS -->
        <file src="MathFuncs.iOS/bin/Release/MathFuncs.dll" target="lib/Xamarin.iOS10/MathFuncs.dll" />
        <file src="MathFuncs.iOS/bin/Release/MathFuncs.pdb" target="lib/Xamarin.iOS10/MathFuncs.pdb" />
    
        <!-- netstandard2.0 -->
        <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.dll" target="lib/netstandard2.0/MathFuncs.dll" />
        <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.pdb" target="lib/netstandard2.0/MathFuncs.pdb" />
    
    </files>
    </package>
    

    Not

    Bu dosya bir Release derlemesinden derleme çıkış yollarını belirtir, bu nedenle çözümü bu yapılandırmayı kullanarak derlemeyi unutmayın.

Bu noktada çözüm 3 .NET derlemesi ve destekleyici bir nuspec bildirimi içerir.

.NET sarmalayıcısını NuGet ile dağıtma

Sonraki adım NuGet paketini paketleyip dağıtmaktır; böylece uygulama tarafından kolayca kullanılabilir ve bağımlılık olarak yönetilebilir. Sarmalama ve tüketimin tümü tek bir çözüm içinde yapılabilir, ancak kitaplığın NuGet aracılığıyla dağıtılması ayırmaya yardımcı olur ve bu kod temellerini bağımsız olarak yönetmemizi sağlar.

Yerel paketler dizini hazırlama

NuGet akışının en basit biçimi yerel bir dizindir:

  1. Bulucu'da uygun bir dizine gidin. Örneğin, /Users.
  2. Dosya menüsünden Yeni Klasör'e tıklayarak local-nuget-feed gibi anlamlı bir ad sağlayın.

Paketi oluşturma

  1. Derleme Yapılandırması'nıYayın olarak ayarlayın ve COMMAND + B kullanarak bir derleme yürütür.

  2. Terminal'i açın ve dizini nuspec dosyasını içeren klasörle değiştirin.

  3. Terminal'de nuspec dosyasını, Sürüm'leri (örneğin, 1.0.0) ve OutputDirectory'yi belirten nuget pack komutunu, önceki adımda oluşturulmuş olan yerel-nuget-feed klasörünü kullanarak yürütebilirsiniz. Örneğin:

    nuget pack MathFuncs.nuspec -Version 1.0.0 -OutputDirectory ~/local-nuget-feed
    
  4. MathFuncs.1.0.0.nupkg dosyasının yerel-nuget-feed dizininde oluşturulduğunu onaylayın.

[İSTEĞE BAĞLı] Azure DevOps ile özel NuGet akışı kullanma

Azure DevOps'ta NuGet paketlerini kullanmaya başlama bölümünde özel akış oluşturma ve paketin (önceki adımda oluşturulan) bu akışa nasıl gönderildiğini gösteren daha sağlam bir teknik açıklanmıştır.

Bu iş akışının, örneğin Azure Pipelines'ı kullanarak tamamen otomatik hale getirmek idealdir. Daha fazla bilgi için bkz . Azure Pipelines'ı kullanmaya başlama.

Xamarin.Forms uygulamasından .NET sarmalayıcısını kullanma

İzlenecek yolu tamamlamak için, yerel NuGet akışında yayımlanan paketi kullanacak bir Xamarin.Forms uygulaması oluşturun.

Xamarin.Forms projesi oluşturma

  1. yeni bir Mac için Visual Studio örneği açın. Bu işlem Terminal'den yapılabilir:

    open -n -a "Visual Studio"
    
  2. Mac için Visual Studio'da, Yeni Proje'ye (Hoş Geldiniz Sayfasından) veya Yeni Çözüm'e (Dosya menüsünden) tıklayın.

  3. Yeni Proje penceresinde Boş Formlar Uygulaması'nı seçin (Çok Platformlu > Uygulama'nın içinden) ve ardından İleri'ye tıklayın.

  4. Aşağıdaki alanları güncelleştirin ve İleri'ye tıklayın:

    • Uygulama Adı: MathFuncsApp.
    • Kuruluş Tanımlayıcısı: Ters ad alanı kullanın; örneğin, com.{your_org}.
    • Hedef Platformlar: Varsayılanı kullanın (hem Android hem de iOS hedefleri).
    • Paylaşılan Kod: Bunu .NET Standard olarak ayarlayın (bir "Paylaşılan Kitaplık" çözümü mümkündür, ancak bu kılavuzun kapsamı dışındadır).
  5. Aşağıdaki alanları güncelleştirin ve Oluştur'a tıklayın:

    • Proje Adı: MathFuncsApp.
    • Çözüm Adı: MathFuncsApp.
    • Konum: Varsayılan kaydetme konumunu kullanın (veya alternatif bir konum seçin).
  6. Çözüm Gezgini ilk test için CONTROL + HEDEFE (MathFuncsApp.Android veya MathFuncs.iOS) tıklayın, ardından Başlangıç Projesi Olarak Ayarla'yı seçin.

  7. Tercih edilen cihazı veya Simülatör Öykünücüsü'ne/ tıklayın.

  8. Şablonlu Xamarin.Forms projesinin derlendiğini ve düzgün çalıştığını doğrulamak için çözümü (COMMAND + RETURN) çalıştırın.

    Not

    iOS (özellikle simülatör) en hızlı derleme/dağıtım süresine sahip olma eğilimindedir.

Yerel NuGet akışını NuGet yapılandırmasına ekleme

  1. Visual Studio'da Tercihler'i seçin (Visual Studio menüsünden).

  2. NuGet bölümünün altındaki Kaynaklardan'ı seçin ve ekle'ye tıklayın.

  3. Aşağıdaki alanları güncelleştirin ve Kaynak Ekle'ye tıklayın:

    • Ad: Yerel Paketler gibi anlamlı bir ad sağlayın.
    • Konum: Önceki adımda oluşturulan local-nuget-feed klasörünü belirtin.

    Not

    Bu durumda Kullanıcı Adı ve Parola belirtmeniz gerekmez.

  4. Tamam'a tıklayın.

Pakete başvurma

Her proje için aşağıdaki adımları yineleyin (MathFuncsApp, MathFuncsApp.Android ve MathFuncsApp.iOS).

  1. CONTROL + Projeye TıKLAYıN, ardından Ekle menüsünden NuGet Paketleri Ekle... öğesini seçin.
  2. MathFuncs araması yapın.
  3. Paketin Sürümünün 1.0.0 olduğunu ve Başlık ve Açıklama, yani MathFuncs ve Sample C++ Sarmalayıcı Kitaplığı gibi diğer ayrıntıların beklendiği gibi göründüğünü doğrulayın.
  4. MathFuncs paketini seçin ve ardından Paket Ekle'ye tıklayın.

Kitaplık işlevlerini kullanma

Şimdi, projelerin her birinde MathFuncs paketine bir başvuruyla, işlevler C# kodu tarafından kullanılabilir.

  1. MathFuncsApp ortak Xamarin.Formsprojesinin içinden MainPage.xaml.cs açın (hem MathFuncsApp.Android hem de MathFuncsApp.iOS tarafından başvurulur).

  2. Dosyanın en üstüne System.Diagnostics ve MathFuncs için using deyimleri ekleyin:

    using System.Diagnostics;
    using MathFuncs;
    
  3. Sınıfının en üstünde bir örneğini MyMathFuncs bildirin MainPage :

    MyMathFuncs myMathFuncs;
    
  4. OnAppearingOnDisappearing ve yöntemlerini temel sınıftan ContentPage geçersiz kılın:

    protected override void OnAppearing()
    {
        base.OnAppearing();
    }
    
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
    }
    
  5. OnAppearing Daha önce bildirilen değişkeni başlatmak için yöntemini güncelleştirinmyMathFuncs:

    protected override void OnAppearing()
    {
        base.OnAppearing();
        myMathFuncs = new MyMathFuncs();
    }
    
  6. OnDisappearing yöntemini üzerinde çağıracak şekilde Dispose güncelleştirinmyMathFuncs:

    protected override void OnDisappearing()
    {
        base.OnAppearing();
        myMathFuncs.Dispose();
    }
    
  7. TestMathFuncs adlı özel bir yöntemi aşağıdaki gibi uygulayın:

    private void TestMathFuncs()
    {
        var numberA = 1;
        var numberB = 2;
    
        // Test Add function
        var addResult = myMathFuncs.Add(numberA, numberB);
    
        // Test Subtract function
        var subtractResult = myMathFuncs.Subtract(numberA, numberB);
    
        // Test Multiply function
        var multiplyResult = myMathFuncs.Multiply(numberA, numberB);
    
        // Test Divide function
        var divideResult = myMathFuncs.Divide(numberA, numberB);
    
        // Output results
        Debug.WriteLine($"{numberA} + {numberB} = {addResult}");
        Debug.WriteLine($"{numberA} - {numberB} = {subtractResult}");
        Debug.WriteLine($"{numberA} * {numberB} = {multiplyResult}");
        Debug.WriteLine($"{numberA} / {numberB} = {divideResult}");
    }
    
  8. Son olarak, TestMathFuncs yönteminin sonunda çağrısı:OnAppearing

    TestMathFuncs();
    
  9. Uygulamayı her hedef platformda çalıştırın ve Uygulama Çıkış Bölmesi'ndeki çıkışın aşağıdaki gibi göründüğünü doğrulayın:

    1 + 2 = 3
    1 - 2 = -1
    1 * 2 = 2
    1 / 2 = 0.5
    

    Not

    Android'de test ederken 'DLLNotFoundException' veya iOS'ta derleme hatasıyla karşılaşırsanız, kullandığınız cihazın/öykünücünün/simülatörün CPU mimarisinin desteklemeyi seçtiğimiz alt kümeyle uyumlu olup olmadığını kontrol ettiğinizden emin olun.

Özet

Bu makalede, NuGet paketi aracılığıyla dağıtılan ortak bir .NET sarmalayıcısı aracılığıyla yerel kitaplıkları kullanan bir Xamarin.Forms uygulamasının nasıl oluşturulacağı açıklanmıştır. Bu kılavuzda sağlanan örnek, yaklaşımı daha kolay göstermek için kasıtlı olarak çok basittir. Gerçek bir uygulamanın özel durum işleme, geri çağırmalar, daha karmaşık türlerin sıralanması ve diğer bağımlılık kitaplıklarıyla bağlantı oluşturma gibi karmaşıklıklarla ilgilenmesi gerekir. Önemli bir nokta, C++ kodunun evriminin sarmalayıcı ve istemci uygulamalarıyla eşgüdümlü ve eşitlenmesi sürecidir. Bu süreç, bu endişelerden birinin veya her ikisinin tek bir ekibin sorumluluğunda olup olmadığına bağlı olarak değişebilir. Her iki durumda da otomasyon gerçek bir avantajdır. Aşağıda, bazı önemli kavramlar ve ilgili indirmeler hakkında daha fazla bilgi sağlayan bazı kaynaklar yer almaktadır.

İndirmeler

Örnekler

Daha Fazla Bilgi

Bu gönderinin içeriğiyle ilgili makaleler