Verwenden der visuellen Ebene mit Windows Forms

Sie können Windows-Runtime Kompositions-APIs (auch als Visualschicht bekannt) in Ihren Windows Forms-Apps verwenden, um moderne Erlebnisse zu schaffen, die für Windows-Benutzer erstrahlen.

Der vollständige Code für dieses Lernprogramm ist für GitHub verfügbar: Windows Forms HelloComposition-Beispiel.

Voraussetzungen

Für die UWP-Hosting-API gelten diese Voraussetzungen.

Verwenden von Kompositions-APIs in Windows Forms

In diesem Lernprogramm erstellen Sie eine einfache Windows Forms UI und fügen diesem animierte Kompositionselemente hinzu. Sowohl die Windows Forms- als auch die Kompositionskomponenten sind einfach gehalten, aber der dargestellte Interoperabilitätscode ist unabhängig von der Komplexität der Komponenten identisch. Die fertige App sieht wie folgt aus.

Benutzeroberfläche der laufenden App

Erstellen eines Windows Forms Projekts

Der erste Schritt besteht darin, das Windows Forms App-Projekt zu erstellen, das eine Anwendungsdefinition und das Hauptformular für die Benutzeroberfläche enthält.

So erstellen Sie ein neues Windows Forms Application-Projekt in Visual C# mit dem Namen HelloComposition:

  1. Öffne Visual Studio, und wähle Datei>Neu>Projekt aus.
    Das Dialogfeld Neues Projekt wird geöffnet.
  2. Erweitern Sie unter der Kategorie Installed den Knoten Visual C#, und wählen Sie dann Windows Desktop aus.
  3. Wählen Sie die Vorlage Windows Forms App (.NET Framework) aus.
  4. Geben Sie den Namen HelloComposition ein, wählen Sie das Framework .NET Framework 4.7.2 aus, und klicken Sie dann auf OK.

Visual Studio erstellt das Projekt und öffnet den Designer für das Standardanwendungsfenster mit dem Namen Form1.cs.

Konfigurieren des Projekts für die Verwendung der Windows-Runtime-APIs

Um Windows-Runtime -APIs (WinRT) in Ihrer Windows Forms-App zu verwenden, müssen Sie Ihr Visual Studio Projekt für den Zugriff auf die Windows-Runtime konfigurieren. Darüber hinaus werden Vektoren umfassend von den Kompositions-APIs verwendet, daher müssen Sie die für die Verwendung von Vektoren erforderlichen Verweise hinzufügen.

NuGet-Pakete sind verfügbar, um beide Anforderungen zu erfüllen. Installieren Sie die neuesten Versionen dieser Pakete, um dem Projekt die erforderlichen Verweise hinzuzufügen.

Note

Während wir die Verwendung der NuGet-Pakete zum Konfigurieren Ihres Projekts empfehlen, können Sie die erforderlichen Verweise manuell hinzufügen. Weitere Informationen finden Sie unter Optimieren Sie Ihre Desktopanwendung für Windows. In der folgenden Tabelle sind die Dateien aufgeführt, auf die Sie Verweise hinzufügen müssen.

