Windows Forms에서 시각적 계층 사용

Windows Forms 앱에서 Windows 런타임 Composition API(비주얼 레이어라고도 함)를 사용하여 Windows 사용자에게 최적화된 최신 경험을 제공할 수 있습니다.

이 자습서의 전체 코드는 GitHub Windows Forms HelloComposition 샘플 사용할 수 있습니다.

사전 요구 사항

UWP 호스팅 API에는 다음과 같은 필수 구성 요소가 있습니다.

Windows Forms 컴퍼지션 API를 사용하는 방법

이 자습서에서는 간단한 Windows Forms UI를 만들고 애니메이션 컴퍼지션 요소를 추가합니다. Windows Forms 구성 요소와 컴퍼지션 구성 요소는 모두 단순하게 유지되지만 표시되는 interop 코드는 구성 요소의 복잡성에 관계없이 동일합니다. 완성된 앱은 다음과 같이 표시됩니다.

실행 중인 앱 UI

Windows Forms 프로젝트 만들기

첫 번째 단계는 애플리케이션 정의와 UI의 기본 양식을 포함하는 Windows Forms 앱 프로젝트를 만드는 것입니다.

Visual C#에서 HelloComposition라는 새 Windows Forms 애플리케이션 프로젝트를 만들려면 다음을 수행합니다.

  1. Visual Studio를 열고 파일>새로 만들기>프로젝트를 선택합니다.
    새 프로젝트 대화 상자가 열립니다.
  2. 사용 범주에서 비전 C# 노드를 확장한 다음, Windows Desktop 선택합니다.
  3. Windows Forms 앱(.NET Framework) 템플릿을 선택합니다.
  4. 이름 HelloComposition, Framework .NET Framework 4.7.2을 선택하고 OK 클릭합니다.

Visual Studio 프로젝트를 만들고 Form1.cs이라는 기본 애플리케이션 창에 대한 디자이너를 엽니다.

Windows 런타임 API를 사용하도록 프로젝트 구성

Windows Forms 앱에서 winRT(Windows 런타임) API를 사용하려면 Windows 런타임 액세스하도록 Visual Studio 프로젝트를 구성해야 합니다. 또한 컴퍼지션 API에서 벡터를 광범위하게 사용하므로 벡터를 사용하는 데 필요한 참조를 추가해야 합니다.

NuGet 패키지는 이러한 두 요구 사항을 모두 해결하는 데 사용할 수 있습니다. 이러한 패키지의 최신 버전을 설치하여 프로젝트에 필요한 참조를 추가합니다.

Note

NuGet 패키지를 사용하여 프로젝트를 구성하는 것이 좋지만 필요한 참조를 수동으로 추가할 수 있습니다. 자세한 내용은 Windows 참조하세요. 다음 표에서는 참조를 추가해야 하는 파일을 보여 줍니다.

File Location
System.Runtime.WindowsRuntime C:\Windows\Microsoft.NET\Framework\v4.0.30319
Windows.Foundation.UniversalApiContract.winmd C:\Program Files (x86)\Windows Kits\10\References<sdk version>\Windows.Foundation.UniversalApiContract<version>
Windows.Foundation.FoundationContract.winmd C:\Program Files (x86)\Windows Kits\10\References<sdk version>\Windows. Foundation.FoundationContract<version>
System.Numerics.Vectors.dll C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Numerics.Vectors\v4.0_4.0.0.0__b03f5f7f11d50a3a
System.Numerics.dll C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.7.2

interop을 관리하는 사용자 지정 컨트롤 만들기

시각적 계층을 사용하여 만든 콘텐츠를 호스트하려면 Control에서 파생된 사용자 지정 컨트롤을 만듭니다. 이 컨트롤을 사용하면 시각적 계층 콘텐츠에 대한 컨테이너를 만들기 위해 필요한 창 핸들에 액세스할 수 있습니다.

여기서 컴퍼지션 API를 호스트하기 위한 대부분의 구성을 수행합니다. 이 컨트롤에서는 PInvoke(Platform Invocation Services) COM Interop을 사용하여 컴퍼지션 API를 Windows Forms 앱으로 가져옵니다. PInvoke 및 COM Interop에 대한 자세한 내용은 관리되지 않는 코드와의 상호 운용을 참조하세요.

