1

My CaptureElements are showing strange behavior. When I set a instantiated MediaCapture as the CaptureElements Source and then call MediaCapture.StartPreviewAsync() the CaptureElement doesn't show anything.

I have one Application (main-app) with a functional BarcodeScanner on the LoginPage. -> Works!

Then I wanted to copy the same code to the SettingsPage with small modifications so in case of several attached cameras, the default one can be set. -> Doesn't work

Then I tried to run the main-app with the help of the remote debugger on other windows tablets with same Windows 10 Version as my machine (keep in mind, that the BarcodeScanner on the Login-Screen works on my machine). -> doesn't work

Because of these failures I copied the running code from the main-apps LoginPage to a completely new solution (lets call it test-app) with the same settings as the original one. I even experimented with referencing the same Dlls, implementing the same design pattern etc. -> doesn't work

My Machine: Win 10 Pro Version 1809 Build 17763.652

DevEnv: MS Visual Studio 2019 Pro Vers. 16.1.6

EDIT: As minimum required Windows Version I selected Build 16229 and my target Version is Build 17763 (my systems Win version)

The "Allow apps to access your camera"-Option in the Widows Settings is switched to ON, so all apps are allowed to access the camera.

Xaml

    <Page
        x:Class="QrCodeTest.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:QrCodeTest"
        xmlns:vm="using:QrCodeTest.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">

        <Page.DataContext>
            <vm:TestViewModel x:Name="ViewModel" />
        </Page.DataContext>

        <ScrollViewer>
            <StackPanel>
                <Button Content="Start Preview" HorizontalAlignment="Center" Click="Button_Click" Margin="5" />

                <CaptureElement x:Name="capturePreview" HorizontalAlignment="Center" Stretch="Uniform" Width="0" Height="0" Margin="10" />

                <Button Content="Stop Preview" HorizontalAlignment="Center" Click="Button_Click_1" Margin="5" />

                <TextBlock Text="{Binding Etikett, Mode=TwoWay}" HorizontalAlignment="Center" Margin="5" />
            </StackPanel>
        </ScrollViewer>
    </Page>

CodeBehind

private BarcodeScanner scanner { get; set; }
private ClaimedBarcodeScanner claimedScanner { get; set; }
private MediaCapture captureManager { get; set; }

internal async Task StartScannerAsync () {
            capturePreview.Visibility = Visibility.Visible;
            capturePreview.Width = 400; capturePreview.Height = 300;

            scanner = null;
            scanner = await DeviceHelpers.GetFirstDeviceAsync(BarcodeScanner.GetDeviceSelector(connectionTypes), async (id) => await BarcodeScanner.FromIdAsync(id));

            if (scanner != null) {
                captureManager = new MediaCapture();
                claimedScanner = await scanner.ClaimScannerAsync();

                if (claimedScanner != null) {
                    claimedScanner.ReleaseDeviceRequested += claimedScanner_ReleaseDeviceRequested;
                    claimedScanner.DataReceived += claimedScanner_DataReceived;

                    claimedScanner.IsDecodeDataEnabled = true;
                    IReadOnlyList<uint> supportedSymbologies = await scanner.GetSupportedSymbologiesAsync();

                    foreach (uint symbology in supportedSymbologies) {
                        listOfSymbologies.Add(new SymbologyListEntry(symbology));
                    }

                    await claimedScanner.EnableAsync();

                    MediaCaptureInitializationSettings _captureInitSettings = new MediaCaptureInitializationSettings {
                        VideoDeviceId = scanner.VideoDeviceId,
                        StreamingCaptureMode = StreamingCaptureMode.AudioAndVideo,
                        PhotoCaptureSource = PhotoCaptureSource.VideoPreview
                    };

                    await captureManager.InitializeAsync(_captureInitSettings);
                    capturePreview.Source = captureManager;

                    try {
                        // Change to false, in case you wanna compare different methods of doing the same
                        bool Like_MP_PAT_UWP = false;

                        if (Like_MP_PAT_UWP) {
                            await capturePreview.Source.StartPreviewAsync();
                            await claimedScanner.StartSoftwareTriggerAsync();
                        } else {


                            LocalDataContext.Etikett = "await captureManager.StartPreviewAsync();";
                            await captureManager.StartPreviewAsync();
                            await claimedScanner.StartSoftwareTriggerAsync();
                            Thread.Sleep(2000);
                            await claimedScanner.StopSoftwareTriggerAsync();
                            await captureManager.StopPreviewAsync();

                            LocalDataContext.Etikett = "await capturePreview.Source.StartPreviewAsync();";
                            await capturePreview.Source.StartPreviewAsync();
                            await claimedScanner.StartSoftwareTriggerAsync();
                            Thread.Sleep(2000);
                            await claimedScanner.StopSoftwareTriggerAsync();
                            await capturePreview.Source.StopPreviewAsync();

                            LocalDataContext.Etikett = "await claimedScanner.ShowVideoPreviewAsync();";
                            await claimedScanner.ShowVideoPreviewAsync();
                            await claimedScanner.StartSoftwareTriggerAsync();
                            Thread.Sleep(2000);
                            await claimedScanner.StopSoftwareTriggerAsync();
                            claimedScanner.HideVideoPreview();
                        }

                    } catch (Exception e) {
                        Exception x = e; displayRequest.RequestRelease();
                    } finally {
                        LocalDataContext.Etikett = string.Empty;
                    }

                }
            }
        }

