C++/CX Windows 런타임 구성 요소를 만들고 JavaScript 또는 C#에서 호출하는 연습

참고 항목

이 항목은 C++/CX 애플리케이션 유지에 도움을 주기 위해 작성되었습니다. 하지만 새로운 응용 프로그램에 대해 C++/WinRT를 사용하는 것이 좋습니다. C++/WinRT는 헤더 파일 기반 라이브러리로 구현된 WinRT(Windows 런타임) API용 최신의 완전한 표준 C++17 언어 프로젝션이며, 최신 Windows API에 최고 수준의 액세스를 제공하도록 설계되었습니다. C++/WinRT를 사용하여 Windows 런타임 구성 요소를 만드는 방법에 자세한 내용은 C++/WinRT를 사용한 Windows 런타임 구성 요소를 참조하세요.

이 연습에서는 JavaScript, C# 또는 Visual Basic에서 호출할 수 있는 기본 Windows 런타임 구성 요소 DLL을 만드는 방법을 보여 줍니다. 이 연습을 시작하려면 먼저 쉽게 ref 클래스를 사용할 수 있게 해주는 ABI(추상 이진 인터페이스), ref 클래스, Visual C++ 구성 요소 확장 등의 개념을 이해해야 합니다. 자세한 내용은 를 사용한 Windows 런타임 구성 요소Visual C++ 언어 참조(C++/CX)를 참조하세요.

C++ 구성 요소 DLL 생성

이 예제에서는 먼저 구성 요소 프로젝트를 만들지만 그 전에 JavaScript 프로젝트를 먼저 만들 수 있습니다. 순서는 중요치 않습니다.

구성 요소의 기본 클래스에는 속성 및 메서드 정의 예와 이벤트 신고가 포함되어 있습니다. 이는 하는 법을 보여 주기 위해서만 제공됩니다. 이러한 코드는 필요하지 않으며, 이 예제에서는 생성된 모든 코드를 자체 코드로 대체합니다.

C++ 구성 요소 프로젝트 생성하기

  1. Visual Studio 메뉴 바에서 File, New, Project를 선택하세요

  2. 새 프로잭트대화박스의 왼편에 있는 Visual C++ 를 확장한 다음 유니버설 Windows 앱 노드를 선택합니다.

  3. 가운데 창에서 Windows 런타임 구성 요소를 선택하고 프로젝트 이름을 WinRT_CPP로 지정합니다.

  4. 확인 단추를 선택합니다.

구성 요소에 활성화 가능한 클래스 추가하기

활성화 가능한 클래스는 클라이언트 코드가 표현식을 사용하여 만들수 있는 클래스입니다. (Visual Basic의 경우 New 혹은 C++에선 ref new입니다.). 구성 요소에서 이를 날인된 공용 ref 클래스로 신고합니다. 사실 Class1.h 및 .cpp 파일에는 이미 ref 클래스가 있습니다. 이름을 변경할 수 있지만 이 예제에서는 기본 이름인 Class1을 사용합니다. 필요한 경우 구성 요소에서 추가 ref 클래스 또는 일반 클래스를 정의할 수 있습니다. Ref 클래스에 대한 자세한 내용은 형식 시스탬 (C++/CX)을 참조하세요.

Class1.h에 다음 #include 지시문을 추가합니다.

#include <collection.h>
#include <ppl.h>
#include <amp.h>
#include <amp_math.h>

collection.h는 platform::Collections::Vector 클래스 및 platform::Collections::Map 클래스와 같은 C++ 구체 클래스 헤더 파일로, Windows 런타임에 의해 정의된 언어 중립적 인터페이스를 구현합니다. 앰프 헤더는 GPU에서 계산을 실행하는 데 사용됩니다. 그들과 동등한 Windows 런타임은 없으며 프라이벳이기 때문에 괜찮습니다. 일반적으로 성능상의 이유로 구성 요소 내부적으로 ISO C++ 코드 및 표준 라이브러리를 사용해야 합니다. 이는 Windows 런타임 형식으로 표현해야 하는 Windows 런타임 인터페이스일 뿐입니다.

네임스페이스 범위에서 대리자를 추가하기