팁 (조언)

필요한 경우 튜토리얼의 끝 부분에 있는 전체 코드를 확인하여 튜토리얼 작업을 진행할 때 모든 코드가 올바른 위치에 있도록 합니다.

  1. Control에서 파생되는 새 사용자 지정 컨트롤 파일을 프로젝트에 추가합니다.

    • 솔루션 탐색기에서 HelloComposition 프로젝트를 마우스 오른쪽 버튼으로 클릭합니다.
    • 상황에 맞는 메뉴에서새 항목>...를 선택합니다.
    • 새 항목 추가 대화 상자에서 사용자 지정 컨트롤을 선택합니다.
    • 컨트롤 이름을 CompositionHost.cs 다음 추가를 클릭합니다. CompositionHost.cs 디자인 보기에서 열립니다.
  2. CompositionHost.cs 대한 코드 보기로 전환하고 다음 코드를 클래스에 추가합니다.

    // Add
    // using Windows.UI.Composition;
    
    IntPtr hwndHost;
    object dispatcherQueue;
    protected ContainerVisual containerVisual;
    protected Compositor compositor;
    
    private ICompositionTarget compositionTarget;
    
    public Visual Child
    {
        set
        {
            if (compositor == null)
            {
                InitComposition(hwndHost);
            }
            compositionTarget.Root = value;
        }
    }
    
  3. 생성자에 코드를 추가합니다.

    생성자에서 InitializeCoreDispatcherInitComposition 메서드를 호출합니다. 다음 단계에서 이러한 메서드를 만듭니다.

    public CompositionHost()
    {
        InitializeComponent();
    
        // Get the window handle.
        hwndHost = Handle;
    
        // Create dispatcher queue.
        dispatcherQueue = InitializeCoreDispatcher();
    
        // Build Composition tree of content.
        InitComposition(hwndHost);
    }
    
  4. CoreDispatcher를 사용하여 스레드를 초기화합니다. 핵심 디스패처는 창 메시지를 처리하고 WinRT API에 대한 이벤트를 디스패치합니다. CoreDispatcher가 있는 스레드에서 Compositor 의 새 인스턴스를 만들어야 합니다.

    • InitializeCoreDispatcher라는 메서드를 만들고 코드를 추가하여 디스패처 큐를 설정합니다.
    // Add
    // using System.Runtime.InteropServices;
    
    private object InitializeCoreDispatcher()
    {
        DispatcherQueueOptions options = new DispatcherQueueOptions();
        options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA;
        options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT;
        options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
    
        object queue = null;
        CreateDispatcherQueueController(options, out queue);
        return queue;
    }
    
    • 디스패처 큐에는 PInvoke 선언이 필요합니다. 클래스의 코드 끝에 이 선언을 배치합니다. (이 코드를 지역 내에 배치하여 클래스 코드를 깔끔하게 유지합니다.)
    #region PInvoke declarations
    
    //typedef enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
    //{
    //    DQTAT_COM_NONE,
    //    DQTAT_COM_ASTA,
    //    DQTAT_COM_STA
    //};
    internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
    {
        DQTAT_COM_NONE = 0,
        DQTAT_COM_ASTA = 1,
        DQTAT_COM_STA = 2
    };
    
    //typedef enum DISPATCHERQUEUE_THREAD_TYPE
    //{
    //    DQTYPE_THREAD_DEDICATED,
    //    DQTYPE_THREAD_CURRENT
    //};
    internal enum DISPATCHERQUEUE_THREAD_TYPE
    {
        DQTYPE_THREAD_DEDICATED = 1,
        DQTYPE_THREAD_CURRENT = 2,
    };
    
    //struct DispatcherQueueOptions
    //{
    //    DWORD dwSize;
    //    DISPATCHERQUEUE_THREAD_TYPE threadType;
    //    DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
    //};
    [StructLayout(LayoutKind.Sequential)]
    internal struct DispatcherQueueOptions
    {
        public int dwSize;
    
        [MarshalAs(UnmanagedType.I4)]
        public DISPATCHERQUEUE_THREAD_TYPE threadType;
    
        [MarshalAs(UnmanagedType.I4)]
        public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
    };
    
    //HRESULT CreateDispatcherQueueController(
    //  DispatcherQueueOptions options,
    //  ABI::Windows::System::IDispatcherQueueController** dispatcherQueueController
    //);
    [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)]
    internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options,
                                            [MarshalAs(UnmanagedType.IUnknown)]
                                            out object dispatcherQueueController);
    
    #endregion PInvoke declarations
    

    이제 디스패처 큐가 준비되었으며 컴퍼지션 콘텐츠를 초기화하고 만들 수 있습니다.

  5. Compositor를 초기화합니다. Compositor는 Windows.UI.Composition 네임스페이스 내에서 시각적 계층, 효과 시스템 및 애니메이션 시스템에 걸쳐 다양한 형식을 생성하는 팩터리입니다. Compositor 클래스는 팩터리에서 만든 개체의 수명도 관리합니다.

    private void InitComposition(IntPtr hwndHost)
    {
        ICompositorDesktopInterop interop;
    
        compositor = new Compositor();
        object iunknown = compositor as object;
        interop = (ICompositorDesktopInterop)iunknown;
        IntPtr raw;
        interop.CreateDesktopWindowTarget(hwndHost, true, out raw);
    
        object rawObject = Marshal.GetObjectForIUnknown(raw);
        compositionTarget = (ICompositionTarget)rawObject;
    
        if (raw == null) { throw new Exception("QI Failed"); }
    
        containerVisual = compositor.CreateContainerVisual();
        Child = containerVisual;
    }
    
    • ICompositorDesktopInteropICompositionTarget 에는 COM 가져오기가 필요합니다. 이 코드를 CompositionHost 클래스 뒤의 네임스페이스 선언 안에 배치합니다.
    #region COM Interop
    
    /*
    #undef INTERFACE
    #define INTERFACE ICompositorDesktopInterop
        DECLARE_INTERFACE_IID_(ICompositorDesktopInterop, IUnknown, "29E691FA-4567-4DCA-B319-D0F207EB6807")
        {
            IFACEMETHOD(CreateDesktopWindowTarget)(
                _In_ HWND hwndTarget,
                _In_ BOOL isTopmost,
                _COM_Outptr_ IDesktopWindowTarget * *result
                ) PURE;
        };
    */
    [ComImport]
    [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ICompositorDesktopInterop
    {
        void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test);
    }
    
    //[contract(Windows.Foundation.UniversalApiContract, 2.0)]
    //[exclusiveto(Windows.UI.Composition.CompositionTarget)]
    //[uuid(A1BEA8BA - D726 - 4663 - 8129 - 6B5E7927FFA6)]
    //interface ICompositionTarget : IInspectable
    //{
    //    [propget] HRESULT Root([out] [retval] Windows.UI.Composition.Visual** value);
    //    [propput] HRESULT Root([in] Windows.UI.Composition.Visual* value);
    //}
    
    [ComImport]
    [Guid("A1BEA8BA-D726-4663-8129-6B5E7927FFA6")]
    [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
    public interface ICompositionTarget
    {
        Windows.UI.Composition.Visual Root
        {
            get;
            set;
        }
    }
    
    #endregion COM Interop
    

컴퍼지션 요소를 호스트하는 사용자 지정 컨트롤 만들기

CompositionHost에서 파생되는 별도의 컨트롤에 컴퍼지션 요소를 생성하고 관리하는 코드를 배치하는 것이 좋습니다. 이렇게 하면 CompositionHost 클래스에서 만든 interop 코드를 다시 사용할 수 있습니다.

여기서는 CompositionHost에서 파생된 사용자 지정 컨트롤을 만듭니다. 이 컨트롤은 양식에 추가할 수 있도록 Visual Studio 도구 상자에 추가됩니다.

  1. CompositionHost에서 파생되는 새 사용자 지정 컨트롤 파일을 프로젝트에 추가합니다.

    • 솔루션 탐색기에서 HelloComposition 프로젝트를 마우스 오른쪽 버튼으로 클릭합니다.
    • 상황에 맞는 메뉴에서새 항목>...를 선택합니다.
    • 새 항목 추가 대화 상자에서 사용자 지정 컨트롤을 선택합니다.
    • 컨트롤 이름을 CompositionHostControl.cs 다음 추가를 클릭합니다. CompositionHostControl.cs 디자인 보기에서 열립니다.
  2. CompositionHostControl.cs 디자인 보기의 속성 창에서 BackColor 속성을 ControlLight로 설정합니다.

    배경색 설정은 선택 사항입니다. 양식 배경에 대한 사용자 지정 컨트롤을 볼 수 있도록 여기에서 수행합니다.

  3. CompositionHostControl.cs 대한 코드 뷰로 전환하고 CompositionHost에서 파생되도록 클래스 선언을 업데이트합니다.

    class CompositionHostControl : CompositionHost
    
  4. 기본 생성자를 호출하도록 생성자를 업데이트합니다.

    public CompositionHostControl() : base()
    {
    
    }
    

컴퍼지션 요소 추가

인프라를 사용하면 이제 컴퍼지션 콘텐츠를 앱 UI에 추가할 수 있습니다.

이 예제에서는 간단한 SpriteVisual을 만들고 애니메이션 효과를 주는 CompositionHostControl 클래스에 코드를 추가합니다.

  1. 컴퍼지션 요소를 추가합니다.

    CompositionHostControl.cs CompositionHostControl 클래스에 이러한 메서드를 추가합니다.

    // Add
    // using Windows.UI.Composition;
    
    public void AddElement(float size, float offsetX, float offsetY)
    {
        var visual = compositor.CreateSpriteVisual();
        visual.Size = new Vector2(size, size); // Requires references
        visual.Brush = compositor.CreateColorBrush(GetRandomColor());
        visual.Offset = new Vector3(offsetX, offsetY, 0);
        containerVisual.Children.InsertAtTop(visual);
    
        AnimateSquare(visual, 3);
    }
    
    private void AnimateSquare(SpriteVisual visual, int delay)
    {
        float offsetX = (float)(visual.Offset.X);
        Vector3KeyFrameAnimation animation = compositor.CreateVector3KeyFrameAnimation();
        float bottom = Height - visual.Size.Y;
        animation.InsertKeyFrame(1f, new Vector3(offsetX, bottom, 0f));
        animation.Duration = TimeSpan.FromSeconds(2);
        animation.DelayTime = TimeSpan.FromSeconds(delay);
        visual.StartAnimation("Offset", animation);
    }
    
    private Windows.UI.Color GetRandomColor()
    {
        Random random = new Random();
        byte r = (byte)random.Next(0, 255);
        byte g = (byte)random.Next(0, 255);
        byte b = (byte)random.Next(0, 255);
        return Windows.UI.Color.FromArgb(255, r, g, b);
    }
    

폼에 컨트롤 추가

이제 컴퍼지션 콘텐츠를 호스트하는 사용자 지정 컨트롤이 있으므로 앱 UI에 추가할 수 있습니다. 여기서는 이전 단계에서 만든 CompositionHostControl의 인스턴스를 추가합니다. CompositionHostControl은 프로젝트 이름 구성 요소 아래의 Visual Studio 도구 상자에 자동으로 추가됩니다.

  1. Form1.CS 디자인 보기에서 UI에 단추를 추가합니다.

    • 도구 상자에서 Form1로 단추를 끕니다. 폼의 왼쪽 위 모서리에 배치합니다. (컨트롤의 배치를 확인하려면 자습서의 시작 부분에 있는 이미지를 참조하세요.)
    • 속성 창에서 Text 속성을 button1 에서 컴퍼지션 요소 추가로 변경합니다.
    • 모든 텍스트가 표시되도록 단추의 크기를 조정합니다.

    (자세한 내용은 방법: Windows Forms 참조하세요.)

  2. CompositionHostControl을 UI에 추가합니다.

    • CompositionHostControl을 도구 상자에서 Form1로 끕니다. 단추 오른쪽에 배치합니다.
    • Form의 나머지 부분을 채우도록 CompositionHost의 크기를 조정합니다.
  3. 단추 클릭 이벤트를 처리합니다.

    • 속성 창에서 번개를 클릭하여 이벤트 보기로 전환합니다.
    • 이벤트 목록에서 Click 이벤트를 선택하고 Button_Click 입력한 다음 Enter 키를 누릅니다.
    • 이 코드는 Form1.cs 추가됩니다.
    private void Button_Click(object sender, EventArgs e)
    {
    
    }
    
  4. 단추 클릭 처리기에 코드를 추가하여 새 요소를 만듭니다.

    • Form1.cs 이전에 만든 Button_Click 이벤트 처리기에 코드를 추가합니다. 이 코드는 CompositionHostControl1.AddElement 를 호출하여 임의로 생성된 크기와 오프셋을 사용하여 새 요소를 만듭니다. (CompositionHostControl 인스턴스는 폼으로 끌어올 때 compositionHostControl1 이라는 이름이 자동으로 지정됩니다.)
    // Add
    // using System;
    
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Random random = new Random();
        float size = random.Next(50, 150);
        float offsetX = random.Next(0, (int)(compositionHostControl1.Width - size));
        float offsetY = random.Next(0, (int)(compositionHostControl1.Height/2 - size));
        compositionHostControl1.AddElement(size, offsetX, offsetY);
    }
    