ViewModel:

public class TestViewModel: INotifyPropertyChanged {
        public static TestViewModel Instance { get; set; }

        private string _Etikett;
        public string Etikett { get { return _Etikett; } set { _Etikett = value; NotifyPropertyChanged(); } }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged ([CallerMemberName] String propertyName = "") {
            //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

            if (PropertyChanged != null) {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
}

I've wasted already 4 working days just to compare the solutions, the codes etc. The above code was copied from the test-app but it's mostly identical to the one on the main-apps LoginPage (except for the "if (Like_MP_PAT_UWP) {...}".

Every hint is welcome.

Thank you in advance.

  • Hello, have you turned on `Pointer of Services` permissions in `Package.appxmanifest`? – Richard Zhang Jul 31 '19 at 13:36
  • Hello, yes i did. PointOfService as well as WebCam. In the Test-app I granted all available permissions in the appxmanifest. I even tried several Code signing certificates. Nothing helped. Additional Info: As minimum required WindowsVersion I selected Build 16229 and my target Build is 17763 (my systems Win version) – SeriousBusinessGuy Jul 31 '19 at 14:09
  • Did you add a breakpoint while the program is running? Since your code lacks the key function `GetFisrtDeviceAsync`, I can't tell if the problem is there. You can try to step through the program to see if it runs smoothly to `captureManager.StartPreviewAsync()` – Richard Zhang Jul 31 '19 at 14:53
  • Yes I have set thousands of debug points. I'm calling GetFirstDeviceAsync() in the CodeBehind. Please have a look in the second code block. There You will see: scanner = await DeviceHelpers.GetFirstDeviceAsync( – SeriousBusinessGuy Jul 31 '19 at 15:08
  • 1
    I understand. I found a similar method in the [example](https://github.com/microsoft/Windows-universal-samples/blob/master/SharedContent/cs/DeviceHelpers.cs) provided by Microsoft, but I don't know if you implemented it like this. – Richard Zhang Jul 31 '19 at 15:18
  • my DeviceHelper-Class is identical to the code in the ressource you posted. I just copied it from somewhere else but that's the way the internet is, i guess. For me it looks like a random Microsoft behavior (easiest explanation). What's the best place to upload the sln so someone can have a look? Maybe it'll run on someone elses machine (restrictsearch area). or someone else may find a wrong setting I've overlooked. The other problem I have, when I run main-app on one of our tablets is described [here](https://stackoverflow.com/questions/56649293/deviceinformation-findallasync-doing-nothing) – SeriousBusinessGuy Aug 01 '19 at 07:22

2 Answers2

3

The Problem was Kaspersky Endpoint Security's "advanced Threat Protection/host intrusion prevention"-setting. It prevented ALL apps outside from our dev-harddrive (i. e. on our tablets or from our network-drive) to access the camera (Dev-Drive = "Trusted Zone").

It was necessary to reconfigure that feature in Kaspersky Endpoint Security for the whole environment (declare necessary locations/clients as a trusted zone).

Hope, this might help someone with a similar problem or at least give a hint to someone.

1

Just spit-balling here, but I'd suggest attempting to reduce your test to just code that controls the MediaCapture object as much as possible, as that seems to be the symptom where you describe the main issue.

After that, try lowering the SharingMode of the camera to just read-only, in case another app is using the camera with exclusive access. Also, you can reduce the pop up consent check to just the camera, and not have the microphone consent. Sometimes if you accidentally don't agree during the consent pop-up, the app will be denied access to the camera until you allow it again from the system settings (Settings -> Privacy -> Camera).

Below is a sub-optimal and boiled down version of what you described above, but all parts included. I tried to segregate your starting a barcode session from disposing things. Using the MS samples as a guide will be far more reliable than this one. Nonetheless, there's lots more trace points to add, but below has a few traces around where the MediaCapture fails, and other points in the barcode scanner enable section. Hope it helps.

using System;
using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

using Windows.Devices.PointOfService;
using System.Threading.Tasks;
using Windows.Media.Capture;
using Windows.Devices.Enumeration;
using System.Diagnostics;
using Windows.Storage.Streams;

namespace StackOverflowQrTest
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            if (claimedScanner == null)
            { 
                await StartScannerAsync();
            }
        }
        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            await StopScannerAsync();
        }

        private BarcodeScanner scanner { get; set; }
        private ClaimedBarcodeScanner claimedScanner { get; set; }
        private MediaCapture captureManager { get; set; }

        internal async Task StartScannerAsync()
        {
            capturePreview.Visibility = Visibility.Visible;
            capturePreview.Width = 400; capturePreview.Height = 300;

            scanner = await DeviceHelpers.GetFirstDeviceAsync(BarcodeScanner.GetDeviceSelector(), async (id) => await BarcodeScanner.FromIdAsync(id));

            if (scanner != null)
            {
                claimedScanner = await scanner.ClaimScannerAsync();
                if (claimedScanner != null)
                {
                    claimedScanner.ReleaseDeviceRequested += ClaimedScanner_ReleaseDeviceRequested;
                    claimedScanner.DataReceived += ClaimedScanner_DataReceived;
                    claimedScanner.IsDecodeDataEnabled = true;
                    await claimedScanner.EnableAsync();
                    try
                    {
                        bool haveAssociatedCamera = !string.IsNullOrEmpty(scanner.VideoDeviceId);
                        if (haveAssociatedCamera)
                        {
                            captureManager = new MediaCapture();
                            captureManager.Failed += CaptureManager_Failed;
                            MediaCaptureInitializationSettings _captureInitSettings = new MediaCaptureInitializationSettings
                            {
                                VideoDeviceId = scanner.VideoDeviceId,
                                SharingMode = MediaCaptureSharingMode.SharedReadOnly, // share
                                StreamingCaptureMode = StreamingCaptureMode.Video     // just video
                            };
                            await captureManager.InitializeAsync(_captureInitSettings);
                            capturePreview.Source = captureManager;
                        }

                        UpdateMessage("waiting..." + (!haveAssociatedCamera ? "But scanner not camera type" : ""));
                        if (captureManager != null) await captureManager.StartPreviewAsync();
                        await claimedScanner.StartSoftwareTriggerAsync();
                    }
                    catch (Exception e)
                    {
                        UpdateMessage(e.Message);
                        Debug.WriteLine("EXCEPTION: " + e.Message);
                    }
                }
                else
                {
                    UpdateMessage("Could not claim barcode scanner");
                }
            }
            else
            {
                UpdateMessage("No barcode scanners found");
            }

        }

        private void CaptureManager_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs)
        {
            string msg = string.Format("MediaCapture_Failed: (0x{0:X}) {1}", errorEventArgs.Code, errorEventArgs.Message);
            UpdateMessage(msg);
        }

        internal async Task StopScannerAsync()
        {
            if (captureManager != null)
            {
                if (captureManager.CameraStreamState == Windows.Media.Devices.CameraStreamState.Streaming)
                {
                    await captureManager.StopPreviewAsync();
                }
                captureManager.Dispose();
                captureManager = null;
            }
            if (claimedScanner != null)
            {
                claimedScanner.Dispose();
                claimedScanner = null;
            }
            if (scanner != null)
            {
                scanner.Dispose();
                scanner = null;
            }
        }

        private void ClaimedScanner_DataReceived(ClaimedBarcodeScanner sender, BarcodeScannerDataReceivedEventArgs args)
        {
            var scanDataLabelReader = DataReader.FromBuffer(args.Report.ScanDataLabel);
            string barcode = scanDataLabelReader.ReadString(args.Report.ScanDataLabel.Length);

            UpdateMessage(barcode);
        }

        private void ClaimedScanner_ReleaseDeviceRequested(object sender, ClaimedBarcodeScanner e)
        {
            UpdateMessage("Another process is requesting barcode scanner device.");
            e.RetainDevice(); 
        }

        private async void UpdateMessage (string message)
        {
            await LastBarcodeRead.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                LastBarcodeRead.Text = message;
            });
        }
    }

    public partial class DeviceHelpers
    {
        // We use a DeviceWatcher instead of DeviceInformation.FindAllAsync because
        // the DeviceWatcher will let us see the devices as they are discovered,
        // whereas FindAllAsync returns results only after discovery is complete.
        public static async Task<T> GetFirstDeviceAsync<T>(string selector, Func<string, Task<T>> convertAsync)
            where T : class
        {
            var completionSource = new TaskCompletionSource<T>();
            var pendingTasks = new List<Task>();
            DeviceWatcher watcher = DeviceInformation.CreateWatcher(selector);

            watcher.Added += (DeviceWatcher sender, DeviceInformation device) =>
            {
                Func<string, Task> lambda = async (id) =>
                {
                    T t = await convertAsync(id);
                    if (t != null)
                    {
                        completionSource.TrySetResult(t);
                    }
                };
                pendingTasks.Add(lambda(device.Id));
            };

            watcher.EnumerationCompleted += async (DeviceWatcher sender, object args) =>
            {
                // Wait for completion of all the tasks we created in our "Added" event handler.
                await Task.WhenAll(pendingTasks);
                // This sets the result to "null" if no task was able to produce a device.
                completionSource.TrySetResult(null);
            };

            watcher.Start();
            // Wait for enumeration to complete or for a device to be found.
            T result = await completionSource.Task;
            watcher.Stop();
            return result;
        }
    }
}

