Xamarin에서 CC++ /라이브러리 사용Use C/C++ libraries with Xamarin

개요Overview

Xamarin을 통해 개발자는 Visual Studio를 사용 하 여 플랫폼 간 네이티브 모바일 앱을 만들 수 있습니다.Xamarin enables developers to create cross-platform native mobile apps with Visual Studio. 일반적으로 C# 바인딩은 기존 플랫폼 구성 요소를 개발자에 게 노출 하는 데 사용 됩니다.Generally, C# bindings are used to expose existing platform components to developers. 그러나 Xamarin 앱에서 기존 코드 베이스를 사용 해야 하는 경우도 있습니다.However, there are times when Xamarin apps need to work with existing codebases. 때로는 팀은 매우 크고 잘 테스트 되 고 최적화 된 코드 베이스를로 C#이식 하는 시간, 예산 또는 리소스가 없는 경우가 있습니다.Sometimes teams simply don't have the time, budget, or resources to port a large, well-tested, and highly optimized codebase to C#.

플랫폼 C++ 간 모바일 개발용 시각적 개체 를 사용 하면 C/C++ 및 C# 코드를 동일한 솔루션의 일부로 작성 하 여 통합 된 디버깅 환경을 비롯 한 다양 한 이점을 제공할 수 있습니다.Visual C++ for cross-platform mobile development enables the C/C++ and C# code to be built as part of the same solution, offering many advantages including a unified debugging experience. Microsoft는이 방식으로C++ 하이퍼 경과 모바일Pix 카메라와 같은 앱을 제공 하는 C/및 Xamarin을 사용 했습니다.Microsoft has used C/C++ and Xamarin in this way to deliver apps such as Hyperlapse Mobile and Pix Camera.

그러나 경우에 따라 기존 C/C++ 도구 및 프로세스를 그대로 유지 하 고 라이브러리 코드를 응용 프로그램에서 분리 된 상태로 유지 해야 하는 경우도 있습니다 .이는 타사 구성 요소와 유사한 것 처럼 라이브러리를 처리 하는 것입니다.However, in some cases there is a desire (or requirement) to keep existing C/C++ tools and processes in place and to keep the library code decoupled from the application, treating the library as if it were similar to a third-party component. 이러한 상황에서 챌린지는 관련 멤버를에 표시 하는 것 뿐 C# 아니라 종속성으로 라이브러리를 관리 하는 것입니다.In these situations, the challenge is not only exposing the relevant members to C# but managing the library as a dependency. 물론 최대한 많은이 프로세스를 자동화할 수 있습니다.And, of course, automating as much of this process as possible.

이 게시물에서는이 시나리오에 대 한 개략적인 접근 방식을 간략하게 설명 하 고 간단한 예제를 안내 합니다.This post outlines a high-level approach for this scenario and walks through a simple example.

배경Background

C/C++ 는 플랫폼 간 언어로 간주 되지만, 소스 코드는 모든 대상 컴파일러에서 지원 되는 c/C++ 를 사용 하 고 조건부로 포함 된 플랫폼 또는 컴파일러 관련 코드를 포함 하지 않는 부분을 사용 하는 것이 중요 합니다.C/C++ is considered a cross-platform language, but great care must be taken to ensure that the source code is indeed cross-platform, using only C/C++ supported by all target compilers and containing little or no conditionally-included platform or compiler-specific code.

궁극적으로 코드는 모든 대상 플랫폼에서 성공적으로 컴파일되고 실행 되어야 하므로 대상으로 지정 되는 플랫폼 (및 컴파일러)에서 구축 됩니다.Ultimately the code must compile and run successfully on all target platforms therefore this boils down to the commonality across the platforms (and compilers) being targeted. 컴파일러 간의 사소한 차이로 인해 문제가 발생 하 여 각 대상 플랫폼에 대 한 철저 한 테스트 (가능한 자동화)가 점점 더 중요 해지고 있습니다.Issues may still arise from minor differences between compilers and so thorough testing (preferably automated) on each target platform becomes increasingly important.

상위 수준 접근 방식High-level approach

아래 그림은 C/C++ 소스 코드를 NuGet을 통해 공유 되는 플랫폼 간 xamarin 라이브러리로 변환 하는 데 사용 되는 4 단계 방법을 보여 줍니다. 그러면 xamarin.ios 앱에서 사용 됩니다.The illustration below represents the four-stage approach used to transform C/C++ source code into a cross-platform Xamarin library that is shared via NuGet and then is consumed in a Xamarin.Forms app.

Xamarin을 사용 하 여 C/C++ 를 사용 하는 상위 수준 접근 방법

4 단계는 다음과 같습니다.The 4 stages are:

  1. C/C++ 소스 코드를 플랫폼별 네이티브 라이브러리로 컴파일합니다.Compiling the C/C++ source code into platform-specific native libraries.
  2. Visual Studio 솔루션을 사용 하 여 네이티브 라이브러리 래핑Wrapping the native libraries with a Visual Studio solution.
  3. .NET 래퍼에 대 한 NuGet 패키지를 압축 하 고 푸시합니다.Packing and pushing a NuGet package for the .NET wrapper.
  4. Xamarin 앱에서 NuGet 패키지 사용Consuming the NuGet package from a Xamarin app.

1 단계: 플랫폼 특정 네이티브 라이브러리로C++ C/소스 코드 컴파일Stage 1: Compiling the C/C++ source code into platform-specific native libraries

이 단계의 목표는 C# 래퍼에 의해 호출 될 수 있는 네이티브 라이브러리를 만드는 것입니다.The goal of this stage is to create native libraries that can be called by the C# wrapper. 이는 상황에 따라 달라질 수도 있고 관련이 없을 수도 있습니다.This may or may not be relevant depending on your situation. 이 일반적인 시나리오에서 다룰 수 있는 많은 도구와 프로세스는이 문서의 범위를 벗어나는 것입니다.The many tools and processes that can be brought to bear in this common scenario are beyond the scope of this article. 핵심 고려 사항은 C/C++ 코드 베이스를 모든 네이티브 래퍼 코드, 충분 한 단위 테스트 및 빌드 자동화와 동기화 하는 것입니다.Key considerations are keeping the C/C++ codebase in sync with any native wrapper code, sufficient unit testing, and build automation.

연습에서 라이브러리는 함께 제공 되는 셸 스크립트와 함께 Visual Studio Code를 사용 하 여 만들어졌습니다.The libraries in the walk-through were created using Visual Studio Code with an accompanying shell script. 이 연습에 대 한 확장 버전은 샘플의이 부분에 대해 자세히 설명 하는 모바일 CAT GitHub 리포지토리에서 찾을 수 있습니다.An extended version of this walk-through can be found in the Mobile CAT GitHub repository that discusses this part of the sample in greater depth. 이 경우 네이티브 라이브러리는 타사 종속성으로 처리 되지만이 단계는 컨텍스트에 대해 설명 되어 있습니다.The native libraries are being treated as a third-party dependency in this case however this stage is illustrated for context.