대리자는 메서드에 대한 매개 변수 및 출력 형식을 정의하는 구성입니다. 이벤트는 특정 대리자 형식의 인스턴스이며 이벤트를 수신하는 모든 이벤트 처리기 메서드에는 대리자에 지정된 서명이 있어야 합니다. 다음 코드는 int를 사용하고 void를 출력하는 대리자 형식을 정의합니다. 다음으로 코드는 이 형식의 퍼블릭 이벤트를 신고합니다. 이렇게 하면 클라이언트 코드가 이벤트가 발생할 때 호출되는 메서드를 제공할 수 있습니다.

Class1 신고 바로 앞에 Class1.h의 네임스페이스 범위에 다음 대리자 신고를 추가합니다.

public delegate void PrimeFoundHandler(int result);

Visual Studio에 붙여넣을 때 코드가 제대로 라인업 되어 있지 않으면 Ctrl+K+D를 눌러 전체 파일의 들여쓰기를 수정하면 됩니다.

공용 멤버 추가하기

이 클래스는 공용 메서드 3개와 공용 이벤트 1개를 노출합니다. 첫 번째 메서드는 항상 매우 빠르게 실행되므로 동기적입니다. 다른 두 메서드는 다소 시간이 걸릴 수 있으므로 UI 스레드를 차단하지 않도록 비동기적입니다. 이러한 메서드는 IAsyncOperationWithProgress 및 IAsyncActionWithProgress를 출력합니다. 전자는 결과를 출력하는 비동기 메서드를 정의하고 후자는 void를 출력하는 비동기 메서드를 정의합니다. 또한 이러한 인터페이스를 사용하면 클라이언트 코드가 작업 진행률에 대한 업데이트를 받을 수 있습니다.

public:

        // Synchronous method.
        Windows::Foundation::Collections::IVector<double>^  ComputeResult(double input);

        // Asynchronous methods
        Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^
            GetPrimesOrdered(int first, int last);
        Windows::Foundation::IAsyncActionWithProgress<double>^ GetPrimesUnordered(int first, int last);

        // Event whose type is a delegate "class"
        event PrimeFoundHandler^ primeFoundEvent;

프라이벳 맴버 추가하기

클래스에는 숫자 계산을 위한 두 개의 도우미 메서드와 작업자 스레드에서 UI 스레드로 이벤트 호출을 다시 마샬링하는 데 사용되는 CoreDispatcher 개체의 세 가지 프라이빗 멤버가 포함됩니다.

private:
        bool is_prime(int n);
        Windows::UI::Core::CoreDispatcher^ m_dispatcher;

헤더 및 네임스페이스 지시문 추가하기

  1. Class1.cpp 다음 #include 지시문을 추가합니다.
#include <ppltasks.h>
#include <concurrent_vector.h>
  1. 이제 다음 using 문을 추가하여 필요한 네임스페이스를 가져옵니다.
using namespace concurrency;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;

ComputeResult 구현 추가하기

Class1.cpp에 다음 메서드 구현을 추가합니다. 이 메서드는 호출 스레드에서 동기적으로 실행되지만 C++ AMP를 사용하여 GPU에서 계산을 병렬화하기 때문에 매우 빠릅니다. 자세한 내용은 C++ AMP 개요를 참조하세요. 결과는 반환 시 Windows::Foundation::Collections::IVector<T>로 암시적으로 변환되는 Platform::Collections::Vector<T> 구체적 형식에 추가됩니다.

//Public API
IVector<double>^ Class1::ComputeResult(double input)
{
    // Implement your function in ISO C++ or
    // call into your C++ lib or DLL here. This example uses AMP.
    float numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 10000.0 };
    array_view<float, 1> logs(6, numbers);

    // See http://msdn.microsoft.com/library/hh305254.aspx
    parallel_for_each(
        logs.extent,
        [=] (index<1> idx) restrict(amp)
    {
        logs[idx] = concurrency::fast_math::log10(logs[idx]);
    }
    );

    // Return a Windows Runtime-compatible type across the ABI
    auto res = ref new Vector<double>();
    int len = safe_cast<int>(logs.extent.size());
    for(int i = 0; i < len; i++)
    {      
        res->Append(logs[i]);
    }

    // res is implicitly cast to IVector<double>
    return res;
}

GetPrimesOrdered 및 해당 도우미 메서드에 대한 구현 추가하기

