smourier/DirectN

IDCompositionRectangleClip Not Working

Closed this issue · 2 comments

I have been porting a C++ direct composition Code to CS, In my C++ code I use IDCompositionRectangleClip to clip the visual, it is working fine with C++, when I am trying to implement it in CS using DirectN, the visual Disappears.

in C++ I used :

ComPtr<IDCompositionRectangleClip> clip;

(dcompDevice3->CreateRectangleClip(&clip)

GetWindowRect(hwnd, &hostWindowRect);
clip->SetLeft((float)hostWindowRect.left);
clip->SetRight((float)hostWindowRect.right);
clip->SetTop((float)hostWindowRect.top);
clip->SetBottom((float)hostWindowRect.bottom);
rootVisual->SetClip(clip.Get());

dcompDevice->Commit()

The C# Code I Used:

public IComObject<IDCompositionRectangleClip> clip;

hr = dcompDevice3.Object.CreateRectangleClip(out var c);
clip = new ComObject<IDCompositionRectangleClip>(c);
if (hr != HRESULTS.S_OK)
{
       MessageBox.Show("Failed to Create Rectangle Clip");
}

clip.Object.SetLeft(50.0f);
clip.Object.SetTop(50.0f);
clip.Object.SetRight(500.0f);
clip.Object.SetBottom(500.0f);
visual.Object.SetClip(clip.Object);
dcompDevice3.Object.Commit();

This is how my Window looks like before clipping:

image

After Clipping the entire visual is missing, and only the window frame is visible:

image

Edited:

It's not only the RectangleClip not working, i tried SetOffsetX() method of the root Visual it is also having issues, the offset is not set. (in the code below i haven't used the rectangle clip just offset)

Here is the complete code i used to set offset:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DirectN;
using MessageBox = System.Windows.MessageBox;