간단히 하기 위해이 연습은 아키텍처의 하위 집합만을 대상으로 합니다.For simplicity, the walkthrough targets only a subset of architectures. IOS의 경우 lipo 유틸리티를 사용 하 여 개별 아키텍처 관련 이진 파일에서 단일 fat 이진 파일을 만듭니다.For iOS, it uses the lipo utility to create a single fat binary from the individual architecture-specific binaries. Android는와 함께 동적 이진 파일을 사용 하므로 확장명 및 iOS는 확장명이 인 정적 fat 이진 파일을 사용 합니다.Android will use dynamic binaries with a .so extension and iOS will use a static fat binary with a .a extension.

2 단계: Visual Studio 솔루션을 사용 하 여 네이티브 라이브러리 래핑Stage 2: Wrapping the native libraries with a Visual Studio solution

다음 단계는 .NET에서 쉽게 사용할 수 있도록 네이티브 라이브러리를 래핑하는 것입니다.The next stage is to wrap the native libraries so that they are easily used from .NET. 이 작업은 네 개의 프로젝트를 포함 하는 Visual Studio 솔루션을 사용 하 여 수행 됩니다.This is done with a Visual Studio solution with four projects. 공유 프로젝트는 공용 코드를 포함 합니다.A shared project contains the common code. 각 Xamarin.ios, Xamarin.ios 및 .NET Standard를 대상으로 하는 프로젝트는 라이브러리를 플랫폼에 관계 없이 참조할 수 있습니다.Projects targeting each of Xamarin.Android, Xamarin.iOS, and .NET Standard allow the library to be referenced in a platform-agnostic manner.

이 래퍼는 Paul bait에서 설명 하는 ' theand switch 트릭'을 사용 합니다.The wrapper uses 'the bait and switch trick,' described by Paul Betts. 이는 유일한 방법이 아니라 라이브러리를 쉽게 참조할 수 있도록 하므로 소비 응용 프로그램 자체 내에서 플랫폼별 구현을 명시적으로 관리 하지 않아도 됩니다.This is not the only way, but it makes it easy to reference the library and it avoids the need to explicitly manage platform-specific implementations within the consuming application itself. 트릭은 기본적으로 대상 (.NET Standard, Android, iOS)이 동일한 네임 스페이스, 어셈블리 이름 및 클래스 구조를 공유 하는지 확인 하는 것입니다.The trick is essentially ensuring that the targets (.NET Standard, Android, iOS) share the same namespace, assembly name, and class structure. NuGet은 항상 플랫폼별 라이브러리를 선호 하므로 .NET Standard 버전은 런타임에 사용 되지 않습니다.Since NuGet will always prefer a platform-specific library, the .NET Standard version is never used at runtime.

이 단계의 대부분의 작업에서는 P/Invoke를 사용 하 여 네이티브 라이브러리 메서드를 호출 하 고 기본 개체에 대 한 참조를 관리 하는 데 중점을 둡니다.Most of the work in this step will focus on using P/Invoke to call the native library methods and managing the references to the underlying objects. 목표는 복잡성을 추상화 하는 동시에 라이브러리의 기능을 소비자에 게 노출 하는 것입니다.The goal is to expose the library’s functionality to the consumer while abstracting out any complexity. Xamarin.ios 개발자는 관리 되지 않는 라이브러리의 내부 작업에 대 한 작업 지식을 가질 필요가 없습니다.The Xamarin.Forms developers do not need to have working knowledge on the inner workings of the unmanaged library. 다른 관리 되 C# 는 라이브러리를 사용 하는 것과 같습니다.It should feel like they are using any other managed C# library.

궁극적으로이 단계의 출력은 다음 단계에서 패키지를 작성 하는 데 필요한 정보가 포함 된 nuspec 문서와 함께 대상 마다 하나씩 .NET 라이브러리 집합입니다.Ultimately, the output of this stage is a set of .NET libraries, one per target, along with a nuspec document that contains the information required in order to build the package in the next step.

3 단계: .NET 래퍼에 대 한 NuGet 패키지 압축 및 푸시Stage 3: Packing and pushing a NuGet package for the .NET wrapper

세 번째 단계는 이전 단계의 빌드 아티팩트를 사용 하 여 NuGet 패키지를 만드는 것입니다.The third stage is creating a NuGet package using the build artifacts from the previous step. 이 단계의 결과는 Xamarin 앱에서 사용할 수 있는 NuGet 패키지입니다.The outcome from this step is a NuGet package that can be consumed from a Xamarin app. 이 연습에서는 로컬 디렉터리를 사용 하 여 NuGet 피드 역할을 수행 합니다.The walkthrough uses a local directory to serve as the NuGet feed. 프로덕션 환경에서이 단계는 패키지를 공용 또는 개인 NuGet 피드에 게시 해야 하며 완전히 자동화 되어야 합니다.In production, this step should publish a package to a public or private NuGet feed and should be fully automated.

4 단계: Xamarin.ios 앱에서 NuGet 패키지 사용Stage 4: Consuming the NuGet package from a Xamarin.Forms app

마지막 단계는 Xamarin.ios 앱에서 NuGet 패키지를 참조 하 고 사용 하는 것입니다.The final step is to reference and use the NuGet package from a Xamarin.Forms app. 이렇게 하려면 이전 단계에서 정의한 피드를 사용 하도록 Visual Studio에서 NuGet 피드를 구성 해야 합니다.This requires configuring the NuGet feed in Visual Studio to use the feed defined in the previous step.

피드가 구성 되 면 플랫폼 간 Xamarin.ios 앱의 각 프로젝트에서 패키지를 참조 해야 합니다.Once the feed is configured, the package needs to be referenced from each project in the cross-platform Xamarin.Forms app. ' Bait-switch 트릭 '은 동일한 인터페이스를 제공 하므로 단일 위치에 정의 된 코드를 사용 하 여 네이티브 라이브러리 기능을 호출할 수 있습니다.‘The bait-and-switch trick’ provides identical interfaces, so the native library functionality can be called using code defined in a single location.

소스 코드 리포지토리에는 Azure DevOps에서 개인 NuGet 피드를 설정 하는 방법 및 해당 피드에 패키지를 푸시하는 방법에 대 한 문서가 포함 된 추가 읽기 목록이 포함 되어 있습니다.The source code repository contains a list of further reading that includes articles on how to set up a private NuGet feed on Azure DevOps and how to push the package to that feed. 로컬 디렉터리 보다 설치 시간이 약간 더 필요 하지만이 유형의 피드는 팀 개발 환경에서 더 효율적입니다.While requiring a little more setup time than a local directory, this type of feed is better in a team development environment.

연습Walk-through

제공 된 단계는 Mac용 Visual Studio에만 해당 되지만 구조는 Visual Studio 2017 에서도 작동 합니다.The steps provided are specific to Visual Studio for Mac, but the structure works in Visual Studio 2017 as well.