Class1.cpp에 GetPrimesOrdered에 대한 구현과 is_prime 도우미 메서드를 추가합니다. GetPrimesOrdered는 concurrent_vector 클래스와 parallel_for 함수 루프를 사용하여 작업을 분할하고 프로그램이 실행 중인 컴퓨터의 최대 리소스를 사용하여 결과를 생성합니다. 결과가 계산, 저장 및 정렬된 후 Platform::Collections::Vector<T>에 추가되며 클라이언트 코드에 Windows::Foundation::Collections::IVector<T>로 반환됩니다.

진행률 보고자의 코드를 확인하세요. 클라이언트가 진행률 표시줄 또는 다른 UI를 연결하여 사용자에게 작업이 얼마나 오래 걸리는지 표시할 수 있습니다. 진행률 보고에는 대가가 있습니다. 구성 요소 쪽에서 이벤트를 발생시키고 UI 스레드에서 처리해야 하며 진행률 값은 각각 시행시 저장되어야 합니다. 단점을 줄이는 한 가지 방법은 진행률 이벤트가 발생하는 빈도를 제한하는 것입니다. 여전히 부하가 걸리거나 작업 길이를 예측할 수 없는 경우 작업이 진행 중이지만 완료될 때까지 기본 시간을 표시하지 않는 진행률 고리를 사용하는 것이 좋습니다.

// Determines whether the input value is prime.
bool Class1::is_prime(int n)
{
    if (n < 2)
        return false;
    for (int i = 2; i < n; ++i)
    {
        if ((n % i) == 0)
            return false;
    }
    return true;
}

// This method computes all primes, orders them, then returns the ordered results.
IAsyncOperationWithProgress<IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last)
{
    return create_async([this, first, last]
    (progress_reporter<double> reporter) -> IVector<int>^ {
        // Ensure that the input values are in range.
        if (first < 0 || last < 0) {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        concurrent_vector<int> primes;
        long operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;

        parallel_for(first, last + 1, [this, &primes, &operation,
            range, &lastPercent, reporter](int n) {

                // Increment and store the number of times the parallel
                // loop has been called on all threads combined. There
                // is a performance cost to maintaining a count, and
                // passing the delegate back to the UI thread, but it's
                // necessary if we want to display a determinate progress
                // bar that goes from 0 to 100%. We can avoid the cost by
                // setting the ProgressBar IsDeterminate property to false
                // or by using a ProgressRing.
                if(InterlockedIncrement(&operation) % 100 == 0)
                {
                    reporter.report(100.0 * operation / range);
                }

                // If the value is prime, add it to the local vector.
                if (is_prime(n)) {
                    primes.push_back(n);
                }
        });

        // Sort the results.
        std::sort(begin(primes), end(primes), std::less<int>());		
        reporter.report(100.0);

        // Copy the results to a Vector object, which is
        // implicitly converted to the IVector return type. IVector
        // makes collections of data available to other
        // Windows Runtime components.
        return ref new Vector<int>(primes.begin(), primes.end());
    });
}

GetPrimesUnordered에 대한 구현 추가하기

C++ 구성 요소를 만드는 마지막 단계는 Class1.cpp GetPrimesUnordered에 대한 구현을 추가하는 것입니다. 이 메서드는 모든 결과를 찾을 때까지 기다리지 않고 각 결과를 찾으면 즉시 출력합니다. 각 결과는 이벤트 처리기에서 출력되고 실시간으로 UI에 표시됩니다. 다시 한 번 진행률 보고자가 사용됨을 확인하세요. 이 메서드는 is_prime 도우미 메서드도 사용합니다.

// This method returns no value. Instead, it fires an event each time a
// prime is found, and passes the prime through the event.
// It also passes progress info.
IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last)
{

    auto window = Windows::UI::Core::CoreWindow::GetForCurrentThread();
    m_dispatcher = window->Dispatcher;


    return create_async([this, first, last](progress_reporter<double> reporter) {

        // Ensure that the input values are in range.
        if (first < 0 || last < 0) {
            throw ref new InvalidArgumentException();
        }

        // In this particular example, we don't actually use this to store
        // results since we pass results one at a time directly back to
        // UI as they are found. However, we have to provide this variable
        // as a parameter to parallel_for.
        concurrent_vector<int> primes;
        long operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;

        // Perform the computation in parallel.
        parallel_for(first, last + 1,
            [this, &primes, &operation, range, &lastPercent, reporter](int n)
        {
            // Store the number of times the parallel loop has been called  
            // on all threads combined. See comment in previous method.
            if(InterlockedIncrement(&operation) % 100 == 0)
            {
                reporter.report(100.0 * operation / range);
            }

            // If the value is prime, pass it immediately to the UI thread.
            if (is_prime(n))
            {                
                // Since this code is probably running on a worker
                // thread, and we are passing the data back to the
                // UI thread, we have to use a CoreDispatcher object.
                m_dispatcher->RunAsync( CoreDispatcherPriority::Normal,
                    ref new DispatchedHandler([this, n, operation, range]()
                {
                    this->primeFoundEvent(n);

                }, Platform::CallbackContext::Any));

            }
        });
        reporter.report(100.0);
    });
}