Datei Standort
System.Runtime.WindowsRuntime C:\Windows\Microsoft.NET\Framework\v4.0.30319
Windows.Foundation.UniversalApiContract.winmd C:\Programme (x86)\Windows Kits\10\References<sdk version>\Windows.Foundation.UniversalApiContract<version>
Windows.Foundation.FoundationContract.winmd C:\Programme (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

Erstellen eines benutzerdefinierten Steuerelements zum Verwalten der Interoperabilität

Zum Hosten von Inhalten, die Sie mit der visuellen Ebene erstellen, erstellen Sie ein von Steuerelement abgeleitetes benutzerdefiniertes Steuerelement. Mit diesem Steuerelement erhalten Sie Zugriff auf ein Fensterhandle, das Sie benötigen, um den Container für Ihre visuellen Layerinhalte zu erstellen.

Hier führen Sie die meisten Konfigurationen für das Hosten von Kompositions-APIs durch. In diesem Steuerelement verwenden Sie Platform Invocation Services (PInvoke) und COM Interop, um Kompositions-APIs in Ihre Windows Forms-App zu integrieren. Weitere Informationen zu PInvoke und COM-Interoperabilität finden Sie unter Interoperieren mit nicht verwalteten Code.

Tipp

Überprüfe bei Bedarf am Ende des Tutorials den vollständigen Code, um sicherzustellen, dass sich der gesamte Code während des Durcharbeitens an den richtigen Stellen befindet.

  1. Fügen Sie ihrem Projekt eine neue benutzerdefinierte Steuerelementdatei hinzu, die von Control abgeleitet wird.

    • Klicke im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt HelloComposition.
    • Wählen Sie im Kontextmenü "Neues Element hinzufügen>..." aus.
    • Wählen Sie im Dialogfeld " Neues Element hinzufügen " die Option "Benutzerdefiniertes Steuerelement" aus.
    • Benennen Sie das Steuerelement CompositionHost.cs, und klicken Sie dann auf "Hinzufügen". CompositionHost.cs wird in der Entwurfsansicht geöffnet.
  2. Wechseln Sie zur Codeansicht für CompositionHost.cs, und fügen Sie der Klasse den folgenden Code hinzu.

    // 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. Fügen Sie dem Konstruktor Code hinzu.

    Im Konstruktor rufen Sie die Methoden InitializeCoreDispatcher und InitComposition auf. Du erstellst diese Methoden in den nächsten Schritten.

    public CompositionHost()
    {
        InitializeComponent();
    
        // Get the window handle.
        hwndHost = Handle;
    
        // Create dispatcher queue.
        dispatcherQueue = InitializeCoreDispatcher();
    
        // Build Composition tree of content.
        InitComposition(hwndHost);
    }
    
  4. Initialisieren sie einen Thread mit einem CoreDispatcher. Der Kernverteiler ist für die Verarbeitung von Fenstermeldungen und Verteilerereignissen für WinRT-APIs verantwortlich. Neue Instanzen von Compositor müssen in einem Thread erstellt werden, der über einen CoreDispatcher verfügt.

    • Erstellen Sie eine Methode mit dem Namen InitializeCoreDispatcher , und fügen Sie Code zum Einrichten der Dispatcherwarteschlange hinzu.
    // 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;
    }
    
    • Für die Dispatcher-Warteschlange ist eine PInvoke-Deklaration erforderlich. Platzieren Sie diese Deklaration am Ende des Codes für die Klasse. (Wir platzieren diesen Code in einem Bereich, um den Klassencode übersichtlich zu halten.)
    #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
    

    Sie haben nun die Dispatcher-Warteschlange bereit und können mit Initialisierung und Erstellung von Kompositionsinhalten beginnen.

  5. Initialisieren Sie den Kompositor. Der Compositor ist eine Fabrik, die eine Vielzahl von Typen im Windows.UI.Composition-Namespace erstellt und sich über die visuelle Ebene, das Effektsystem und das Animationssystem erstreckt. Die Compositor-Klasse verwaltet auch die Lebensdauer von Objekten, die von der Factory erstellt wurden.

    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;
    }
    
    • ICompositorDesktopInterop und ICompositionTarget erfordern COM-Importe. Platzieren Sie diesen Code nach der CompositionHost-Klasse , aber innerhalb der Namespacedeklaration.
    #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
    

Erstellen eines benutzerdefinierten Steuerelements zum Hosten von Kompositionselementen

Es empfiehlt sich, den Code zu platzieren, der Ihre Kompositionselemente generiert und verwaltet, in einem separaten Steuerelement, das von CompositionHost abgeleitet wird. Dadurch bleibt der Interoperabilitätscode, den Sie in der CompositionHost-Klasse erstellt haben, wiederverwendbar.

Hier erstellen Sie ein benutzerdefiniertes Steuerelement, das von CompositionHost abgeleitet ist. Dieses Steuerelement wird der toolbox Visual Studio hinzugefügt, damit Sie es ihrem Formular hinzufügen können.

  1. Fügen Sie Ihrem Projekt eine neue benutzerdefinierte Steuerelementdatei hinzu, die von CompositionHost abgeleitet wird.

    • Klicke im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt HelloComposition.
    • Wählen Sie im Kontextmenü "Neues Element hinzufügen>..." aus.
    • Wählen Sie im Dialogfeld " Neues Element hinzufügen " die Option "Benutzerdefiniertes Steuerelement" aus.
    • Benennen Sie das Steuerelement CompositionHostControl.cs, und klicken Sie dann auf "Hinzufügen". CompositionHostControl.cs wird in der Entwurfsansicht geöffnet.
  2. Legen Sie im Eigenschaftenbereich für CompositionHostControl.cs Entwurfsansicht die BackColor-Eigenschaft auf ControlLight fest.

    Das Festlegen der Hintergrundfarbe ist optional. Wir tun es hier, damit Sie Ihr benutzerdefiniertes Steuerelement im Formularhintergrund sehen können.

  3. Wechseln Sie zur Codeansicht für CompositionHostControl.cs, und aktualisieren Sie die Klassendeklaration so, dass sie von CompositionHost abgeleitet wird.

    class CompositionHostControl : CompositionHost
    
  4. Aktualisieren Sie den Konstruktor, um den Basiskonstruktor aufzurufen.

    public CompositionHostControl() : base()
    {
    
    }
    