PrerequisitesPrerequisites

이를 수행 하려면 개발자가 다음을 수행 해야 합니다.In order to follow along, the developer will need:

참고

IPhone에 앱을 배포 하려면 활성 Apple 개발자 계정이 필요 합니다.An active Apple Developer Account is required in order to deploy apps to an iPhone.

네이티브 라이브러리 만들기 (1 단계)Creating the native libraries (Stage 1)

네이티브 라이브러리 기능은 연습: 정적 라이브러리 만들기 및 사용 (C++)의 예제를 기반으로 합니다.The native library functionality is based on the example from Walkthrough: Creating and Using a Static Library (C++).

이 연습에서는이 시나리오에서 라이브러리가 타사 종속성으로 제공 되기 때문에 기본 라이브러리를 빌드하는 첫 번째 단계를 건너뜁니다.This walkthrough skips the first stage, building the native libraries, since the library is provided as a third-party dependency in this scenario. 미리 컴파일된 네이티브 라이브러리는 샘플 코드 와 함께 포함 되거나 직접 다운로드할 수 있습니다.The precompiled native libraries are included alongside the sample code or can be downloaded directly.

네이티브 라이브러리 작업Working with the native library

원래 MathFuncsLib 예제에는 다음 정의가 포함 된 MyMathFuncs 라는 단일 클래스가 포함 되어 있습니다.The original MathFuncsLib example includes a single class called MyMathFuncs with the following definition:

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);
    };
}

추가 클래스는 .NET 소비자가 기본 네이티브 MyMathFuncs 클래스를 만들고, 삭제 하 고, 상호 작용할 수 있도록 하는 래퍼 함수를 정의 합니다.An additional class defines wrapper functions that allow a .NET consumer to create, dispose, and interact with the underlying native MyMathFuncs class.

#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);
}

Xamarin 쪽에서 사용 되는 이러한 래퍼 함수가 됩니다.It will be these wrapper functions that are used on the Xamarin side.

네이티브 라이브러리 래핑 (2 단계)Wrapping the native library (Stage 2)

이 단계를 수행 하려면 이전 섹션에서 설명 하는 미리 컴파일된 라이브러리가 필요 합니다.This stage requires the precompiled libraries described in the previous section.

Visual Studio 솔루션 만들기Creating the Visual Studio solution

  1. Mac용 Visual Studio에서 시작 페이지에서 새 프로젝트 를 클릭 하거나 파일 메뉴에서 새 솔루션 을 클릭 합니다.In Visual Studio for Mac, click New Project (from the Welcome Page) or New Solution (from the File menu).

  2. 새 프로젝트 창에서 공유 프로젝트 ( 다중 플랫폼 > 라이브러리에서)를 선택 하 고 다음을 클릭 합니다.From the New Project window, choose Shared Project (from within Multiplatform > Library) and then click Next.

  3. 다음 필드를 업데이트 한 후 만들기를 클릭 합니다.Update the following fields then click Create:

    • 프로젝트 이름: MathFuncsProject Name: MathFuncs.Shared
    • 솔루션 이름: MathFuncsSolution Name: MathFuncs
    • 위치: 기본 저장 위치 사용 (또는 대안 선택)Location: Use the default save location (or pick an alternative)
    • 솔루션 디렉터리 내에 프로젝트를 만듭니다. 이 확인란을 선택 된 상태로 설정 합니다.Create a project within the solution directory: Set this to checked
  4. 솔루션 탐색기에서 MathFuncs 프로젝트를 두 번 클릭 하 고 주 설정으로 이동 합니다.From Solution Explorer, double-click on the MathFuncs.Shared project and navigate to Main Settings.

  5. 제거 . MathFuncs only로 설정 되도록 기본 네임 스페이스 에서 공유 하 고 확인을 클릭 합니다.Remove .Shared from the Default Namespace so it is set to MathFuncs only, then click OK.

  6. 템플릿에서 만든 MyClass.cs 을 연 다음 클래스와 파일 이름을 모두 MyMathFuncsWrapper 로 바꾸고 네임 스페이스를 MathFuncs로 변경 합니다.Open MyClass.cs (created by the template), then rename both the class and the filename to MyMathFuncsWrapper and change the namespace to MathFuncs.

  7. MathFuncs솔루션을 제어 하 고 클릭 한 다음 추가 메뉴에서 새 프로젝트 추가 ...를 선택 합니다.CONTROL + CLICK on the solution MathFuncs, then choose Add New Project... from the Add menu.

  8. 새 프로젝트 창에서 .NET Standard 라이브러리 ( 다중 플랫폼 > 라이브러리에서)를 선택 하 고 다음을 클릭 합니다.From the New Project window, choose .NET Standard Library (from within Multiplatform > Library) and then click Next.

  9. .NET Standard 2.0 을 선택 하 고 다음을 클릭 합니다.Choose .NET Standard 2.0 and then click Next.

  10. 다음 필드를 업데이트 한 후 만들기를 클릭 합니다.Update the following fields then click Create:

    • 프로젝트 이름: MathFuncsProject Name: MathFuncs.Standard
    • 위치: 공유 프로젝트와 동일한 저장 위치 사용Location: Use the same save location as the shared project
  11. 솔루션 탐색기에서 MathFuncs 프로젝트를 두 번 클릭 합니다.From Solution Explorer, double-click on the MathFuncs.Standard project.

  12. 주 설정으로 이동 하 고 기본 네임 스페이스MathFuncs로 업데이트 합니다.Navigate to Main Settings, then update Default Namespace to MathFuncs.

  13. 출력 설정으로 이동한 다음 어셈블리 이름을 MathFuncs로 업데이트 합니다.Navigate to the Output settings, then update Assembly name to MathFuncs.

  14. 컴파일러 설정으로 이동 하 여 구성을 릴리스로 변경 하 고 디버그 정보기호로 설정한 다음 확인을 클릭 합니다.Navigate to the Compiler settings, change the Configuration to Release, setting Debug information to Symbols Only then click OK.

  15. 프로젝트에서 시작 된 Class1.cs/Getting 를 삭제 합니다 (이러한 항목 중 하나가 템플릿의 일부로 포함 된 경우).Delete Class1.cs/Getting Started from the project (if one of these has been included as part of the template).

  16. 프로젝트 종속성/참조 폴더를 제어 하 고 클릭 한 다음 참조 편집을 선택 합니다.CONTROL + CLICK on the project Dependencies/References folder, then choose Edit References.

  17. 프로젝트 탭에서 MathFuncs 를 선택 하 고 확인을 클릭 합니다.Select MathFuncs.Shared from the Projects tab, then click OK.

  18. 다음 구성을 사용 하 여 7-17 단계 (9 단계 무시)를 반복 합니다.Repeat steps 7-17 (ignoring step 9) using the following configurations:

    프로젝트 이름PROJECT NAME 템플릿 이름TEMPLATE NAME 새 프로젝트 메뉴NEW PROJECT MENU
    MathFuncsMathFuncs.Android 클래스 라이브러리Class Library Android > 라이브러리Android > Library
    MathFuncsMathFuncs.iOS 바인딩 라이브러리Binding Library iOS > 라이브러리iOS > Library
  19. 솔루션 탐색기에서 MathFuncs 프로젝트를 두 번 클릭 한 다음 컴파일러 설정으로 이동 합니다.From Solution Explorer, double-click on the MathFuncs.Android project, then navigate to the Compiler settings.

  20. 구성이 디버그로 설정 된 상태에서 Android를 포함 하도록 기호 정의 를 편집 합니다.With the Configuration set to Debug, edit Define Symbols to include Android;.

  21. 구성을 릴리스로변경한 다음 기호 정의 를 편집 하 여 Android; 도 포함 합니다.Change the Configuration to Release, then edit Define Symbols to also include Android;.

  22. MathFuncs에 대해 19-20 단계를 반복 하 고, 두 경우 모두 Android 대신 IOS 를 포함 하도록 기호 정의 를 편집 합니다.Repeat steps 19-20 for MathFuncs.iOS, editing Define Symbols to include iOS; instead of Android; in both cases.

  23. 릴리스 구성 (컨트롤 + 명령 + B)에서 솔루션을 빌드하고 세 개의 출력 어셈블리 (Android, iOS, .NET Standard) (각 프로젝트 bin 폴더)가 동일한 이름 MathFuncs를 공유 하는지 확인 합니다.Build the solution in Release configuration (CONTROL + COMMAND + B) and validate that all three output assemblies (Android, iOS, .NET Standard) (in the respective project bin folders) share the same name MathFuncs.dll.