JavaScript 클라이언트 앱 만들기(Visual Studio 2017)

C# 클라이언트를 만들려는 경우 이 섹션을 건너뛸 수 있습니다.

참고 항목

Visual Studio 2019에서는 JavaScript를 사용하는 UWP(유니버설 Windows 플랫폼) 프로젝트가 지원되지 않습니다. Visual Studio 2019의 JavaScript 및 TypeScript를 참조하세요. 이 섹션을 따라 작업하려면 Visual Studio 2017을 사용하는 것이 좋습니다. Visual Studio 2017의 JavaScript를 참조하세요.

JavaScript 프로젝트 생성하기

  1. 솔루션 탐색기(Visual Studio 2017에서는 위의 참고 사항 참조)에서 솔루션 노드에 대한 바로 가기 메뉴를 열고 추가, 새 프로젝트를 선택합니다.

  2. JavaScript(다른 언어 아래 중첩될 수 있음)를 확장하고 빈 앱 (유니버설 Windows)을 선택합니다.

  3. 확인 버튼을 선택해서 기본 이름으로 설정하세요.

  4. App1 프로젝트 노드에 대한 바로 가기 메뉴를 열고 스타트업 프로젝트로 설정하기를 선택합니다.

  5. WinRT_CPP에 프로젝트 참조를 추가합니다:

  6. 레퍼런스 노드에 대한 바로 가기 메뉴를 열고 레퍼런스 추가를 선택합니다.

  7. 참조 관리자 대화 상자의 왼편에서 프로젝트 그리고 다음으로 솔루션을 선택합니다.

  8. 가운데 창에서 WinRT_CPP를 선택한 다음 확인 버튼을 선택합니다.

JavaScript 이벤트 처리기를 호출하는 HTML 추가하기

default.html 페이지의 <body> 노드에 이 HTML을 붙여넣습니다.

<div id="LogButtonDiv">
     <button id="logButton">Logarithms using AMP</button>
 </div>
 <div id="LogResultDiv">
     <p id="logResult"></p>
 </div>
 <div id="OrderedPrimeButtonDiv">
     <button id="orderedPrimeButton">Primes using parallel_for with sort</button>
 </div>
 <div id="OrderedPrimeProgress">
     <progress id="OrderedPrimesProgressBar" value="0" max="100"></progress>
 </div>
 <div id="OrderedPrimeResultDiv">
     <p id="orderedPrimes">
         Primes found (ordered):
     </p>
 </div>
 <div id="UnorderedPrimeButtonDiv">
     <button id="ButtonUnordered">Primes returned as they are produced.</button>
 </div>
 <div id="UnorderedPrimeDiv">
     <progress id="UnorderedPrimesProgressBar" value="0" max="100"></progress>
 </div>
 <div id="UnorderedPrime">
     <p id="unorderedPrimes">
         Primes found (unordered):
     </p>
 </div>
 <div id="ClearDiv">
     <button id="Button_Clear">Clear</button>
 </div>

스타일 추가하기

default.css에 본문 스타일을 제거한 다음, 다음 스타일을 추가합니다.

#LogButtonDiv {
border: orange solid 1px;
-ms-grid-row: 1; /* default is 1 */
-ms-grid-column: 1; /* default is 1 */
}
#LogResultDiv {
background: black;
border: red solid 1px;
-ms-grid-row: 1;
-ms-grid-column: 2;
}
#UnorderedPrimeButtonDiv, #OrderedPrimeButtonDiv {
border: orange solid 1px;
-ms-grid-row: 2;   
-ms-grid-column:1;
}
#UnorderedPrimeProgress, #OrderedPrimeProgress {
border: red solid 1px;
-ms-grid-column-span: 2;
height: 40px;
}
#UnorderedPrimeResult, #OrderedPrimeResult {
border: red solid 1px;
font-size:smaller;
-ms-grid-row: 2;
-ms-grid-column: 3;
-ms-overflow-style:scrollbar;
}