이제 Windows Forms 앱을 빌드하고 실행할 수 있습니다. 단추를 클릭하면 UI에 애니메이션 사각형이 추가됩니다.

다음 단계

동일한 인프라에서 빌드하는 보다 완전한 예제는 GitHub Windows Forms 시각적 계층 통합 샘플 참조하세요.

추가 리소스

전체 코드

이 자습서의 전체 코드는 다음과 같습니다.

Form1.cs

using System;
using System.Windows.Forms;

namespace HelloComposition
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, EventArgs e)
        {
            Random random = new Random();
            float size = random.Next(50, 150);
            float offsetX = random.Next(0, (int)(compositionHostControl1.Width - size));
            float offsetY = random.Next(0, (int)(compositionHostControl1.Height/2 - size));
            compositionHostControl1.AddElement(size, offsetX, offsetY);
        }
    }
}

CompositionHostControl.cs

using System;
using System.Numerics;
using Windows.UI.Composition;

namespace HelloComposition
{
    class CompositionHostControl : CompositionHost
    {
        public CompositionHostControl() : base()
        {

        }

        public void AddElement(float size, float offsetX, float offsetY)
        {
            var visual = compositor.CreateSpriteVisual();
            visual.Size = new Vector2(size, size); // Requires references
            visual.Brush = compositor.CreateColorBrush(GetRandomColor());
            visual.Offset = new Vector3(offsetX, offsetY, 0);
            containerVisual.Children.InsertAtTop(visual);

            AnimateSquare(visual, 3);
        }