이 단계에서 솔루션에는 Android, iOS 및 .NET Standard에 대 한 하나의 각각 세 개의 대상이 각각 참조 하는 공유 프로젝트인 3 개의 대상이 있어야 합니다.At this stage, the solution should have three targets, one apiece for Android, iOS and .NET Standard, and a shared project that is referenced by each of the three targets. 동일한 이름을 가진 동일한 기본 네임 스페이스와 출력 어셈블리를 사용 하도록 구성 해야 합니다.These should be configured to use the same default namespace and output assemblies with the same name. 이는 앞에서 언급 한 ' bait 및 switch ' 접근 방식에 필요 합니다.This is necessary for the 'bait and switch' approach mentioned previously.

네이티브 라이브러리 추가Adding the native libraries

네이티브 라이브러리를 래퍼 솔루션에 추가 하는 프로세스는 Android와 iOS 사이에서 약간 다릅니다.The process of adding the native libraries to the wrapper solution varies slightly between Android and iOS.

MathFuncs에 대 한 네이티브 참조Native references for MathFuncs.Android

  1. MathFuncs 프로젝트를 제어 하 고 클릭 한 다음 추가 메뉴 이름 지정 라이브러리에서 새 폴더 를 선택 합니다.CONTROL + CLICK on the MathFuncs.Android project, then choose New Folder from the Add menu naming it libs.

  2. abi (응용 프로그램 이진 인터페이스)에 대해 라이브러리 폴더를 Ctrl + 클릭 한 다음 추가 메뉴에서 새 폴더 를 선택 하 여 해당 ABI뒤의 이름을 지정 합니다.For each ABI (Application Binary Interface), CONTROL + CLICK on the libs folder, then choose New Folder from the Add menu, naming it after that respective ABI. 이 경우:In this case:

    • arm64-v8aarm64-v8a
    • armeabi-v7aarmeabi-v7a
    • x86x86
    • x86_64x86_64

    참고

    자세한 개요는 NDK 개발자 가이드아키텍처 및 cpu 항목, 특히 앱 패키지의 네이티브 코드주소 지정 섹션을 참조 하세요.For a more detailed overview, see the Architectures and CPUs topic from the NDK developer guide, specifically the section on addressing native code in app packages.

  3. 폴더 구조를 확인 합니다.Verify the folder structure:

    - lib
        - arm64-v8a
        - armeabi-v7a
        - x86
        - x86_64
    
  4. 다음 매핑을 기반으로 각 ABI 폴더에 해당 하는 라이브러리를 추가 합니다 .Add the corresponding .so libraries to each of the ABI folders based on the following mapping:

    arm64-arm64-v8a: Lib/Android/arm64arm64-v8a: libs/Android/arm64

    armeabi-armeabi-v7a: Lib/Android/armarmeabi-v7a: libs/Android/arm

    x86: Lib/Android/x86x86: libs/Android/x86

    x86_64: Lib/Android/x86_64x86_64: libs/Android/x86_64

    참고

    파일을 추가 하려면 해당 ABI를 나타내는 폴더를 ctrl + 클릭 한 다음 추가 메뉴에서 파일 추가 ... 를 선택 합니다.To add files, CONTROL + CLICK on the folder representing the respective ABI, then choose Add Files... from the Add menu. PrecompiledLibs 디렉터리에서 적절 한 라이브러리를 선택 하 고 열기 를 클릭 한 다음 확인 을 클릭 하 여 디렉터리에 파일을 복사합니다.Choose the appropriate library (from the PrecompiledLibs directory) then click Open and then click OK leaving the default option to Copy the file to the directory.

  5. 각 파일에 대해 ctrl + 클릭 을 누른 다음 빌드 작업 메뉴에서 EmbeddedNativeLibrary 옵션을 선택 합니다 .For each of the .so files, CONTROL + CLICK then choose the EmbeddedNativeLibrary option from the Build Action menu.

이제 라이브러리 폴더가 다음과 같이 표시 됩니다.Now the libs folder should appear as follows:

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