구성 요소 DLL에 호출하는 JavaScript 이벤트 처리기 추가하기

default.js 파일의 끝에 다음 함수를 추가합니다. 이러한 함수는 기본 페이지의 버튼이 선택될 때 호출됩니다. JavaScript가 어떻게 C++ 클래스를 활성화하고 다음 해당 메서드를 호출하고 출력 값을 사용하여 HTML 레이블을 채우는지 확인하세요.

var nativeObject = new WinRT_CPP.Class1();

function LogButton_Click() {

    var val = nativeObject.computeResult(0);
    var result = "";

    for (i = 0; i < val.length; i++) {
        result += val[i] + "<br/>";
    }

    document.getElementById('logResult').innerHTML = result;
}

function ButtonOrdered_Click() {
    document.getElementById('orderedPrimes').innerHTML = "Primes found (ordered): ";

    nativeObject.getPrimesOrdered(2, 10000).then(
        function (v) {
            for (var i = 0; i < v.length; i++)
                document.getElementById('orderedPrimes').innerHTML += v[i] + " ";
        },
        function (error) {
            document.getElementById('orderedPrimes').innerHTML += " " + error.description;
        },
        function (p) {
            var progressBar = document.getElementById("OrderedPrimesProgressBar");
            progressBar.value = p;
        });
}

function ButtonUnordered_Click() {
    document.getElementById('unorderedPrimes').innerHTML = "Primes found (unordered): ";
    nativeObject.onprimefoundevent = handler_unordered;

    nativeObject.getPrimesUnordered(2, 10000).then(
        function () { },
        function (error) {
            document.getElementById("unorderedPrimes").innerHTML += " " + error.description;
        },
        function (p) {
            var progressBar = document.getElementById("UnorderedPrimesProgressBar");
            progressBar.value = p;
        });
}

var handler_unordered = function (n) {
    document.getElementById('unorderedPrimes').innerHTML += n.target.toString() + " ";
};

function ButtonClear_Click() {

    document.getElementById('logResult').innerHTML = "";
    document.getElementById("unorderedPrimes").innerHTML = "";
    document.getElementById('orderedPrimes').innerHTML = "";
    document.getElementById("UnorderedPrimesProgressBar").value = 0;
    document.getElementById("OrderedPrimesProgressBar").value = 0;
}

default.js app.onactivated의 WinJS.UI.processAll에 대한 기존 호출을 다음 블록에서 이벤트 등록을 구현하는 다음 코드로 바꿔 이벤트 수신기를 추가하는 코드를 추가합니다. 자세한 내용은 “Hello World” 앱 만들기(JS)를 참조하세요.

args.setPromise(WinJS.UI.processAll().then( function completed() {
    var logButton = document.getElementById("logButton");
    logButton.addEventListener("click", LogButton_Click, false);
    var orderedPrimeButton = document.getElementById("orderedPrimeButton");
    orderedPrimeButton.addEventListener("click", ButtonOrdered_Click, false);
    var buttonUnordered = document.getElementById("ButtonUnordered");
    buttonUnordered.addEventListener("click", ButtonUnordered_Click, false);
    var buttonClear = document.getElementById("Button_Clear");
    buttonClear.addEventListener("click", ButtonClear_Click, false);
}));

F5 키를 눌러 앱을 실행합니다.

C# 클라이언트 앱 생성

C# 프로젝트 만들기

  1. 솔루션 탐색기에서 은행 솔루션 노드의 바로가기 메뉴를 열고 신규 프로잭트 추가를 선택합니다.

  2. Visual C#를 확장하여(다른 언어 아래 중첩되있을 수 있음), Windows 를 선택하고 왼편의 유니버설을 선택해 가운대 있는 빈 앱을 선택합니다.

  3. 이 앱의 이름을 CS_Client로 정하고 확인 버튼을 선택합니다.

  4. CS_클라이언트 프로젝트 노드의 바로가기를 열고 스타트업 프로젝트로 설정을 선택합니다.

  5. WinRT_CPP에 프로젝트 참조를 추가합니다:

    • 레퍼런스 노드의 바로가기 메뉴를 열고 레퍼런스 추가를 선택합니다.

    • 래퍼런스 관리자 대화 상자의 왼편에서, 프로젝트를 선택하고 솔루션을 선택합니다.

    • 창 가운대에서 WinRT_CPP를 선택한 다음 확인 버튼을 선택합니다.