Where main xaml is...

<Page
    x:Class="StackOverflowQrTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:StackOverflowQrTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <ScrollViewer>
        <StackPanel>
            <Button Content="Start Preview" HorizontalAlignment="Center" Click="Button_Click"  Margin="5" />
            <CaptureElement x:Name="capturePreview" HorizontalAlignment="Center" Stretch="Uniform" Width="0" Height="0" Margin="10" />
            <Button Content="Stop Preview" HorizontalAlignment="Center" Click="Button_Click_1"  Margin="5" />
            <TextBox Header="LastBarcode" Name="LastBarcodeRead" IsReadOnly="True" HorizontalAlignment="Center" Margin="5" />
        </StackPanel>
    </ScrollViewer>
</Page>
Tom Kennard
  • 146
  • 4
  • Thank you tom for your reply and the time you've spent. I assume, that your code is running on your machine? Unfortunatelly not on mine (where main-app LoginPages Scanner runs). Yeah, my code is messy. I started with the Microsoft-samples too but that's the result of thousands of experiments. By the way: The Microsoft samples doesn't show any preview for me, except I call ClaimedBarcodeScanners StartPreviewAsync-Method (the Popup Window). In case you're interessted and want to cross-check my sln: https://cutt.ly/T8ACTy (my google drive) May I ask what is your Windows 10 Build? – SeriousBusinessGuy Aug 02 '19 at 10:16
  • Ok, I took the different versions of the test-project home with me and everyone is working perfectly. I see two reasons for that: 1. My private system isn't affected by our angry admins arbitrariness 2. My private system runs 1903 (build: 18362.239) and has the latest Windows updates. Both systems have Windows Professional Editions As soon as I find the correct reason for that strange behavior I'll write an answer here. – SeriousBusinessGuy Aug 02 '19 at 12:52
  • 1
    Mine is 1903, and it also runs on 1809. If the MS sample does not work, that does point to your environment rather than the apps. – Tom Kennard Aug 02 '19 at 13:38
  • Just a guess, but by chance is the "Allow apps to access your camera" setting On or Off? It's in Settings -> Privacy -> Camera. Turning it Off would give you the symptom of preview appearing not to work. Having it On, and On for your app listed below that global setting, is needed for camera access. You can try an app like [JustScanIt](http://aka.ms/justscanit) in the MS App Store and try playing with that setting, as well as the MS sample app. – Tom Kennard Aug 02 '19 at 14:25
  • Hello Tom, this is a good point but I switched that option on so it's available for all applications. Otherwise the Main-Apps LoginPage woudn't work too, I guess :) I should've that mentioned in the Initial post. Let me edit it, so everyone else will see it directly without digging trough all the comments. – SeriousBusinessGuy Aug 05 '19 at 07:14
  • I mistakenly thought you had gotten to a point where no app (Main-app, or test) was showing a preview on more than one machine. It sounds as if on a specific machine with 17763 (or better), you have a specific app of yours that works, and test apps that do not. If that is the case, then I would only be able to suggest making sure that just one app of yours is running at a time (use Task Manager to know) and attempt to catch error codes from starting preview to diagnose. Otherwise, ensure SharingMode is RO for MediaCapture on all apps. Try Camera App, and JustScanIt to see if they preview. – Tom Kennard Aug 06 '19 at 08:35
  • Hi Tom, thank you for your reply. Maybe I should've expressed myself a bit better. To summarize everything I can say - Talbet -> nothing works except Msft's Camera-App - My DevMachine -> LoginPage + Msft's Camera app works (settings-Page and Test-App doesn't) - My Private PC at home -> Everythings works fine. We have a similar Functionality in a WPF-Application that stopped working too, so I'm pretty sure it has something to do with our restrictive infrastructure. As soon as our admin comes back from vacation and we figure out what exactly was the problem, I'll post it here. – SeriousBusinessGuy Aug 06 '19 at 09:51