MathFuncs에 대 한 네이티브 참조Native references for MathFuncs.iOS

  1. MathFuncs 프로젝트를 제어 하 고 클릭 한 다음 추가 메뉴에서 네이티브 참조 추가 를 선택 합니다.CONTROL + CLICK on the MathFuncs.iOS project, then choose Add Native Reference from the Add menu.

  2. LibMathFuncs 라이브러리 ( PrecompiledLibs 디렉터리 아래의 lib/ios에서)를 선택 하 고 열기 를 클릭 합니다.Choose the libMathFuncs.a library (from libs/ios under the PrecompiledLibs directory) then click Open

  3. 네이티브 참조 폴더에서 LibMathFuncs 파일을 제어 하 고 클릭 한 다음 메뉴에서 속성 옵션을 선택 합니다.CONTROL + CLICK on the libMathFuncs file (within the Native References folder, then choose the Properties option from the menu

  4. 속성 패드에서 체크 인 되는 기본 참조 속성 (눈금 아이콘 표시)을 구성 합니다.Configure the Native Reference properties so they are checked (showing a tick icon) in the Properties Pad:

    • 강제 로드Force Load
    • 되었습니다C++Is C++
    • 스마트 링크Smart Link

    참고

    바인딩 라이브러리 프로젝트 형식을 네이티브 참조 와 함께 사용 하면 정적 라이브러리가 포함 되 고,이 라이브러리를 참조 하는 xamarin.ios 앱과 자동으로 연결 될 수 있습니다 (NuGet 패키지를 통해 포함 된 경우에도).Using a binding library project type along with a native reference embeds the static library and enables it to be automatically linked with the Xamarin.iOS app that references it (even when it is included via a NuGet package).

  5. ApiDefinition.cs를 열고 템플릿 처리 된 코드 (MathFuncs 네임 스페이스만 남겨 두고)를 삭제 한 다음 Structs.cs 에 대해 동일한 단계를 수행 합니다.Open ApiDefinition.cs, deleting the templated commented code (leaving only the MathFuncs namespace), then perform the same step for Structs.cs

    참고

    바인딩 라이브러리 프로젝트를 빌드하려면 이러한 파일 ( ObjcbindingapidefinitionObjcbindingcor9build 작업 포함)이 필요 합니다.A Binding library project requires these files (with the ObjCBindingApiDefinition and ObjCBindingCoreSource build actions) in order to build. 그러나 표준 P/Invoke를 사용 하 여 Android 및 iOS 라이브러리 대상 모두에서 공유할 수 있는 방식으로 이러한 파일 외부에서 네이티브 라이브러리를 호출 하는 코드를 작성 합니다.However, we will write the code, to call our native library, outside of these files in a way that can be shared between both Android and iOS library targets using standard P/Invoke.

관리 되는 라이브러리 코드 작성Writing the managed library code

이제 네이티브 라이브러리를 C# 호출 하는 코드를 작성 합니다.Now, write the C# code to call the native library. 목표는 기본적인 복잡성을 숨기는 것입니다.The goal is to hide any underlying complexity. 소비자는 네이티브 라이브러리 내부 또는 P/Invoke 개념에 대 한 작업 지식이 필요 하지 않습니다.The consumer should not need any working knowledge of the native library internals or of P/Invoke concepts.

SafeHandle 만들기Creating a SafeHandle

  1. MathFuncs 프로젝트를 제어 하 고 클릭 한 다음 추가 메뉴에서 파일 추가 ... 를 선택 합니다.CONTROL + CLICK on the MathFuncs.Shared project, then choose Add File... from the Add menu.

  2. 새 파일 창에서 빈 클래스 를 선택 하 고 이름을 MyMathFuncsSafeHandle 로 지정한 다음 새로 만들기 를 클릭 합니다.Choose Empty Class from the New File window, name it MyMathFuncsSafeHandle and then click New

  3. MyMathFuncsSafeHandle 클래스를 구현 합니다.Implement the MyMathFuncsSafeHandle class:

    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;
            }
        }
    }
    

    참고

    SafeHandle 은 관리 코드에서 관리 되지 않는 리소스로 작업 하는 데 선호 되는 방법입니다.A SafeHandle is the preferred way to work with unmanaged resources in managed code. 이는 중요 한 종료 및 개체 수명 주기와 관련 된 많은 상용구 코드를 요약 합니다.This abstracts away a lot of boilerplate code related to critical finalization and object lifecycle. 이 핸들의 소유자는 나중에 다른 관리 되는 리소스와 같이 취급할 수 있으며 전체 삭제 가능 패턴을 구현 하지 않아도 됩니다.The owner of this handle can subsequently treat it like any other managed resource and will not have to implement the full Disposable pattern.

내부 래퍼 클래스 만들기Creating the internal wrapper class

  1. MyMathFuncsWrapper.cs를 열고 내부 정적 클래스로 변경 합니다.Open MyMathFuncsWrapper.cs, changing it to an internal static class

    namespace MathFuncs
    {
        internal static class MyMathFuncsWrapper
        {
        }
    }
    
  2. 동일한 파일에서 다음 조건문을 클래스에 추가 합니다.In the same file, add the following conditional statement to the class:

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

    참고

    그러면 라이브러리가 Android 또는 iOS용으로 빌드 되었는지 여부에 따라 DllName 상수 값이 설정 됩니다.This sets the DllName constant value based on whether the library is being built for Android or iOS. 이는 각 플랫폼에서 사용 되는 다양 한 명명 규칙을 해결 하는 것입니다 .이 경우에 사용 되는 라이브러리의 형식 이기도 합니다.This is to address the different naming conventions used by each respective platform but also the type of library being used in this case. Android는 동적 라이브러리를 사용 하므로 확장명을 포함 하는 파일 이름이 필요 합니다.Android is using a dynamic library and so expects a filename including extension. IOS의 경우 정적 라이브러리를 사용 하 고 있으므로 ' __Internal'가 필요 합니다.For iOS, '__Internal' is required since we are using a static library.

  3. MyMathFuncsWrapper.cs 파일의 맨 위에 t e m에 대 한 참조를 추가 합니다 .Add a reference to System.Runtime.InteropServices at the top of the MyMathFuncsWrapper.cs file

    using System.Runtime.InteropServices;
    
  4. MyMathFuncs 클래스의 생성 및 삭제를 처리 하는 래퍼 메서드를 추가 합니다.Add the wrapper methods to handle the creation and disposal of the MyMathFuncs class:

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

    참고

    .NET 런타임에 해당 라이브러리 내에서 호출할 함수의 이름을 명시적으로 알려주는 EntryPoint 와 함께 상수 DllNameDllImport 특성에 전달 합니다.We are passing in our constant DllName to the DllImport attribute along with the EntryPoint which explicitly tells the .NET runtime the name of the function to call within that library. 기술적으로 관리 되는 메서드 이름이 관리 되지 않는 메서드와 동일한 경우 EntryPoint 값을 제공할 필요가 없습니다.Technically, we do not need to provide the EntryPoint value if our managed method names were identical to the unmanaged one. 제공 되지 않은 경우 관리 되는 메서드 이름이 대신 EntryPoint 로 사용 됩니다.If one is not provided, the managed method name would be used as the EntryPoint instead. 그러나 명시적으로 하는 것이 좋습니다.However, it is better to be explicit.

  5. 다음 코드를 사용 하 여 MyMathFuncs 클래스를 사용할 수 있도록 래퍼 메서드를 추가 합니다.Add the wrapper methods to enable us to work with the MyMathFuncs class using the following code:

    [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);
    

    참고

    이 예제에서는 매개 변수에 대해 단순 유형을 사용 합니다.We're using simple types for the parameters in this example. 이 경우에는 마샬링이 비트 복사 이므로 파트에 대 한 추가 작업이 필요 하지 않습니다.Since marshalling is a bitwise-copy in this case it requires no additional work on our part. 또한 표준 IntPtr대신 MyMathFuncsSafeHandle 클래스를 사용 하는 것을 확인 합니다.Also notice the use of the MyMathFuncsSafeHandle class instead of the standard IntPtr. IntPtr 은 마샬링 프로세스의 일부로 자동으로 SafeHandle 에 매핑됩니다.The IntPtr is automatically mapped to the SafeHandle as part of the marshalling process.

  6. 완성 된 MyMathFuncsWrapper 클래스가 아래와 같이 표시 되는지 확인 합니다.Verify that the finished MyMathFuncsWrapper class appears as below:

    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);
        }
    }
    