사용자 인터페이스를 정의하는 XAML을 추가하기

MainPage.xaml의 Grid 요소에 다음 코드를 복사합니다.

<ScrollViewer>
            <StackPanel Width="1400">

                <Button x:Name="Button1" Width="340" Height="50"  Margin="0,20,20,20" Content="Synchronous Logarithm Calculation" FontSize="16" Click="Button1_Click_1"/>
                <TextBlock x:Name="Result1" Height="100" FontSize="14"></TextBlock>
            <Button x:Name="PrimesOrderedButton" Content="Prime Numbers Ordered" FontSize="16" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesOrderedButton_Click_1"></Button>
            <ProgressBar x:Name="PrimesOrderedProgress" IsIndeterminate="false" Height="40"></ProgressBar>
                <TextBlock x:Name="PrimesOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock>
            <Button x:Name="PrimesUnOrderedButton" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesUnOrderedButton_Click_1" Content="Prime Numbers Unordered" FontSize="16"></Button>
            <ProgressBar x:Name="PrimesUnOrderedProgress" IsIndeterminate="false" Height="40" ></ProgressBar>
            <TextBlock x:Name="PrimesUnOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock>

            <Button x:Name="Clear_Button" Content="Clear" HorizontalAlignment="Left" Margin="0,20,20,20" VerticalAlignment="Top" Width="341" Click="Clear_Button_Click" FontSize="16"/>
        </StackPanel>
</ScrollViewer>

버튼에 대한 이벤트 처리기 추가하기

솔루션 탐색기에서 MainPage.xaml을 엽니다. (파일은 MainPage.xaml에 중첩되어 있을 수 있습니다.) System.Text에 대한 using 지시문을 추가한 다음 MainPage 클래스에서 로그 계산에 대한 이벤트 처리기를 추가합니다.

private void Button1_Click_1(object sender, RoutedEventArgs e)
{
    // Create the object
    var nativeObject = new WinRT_CPP.Class1();

    // Call the synchronous method. val is an IList that
    // contains the results.
    var val = nativeObject.ComputeResult(0);
    StringBuilder result = new StringBuilder();
    foreach (var v in val)
    {
        result.Append(v).Append(System.Environment.NewLine);
    }
    this.Result1.Text = result.ToString();
}

정렬된 결과에 대한 이벤트 처리기를 추가합니다.

async private void PrimesOrderedButton_Click_1(object sender, RoutedEventArgs e)
{
    var nativeObject = new WinRT_CPP.Class1();

    StringBuilder sb = new StringBuilder();
    sb.Append("Primes found (ordered): ");

    PrimesOrderedResult.Text = sb.ToString();

    // Call the asynchronous method
    var asyncOp = nativeObject.GetPrimesOrdered(2, 100000);

    // Before awaiting, provide a lambda or named method
    // to handle the Progress event that is fired at regular
    // intervals by the asyncOp object. This handler updates
    // the progress bar in the UI.
    asyncOp.Progress = (asyncInfo, progress) =>
        {
            PrimesOrderedProgress.Value = progress;
        };

    // Wait for the operation to complete
    var asyncResult = await asyncOp;

    // Convert the results to strings
    foreach (var result in asyncResult)
    {
        sb.Append(result).Append(" ");
    }

    // Display the results
    PrimesOrderedResult.Text = sb.ToString();
}

정렬되지 않은 결과 및 코드를 다시 실행할 수 있도록 결과값을 지우는 버튼에 대한 이벤트 처리기를 추가합니다.