Hinzufügen von Composition-Elementen

Nachdem die Infrastruktur eingerichtet wurde, können Sie der App-Benutzeroberfläche jetzt Kompositionsinhalte hinzufügen.

In diesem Beispiel fügen Sie der CompositionHostControl-Klasse Code hinzu, die ein einfaches SpriteVisual erstellt und animiert.

  1. Füge ein Kompositionselement hinzu.

    Fügen Sie in CompositionHostControl.cs diese Methoden der CompositionHostControl-Klasse hinzu.

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

Hinzufügen des Steuerelements zu Ihrem Formular

Nachdem Sie nun über ein benutzerdefiniertes Steuerelement zum Hosten von Kompositionsinhalten verfügen, können Sie es der App-Benutzeroberfläche hinzufügen. Hier fügen Sie eine Instanz von CompositionHostControl hinzu, die Sie im vorherigen Schritt erstellt haben. CompositionHostControl wird der toolbox Visual Studio unter project name Components automatisch hinzugefügt.

  1. Fügen Sie in Form1.CS Entwurfsansicht der Benutzeroberfläche eine Schaltfläche hinzu.

    • Ziehen Sie eine Schaltfläche aus der Toolbox auf Form1. Platzieren Sie es in der oberen linken Ecke des Formulars. (Sehen Sie sich das Bild am Anfang des Lernprogramms an, um die Platzierung der Steuerelemente zu überprüfen.)
    • Ändern Sie im Eigenschaftenbereich die Texteigenschaft von schaltfläche1 in "Kompositionselement hinzufügen".
    • Ändern Sie die Größe der Schaltfläche so, dass der gesamte Text angezeigt wird.

    (Weitere Informationen finden Sie unter How to: Add Controls to Windows Forms.)

  2. Fügen Sie der Benutzeroberfläche ein CompositionHostControl-Objekt hinzu.

    • Ziehen Sie ein CompositionHostControl-Objekt aus der Toolbox auf Form1. Platzieren Sie es rechts neben der Schaltfläche.
    • Ändern Sie die Größe des CompositionHost so, dass sie den Rest des Formulars ausfüllt.
  3. Behandeln Sie das Ereignis des Klicks auf die Schaltfläche.

    • Klicken Sie im Eigenschaftenbereich auf den Blitz, um zur Ansicht "Ereignisse" zu wechseln.
    • Wählen Sie in der Ereignisliste das Click-Ereignis aus, geben Sie Button_Click ein, und drücken Sie die EINGABETASTE.
    • Dieser Code wird in Form1.cs hinzugefügt:
    private void Button_Click(object sender, EventArgs e)
    {
    
    }
    
  4. Fügen Sie dem Schaltflächenklickhandler Code hinzu, um neue Elemente zu erstellen.

    • Fügen Sie in Form1.cs Code zur Ereignishandler-Methode Button_Click hinzu, die Sie zuvor erstellt haben. Dieser Code ruft CompositionHostControl1.AddElement auf, um ein neues Element mit einer zufällig generierten Größe und einem Offset zu erstellen. (Die Instanz von CompositionHostControl wurde automatisch als compositionHostControl1 bezeichnet, als Sie es auf das Formular gezogen haben.)
    // 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);
    }
    

Sie können jetzt Ihre Windows Forms-App erstellen und ausführen. Wenn Sie auf die Schaltfläche klicken, sollten animierte Quadrate angezeigt werden, die der Benutzeroberfläche hinzugefügt wurden.

Nächste Schritte

Ein vollständiges Beispiel, das auf derselben Infrastruktur basiert, finden Sie im Beispiel Windows Forms Visual Layer Integration für GitHub.

Weitere Ressourcen

Vollständiger Code

Hier ist der vollständige Code für dieses Lernprogramm.

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
}