MyMathFuncsSafeHandle 클래스 완료Completing the MyMathFuncsSafeHandle class

  1. MyMathFuncsSafeHandle 클래스를 열고 releasehandle 메서드 내의 자리 표시자 TODO 주석으로 이동 합니다.Open the MyMathFuncsSafeHandle class, navigate to the placeholder TODO comment within the ReleaseHandle method:

    // TODO: Release the handle here
    
  2. TODO 줄을 바꿉니다.Replace the TODO line:

    MyMathFuncsWrapper.DisposeMyMathFuncs(handle);
    

MyMathFuncs 클래스 작성Writing the MyMathFuncs class

이제 래퍼가 완성 되었으므로 관리 되지 않는 C++ MyMathFuncs 개체에 대 한 참조를 관리 하는 MyMathFuncs 클래스를 만듭니다.Now that the wrapper is complete, create a MyMathFuncs class that will manage the reference to the unmanaged C++ MyMathFuncs object.

  1. MathFuncs 프로젝트를 제어 하 고 클릭 한 다음 추가 메뉴에서 파일 추가 ... 를 선택 합니다.CONTROL + CLICK on the MathFuncs.Shared project, then choose Add File... from the Add menu.

  2. 새 파일 창에서 빈 클래스 를 선택 하 고 이름을 MyMathFuncs 로 지정한 다음 새로 만들기 를 클릭 합니다.Choose Empty Class from the New File window, name it MyMathFuncs and then click New

  3. MyMathFuncs 클래스에 다음 멤버를 추가 합니다.Add the following members to the MyMathFuncs class:

    readonly MyMathFuncsSafeHandle handle;
    
  4. 클래스가 인스턴스화될 때 네이티브 MyMathFuncs 개체에 대 한 핸들을 만들어 저장 하도록 클래스에 대 한 생성자를 구현 합니다.Implement the constructor for the class so it creates and stores a handle to the native MyMathFuncs object when the class is instantiated:

    public MyMathFuncs()
    {
        handle = MyMathFuncsWrapper.CreateMyMathFuncs();
    }
    
  5. 다음 코드를 사용 하 여 IDisposable 인터페이스를 구현 합니다.Implement the IDisposable interface using the following code:

    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. MyMathFuncsWrapper 클래스를 사용 하 여 MyMathFuncs 메서드를 구현 하 여 내부 관리 되지 않는 개체에 저장 한 포인터를 전달 함으로써 내부적으로 실제 작업을 수행 합니다.Implement the MyMathFuncs methods using the MyMathFuncsWrapper class to perform the real work under the hood by passing in the pointer we have stored to the underlying unmanaged object. 코드는 다음과 같아야 합니다.The code should be as follows:

    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 만들기Creating the nuspec

NuGet을 통해 라이브러리를 패키지 하 고 배포 하기 위해 솔루션에는 nuspec 파일이 필요 합니다.In order to have the library packaged and distributed via NuGet, the solution needs a nuspec file. 그러면 지원 되는 각 플랫폼에 대해 포함 되는 결과 어셈블리를 식별할 수 있습니다.This will identify which of the resulting assemblies will be included for each supported platform.

  1. MathFuncs솔루션을 제어 하 고 클릭 한 다음 추가 메뉴에서 솔루션 폴더 추가 를 선택 하 여 솔루션 폴더 이름을 지정 합니다.CONTROL + CLICK on the solution MathFuncs, then choose Add Solution Folder from the Add menu naming it SolutionItems.

  2. 솔루션에서 솔루션 을 마우스 단추로 클릭 한 다음 추가 메뉴에서 새 파일 ... 을 선택 합니다.CONTROL + CLICK on the SolutionItems folder, then choose New File... from the Add menu.

  3. 새 파일 창에서 빈 XML 파일 을 선택 하 고 이름을 MathFuncs. Nuspec 로 지정한 후 새로 만들기를 클릭 합니다.Choose Empty XML File from the New File window, name it MathFuncs.nuspec and then click New.

  4. NuGet 소비자에 게 표시 되는 기본 패키지 메타 데이터를 사용 하 여 MathFuncs. nuspec 를 업데이트 합니다.Update MathFuncs.nuspec with the basic package metadata to be displayed to the NuGet consumer. 예를 들면,For example:

    <?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>
    

    참고

    이 매니페스트에 사용 된 스키마에 대 한 자세한 내용은 nuspec 참조 문서를 참조 하세요.See the nuspec reference document for further detail on the schema used for this manifest.

  5. 별도의 <file> 요소를 사용 하 여 각 파일을 식별 하는 <package> 요소의 자식으로 <files> 요소를 추가 합니다 (<metadata>바로 아래).Add a <files> element as a child of the <package> element (just below <metadata>), identifying each file with a separate <file> element:

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

    참고

    패키지를 프로젝트에 설치 하 고 동일한 이름으로 여러 어셈블리를 지정 하는 경우 NuGet은 지정 된 플랫폼과 가장 고유한 어셈블리를 효과적으로 선택 합니다.When a package is installed into a project, and where there are multiple assemblies specified by the same name, NuGet will effectively choose the assembly that is most specific to the given platform.

  6. Android 어셈블리에 대 한 <file> 요소를 추가 합니다.Add the <file> elements for the Android assemblies:

    <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. IOS 어셈블리에 대 한 <file> 요소를 추가 합니다.Add the <file> elements for the iOS assemblies:

    <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. Netstandard 2.0 어셈블리에 대 한 <file> 요소를 추가 합니다.Add the <file> elements for the netstandard2.0 assemblies:

    <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 매니페스트를 확인 합니다.Verify the nuspec manifest:

    <?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>
    

    참고

    이 파일은 릴리스 빌드에서 어셈블리 출력 경로를 지정 하므로 해당 구성을 사용 하 여 솔루션을 빌드해야 합니다.This file specifies the assembly output paths from a Release build, so be sure to build the solution using that configuration.

이 시점에서 솔루션은 3 개의 .NET 어셈블리와 지원 nuspec 매니페스트를 포함 합니다.At this point, the solution contains 3 .NET assemblies and a supporting nuspec manifest.

NuGet을 사용 하 여 .NET 래퍼 배포Distributing the .NET wrapper with NuGet