private void PrimesUnOrderedButton_Click_1(object sender, RoutedEventArgs e)
{
    var nativeObject = new WinRT_CPP.Class1();

    StringBuilder sb = new StringBuilder();
    sb.Append("Primes found (unordered): ");
    PrimesUnOrderedResult.Text = sb.ToString();

    // primeFoundEvent is a user-defined event in nativeObject
    // It passes the results back to this thread as they are produced
    // and the event handler that we define here immediately displays them.
    nativeObject.primeFoundEvent += (n) =>
    {
        sb.Append(n.ToString()).Append(" ");
        PrimesUnOrderedResult.Text = sb.ToString();
    };

    // Call the async method.
    var asyncResult = nativeObject.GetPrimesUnordered(2, 100000);

    // Provide a handler for the Progress event that the asyncResult
    // object fires at regular intervals. This handler updates the progress bar.
    asyncResult.Progress += (asyncInfo, progress) =>
        {
            PrimesUnOrderedProgress.Value = progress;
        };
}

private void Clear_Button_Click(object sender, RoutedEventArgs e)
{
    PrimesOrderedProgress.Value = 0;
    PrimesUnOrderedProgress.Value = 0;
    PrimesUnOrderedResult.Text = "";
    PrimesOrderedResult.Text = "";
    Result1.Text = "";
}

앱 실행하기

솔루션 탐색기 프로젝트 노드의 바로 가기 메뉴를 열고 스타트업 프로젝트로 설정을 선택하여 C# 프로젝트 또는 JavaScript 프로젝트를 스타트업 프로젝트로 선택합니다 F5 키를 눌러 디버깅 실행 상태로 앱을 실행하거나 Ctrl+F5를 눌러 디버깅 없이 실행합니다.

개체 브라우저에서 구성 요소 검사(선택 사항)

개체 브라우저에서 .winmd 파일에 정의된 모든 Windows 런타임 형식을 검사할 수 있습니다. 여기에는 플랫폼 네임스페이스의 형식과 기본 네임스페이스가 포함됩니다. 그러나 Platform::Collections 네임스페이스의 형식은 winmd 파일이 아니라 header file collections.h에 정의되어 있으므로 개체 브라우저에 표시되지 않습니다.

구성 요소를 검사하려면

  1. 메뉴 바에서 개체 브라우저 보기 (Ctrl+Alt+J)를 선택합니다.

  2. 개체 브라우저의 왼편에서 WinRT_CPP 노드를 확장하여 구성 요소에 정의된 형식과 메서드를 표시합니다.

디버깅 팁

디버깅 환경을 개선하려면 공용 Microsoft 심볼 서버에서 디버깅 심볼을 다운로드합니다.

디버깅 기호를 다운로드하려면

  1. 메뉴 모음에서 도구, 옵션을 선택합니다.

  2. 옵션 대화 상자에서 디버깅을 확장하고 심볼을 선택합니다.

  3. Microsoft 심볼 서버를 선택하고 확인 버튼을 선택합니다.

심볼을 처음 다운로드하는 데는 다소 시간이 걸릴 수 있습니다. 더 빠른 성능을 위해 다음에 F5 키를 누를 때 심볼을 캐시할 로컬 디렉터리를 지정합니다.

구성 요소 DLL이 있는 JavaScript 솔루션을 디버그할 때, 디버거가 스크립트를 단계별로 또는 구성 요소의 네이티브 코드를 단계별로 실행하도록 설정할 수 있지만 동시에 둘 다 실행하도록 할 수 없습니다. 설정을 변경하려면 솔루션 탐색기에서 JavaScript 프로젝트 노드를 선택한 다음 속성, 디버깅, 디버거 형식을 선택합니다.

패키지 디자이너에서 적절한 기능을 선택해야 합니다. Package.appxmanifest 파일을 열어 패키지 디자이너를 열 수 있습니다. 예를 들어 사진 폴더의 파일에 프로그래밍 방식으로 액세스하려는 경우 패키지 디자이너에서 사진 라이브러리 체크박스를 선택해야 합니다. 체크박스는 기능 창에 있습니다.

JavaScript 코드가 구성 요소의 public 속성 또는 메서드를 인식하지 못하는 경우 JavaScript에서 카멜식 대/소문자 표기를 사용하고 있는지 확인합니다. 예를 들어 ComputeResult C++ 메서드는 JavaScript의 computeResult 와 같이 참조되어야 합니다.

솔루션에서 C++/CX Windows 런타임 구성 요소 프로젝트를 제거하는 경우 JavaScript 프로젝트에서 프로젝트 레퍼런스를 수동으로 제거해야 합니다. 이렇게 하지 않으면 이후 디버그 또는 빌드 작업을 수행할 수 없습니다. 필요한 경우 DLL에 대한 어셈블리 참조를 추가할 수 있습니다.