Windows 런타임 구성 요소에서 이벤트 발생Raising events in Windows Runtime components

참고

C + +/winrt Windows 런타임 구성 요소에서 이벤트를 발생 시키는 방법에 대 한 자세한 내용은 c + +/Winrt의 Author 이벤트를 참조 하세요.For more info about raising events in a C++/WinRT Windows Runtime Component, see Author events in C++/WinRT.

Windows 런타임 구성 요소가 백그라운드 스레드 (작업자 스레드)에서 사용자 정의 대리자 형식의 이벤트를 발생 시키고 JavaScript가 이벤트를 받을 수 있도록 하려는 경우 이러한 방법 중 하나를 사용 하 여 구현 하거나 생성할 수 있습니다.If your Windows Runtime component raises an event of a user-defined delegate type on a background thread (worker thread), and you want JavaScript to be able to receive the event, then you can implement and/or raise it in any one of these ways.

  • (옵션 1) 이벤트를 JavaScript 스레드 컨텍스트로 마샬링하기 위해 CoreDispatcher 를 통해 이벤트를 발생 시킵니다.(Option 1) Raise the event through the Windows.UI.Core.CoreDispatcher to marshal the event to the JavaScript thread context. 일반적으로 이 방법이 최선의 옵션이지만 이 방법을 사용해도 가장 빠른 성능이 제공되지 않는 경우도 있습니다.Although typically this is the best option, in some scenarios it might not provide the fastest performance.
  • (옵션 2) ** Windows 기반 처리기 <Object> ** 를 사용 합니다 (이벤트 유형 정보는 손실 되지 않음).(Option 2) Use Windows.Foundation.EventHandler<Object> (but lose the event type information). 옵션 1을 사용할 수 없거나 성능이 적절 하지 않은 경우 형식 정보 손실이 허용 되는 경우이 옵션을 선택 하는 것이 좋습니다.If Option 1 is not feasible, or if its performance is not adequate, then this is a good second choice provided that loss of type information is acceptable. C # Windows 런타임 구성 요소를 작성 하는 경우에는 **Windows <Object> ** 를 사용할 수 없습니다. 대신 해당 형식이 system.object로 프로젝션 되므로이를 대신 사용 해야 합니다.If you're authoring a C# Windows Runtime Component, then the Windows.Foundation.EventHandler<Object> type is not available; instead, that type is projected to System.EventHandler, so you should use that instead.
  • (옵션 3) 구성 요소에 대 한 고유한 프록시 및 스텁을 만듭니다.(Option 3) Create your own proxy and stub for the component. 이 옵션은 구현하기가 가장 어렵지만 형식 정보를 유지하며, 요구되는 시나리오에서 옵션 1에 비해 더 나은 성능을 제공할 수 있습니다.This option is the most difficult to implement, but it preserves type information and might provide better performance compared to Option 1 in demanding scenarios.

이러한 옵션 중 하나를 사용하지 않고 백그라운드 스레드에서 이벤트를 발생시키면 JavaScript 클라이언트가 이벤트를 받지 못합니다.If you just raise an event on a background thread without using one of these options, a JavaScript client will not receive the event.

배경Background

모든 Windows 런타임 구성 요소와 앱은 만들 때 사용한 언어에 관계없이 기본적으로 COM 개체입니다.All Windows Runtime components and apps are fundamentally COM objects, no matter what language you use to create them. Windows API에서 대부분의 구성 요소는 백그라운드 스레드 및 UI 스레드의 개체와 동일하게 제대로 통신할 수 있는 Agile COM 개체입니다.In the Windows API, most of the components are agile COM objects that can communicate equally well with objects on the background thread and on the UI thread. COM 개체를 Agile 개체로 만들 수 없는 경우에는 UI 스레드 백그라운드 스레드 경계의 다른 COM 개체와 통신하기 위해서는 프록시 및 스텁이라는 도우미 개체가 필요합니다.If a COM object can’t be made agile, then it requires helper objects known as proxies and stubs to communicate with other COM objects across the UI thread-background thread boundary. COM 용어로는 이를 스레드 아파트 간의 통신이라고 합니다.(In COM terms, this is known as communication between thread apartments.)

Windows API에서 대부분의 개체는 Agile 개체이거나 기본 제공되는 프록시 및 스텁을 가지고 있습니다.Most of the objects in the Windows API are either agile or have proxies and stubs built in. 그러나 Windows.Foundation.TypedEventHandler<TSender, TResult> 같은 제네릭 형식의 경우 형식 인수를 제공할 때까지 완전한 형식이 아니기 때문에 해당 프록시 및 스텁을 만들 수 없습니다.However, proxies and stubs can’t be created for generic types such as Windows.Foundation.TypedEventHandler<TSender, TResult> because they are not complete types until you provide the type argument. JavaScript 클라이언트에서만 프록시나 스텁이 부족한 것이 문제가 되지만 JavaScript뿐 아니라 C++ 또는 .NET 언어에서 구성 요소를 사용할 수 있도록 하려면 다음 세 옵션 중 하나를 사용해야 합니다.It's only with JavaScript clients that the lack of proxies or stubs becomes an issue, but if you want your component to be usable from JavaScript as well as from C++ or a .NET language, then you must use one of the following three options.

(옵션 1) CoreDispatcher를 통해 이벤트 발생(Option 1) Raise the event through the CoreDispatcher

CoreDispatcher를 사용 하 여 사용자 정의 대리자 형식의 이벤트를 보낼 수 있으며 JavaScript는 이러한 이벤트를 받을 수 있습니다.You can send events of any user-defined delegate type by using the Windows.UI.Core.CoreDispatcher, and JavaScript will be able to receive them. 어떤 옵션을 사용해야 할지 잘 모를 경우 먼저 이 옵션을 사용해 보세요.If you are unsure which option to use, try this one first. 이벤트 발생과 이벤트 처리 간 대기 시간이 문제가 되는 경우에는 다른 옵션 중 하나를 사용해 보십시오.If latency between the event firing and the event handling becomes an issue, then try one of the other options.

다음 예제에서는 CoreDispatcher를 사용하여 강력한 형식의 이벤트를 발생시키는 방법을 보여 줍니다.The following example shows how to use the CoreDispatcher to raise a strongly-typed event. 형식 인수는 개체가 아니라 알림입니다.Notice that the type argument is Toast, not Object.