다음 단계는 NuGet 패키지를 패키지 및 배포 하 여 앱에서 쉽게 사용 하 고 종속성으로 관리할 수 있도록 하는 것입니다.The next step is to package and distribute the NuGet package so it may be easily consumed by the app and managed as a dependency. 래핑 및 소비는 모두 단일 솔루션 내에서 수행할 수 있지만 NuGet 지원을 통해 라이브러리를 배포 하면 이러한 코드 베이스를 독립적으로 관리할 수 있습니다.The wrapping and consumption could all be done within a single solution, but distributing the library via NuGet aids in decoupling and enables us to manage these codebases independently.

로컬 패키지 디렉터리 준비Preparing a local packages directory

NuGet 피드의 가장 간단한 형태는 로컬 디렉터리입니다.The simplest form of NuGet feed is a local directory:

  1. Finder에서 편리한 디렉터리로 이동 합니다.In Finder, navigate to a convenient directory. 예를 들면 /사용자입니다.For example, /Users.
  2. 파일 메뉴에서 새 폴더 를 선택 하 여 로컬 nuget 피드와같은 의미 있는 이름을 제공 합니다.Choose New Folder from the File menu, providing a meaningful name such as local-nuget-feed.

패키지 만들기Creating the package

  1. 빌드 구성을 릴리스로 설정 하 고 명령 + B를 사용 하 여 빌드를 실행 합니다.Set the Build Configuration to Release, and execute a build using COMMAND + B.

  2. 터미널 을 열고 nuspec 파일이 포함 된 폴더로 디렉터리를 변경 합니다.Open Terminal and change directory to the folder containing the nuspec file.

  3. 터미널에서 Nuspec 파일, 버전 (예: 1.0.0)을 지정 하 고, 이전 단계에서 만든 폴더 (예: 로컬 nuget 피드)를 사용 하 여 OutputDirectory 를 지정 하는 nuget pack 명령을 실행 합니다.In Terminal, execute the nuget pack command specifying the nuspec file, the Version (for example, 1.0.0), and the OutputDirectory using the folder created in the previous step, that is, local-nuget-feed. 예를 들면,For example:

    nuget pack MathFuncs.nuspec -Version 1.0.0 -OutputDirectory ~/local-nuget-feed
    
  4. MathFuncs nupkg로컬 nuget 피드 디렉터리에 만들어졌는지 확인 합니다.Confirm that MathFuncs.1.0.0.nupkg has been created in the local-nuget-feed directory.

필드 Azure DevOps에서 개인 NuGet 피드 사용[OPTIONAL] Using a private NuGet feed with Azure DevOps

보다 강력한 기법은 Azure DevOps의 NuGet 패키지 시작에 설명 되어 있습니다. 여기에는 개인 피드를 만들고 이전 단계에서 생성 된 패키지를 해당 피드에 푸시하는 방법을 보여 줍니다.A more robust technique is described in Get started with NuGet packages in Azure DevOps, which shows how to create a private feed and push the package (generated in the previous step) to that feed.

이 워크플로를 완전히 자동화 하는 것이 좋습니다 (예: Azure Pipelines사용).It is ideal to have this workflow fully automated, for example using Azure Pipelines. 자세한 내용은 Azure Pipelines 시작을 참조 하세요.For more information, see Get started with Azure Pipelines.

Xamarin Forms 앱에서 .NET 래퍼 사용Consuming the .NET wrapper from a Xamarin.Forms app

이 연습을 완료 하려면 로컬 NuGet 피드에 게시 된 패키지를 사용 하는 Xamarin Forms 앱을 만듭니다.To complete the walkthrough, create a Xamarin.Forms app to consume the package just published to the local NuGet feed.

Xamarin Forms 프로젝트 만들기Creating the Xamarin.Forms project

  1. Mac용 Visual Studio의 새 인스턴스를 엽니다.Open a new instance of Visual Studio for Mac. 터미널에서이 작업을 수행할 수 있습니다.This can be done from Terminal:

    open -n -a "Visual Studio"
    
  2. Mac용 Visual Studio에서 시작 페이지에서 새 프로젝트 를 클릭 하거나 파일 메뉴에서 새 솔루션 을 클릭 합니다.In Visual Studio for Mac, click New Project (from the Welcome Page) or New Solution (from the File menu).

  3. 새 프로젝트 창에서 다중 플랫폼 > 앱내에서 빈 양식 앱 을 선택 하 고 다음을 클릭 합니다.From the New Project window, choose Blank Forms App (from within Multiplatform > App) and then click Next.

  4. 다음 필드를 업데이트 하 고 다음을 클릭 합니다.Update the following fields then click Next:

    • 앱 이름: MathFuncsApp.App Name: MathFuncsApp.
    • 조직 식별자: 역방향 네임 스페이스 (예: com. {your_org} )를 사용 합니다.Organization Identifier: Use a reverse namespace, for example, com.{your_org}.
    • 대상 플랫폼: 기본 (Android 및 iOS 대상 모두)을 사용 합니다.Target Platforms: Use the default (both Android and iOS targets).
    • 공유 코드: 이를 .NET Standard ("공유 라이브러리" 솔루션은 가능 하지만이 연습에서는 다루지 않음)로 설정 합니다.Shared Code: Set this to .NET Standard (a “Shared Library” solution is possible, but beyond the scope of this walkthrough).
  5. 다음 필드를 업데이트 한 후 만들기를 클릭 합니다.Update the following fields then click Create:

    • 프로젝트 이름: MathFuncsApp.Project Name: MathFuncsApp.
    • 솔루션 이름: MathFuncsApp.Solution Name: MathFuncsApp.
    • 위치: 기본 저장 위치를 사용 하거나 다른 위치를 선택 합니다.Location: Use the default save location (or pick an alternative).
  6. 솔루션 탐색기에서 초기 테스트를 위해 대상 (MathFuncsApp 또는 MathFuncs)을 제어 하 고 클릭 한 다음 시작 프로젝트로 설정을 선택 합니다.In Solution Explorer, CONTROL + CLICK on the target (MathFuncsApp.Android or MathFuncs.iOS) for initial testing, then choose Set As Startup Project.

  7. 선호 하는 장치 또는 시뮬레이터/에뮬레이터를 선택 합니다.Choose the preferred device or Simulator/Emulator.

  8. 솔루션 (명령 + 반환)을 실행 하 여 템플릿 xamarin.ios 프로젝트를 빌드하고 실행 하는지 확인 합니다.Run the solution (COMMAND + RETURN) to validate that the templated Xamarin.Forms project builds and runs okay.

    참고

    iOS (특히 시뮬레이터)는 빌드/배포 시간이 가장 빠릅니다.iOS (specifically the simulator) tends to have the fastest build/deploy time.

