microsoft / Windows.UI.Composition-Win32-Samples

Windows.UI.Composition Win32 Samples
MIT License
459 stars 186 forks source link

Screen Capture WinForms: System.UnauthorizedAccessException #108

Closed Stealth01 closed 2 years ago

Stealth01 commented 2 years ago

I'm trying to apply the WPF Screen Capture sample in a Windows Forms project, but I get the exception System.UnauthorizedAccessException at this line:

compositor = new Compositor();

I attach my Windows Forms project. It's just a "translation" of the WPF Screen Capture sample and probably contains several errors.

WinForms.zip

robmikh commented 2 years ago

You need to create a DispatcherQueue(Controller) on the same thread that creates the Compositor.

Stealth01 commented 2 years ago

Ok, thanks for the quick reply. I've created the DispatcherQueue (just copy and paste from the WinForms Hello Composition sample) and now the project run without errors. However, the cloned image is not drawn on the form. I think there is something to change in BasicCapture.cs.

WinForms ScreenCapture with DispatcherQueue.zip

robmikh commented 2 years ago

No, it's because you positioned your content outside the bounds of the window's initial size:

Inside Form1.cs you set controlsWidth to the width of the window in Form1_Load:

private void Form1_Load(object sender, EventArgs e)
{
    //var interopWindow = new WindowInteropHelper(this);
    hwnd = Handle; //interopWindow.Handle;

    //var presentationSource = PresentationSource.FromVisual(this);
    //double dpiX = 1.0;
    //double dpiY = 1.0;
    //if (presentationSource != null)
    //{
    //    dpiX = presentationSource.CompositionTarget.TransformToDevice.M11;
    //    dpiY = presentationSource.CompositionTarget.TransformToDevice.M22;
    //}
    var controlsWidth = (float)(ClientSize.Width); //* dpiX);  // <---- The width of the window

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

    InitComposition(controlsWidth);
    InitWindowList();
    InitMonitorList();
}

Later, you use this to set the offset of the root visual in InitComposition:

private void InitComposition(float controlsWidth)
{
    // Create the compositor.
    compositor = new Compositor();

    // Create a target for the window.
    target = compositor.CreateDesktopWindowTarget(hwnd, true);

    // Attach the root visual.
    root = compositor.CreateContainerVisual();
    root.RelativeSizeAdjustment = Vector2.One;
    root.Size = new Vector2(-controlsWidth, 0);
    root.Offset = new Vector3(controlsWidth, 0, 0);   // <---- This visual will now be positioned to the right of the right edge of the window
    target.Root = root;

    // Setup the rest of the sample application.
    sample = new BasicSampleApplication(compositor);
    root.Children.InsertAtTop(sample.Visual);
}

If you resize your window to be larger, you'll see the capture content is there.

The WPF Sample has a similar pattern, but it isn't using the width of the window. It's using the width of the grid used to house the controls. This ensures that the content is positioned to the right of the controls.