public event EventHandler<Toast> ToastCompletedEvent;
private void OnToastCompleted(Toast args)
{
    var completedEvent = ToastCompletedEvent;
    if (completedEvent != null)
    {
        completedEvent(this, args);
    }
}

public void MakeToastWithDispatcher(string message)
{
    Toast toast = new Toast(message);
    // Assume you have a CoreDispatcher at class scope.
    // Initialize it here, then use it from the background thread.
    var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
    m_dispatcher = window.Dispatcher;

    Task.Run( () =>
    {
        if (ToastCompletedEvent != null)
        {
            m_dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            new DispatchedHandler(() =>
            {
                this.OnToastCompleted(toast);
            })); // end m_dispatcher.RunAsync
         }
     }); // end Task.Run
}

(옵션 2) EventHandler 개체를 사용 < > 하지만 형식 정보를 손실 합니다.(Option 2) Use EventHandler<Object> but lose type information

참고

C # Windows 런타임 구성 요소를 작성 하는 경우에는 **Windows <Object> ** 를 사용할 수 없습니다. 대신 해당 형식이 system.object로 프로젝션 되므로이를 대신 사용 해야 합니다.If you're authoring a C# Windows Runtime Component, then the Windows.Foundation.EventHandler<Object> type is not available; instead, that type is projected to System.EventHandler, so you should use that instead.

백그라운드 스레드에서 이벤트를 보내는 또 다른 방법은 Windows.Foundation.EventHandler < > 이벤트의 형식으로 Windows를 사용 하는 것입니다.Another way to send an event from a background thread is to use Windows.Foundation.EventHandler<Object> as the type of the event. Windows에서는 이러한 제네릭 형식의 구체적인 인스턴스화를 제공하며 이를 위한 프록시 및 스텁을 제공합니다.Windows provides this concrete instantiation of the generic type and provides a proxy and stub for it. 단점은 이벤트 인수 및 전송자의 형식 정보가 손실된다는 점입니다.The downside is that the type information of your event args and sender is lost. C++ 및 .NET 클라이언트는 이벤트를 받았을 때 어떤 형식을 캐스팅해야 할지 설명서를 통해 알아야 합니다.C++ and .NET clients must know through documentation what type to cast back to when the event is received. JavaScript 클라이언트에는 원래 형식 정보가 필요하지 않습니다.JavaScript clients don’t need the original type information. JavaScript 클라이언트는 메타데이터에 있는 이름을 기반으로 인수 속성을 찾습니다.They find the arg properties, based on their names in the metadata.

이 예제에서는 < > c #에서 다음과 같이 Windows를 사용 하는 방법을 보여 줍니다.This example shows how to use Windows.Foundation.EventHandler<Object> in C#:

public sealed Class1
{
// Declare the event
public event EventHandler<Object> ToastCompletedEvent;

    // Raise the event
    public async void MakeToast(string message)
    {
        Toast toast = new Toast(message);
        // Fire the event from a background thread to allow this thread to continue
        Task.Run(() =>
        {
            if (ToastCompletedEvent != null)
            {
                OnToastCompleted(toast);
            }
        });
    }

    private void OnToastCompleted(Toast args)
    {
        var completedEvent = ToastCompletedEvent;
        if (completedEvent != null)
        {
            completedEvent(this, args);
        }
    }
}

다음과 같이 JavaScript 쪽에서 이 이벤트를 사용합니다.You consume this event on the JavaScript side like this:

toastCompletedEventHandler: function (event) {
   var toastType = event.toast.toastType;
   document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>";
}

(옵션 3) 사용자 고유 프록시 및 스텁 만들기(Option 3) Create your own proxy and stub

완전히 유지되는 형식 정보가 있는 사용자 정의 이벤트 형식에서 잠재적인 성능 향상 이점을 얻으려면 사용자 고유 프록시 및 스텁 개체를 만들어 앱 패키지에 포함해야 합니다.For potential performance gains on user-defined event types that have fully-preserved type information, you have to create your own proxy and stub objects and embed them in your app package. 일반적으로 드문 경우지만 다른 두 옵션 모두 적절하지 않은 경우에만 이 옵션을 사용해야 합니다.Typically, you have to use this option only in rare situations where neither of the other two options are adequate. 또한 이 옵션을 사용할 경우 다른 두 옵션을 사용할 때보다 성능이 더 낫다는 보장이 없습니다.Also, there is no guarantee that this option will provide better performance than the other two options. 실제 성능은 여러 요인에 따라 달라집니다.Actual performance depends on many factors. Visual Studio 프로파일러 또는 기타 프로파일링 도구를 사용하여 실제 응용 프로그램 성능을 측정하고 이벤트에 병목 현상이 있는지 여부를 확인합니다.Use the Visual Studio profiler or other profiling tools to measure actual performance in your application and determine whether the event is in fact a bottleneck.

이 문서의 나머지 부분에서는 c #을 사용 하 여 기본 Windows 런타임 구성 요소를 만든 다음 c + +를 사용 하 여 JavaScript가 < > 비동기 작업에서 구성 요소에 의해 발생 하는 Windows Foundation. TypedEventHandler tsender, TResult 이벤트를 사용할 수 있도록 하는 프록시 및 스텁을 위한 DLL을 만드는 방법을 보여 줍니다.The rest of this article shows how to use C# to create a basic Windows Runtime component, and then use C++ to create a DLL for the proxy and stub that will enable JavaScript to consume a Windows.Foundation.TypedEventHandler<TSender, TResult> event that's raised by the component in an async operation. C++ 또는 Visual Basic을 사용하여 구성 요소를 만들 수도 있습니다.(You can also use C++ or Visual Basic to create the component. 프록시 및 스텁 만들기와 관련 된 단계는 동일 합니다. 이 연습은 Windows 런타임 in-process 구성 요소 샘플 만들기 (c + +/CX)를 기반으로 하며 용도를 설명 하는 데 도움이 됩니다.The steps that are related to creating the proxies and stubs are the same.) This walkthrough is based on Creating a Windows Runtime in-process component sample (C++/CX) and helps explain its purposes.