NuGet 구성에 로컬 NuGet 피드 추가Adding the local NuGet feed to the NuGet configuration

  1. Visual studiovisual Studio 메뉴에서 기본 설정 을 선택 합니다.In Visual Studio, choose Preferences (from the Visual Studio menu).

  2. NuGet 섹션에서 소스 를 선택 하 고 추가를 클릭 합니다.Choose Sources from under the NuGet section, then click Add.

  3. 다음 필드를 업데이트 한 다음 원본 추가를 클릭 합니다.Update the following fields then click Add Source:

    • 이름: 로컬 패키지와 같은 의미 있는 이름을 제공 합니다.Name: Provide a meaningful name, for example, Local-Packages.
    • 위치: 이전 단계에서 만든 로컬 nuget 피드 폴더를 지정 합니다.Location: Specify the local-nuget-feed folder created in the previous step.

    참고

    이 경우 사용자 이름과 암호를 지정할 필요가 없습니다.In this case there is no need to specify a Username and Password.

  4. 확인을 클릭합니다.Click OK.

패키지 참조Referencing the package

각 프로젝트에 대해 다음 단계를 반복 합니다 (MathFuncsApp, MathFuncsAppMathFuncsApp).Repeat the following steps for each project (MathFuncsApp, MathFuncsApp.Android, and MathFuncsApp.iOS).

  1. 프로젝트를 제어 하 고 클릭 한 다음 추가 메뉴에서 NuGet 패키지 추가 ... 를 선택 합니다.CONTROL + CLICK on the project, then choose Add NuGet Packages... from the Add menu.
  2. MathFuncs를 검색 합니다.Search for MathFuncs.
  3. 패키지 버전이 1.0.0 인지 확인 하 고 다른 세부 정보는 제목설명, 즉 MathFuncsC++ 샘플 래퍼 라이브러리와 같이 예상 대로 표시 됩니다.Verify the Version of the package is 1.0.0 and the other details appear as expected such as the Title and Description, that is, MathFuncs and Sample C++ Wrapper Library.
  4. MathFuncs 패키지를 선택 하 고 패키지 추가를 클릭 합니다.Select the MathFuncs package, then click Add Package.

라이브러리 함수 사용Using the library functions

이제 각 프로젝트의 MathFuncs 패키지에 대 한 참조를 사용 하 여 C# 코드에 함수를 사용할 수 있습니다.Now, with a reference to the MathFuncs package in each of the projects, the functions are available to the C# code.

  1. MathFuncsApp 일반 xamarin.ios프로젝트 내에서 MainPage.xaml.cs 를 엽니다 ( MathFuncsAppMathFuncsApp둘 다에서 참조).Open MainPage.xaml.cs from within the MathFuncsApp common Xamarin.Formsproject (referenced by both MathFuncsApp.Android and MathFuncsApp.iOS).

  2. 파일의 맨 위에 있는 Diagnostics 및 MathFuncsusing 문을 추가 합니다 .Add using statements for System.Diagnostics and MathFuncs at the top of the file:

    using System.Diagnostics;
    using MathFuncs;
    
  3. MainPage 클래스 맨 위에 MyMathFuncs 클래스의 인스턴스를 선언 합니다.Declare an instance of the MyMathFuncs class at the top of the MainPage class:

    MyMathFuncs myMathFuncs;
    
  4. ContentPage 기본 클래스에서 OnAppearingOnDisappearing 메서드를 재정의 합니다.Override the OnAppearing and OnDisappearing methods from the ContentPage base class:

    protected override void OnAppearing()
    {
        base.OnAppearing();
    }
    
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
    }
    
  5. OnAppearing 메서드를 업데이트 하 여 이전에 선언 된 myMathFuncs 변수를 초기화 합니다.Update the OnAppearing method to initialize the myMathFuncs variable declared previously:

    protected override void OnAppearing()
    {
        base.OnAppearing();
        myMathFuncs = new MyMathFuncs();
    }
    
  6. myMathFuncs에서 Dispose 메서드를 호출 하도록 OnDisappearing 메서드를 업데이트 합니다.Update the OnDisappearing method to call the Dispose method on myMathFuncs:

    protected override void OnDisappearing()
    {
        base.OnAppearing();
        myMathFuncs.Dispose();
    }
    
  7. 다음과 같이 TestMathFuncs 라는 private 메서드를 구현 합니다.Implement a private method called TestMathFuncs as follows:

    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. 마지막으로 OnAppearing 메서드의 끝에서 TestMathFuncs를 호출 합니다.Finally, call TestMathFuncs at the end of the OnAppearing method:

    TestMathFuncs();
    
  9. 각 대상 플랫폼에서 앱을 실행 하 고 다음과 같이 응용 프로그램 출력 패드에서 출력이 표시 되는지 확인 합니다.Run the app on each target platform and validate the output in the Application Output Pad appears as follows:

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

    참고

    Android에서 테스트할 때 'system.dllnotfoundexception'가 발생 하거나 iOS에서 빌드 오류가 발생 하는 경우 사용 중인 장치/에뮬레이터/시뮬레이터의 CPU 아키텍처가 지원 하도록 선택한 하위 집합과 호환 되는지 확인 해야 합니다.If you encounter a 'DLLNotFoundException' when testing on Android, or a build error on iOS, be sure to check that the CPU architecture of the device/emulator/simulator you are using is compatible with the subset that we chose to support.

요약Summary

이 문서에서는 NuGet 패키지를 통해 배포 되는 공용 .NET 래퍼를 통해 네이티브 라이브러리를 사용 하는 Xamarin Forms 앱을 만드는 방법을 설명 했습니다.This article explained how to create a Xamarin.Forms app that uses native libraries through a common .NET wrapper distributed via a NuGet package. 이 연습에서 제공 하는 예제는 의도적으로 간단 하 게 방법을 보여 주는 매우 간단한 방법입니다.The example provided in this walkthrough is intentionally very simplistic to more easily demonstrate the approach. 실제 응용 프로그램은 예외 처리, 콜백, 더 복잡 한 형식의 마샬링, 다른 종속성 라이브러리와의 연결 등의 복잡성을 처리 해야 합니다.A real application will have to deal with complexities such as exception handling, callbacks, the marshalling of more complex types, and linking with other dependency libraries. 핵심 고려 사항은 C++ 코드의 진화를 조정 하 고 래퍼 및 클라이언트 응용 프로그램과 동기화 하는 프로세스입니다.A key consideration is the process by which the evolution of the C++ code is coordinated and synced with the wrapper and client applications. 이 프로세스는 이러한 문제 중 하나 또는 둘 다가 단일 팀에서 담당 하는지 여부에 따라 달라질 수 있습니다.This process may vary depending on whether one or both of those concerns are the responsibility of a single team. 어느 방법이 든 자동화는 진정한 장점입니다.Either way, automation is a real benefit. 다음은 관련 다운로드와 함께 몇 가지 주요 개념에 대 한 추가 정보를 제공 하는 리소스입니다.Below are some resources providing further reading around some of the key concepts along with the relevant downloads.

다운로드Downloads

예제Examples

추가 정보Further Reading

이 게시물의 내용과 관련 된 문서Articles relating to the content of this post