        private void AnimateSquare(SpriteVisual visual, int delay)
        {
            float offsetX = (float)(visual.Offset.X);
            Vector3KeyFrameAnimation animation = compositor.CreateVector3KeyFrameAnimation();
            float bottom = Height - visual.Size.Y;
            animation.InsertKeyFrame(1f, new Vector3(offsetX, bottom, 0f));
            animation.Duration = TimeSpan.FromSeconds(2);
            animation.DelayTime = TimeSpan.FromSeconds(delay);
            visual.StartAnimation("Offset", animation);
        }

        private Windows.UI.Color GetRandomColor()
        {
            Random random = new Random();
            byte r = (byte)random.Next(0, 255);
            byte g = (byte)random.Next(0, 255);
            byte b = (byte)random.Next(0, 255);
            return Windows.UI.Color.FromArgb(255, r, g, b);
        }
    }
}

CompositionHost.cs

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Windows.UI.Composition;

namespace HelloComposition
{
    public partial class CompositionHost : Control
    {
        IntPtr hwndHost;
        object dispatcherQueue;
        protected ContainerVisual containerVisual;
        protected Compositor compositor;
        private ICompositionTarget compositionTarget;

        public Visual Child
        {
            set
            {
                if (compositor == null)
                {
                    InitComposition(hwndHost);
                }
                compositionTarget.Root = value;
            }
        }