이 연습에는 이러한 부분이 있습니다.This walkthrough has these parts.

  • 여기서는 두 가지 기본 Windows 런타임 클래스를 만듭니다.Here you will create two basic Windows Runtime classes. 한 클래스는 TValue 형식의 이벤트를 노출 하 고, 다른 클래스는 JavaScript에 반환 되는 형식으로 서 < , > JavaScript로 반환 됩니다.One class exposes an event of type Windows.Foundation.TypedEventHandler<TSender, TResult> and the other class is the type that's returned to JavaScript as the argument for TValue. 이후 단계를 완료할 때까지 이러한 클래스는 JavaScript와 통신할 수 없습니다.These classes can't communicate with JavaScript until you complete the later steps.
  • 이 앱은 기본 클래스 개체를 활성화 하 고, 메서드를 호출 하 고, Windows 런타임 구성 요소에 의해 발생 하는 이벤트를 처리 합니다.This app activates the main class object, calls a method, and handles an event that's raised by the Windows Runtime component.
  • 프록시 및 스텁 클래스를 생성 하는 도구에 필요 합니다.These are required by the tools that generate the proxy and stub classes.
  • 그런 다음 IDL 파일을 사용 하 여 프록시 및 스텁 용 C 소스 코드를 생성 합니다.You then use the IDL file to generate the C source code for the proxy and stub.
  • COM 런타임이 해당 개체를 찾을 수 있도록 프록시-스텁 개체를 등록 하 고 앱 프로젝트에서 프록시-스텁 DLL을 참조할 수 있습니다.Register the proxy-stub objects so that the COM runtime can find them, and reference the proxy-stub DLL in the app project.

Windows 런타임 구성 요소를 만들려면To create the Windows Runtime component

Visual Studio의 메뉴 모음에서 파일 > 새로 만들기 프로젝트를 선택 합니다.In Visual Studio, on the menu bar, choose File > New Project. 새 프로젝트 대화 상자에서 JavaScript > 유니버설 Windows 를 확장 한 다음 새 을 선택 합니다.In the New Project dialog box, expand JavaScript > Universal Windows and then select Blank App. 프로젝트 이름을 ToasterApplication로 지정한 다음 확인 단추를 선택 합니다.Name the project ToasterApplication and then choose the OK button.

C # Windows 런타임 구성 요소를 솔루션에 추가 합니다. 솔루션 탐색기에서 솔루션에 대 한 바로 가기 메뉴를 열고 ** > 새 프로젝트 추가**를 선택 합니다.Add a C# Windows Runtime component to the solution: In Solution Explorer, open the shortcut menu for the solution and then choose Add > New Project. Visual c # > Microsoft Store 를 확장 한 다음 Windows 런타임 구성 요소를 선택 합니다.Expand Visual C# > Microsoft Store and then select Windows Runtime Component. 프로젝트 이름을 ToasterComponent로 지정한 다음 확인 단추를 선택 합니다.Name the project ToasterComponent and then choose the OK button. ToasterComponent는 이후 단계에서 만들 구성 요소의 루트 네임스페이스가 됩니다.ToasterComponent will be the root namespace for the components you will create in later steps.

솔루션 탐색기에서 솔루션에 대 한 바로 가기 메뉴를 열고 속성을 선택 합니다.In Solution Explorer, open the shortcut menu for the solution and then choose Properties. 속성 페이지 대화 상자의 왼쪽 창에서 구성 속성 을 선택한 다음 대화 상자의 위쪽에서 구성디버그 로 설정 하 고 플랫폼 을 x86, x64 또는 ARM으로 설정 합니다.In the Property Pages dialog box, select Configuration Properties in the left pane, and then at the top of the dialog box, set Configuration to Debug and Platform to x86, x64, or ARM. 확인 단추를 선택합니다.Choose the OK button.

중요   Platform = 모든 CPU는 나중에 솔루션에 추가할 네이티브 코드 Win32 DLL에 사용할 수 없으므로 작동 하지 않습니다.Important Platform = Any CPU won’t work because it's not valid for the native-code Win32 DLL that you'll add to the solution later.

솔루션 탐색기에서 class1.cs의 이름을 프로젝트 이름과 일치하도록 ToasterComponent.cs로 바꿉니다.In Solution Explorer, rename class1.cs to ToasterComponent.cs so that it matches the name of the project. 파일에 있는 클래스가 새 파일 이름에 맞게 자동으로 바뀝니다.Visual Studio automatically renames the class in the file to match the new file name.

.Cs 파일에서 Windows Foundation 네임 스페이스에 대 한 using 지시문을 추가 하 여 TypedEventHandler를 범위로 가져옵니다.In the .cs file, add a using directive for the Windows.Foundation namespace to bring TypedEventHandler into scope.

프록시 및 스텁이 필요한 경우 구성 요소가 인터페이스를 사용하여 public 멤버를 노출해야 합니다.When you require proxies and stubs, your component must use interfaces to expose its public members. ToasterComponent.cs에서 토스터에 대한 인터페이스를 하나 정의하고 토스터가 생성하는 알림에 대해 다른 인터페이스를 정의합니다.In ToasterComponent.cs, define an interface for the toaster, and another one for the Toast that the toaster produces.

참고   C #에서는이 단계를 건너뛸 수 있습니다.Note In C# you can skip this step. 대신 먼저 클래스를 만든 다음 바로 가기 메뉴를 열고 리팩터링 > 인터페이스 추출을 선택 합니다.Instead, first create a class, and then open its shortcut menu and choose Refactor > Extract Interface. 생성되는 코드에서 인터페이스에 public 액세스 가능성을 수동으로 제공합니다.In the code that's generated, manually give the interfaces public accessibility.

    public interface IToaster
        {
            void MakeToast(String message);
            event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;

        }
        public interface IToast
        {
            String ToastType { get; }
        }

IToast 인터페이스에는 알림 형식을 설명 하기 위해 검색할 수 있는 문자열이 있습니다.The IToast interface has a string that can be retrieved to describe the type of toast. IToaster 인터페이스에는 알림을 만드는 메서드와 알림이 생성 되었음을 나타내는 이벤트가 있습니다.The IToaster interface has a method to make toast, and an event to indicate that the toast is made. 이 이벤트는 알림의 특정 부분(형식)을 반환하므로 형식화된 이벤트라고 합니다.Because this event returns the particular piece (that is, type) of toast, it's known as a typed event.