namespace WPF_dllhost
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left, top, right, bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct DWM_THUMBNAIL_PROPERTIES
        {
            public int dwFlags;
            public RECT rcDestination;
            public RECT rcSource;
            public byte opacity;
            public int fVisible; //BOOL{TRUE,FALSE}
            public int fSourceClientAreaOnly; //BOOL{TRUE,FALSE}
        }

        public IComObject<ID3D11Device> d3ddevice;
        public IComObject<IDXGIDevice> dxgiDevice;
        public IComObject<IDXGIFactory2> dxFactory;
        public IComObject<IDCompositionDesktopDevice> desktopDevice;
        public IComObject<IDCompositionDevice3> dcompDevice3;
        public IComObject<IDCompositionTarget> target;
        public IComObject<IDCompositionVisual2> visual;
        public IComObject<IDCompositionVisual2> rootVisual;
        public IComObject<IDCompositionRectangleClip> clip;

        IntPtr dcomp = IntPtr.Zero;

        [DllImport("dwmapi.dll", CharSet = CharSet.Auto, EntryPoint = "#147")]
        private static extern IntPtr DwmpCreateSharedThumbnailVisual(IntPtr hwndDest, IntPtr hwndSource,uint thumbnailFlags, DWM_THUMBNAIL_PROPERTIES prop,IntPtr device,out IntPtr visualout, out IntPtr thumb);

        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);


        public MainWindow()
        {
            InitializeComponent();
        }

        private void CreateCompositionDevice(IntPtr hwnd)
        {
            d3ddevice = D3D11Functions.D3D11CreateDevice(null, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE, D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_BGRA_SUPPORT, null, 7);
            if(d3ddevice==null)
            {
                MessageBox.Show("Failed to create D3D11 Device");
            }

            dxgiDevice = new ComObject<IDXGIDevice>(d3ddevice.As<IDXGIDevice>());
            if (dxgiDevice == null)
            {
                MessageBox.Show("Failed to get DXGI Device");
            }

            Functions.DCompositionCreateDevice3(dxgiDevice.Object, new Guid("5f4633fe-1e08-4cb8-8c75-ce24333f5602"), out var pdesktopDevice);
            desktopDevice = new ComObject<IDCompositionDesktopDevice>(Marshal.GetObjectForIUnknown(pdesktopDevice) as IDCompositionDesktopDevice);
            if (desktopDevice == null)
            {
                MessageBox.Show("Failed to Create Composition Desktop Device");
            }

            Guid IID_IDcompositionDevice3 = new Guid("0987CB06-F916-48BF-8D35-CE7641781BD9");
            Marshal.QueryInterface(pdesktopDevice, ref IID_IDcompositionDevice3, out var dcomp3);
            dcompDevice3 = new ComObject<IDCompositionDevice3>(Marshal.GetObjectForIUnknown(dcomp3) as IDCompositionDevice3);
            if (dcompDevice3 == null)
            {
                MessageBox.Show("Failed to Query IDCompositionDevice3 Interface");
            }

            HRESULT hr = desktopDevice.Object.CreateTargetForHwnd(hwnd, true, out var t);
            target = new ComObject<IDCompositionTarget>(t);
            if (hr != HRESULTS.S_OK)
            {
                MessageBox.Show("Failed to Create Composition Target");
            }

            hr = dcompDevice3.Object.CreateVisual(out var v);
            rootVisual = new ComObject<IDCompositionVisual2>(v);
            if (hr != HRESULTS.S_OK)
            {
                MessageBox.Show("Failed to Create Root Visual");
            }

            hr = dcompDevice3.Object.CreateRectangleClip(out var c);
            clip = new ComObject<IDCompositionRectangleClip>(c);
            if (hr != HRESULTS.S_OK)
            {
                MessageBox.Show("Failed to Create Rectangle Clip");
            }


            DWM_THUMBNAIL_PROPERTIES thumbnail=new DWM_THUMBNAIL_PROPERTIES();
            var sourceRect = new RECT
            {
                left =0,
                top = 0,
                bottom = 768,
                right =1366
            };

            var destRect = new RECT
            {
                left = 0,
                top = 0,
                bottom = 768,
                right = 1366
            };


            thumbnail.dwFlags = 0x00000010 | 0x00000008 | 0x00000001 | 0x00000002 | 0x00000004 | 0x4000000;
            thumbnail.opacity = 255;
            thumbnail.fVisible = 1;
            thumbnail.fSourceClientAreaOnly = 1;
            thumbnail.rcDestination = destRect;
            thumbnail.rcSource = sourceRect;

            DwmpCreateSharedThumbnailVisual(hwnd, FindWindow("progman",null), 2, thumbnail, dcomp3, out var desktopVisual, out var thumb);

            visual = new ComObject<IDCompositionVisual2>((IDCompositionVisual2)Marshal.GetObjectForIUnknown(desktopVisual));


            rootVisual.Object.AddVisual(visual.Object, true, null);
            rootVisual.Object.SetOffsetX(200); // not working
            target.Object.SetRoot(rootVisual.Object);
            dcompDevice3.Object.Commit();
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            new BorderlessWindow(new WindowInteropHelper(this).EnsureHandle());
            CreateCompositionDevice(new WindowInteropHelper(this).EnsureHandle());
            this.SizeChanged += MainWindow_SizeChanged;
        }

        private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
           

        }
    }
}

I think it may be due to what's described here: https://stackoverflow.com/questions/61527633/wrong-vtable-generated-by-c-compiler-for-com-object

Many DComp interfaces have members with the same name but with different arguments, for example: IDCompositionRectangleClip's SetLeft has two "overloads".

You can try to redefine them manually (invert members) just to check if it works better, ie:

[ComImport, Guid("9842ad7d-d9cf-4908-aed7-48b51da5e7c2"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public partial interface IDCompositionRectangleClip : IDCompositionClip
{
    // IDCompositionClip
    
    // IDCompositionRectangleClip
    [PreserveSig]
    HRESULT SetLeft(/* THIS_ _In_ */ IDCompositionAnimation animation);
    
    [PreserveSig]
    HRESULT SetLeft(float left);
    ...
}

Note other notable interop projects have the same issue (but they are not even aware): https://github.com/microsoft/win32metadata

PS: you should use the newer WinRT's Windows.UI.Composition, it's much easier to use in .NET, you won't face this issue, and you don't need DirectN.

Thank you for helping.