        public CompositionHost()
        {
            // Get the window handle.
            hwndHost = Handle;

            // Create dispatcher queue.
            dispatcherQueue = InitializeCoreDispatcher();

            // Build Composition tree of content.
            InitComposition(hwndHost);
        }

        private object InitializeCoreDispatcher()
        {
            DispatcherQueueOptions options = new DispatcherQueueOptions();
            options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA;
            options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT;
            options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));

            object queue = null;
            CreateDispatcherQueueController(options, out queue);
            return queue;
        }

        private void InitComposition(IntPtr hwndHost)
        {
            ICompositorDesktopInterop interop;

            compositor = new Compositor();
            object iunknown = compositor as object;
            interop = (ICompositorDesktopInterop)iunknown;
            IntPtr raw;
            interop.CreateDesktopWindowTarget(hwndHost, true, out raw);

            object rawObject = Marshal.GetObjectForIUnknown(raw);
            compositionTarget = (ICompositionTarget)rawObject;

            if (raw == null) { throw new Exception("QI Failed"); }

            containerVisual = compositor.CreateContainerVisual();
            Child = containerVisual;
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
        }

        #region PInvoke declarations

        //typedef enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
        //{
        //    DQTAT_COM_NONE,
        //    DQTAT_COM_ASTA,
        //    DQTAT_COM_STA
        //};
        internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
        {
            DQTAT_COM_NONE = 0,
            DQTAT_COM_ASTA = 1,
            DQTAT_COM_STA = 2
        };

        //typedef enum DISPATCHERQUEUE_THREAD_TYPE
        //{
        //    DQTYPE_THREAD_DEDICATED,
        //    DQTYPE_THREAD_CURRENT
        //};
        internal enum DISPATCHERQUEUE_THREAD_TYPE
        {
            DQTYPE_THREAD_DEDICATED = 1,
            DQTYPE_THREAD_CURRENT = 2,
        };

        //struct DispatcherQueueOptions
        //{
        //    DWORD dwSize;
        //    DISPATCHERQUEUE_THREAD_TYPE threadType;
        //    DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
        //};
        [StructLayout(LayoutKind.Sequential)]
        internal struct DispatcherQueueOptions
        {
            public int dwSize;

            [MarshalAs(UnmanagedType.I4)]
            public DISPATCHERQUEUE_THREAD_TYPE threadType;

            [MarshalAs(UnmanagedType.I4)]
            public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
        };

        //HRESULT CreateDispatcherQueueController(
        //  DispatcherQueueOptions options,
        //  ABI::Windows::System::IDispatcherQueueController** dispatcherQueueController
        //);
        [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)]
        internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options,
                                                 [MarshalAs(UnmanagedType.IUnknown)]
                                        out object dispatcherQueueController);

        #endregion PInvoke declarations
    }

    #region COM Interop

    /*
    #undef INTERFACE
    #define INTERFACE ICompositorDesktopInterop
        DECLARE_INTERFACE_IID_(ICompositorDesktopInterop, IUnknown, "29E691FA-4567-4DCA-B319-D0F207EB6807")
        {
            IFACEMETHOD(CreateDesktopWindowTarget)(
                _In_ HWND hwndTarget,
                _In_ BOOL isTopmost,
                _COM_Outptr_ IDesktopWindowTarget * *result
                ) PURE;
        };
    */
    [ComImport]
    [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ICompositorDesktopInterop
    {
        void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test);
    }

    //[contract(Windows.Foundation.UniversalApiContract, 2.0)]
    //[exclusiveto(Windows.UI.Composition.CompositionTarget)]
    //[uuid(A1BEA8BA - D726 - 4663 - 8129 - 6B5E7927FFA6)]
    //interface ICompositionTarget : IInspectable
    //{
    //    [propget] HRESULT Root([out] [retval] Windows.UI.Composition.Visual** value);
    //    [propput] HRESULT Root([in] Windows.UI.Composition.Visual* value);
    //}

    [ComImport]
    [Guid("A1BEA8BA-D726-4663-8129-6B5E7927FFA6")]
    [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
    public interface ICompositionTarget
    {
        Windows.UI.Composition.Visual Root
        {
            get;
            set;
        }
    }
    #endregion COM Interop
}