이제 나중에 프로그래밍할 JavaScript 앱에서 액세스할 수 있도록 이러한 인터페이스를 구현하는 봉인된 공용 클래스가 필요합니다.Next, we need classes that implement these interfaces, and are public and sealed so that they are accessible from the JavaScript app that you'll program later.

    public sealed class Toast : IToast
        {
            private string _toastType;

            public string ToastType
            {
                get
                {
                    return _toastType;
                }
            }
            internal Toast(String toastType)
            {
                _toastType = toastType;
            }

        }
        public sealed class Toaster : IToaster
        {
            public event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;

            private void OnToastCompleted(Toast args)
            {
                var completedEvent = ToastCompletedEvent;
                if (completedEvent != null)
                {
                    completedEvent(this, args);
                }
            }

            public void MakeToast(string message)
            {
                Toast toast = new Toast(message);
                // Fire the event from a thread-pool thread to enable this thread to continue
                Windows.System.Threading.ThreadPool.RunAsync(
                (IAsyncAction action) =>
                {
                    if (ToastCompletedEvent != null)
                    {
                        OnToastCompleted(toast);
                    }
                });
           }
        }

위의 코드에서는 알림을 만든 다음 스레드 풀 작업 항목을 스핀업하여 알림을 발생시킵니다.In the preceding code, we create the toast and then spin up a thread-pool work item to fire the notification. IDE는 비동기 호출에 wait 키워드를 적용 하도록 제안할 수 있지만 메서드는 작업 결과에 따라 달라 지는 작업을 수행 하지 않으므로이 경우에는 필요 하지 않습니다.Although the IDE might suggest that you apply the await keyword to the async call, it isn’t necessary in this case because the method doesn’t do any work that depends on the results of the operation.

참고   이전 코드의 비동기 호출에서는 스레드 풀을 사용 하 여 백그라운드 스레드에서 이벤트를 발생 시키는 간단한 방법을 보여 줍니다.Note The async call in the preceding code uses ThreadPool.RunAsync solely to demonstrate a simple way to fire the event on a background thread. 다음 예제와 같이 이러한 특정 메서드를 작성할 수 있으며 .NET 작업 스케줄러가 async/await 호출을 UI 스레드에 다시 자동으로 마샬링해야 하므로 이 메서드는 제대로 작동할 것입니다.You could write this particular method as shown in the following example, and it would work fine because the .NET Task scheduler automatically marshals async/await calls back to the UI thread.  

    public async void MakeToast(string message)
    {
        Toast toast = new Toast(message)
        await Task.Delay(new Random().Next(1000));
        OnToastCompleted(toast);
    }

지금 프로젝트를 빌드하는 경우 정상적으로 빌드됩니다.If you build the project now, it should build cleanly.

JavaScript 앱을 프로그래밍하려면To program the JavaScript app

이제 알림을 만들기 위해 방금 정의한 클래스를 사용하도록 JavaScript 앱에 단추를 추가할 수 있습니다.Now we can add a button to the JavaScript app to cause it to use the class we just defined to make toast. 이 작업을 수행하기 전에, 방금 만든 ToasterComponent 프로젝트에 대한 참조를 추가해야 합니다.Before we do this, we must add a reference to the ToasterComponent project we just created. 솔루션 탐색기에서 ToasterApplication 프로젝트에 대 한 바로 가기 메뉴를 열고 ** > 참조 추가**를 선택한 다음 새 참조 추가 단추를 선택 합니다.In Solution Explorer, open the shortcut menu for the ToasterApplication project, choose Add > References, and then choose the Add New Reference button. 참조 추가 대화 상자의 솔루션 아래에 있는 왼쪽 창에서 구성 요소 프로젝트를 선택한 다음 가운데 창에서 ToasterComponent를 선택합니다.In the Add Reference dialog box, in the left pane under Solution, select the component project, and then in the middle pane, select ToasterComponent. 확인 단추를 선택합니다.Choose the OK button.

솔루션 탐색기에서 ToasterApplication 프로젝트에 대 한 바로 가기 메뉴를 열고 시작 프로젝트로 설정을 선택 합니다.In Solution Explorer, open the shortcut menu for the ToasterApplication project and then choose Set as Startup Project.

Default.js 파일의 끝에 구성 요소를 호출하고 구성 요소에 의해 다시 호출되는 함수를 포함하는 네임스페이스를 추가합니다.At the end of the default.js file, add a namespace to contain the functions to call the component and be called back by it. 네임스페이스는 두 개의 함수를 포함하게 됩니다. 하나는 알림을 만드는 함수이고, 다른 하나는 알림 완료 이벤트를 처리하는 함수입니다.The namespace will have two functions, one to make toast and one to handle the toast-complete event. MakeToast의 구현은 Toaster 개체를 만들고 이벤트 처리기를 등록 하 고 알림을 만듭니다.The implementation of makeToast creates a Toaster object, registers the event handler, and makes the toast. 지금까지는 이벤트 처리기가 아래와 같이 작업을 많이 수행하지 않습니다.So far, the event handler doesn’t do much, as shown here:

    WinJS.Namespace.define("ToasterApplication"), {
       makeToast: function () {

          var toaster = new ToasterComponent.Toaster();
          //toaster.addEventListener("ontoastcompletedevent", ToasterApplication.toastCompletedEventHandler);
          toaster.ontoastcompletedevent = ToasterApplication.toastCompletedEventHandler;
          toaster.makeToast("Peanut Butter");
       },

       toastCompletedEventHandler: function(event) {
           // The sender of the event (the delegate's first type parameter)
           // is mapped to event.target. The second argument of the delegate
           // is contained in event, which means in this case event is a
           // Toast class, with a toastType string.
           var toastType = event.toastType;

           document.getElementById('toastOutput').innerHTML = "<p>Made " + toastType + " toast</p>";
        },
    });

MakeToast 함수는 단추로 후크 되어야 합니다.The makeToast function must be hooked up to a button. 알림을 만든 결과를 출력할 공간과 단추를 포함하도록 default.html을 업데이트합니다.Update default.html to include a button and some space to output the result of making toast:

    <body>
        <h1>Click the button to make toast</h1>
        <button onclick="ToasterApplication.makeToast()">Make Toast!</button>
        <div id="toasterOutput">
            <p>No Toast Yet...</p>
        </div>
    </body>

TypedEventHandler를 사용 하지 않는 경우 이제 로컬 컴퓨터에서 앱을 실행 하 고 단추를 클릭 하 여 알림을 만들 수 있습니다.If we weren’t using a TypedEventHandler, we would now be able to run the app on the local machine and click the button to make toast. 그러나 앱에서 아무 변화가 없습니다.But in our app, nothing happens. 이유를 확인 하려면 To Completedevent를 실행 하는 관리 코드를 디버그 하겠습니다.To find out why, let’s debug the managed code that fires the ToastCompletedEvent. 프로젝트를 중지 한 다음 메뉴 모음에서 디버그 > Toaster 응용 프로그램 속성을 선택 합니다.Stop the project, and then on the menu bar, choose Debug > Toaster Application properties. 디버거 형식관리 전용으로 변경 합니다.Change Debugger Type to Managed Only. 메뉴 모음에서 디버그 > 예외를 선택한 다음 공용 언어 런타임 예외를 선택 합니다.Again on the menu bar, choose Debug > Exceptions, and then select Common Language Runtime Exceptions.

이제 앱을 실행하고 알림 만들기 단추를 클릭합니다.Now run the app and click the make-toast button. 디버거가 잘못된 캐스팅 예외를 catch합니다.The debugger catches an invalid cast exception. 메시지만으로 확실하지는 않지만 이 예외는 해당 인터페이스에 대한 프록시가 누락되어 발생하고 있습니다.Although it’s not obvious from its message, this exception is occurring because proxies are missing for that interface.

누락 된 프록시

구성 요소에 대한 프록시 및 스텁을 만드는 첫 번째 단계는 인터페이스에 고유 ID 또는 GUID를 를 추가하는 것입니다.The first step in creating a proxy and stub for a component is to add a unique ID or GUID to the interfaces. 그러나 사용할 GUID 형식은 코딩에 사용 중인 언어가 C#, Visual Basic, 다른 .NET 언어 또는 C++인지에 따라 달라집니다.However, the GUID format to use differs depending on whether you're coding in C#, Visual Basic, or another .NET language, or in C++.

구성 요소의 인터페이스에 대 한 Guid를 생성 하려면 (c # 및 기타 .NET 언어)To generate GUIDs for the component's interfaces (C# and other .NET languages)

메뉴 모음에서 도구 > GUID 만들기를 선택 합니다.On the menu bar, choose Tools > Create GUID. 대화 상자에서 5를 선택 합니다.In the dialog box, select 5. [Guid ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ... xxxx ") ] .[Guid("xxxxxxxx-xxxx...xxxx")]. 새 GUID 단추를 선택한 다음 복사 단추를 선택합니다.Choose the New GUID button and then choose the Copy button.

guid 생성기 도구

인터페이스 정의로 돌아가서 다음 예제와 같이 IToaster 인터페이스 바로 앞에 새 GUID를 붙여 넣습니다.Go back to the interface definition, and then paste the new GUID just before the IToaster interface, as shown in the following example. 예제의 GUID는 사용하지 마세요.(Don't use the GUID in the example. 모든 고유 인터페이스마다 고유 GUID를 가져야 합니다.Every unique interface should have its own GUID.)

[Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
        public interface IToaster...

T e m 네임 스페이스에 대 한 using 지시문을 추가 합니다.Add a using directive for the System.Runtime.InteropServices namespace.

IToast 인터페이스에 대해 이러한 단계를 반복 합니다.Repeat these steps for the IToast interface.

구성 요소의 인터페이스에 대 한 Guid를 생성 하려면 (c + +)To generate GUIDs for the component's interfaces (C++)

메뉴 모음에서 도구 > GUID 만들기를 선택 합니다.On the menu bar, choose Tools > Create GUID. 대화 상자에서 3을 선택 합니다.In the dialog box, select 3. static const struct GUID = {...}.static const struct GUID = {...}. 새 GUID 단추를 선택한 다음 복사 단추를 선택합니다.Choose the New GUID button and then choose the Copy button.

IToaster 인터페이스 정의 바로 앞에 GUID를 붙여 넣습니다.Paste the GUID just before the IToaster interface definition. 붙여 넣은 후 GUID는 다음 예제와 비슷해야 합니다.After you paste, the GUID should resemble the following example. 예제의 GUID는 사용하지 마세요.(Don't use the GUID in the example. 모든 고유 인터페이스마다 고유 GUID를 가져야 합니다.Every unique interface should have its own GUID.)

// {F8D30778-9EAF-409C-BCCD-C8B24442B09B}
    static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };

GuidAttribute에 대 한 using 지시문을 추가 하 여 범위를 범위로 가져옵니다.Add a using directive for Windows.Foundation.Metadata to bring GuidAttribute into scope.

이제 다음 예제와 같이 서식이 지정되도록 수동으로 const GUID를 GuidAttribute로 변환합니다.Now manually convert the const GUID to a GuidAttribute so that it's formatted as shown in the following example. 중괄호가 대괄호 및 괄호로 바뀌고 후행 세미콜론은 제거됩니다.Notice that the curly braces are replaced with brackets and parentheses, and the trailing semicolon is removed.

// {E976784C-AADE-4EA4-A4C0-B0C2FD1307C3}
    [GuidAttribute(0xe976784c, 0xaade, 0x4ea4, 0xa4, 0xc0, 0xb0, 0xc2, 0xfd, 0x13, 0x7, 0xc3)]
    public interface IToaster
    {...

IToast 인터페이스에 대해 이러한 단계를 반복 합니다.Repeat these steps for the IToast interface.

인터페이스에는 고유한 Id가 있기 때문에 winmdidl 명령줄 도구에 winmd 파일을 공급 하 여 IDL 파일을 만든 다음 해당 IDL 파일을 MIDL 명령줄 도구에 공급 하 여 프록시와 스텁을 위한 C 소스 코드를 생성할 수 있습니다.Now that the interfaces have unique IDs, we can create an IDL file by feeding the .winmd file into the winmdidl command-line tool, and then generate the C source code for the proxy and stub by feeding that IDL file into the MIDL command-line tool. 다음 단계에서와 같이 빌드 후 이벤트를 만들면 Visual Studio가 자동으로 이 작업을 수행해 줍니다.Visual Studio do this for us if we create post-build events as shown in the following steps.

프록시 및 스텁 소스 코드를 생성하려면To generate the proxy and stub source code

사용자 지정 빌드 후 이벤트를 추가하려면 솔루션 탐색기에서 ToasterComponent 프로젝트에 대한 바로 가기 메뉴를 열고 속성을 선택합니다.To add a custom post-build event, in Solution Explorer, open the shortcut menu for the ToasterComponent project and then choose Properties. 속성 페이지의 왼쪽 창에서 빌드 이벤트를 선택한 다음 빌드 후 편집 단추를 선택합니다.In the left pane of the property pages, select Build Events, and then choose the Edit Post-build button. 빌드 후 명령줄에 다음 명령을 추가합니다.Add the following commands to the post-build command line. Winmdidl 도구를 찾기 위해 환경 변수를 설정 하려면 먼저 배치 파일을 호출 해야 합니다.(The batch file must be called first to set the environment variables to find the winmdidl tool.)

call "$(DevEnvDir)..\..\vc\vcvarsall.bat" $(PlatformName)
winmdidl /outdir:output "$(TargetPath)"
midl /metadata_dir "%WindowsSdkDir%References\CommonConfiguration\Neutral" /iid "$(ProjectDir)$(TargetName)_i.c" /env win32 /h "$(ProjectDir)$(TargetName).h" /winmd "Output\$(TargetName).winmd" /W1 /char signed /nologo /winrt /dlldata "$(ProjectDir)dlldata.c" /proxy "$(ProjectDir)$(TargetName)_p.c" "Output\$(TargetName).idl"

중요    ARM 또는 x64 프로젝트 구성의 경우 MIDL/env 매개 변수를 x64 또는 arm32로 변경 합니다.Important  For an ARM or x64 project configuration, change the MIDL /env parameter to x64 or arm32.

Winmd 파일이 변경 될 때마다 IDL 파일이 다시 생성 되도록 하려면 빌드에서 프로젝트 출력을 업데이트할 때 빌드 후 이벤트 실행 을 변경 합니다.To make sure the IDL file is regenerated every time the .winmd file is changed, change Run the post-build event to When the build updates the project output. 빌드 이벤트 속성 페이지는 빌드 이벤트와 비슷해야 합니다. The Build Events property page should resemble this: build events

솔루션을 다시 빌드하여 IDL을 생성하고 컴파일합니다.Rebuild the solution to generate and compile the IDL.

ToasterComponent 프로젝트 디렉터리에서 ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c 및 dlldata.c를 찾아 MIDL이 솔루션을 올바르게 컴파일했는지 확인할 수 있습니다.You can verify that MIDL correctly compiled the solution by looking for ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, and dlldata.c in the ToasterComponent project directory.

프록시 및 스텁 코드를 DLL로 컴파일하려면To compile the proxy and stub code into a DLL

필요한 파일을 가지고 있으므로 해당 파일을 컴파일하여 C++ 파일인 DLL을 생성할 수 있습니다.Now that you have the required files, you can compile them to produce a DLL, which is a C++ file. 이 작업을 최대한 쉽게 수행하려면 프록시 빌드를 지원하는 새 프로젝트를 추가합니다.To make this as easy as possible, add a new project to support building the proxies. ToasterApplication 솔루션에 대 한 바로 가기 메뉴를 열고 추가 > 새 프로젝트를 선택 합니다.Open the shortcut menu for the ToasterApplication solution and then choose Add > New Project. 새 프로젝트 대화 상자의 왼쪽 창에서 Visual C++ > windows > 유니버설 windows를 확장 한 다음 가운데 창에서 DLL (UWP 앱) 을 선택 합니다.In the left pane of the New Project dialog box, expand Visual C++ > Windows > Univeral Windows, and then in the middle pane, select DLL (UWP apps). 이는 c + + Windows 런타임 구성 요소 프로젝트가 아닙니다. 프로젝트 이름을 프록시로 지정한 다음 확인 단추를 선택 합니다.(Notice that this is NOT a C++ Windows Runtime Component project.) Name the project Proxies and then choose the OK button. C# 클래스를 변경하면 빌드 후 이벤트에 의해 이 파일들이 업데이트됩니다.These files will be updated by the post-build events when something changes in the C# class.

기본적으로 Proxies 프로젝트는 헤더 .h 파일과 C++ .cpp 파일을 생성합니다.By default, the Proxies project generates header .h files and C++ .cpp files. DLL은 MIDL에서 생성된 파일에서 빌드되므로 .h 및 .cpp 파일이 필요하지 않습니다.Because the DLL is built from the files produced from MIDL, the .h and .cpp files are not required. 솔루션 탐색기에서 해당 메뉴에 대 한 바로 가기 메뉴를 열고 제거를 선택한 다음 삭제를 확인 합니다.In Solution Explorer, open the shortcut menu for them, choose Remove, and then confirm the deletion.

프로젝트가 비어 있으므로 MIDL 생성 파일을 다시 추가할 수 있습니다.Now that the project is empty, you can add back the MIDL-generated files. 프록시 프로젝트에 대 한 바로 가기 메뉴를 열고 기존 항목 > 추가를 선택 합니다.Open the shortcut menu for the Proxies project, and then choose Add > Existing Item. 대화 상자에서 ToasterComponent 프로젝트 디렉터리로 이동한 다음 ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c 및 dlldata.c 파일을 선택합니다.In the dialog box, navigate to the ToasterComponent project directory and select these files: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, and dlldata.c files. 추가 단추를 선택합니다.Choose the Add button.

Proxies 프로젝트에서 .def 파일을 만들어 dlldata.c에 설명된 DLL 내보내기를 정의합니다.In the Proxies project, create a .def file to define the DLL exports described in dlldata.c. 프로젝트에 대 한 바로 가기 메뉴를 열고 추가 > 새 항목을 선택 합니다.Open the shortcut menu for the project, and then choose Add > New Item. 대화 상자의 왼쪽 창에서 코드를 선태한 다음 가운데 창에서 모듈 정의 파일을 선택합니다.In the left pane of the dialog box, select Code and then in the middle pane, select Module-Definition File. 파일 이름을 프록시와로 지정한 다음 추가 단추를 선택 합니다.Name the file proxies.def and then choose the Add button. 이 .def 파일을 열고 dlldata.c에 정의된 EXPORTS를 포함하도록 수정합니다.Open this .def file and modify it to include the EXPORTS that are defined in dlldata.c:

EXPORTS
    DllCanUnloadNow         PRIVATE
    DllGetClassObject       PRIVATE

지금 프로젝트를 빌드하면 프로젝트가 실패합니다.If you build the project now, it will fail. 이 프로젝트를 올바르게 컴파일하려면 프로젝트의 컴파일 및 연결 방법을 변경해야 합니다.To correctly compile this project, you have to change how the project is compiled and linked. 솔루션 탐색기에서 프록시 프로젝트에 대 한 바로 가기 메뉴를 열고 속성을 선택 합니다.In Solution Explorer, open the shortcut menu for the Proxies project and then choose Properties. 속성 페이지를 다음과 같이 변경 합니다.Change the property pages as follows.

왼쪽 창에서 C/c + + > 전처리기를 선택한 다음 오른쪽 창에서 전처리기 정의를 선택 하 고 아래쪽 화살표 단추를 선택한 다음 편집을 선택 합니다.In the left pane, select C/C++ > Preprocessor, and then in the right pane, select Preprocessor Definitions, choose the down-arrow button, and then select Edit. 상자에 이러한 정의를 추가합니다.Add these definitions in the box:

WIN32;_WINDOWS

C/c + + > 미리컴파일된 헤더에서 미리 컴파일된 헤더 를 미리 컴파일된 헤더 사용 안 함으로 변경한 다음 적용 단추를 선택 합니다.Under C/C++ > Precompiled Headers, change Precompiled Header to Not Using Precompiled Headers, and then choose the Apply button.

링커 > 일반에서 가져오기 라이브러리 무시변경 하 고 적용 단추를 선택합니다. ApplyUnder Linker > General, change Ignore Import Library to Yes, and then choose the Apply button.

링커 > 입력에서 추가 종속성을 선택 하 고 아래쪽 화살표 단추를 선택한 다음 편집을 선택 합니다.Under Linker > Input, select Additional Dependencies, choose the down-arrow button, and then select Edit. 상자에 이 텍스트를 추가합니다.Add this text in the box:

rpcrt4.lib;runtimeobject.lib

이러한 라이브러리를 목록 행에 직접 붙여 넣지는 마세요.Do not paste these libs directly into the list row. 편집 상자를 사용 하 여 Visual Studio의 MSBuild가 올바른 추가 종속성을 유지 하도록 합니다.Use the Edit box to ensure that MSBuild in Visual Studio will maintain the correct additional dependencies.

이러한 변경을 수행한 후에는 속성 페이지 대화 상자에서 확인 단추를 선택 합니다.When you have made these changes, choose the OK button in the Property Pages dialog box.

이제 ToasterComponent 프로젝트에 대한 종속성을 사용합니다.Next, take a dependency on the ToasterComponent project. 이렇게 하면 프록시 프로젝트가 빌드되기 전에 Toaster가 빌드됩니다.This ensures that the Toaster will build before the proxy project builds. 이는 Toaster 프로젝트가 프록시를 빌드하기 위한 파일 생성을 담당하기 때문에 필요합니다.This is required because the Toaster project is responsible for generating the files to build the proxy.

Proxies 프로젝트에 대한 바로 가기 메뉴를 열고 프로젝트 종속성을 선택합니다.Open the shortcut menu for the Proxies project and then choose Project Dependencies. Proxies 프로젝트가 ToasterComponent 프로젝트에 종속됨을 나타내는 확인란을 선택하여 프로젝트가 올바른 순서로 빌드되도록 합니다.Select the check boxes to indicate that the Proxies project depends on the ToasterComponent project, to ensure that Visual Studio builds them in the correct order.

Visual Studio 메뉴 모음에서 빌드 > 솔루션 다시 빌드를 선택 하 여 솔루션이 올바르게 빌드되는지 확인 합니다.Verify that the solution builds correctly by choosing Build > Rebuild Solution on the Visual Studio menu bar.

프록시 및 스텁을 등록하려면To register the proxy and stub

ToasterApplication 프로젝트에서 appxmanifest.xml에 대 한 바로 가기 메뉴를 연 다음 연결 프로그램을 선택 합니다.In the ToasterApplication project, open the shortcut menu for package.appxmanifest and then choose Open With. 연결 프로그램 대화 상자에서 XML 텍스트 편집기 를 선택한 다음 확인 단추를 선택 합니다.In the Open With dialog box, select XML Text Editor and then choose the OK button. ActivatableClass proxyStub 확장 등록을 제공 하 고 프록시의 Guid를 기반으로 하는 일부 XML로 붙여넣을 예정입니다.We're going to paste in some XML that provides a windows.activatableClass.proxyStub extension registration and which are based on the GUIDs in the proxy. .appxmanifest 파일에서 사용할 GUID를 찾으려면 ToasterComponent_i.c를 엽니다.To find the GUIDs to use in the .appxmanifest file, open ToasterComponent_i.c. 다음 예제에서와 유사한 항목을 찾습니다.Find entries that resemble the ones in the following example. 또한 IToast, IToaster 및 세 번째 인터페이스 (Toaster 및 Toast 라는 두 매개 변수가 있는 형식화 된 이벤트 처리기)에 대 한 정의를 확인 합니다.Also notice the definitions for IToast, IToaster, and a third interface—a typed event handler that has two parameters: a Toaster and Toast. 이는 Toaster 클래스에 정의 된 이벤트와 일치 합니다.This matches the event that's defined in the Toaster class. IToast 및 IToaster의 Guid는 c # 파일의 인터페이스에 정의 된 Guid와 일치 합니다.Notice that the GUIDs for IToast and IToaster match the GUIDs that are defined on the interfaces in the C# file. 형식화된 이벤트 처리기 인터페이스는 자동 생성되므로 이 인터페이스의 GUID도 자동 생성됩니다.Because the typed event handler interface is autogenerated, the GUID for this interface is also autogenerated.

MIDL_DEFINE_GUID(IID, IID___FITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast,0x1ecafeff,0x1ee1,0x504a,0x9a,0xf5,0xa6,0x8c,0x6f,0xb2,0xb4,0x7d);

MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToast,0xF8D30778,0x9EAF,0x409C,0xBC,0xCD,0xC8,0xB2,0x44,0x42,0xB0,0x9B);

MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToaster,0xE976784C,0xAADE,0x4EA4,0xA4,0xC0,0xB0,0xC2,0xFD,0x13,0x07,0xC3);

이제 Guid를 복사 하 여 확장명을 추가 하 고 이름을 지정 하는 노드의 appxmanifest.xml에 붙여넣습니다.Now we copy the GUIDs, paste them in package.appxmanifest in a node that we add and name Extensions, and then reformat them. 매니페스트 항목은 다음 예제와 비슷하지만 반드시 사용자 고유 GUID를 사용하세요.The manifest entry resembles the following example—but again, remember to use your own GUIDs. XML의 ClassId ClassId가 ITypedEventHandler2와 동일합니다.Notice that the ClassId GUID in the XML is the same as ITypedEventHandler2. GUID가 ToasterComponent_i.c에 첫 번째로 나열되는 항목이기 때문입니다.This is because that GUID is the first one that's listed in ToasterComponent_i.c. 여기에서 GUID는 대소문자를 구분합니다.The GUIDs here are case-insensitive. IToast 및 IToaster에 대 한 Guid를 수동으로 다시 포맷 하는 대신 인터페이스 정의로 돌아가서 올바른 형식의 GuidAttribute 값을 가져올 수 있습니다.Instead of manually reformatting the GUIDs for IToast and IToaster, you can go back into the interface definitions and get the GuidAttribute value, which has the correct format. C++에서는 주석에 올바르게 서식이 지정된 GUID가 있습니다.In C++, there is a correctly-formatted GUID in the comment. 어떤 경우에도 ClassId 및 이벤트 처리기에 사용된 GUID의 서식을 수동으로 다시 지정해야 합니다.In any case, you must manually reformat the GUID that's used for both the ClassId and the event handler.

      <Extensions> <!--Use your own GUIDs!!!-->
        <Extension Category="windows.activatableClass.proxyStub">
          <ProxyStub ClassId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d">
            <Path>Proxies.dll</Path>
            <Interface Name="IToast" InterfaceId="F8D30778-9EAF-409C-BCCD-C8B24442B09B"/>
            <Interface Name="IToaster"  InterfaceId="E976784C-AADE-4EA4-A4C0-B0C2FD1307C3"/>  
            <Interface Name="ITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast" InterfaceId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d"/>
          </ProxyStub>      
        </Extension>
      </Extensions>

Extensions XML 노드를 패키지 노드의 직계 자식으로 붙여넣고, 예를 들어 Resources 노드와 같은 피어로 붙여 넣습니다.Paste the Extensions XML node as a direct child of the Package node, and a peer of, for example, the Resources node.

넘어가기 전에 다음 사항을 확인해야 합니다.Before moving on, it’s important to ensure that:

  • ProxyStub ClassId는 ToasterComponent 파일의 첫 번째 GUID로 설정 됩니다 _ .The ProxyStub ClassId is set to the first GUID in the ToasterComponent_i.c file. 이 파일에서 classId로 정의된 첫 번째 GUID를 사용하세요.Use the first GUID that's defined in this file for the classId. 이 GUID는 ITypedEventHandler2의 GUID와 동일할 수 있습니다.(This might be the same as the GUID for ITypedEventHandler2.)
  • 경로는 프록시 이진 파일의 패키지 상대 경로입니다.The Path is the package relative path of the proxy binary. 이 연습에서는 proxies.dll이 ToasterApplication.winmd와 동일한 폴더에 있습니다.(In this walkthrough, proxies.dll is in the same folder as ToasterApplication.winmd.)
  • GUID가 올바른 형식입니다.The GUIDs are in the correct format. 잘못된 형식일 가능성이 높습니다.(This is easy to get wrong.)
  • 매니페스트의 인터페이스 Id는 ToasterComponent 파일의 Iid와 일치 합니다 _ .The interface IDs in the manifest match the IIDs in ToasterComponent_i.c file.
  • 매니페스트에서 인터페이스 이름은 고유합니다.The interface names are unique in the manifest. 이 이름은 시스템에서 사용되지 않으므로 사용자가 값을 선택할 수 있습니다.Because these are not used by the system, you can choose the values. 정의한 인터페이스와 확실하게 일치하는 인터페이스 이름을 선택하는 것이 좋습니다.It is a good practice to choose interface names that clearly match interfaces that you have defined. 생성된 인터페이스의 이름은 생성된 인터페이스를 나타내야 합니다.For generated interfaces, the names should be indicative of the generated interfaces. ToasterComponent 파일을 사용 하 여 _ 인터페이스 이름을 생성 하는 데 도움이 됩니다.You can use the ToasterComponent_i.c file to help you generate interface names.

지금 솔루션을 실행하려고 시도하면 proxies.dll이 페이로드의 일부가 아니라는 오류가 발생합니다.If you try to run the solution now, you will get an error that proxies.dll is not part of the payload. ToasterApplication 프로젝트의 참조 폴더에 대 한 바로 가기 메뉴를 열고 참조 추가를 선택 합니다.Open the shortcut menu for the References folder in the ToasterApplication project and then choose Add Reference. Proxies 프로젝트 옆의 확인란을 선택합니다.Select the check box next to the Proxies project. ToasterComponent 옆의 확인란도 선택되어 있는지 확인합니다.Also, make sure that the check box next to ToasterComponent is also selected. 확인 단추를 선택합니다.Choose the OK button.

이제 프로젝트가 빌드되어야 합니다.The project should now build. 프로젝트를 실행하고 알림을 만들 수 있는지 확인합니다.Run the project and verify that you can make toast.