TimeTrigger not firing in Single-Process UWP Scenario - c#

I've been banging my head against the monitor for the last few days. I'm developing an UWP app for the Windows Store (targeting 14393) and I'm using Prism/Unity frameworks for MVVM / IoC.
Since the data needed to update the Live Tile is stored in a class implementing the Repository pattern and everything is managed through Unity, I'm not creating a separate process for background execution, therefore even simplifying the whole BGTask registration process.
The actual BGTask registration code is as follows:
var servicingTaskAlreadyRegistered = false;
var tileUpdaterTaskAlreadyRegistered = false;
foreach (var t in BackgroundTaskRegistration.AllTasks)
{
if (t.Value.Name == Constants.BgTileUpdaterTaskName)
tileUpdaterTaskAlreadyRegistered = true;
else if (t.Value.Name.Equals(Constants.BgServicingTaskName))
servicingTaskAlreadyRegistered = true;
}
var reqAccess = await BackgroundExecutionManager.RequestAccessAsync();
if (reqAccess == BackgroundAccessStatus.Denied ||
reqAccess == BackgroundAccessStatus.DeniedBySystemPolicy ||
reqAccess == BackgroundAccessStatus.DeniedByUser ||
reqAccess == BackgroundAccessStatus.Unspecified)
return false;
if (!servicingTaskAlreadyRegistered)
{
var servicingTaskBuilder = new BackgroundTaskBuilder();
servicingTaskBuilder.Name = Constants.BgServicingTaskName;
servicingTaskBuilder.SetTrigger(new SystemTrigger(SystemTriggerType.ServicingComplete, false));
servicingTaskBuilder.Register();
}
if (tileUpdaterTaskAlreadyRegistered)
return true;
var builder = new BackgroundTaskBuilder();
builder.Name = Constants.BgTileUpdaterTaskName;
builder.SetTrigger(new TimeTrigger(TileUpdateFrequencyMinutes, false));
//builder.SetTrigger(new MaintenanceTrigger(TileUpdateFrequencyMinutes, false));
builder.IsNetworkRequested = true;
builder.Register();
The registration successfully completes. Executing Get-AppBackgroundTask in PowerShell shows both tasks, as it should be. However, the TimeTrigger never fires. Swapping TimeTrigger with the MaintenanceTrigger fixes the problem, although the smartphone needs to be plugged to the charger which is not an acceptable workaround.
Forcing the task to run via VisualStudio or PowerShell (Start-AppBackgroundTask -TaskID ) correctly executes and the tile gets updated.
Do you have any other useful tip to share?
Edit 12/01/2017
I've created a Repro containing a Visual Studio Solution with two projects:
Live Tile Test Simple: as simple as it gets to have a live tile updating every 15 minutes using UWP. Everything works as expected.
Live Tile Test Prism: again, a simple conversion of the above project using Prism and Unity. Doesn't work because when the OS tries to launch the app to update the tile the Unity container is null (doesn't get initialized).
This explains why I'm having the problem: Unity doesn't get initialized, I'm unable to retrieve data via repositories, the app crashes and GG.
Now I just need to understand why Unity isn't available in the OnBackgroundActivated method. Almost there guys!!
Repro: https://github.com/eraser85/LiveTileTestRepro

The code looks fine to me, the only things I'd try to change here are the use of the IsNetwokRequested property and the TimeTrigger frequency value, are you sure that constant/variable you're using is greater or equal to 15?
Here's a sample:
BackgroundTaskBuilder builder = new BackgroundTaskBuilder { Name = "YourBgTaskName" };
builder.SetTrigger(new TimeTrigger(15, false));
builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
builder.Register();

Ok so after some more tests I've finally come up with a solution.
As mentioned, the problem stemmed from Prism: basically, when launched by the OS and entering via OnBackgroundActivated() the IoC container wasn't getting initialized.
The solution, even if seems hack-ish, is actually perfectly viable and correct (IMHO!). In your OnBackgroundActivated() just initialize everything as if starting from scratch (have a look at Prism's Source for implementation details): in my specific case, I just called CreateAndConfigureContainer() and re-registered everything I've put in OnInitializeAsync() (eg. repos, services..).
I've opened an issue with the devs. Maybe a solution is already on the way, but in the mean time this should do.

Related

Can't restart UWP application via CoreApplication.RequestRestartAsync

I'm trying to restart the application, but I get NotInForeground error every time.
I tried to close every other application and loop the restart attempt to make sure it is in foreground like this
var loopRestart = true;
while (loopRestart)
{
var failureReason = await CoreApplication.RequestRestartAsync("-fastInit -level 1");
loopRestart = failureReason == AppRestartFailureReason.NotInForeground;
}
With or without parameters (from one of the examples found on the internet) it just won't let me do it. When I do CoreApplication.GetCurrentView() is says that the window is ActivatedInForeground quickwatch. At this point I'm lost. Version is 2004

UWP registration of background task for StorageLibraryContentChangedTrigger fails with "not found"

In my UWP desktop application, I am trying to implement a function to follow changes in the images library using a background task. I followed usage examples (like this one https://learn.microsoft.com/en-us/archive/msdn-magazine/2016/december/universal-windows-platform-file-system-monitoring-in-universal-windows-platform-apps) and my code looks like that:
StorageLibrary docLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
var requestStatus = await BackgroundExecutionManager.RequestAccessAsync();
if (!(
requestStatus == BackgroundAccessStatus.AllowedSubjectToSystemPolicy ||
requestStatus == BackgroundAccessStatus.AlwaysAllowed))
{
return;
}
var builder = new BackgroundTaskBuilder();
builder.Name = "Background task";
StorageLibraryContentChangedTrigger libraryTrigger = StorageLibraryContentChangedTrigger.Create(docLib);
builder.SetTrigger(libraryTrigger);
var task = builder.Register();
docLib.ChangeTracker.Enable();
My understanding is that this creates an "in-process" background task, and when the change in folder content happens the App OnBackgroundActivated() will be called, so I can process changes there.
However when I call the task registration the exception is thrown. The exception is:
System.Exception
HResult=0x80070490
Message=Element not found. (Exception from HRESULT: 0x80070490)
I don't understand what is happening. I have tried to replace the trigger with Time Zone Change trigger in the code above, and it works fine (registration is successful and OnBackgroundActivated code is executed) but the content change trigger does not. I have not found any examples of a similar error. I am using Windows 10, build 19041. The only thing that may be non-standard is that I have moved the location of my document libraries from C: drive to D: drive, but that is a standard feature of Windows OS, so I don't think it should cause a problem.

WinAppDriver: Launching of desktop application was working fine, but of late its having issues

Launching of the desktop application was working fine when I automated using winAppDriver.
Of late I observed these tests are failing.
Its failing at this line,
notepadsession = new WindowsDriver(new Uri("http://127.0.0.1:4723"), desiredcapabilities);
I tried both the codes, but still failing:
var currentWindowHandle = notepadsession.CurrentWindowHandle;
Thread.Sleep(TimeSpan.FromSeconds(5));
var allWindowHandles = notepadsession.WindowHandles;
notepadsession.SwitchTo().Window(allWindowHandles[0]);
if (notepadsession.CurrentWindowHandle != notepadsession.WindowHandles.Last())
{
notepadsession.SwitchTo().Window(notepadsession.WindowHandles.Last());
}
NOTE: It takes around 40-50 seconds to load the Desktop application.
Any help in this regard is highly appreciated.
Thanks
Below code solved the problem
notepadsession = new WindowsDriver(new Uri("http://127.0.0.1:4723"), desiredcapabilities);
Thread.Sleep(5000);
notepadsession.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
The ms:waitForAppLaunch capability enables WinAppDriver to wait for a defined amount of time after an app launch is initiated prior to attaching to the application session. The limit for this is 50 seconds.
C#: appCapabilities.SetCapability("ms:waitForAppLaunch", "25"); to add app delay of 25 seconds.
For more information check out the release notes.

Tracking changes of the pictures library on Windows 10 Mobile devices

I am trying to track the changes of the pictures library because I want my app to upload new photos to to a server. To track the changes I followed the MSDN article over here https://msdn.microsoft.com/en-us/magazine/mt790201.aspx
If I run my code on my phone (Windows 10 Mobile with Fall Creators Update) it does not work if the pictures are saved to the sd-card. But if remove the sd-card and reboot my phone I can read the changes from the change-tracker. On a Desktop-PC everything works fine.
This is how I enable the background task for the change-tracker:
public async Task Register()
{
// Check if your app has access to the background
var requestStatus = await BackgroundExecutionManager.RequestAccessAsync();
if (!(requestStatus ==
BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity ||
requestStatus == BackgroundAccessStatus.AllowedSubjectToSystemPolicy ||
requestStatus ==
BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity ||
requestStatus == BackgroundAccessStatus.AlwaysAllowed))
{
Debug.WriteLine("Failed to get access to the background");
return;
}
// Build up the trigger to fire when something changes in the pictures library
var builder = new BackgroundTaskBuilder();
builder.Name = "Photo Change Trigger";
StorageLibrary picturesLib =
await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
var picturesTrigger = StorageLibraryContentChangedTrigger.Create(picturesLib);
// We are registering to be activated in OnBackgroundActivated instead of
// BackgroundTask.Run; either works, but I prefer the single-process model
builder.SetTrigger(picturesTrigger);
BackgroundTaskRegistration task = builder.Register();
}
And this is how I get the changes, which contains the code that does not work:
public async Task GetChanges()
{
StorageLibrary picturesLib =
await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
StorageLibraryChangeTracker picturesTracker = picturesLib.ChangeTracker;
picturesTracker.Enable();
StorageLibraryChangeReader changeReader = picturesTracker.GetChangeReader();
// if photos are saved on the SD-card the next line does not work
IReadOnlyList<StorageLibraryChange> changes = await changeReader.ReadBatchAsync();
}
Am I doing something wrong or is this a bug in Windows 10 Mobile? I already tried to factory reset my device or reformatting my SD-Card, but nothing worked.
As described in the official blogpost:
Calling Enable()
It’s mentioned above, but just to make sure that it is clear: Apps should call ChangeTracker.Enable() as soon as they start tracking the file system and before every enumeration of the changes. This will ensure that the change tracker is not going to miss changes to the folders included in the library.
So I would suggest to call the Enable in the Register method as well, right after the task is registered.

System.Windows.Automation is very slow at enumerating table rows vs. UIAutomationCore

I am trying to do automated testing of my application via UI Automation (mainly using TestStack.White to provide a friendly interface; it uses System.Windows.Automation as a back-end). I have a table with ~200 rows that I need to test the values of (actually I only want to test the first and last couple rows). I have discovered that using COM-interop UIAutomationCore by itself, I can enumerate the rows in a fraction of a second, but only when I don't use White or System.Windows.Automation. As soon as System.Windows.Automation initializes, future UI Automation actions to enumerate rows are slow:
First COM run: it took 0.04 seconds to get 102 rows!
First System.Windows.Automation run: it took 7.18 seconds to get 102 rows!
Second COM run: it took 7.87 seconds to get 102 rows!
I created a simple WinForms test application (TableTest.exe to verify that it was System.Windows.Automation and not something to do with my application:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form() { Text = "TableTest", WindowState = FormWindowState.Maximized };
var dgv = new DataGridView() { Name = "DGV", Dock = DockStyle.Fill, AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill };
dgv.Columns.Add("i", "i");
dgv.Columns.Add("2i", "2i");
dgv.Columns.Add("i^2", "i^2");
dgv.Columns.Add("i^i", "i^i");
for (int i = 0; i < 100; ++i)
dgv.Rows.Add(i, i * 2, i * i, Math.Pow(i, i));
form.Controls.Add(dgv);
Application.Run(form);
}
Then I created another test app to test the first one. It works as either a console app or a WinForms app. First I test with COM automation, then with System.Windows.Automation, then again with COM automation. As you can see from the output I quoted above, the first block executes very quickly, the next two blocks execute excruciatingly slowly. If I comment out the System.Windows.Automation block code then both COM blocks execute quickly.
using UIA = Interop.UIAutomationCore;
static void Main(string[] args)
{
var process = System.Diagnostics.Process.Start("TableTest.exe");
System.Threading.Thread.Sleep(500);
var uia = new UIA.CUIAutomation();
var rootCom = uia.GetRootElement();
var windowCom = rootCom.FindFirst(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_NamePropertyId, "TableTest"));
var dgvCom = windowCom.FindFirst(UIA.TreeScope.TreeScope_Descendants, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_AutomationIdPropertyId, "DGV"));
var start = DateTime.Now;
var rowCount = dgvCom.FindAll(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_ControlTypePropertyId, UIA.UIA_ControlTypeIds.UIA_CustomControlTypeId)).Length;
var elapsed = (DateTime.Now - start).TotalSeconds;
Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount));
process.Kill();
process = System.Diagnostics.Process.Start("TableTest.exe");
System.Threading.Thread.Sleep(500);
var root = AutomationElement.RootElement;
var window = root.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "TableTest"));
var dgv = window.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "DGV"));
start = DateTime.Now;
rowCount = dgv.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom)).Count;
elapsed = (DateTime.Now - start).TotalSeconds;
Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount));
process.Kill();
process = System.Diagnostics.Process.Start("TableTest.exe");
System.Threading.Thread.Sleep(500);
uia = new UIA.CUIAutomation();
rootCom = uia.GetRootElement();
windowCom = rootCom.FindFirst(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_NamePropertyId, "TableTest"));
dgvCom = windowCom.FindFirst(UIA.TreeScope.TreeScope_Descendants, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_AutomationIdPropertyId, "DGV"));
start = DateTime.Now;
rowCount = dgvCom.FindAll(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_ControlTypePropertyId, UIA.UIA_ControlTypeIds.UIA_CustomControlTypeId)).Length;
elapsed = (DateTime.Now - start).TotalSeconds;
Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount));
process.Kill();
}
What the heck is System.Windows.Automation doing that kills the performance of UI Automation? I've looked at the White source code and I don't see anything obvious. I can't profile System.Windows.Automation itself because I can't find any PDB for it. I'm not very familiar with UI Automation so maybe it'll be obvious to someone else. The White is: 0.13.0.0 and I'm testing on 64-bit Windows 7.
I cannot answer your question. But many people will come here from Google who search for the keywords "uiautomation slow" and the first result in Google is your question. (You wrote a bestseller)
For all those coming from Google and struggling with slow UIAutomation I post this answer.
System.Windows.Automation is EXTREMELY slow. Obtaining 30 child elements may take 1000ms on a very fast computer! I have even seen it hanging forever while getting the child elements of a Tree in a QT application.
Apart from that the implementation is not even thread safe.
System.Windows.Automation is deprecated. Do not use it!
In the MSDN you find the following note:
UI Automation was first available in Windows XP as part of the
Microsoft .NET Framework. Although an unmanaged C++ API was also
published at that time, the usefulness of client functions was limited
because of interoperability issues. For Windows 7, the API has been
rewritten in the Component Object Model (COM).
Although the library functions introduced in the earlier version of
UI Automation are still documented, they should not be used in new
applications.
The solution to slow performance is to use the new IUIAutomationElement COM interface instead of the old System.Windows.Automation C# interface. After that the code will be running lightning fast!
Apart from that the new interface offers much more patterns and Microsoft is extending it continously. In the Windows 10 SDK (UIAutomationClient.h and UIAutomationCore.h) several patterns and properties have been added which are not available in the .NET Automation framework.
The following patterns are available in the COM version of UIAutomation which do not exist in System.Windows.Automation:
IUIAutomationLegacyIAccessiblePattern
IUIAutomationObjectModelPattern
IUIAutomationAnnotationPattern
IUIAutomationTextPattern2
IUIAutomationStylesPattern
IUIAutomationSpreadsheetPattern
IUIAutomationSpreadsheetItemPattern
IUIAutomationTransformPattern2
IUIAutomationTextChildPattern
IUIAutomationDragPattern
IUIAutomationDropTargetPattern
IUIAutomationTextEditPattern
IUIAutomationCustomNavigationPattern
Additionally the following Control types have been added:
AppBar
SemanticZoom
Additionally the following Element's have been added:
IUIAutomationElement2
IUIAutomationElement3
IUIAutomationElement4
The examples you posted do not use White... FWIW, White uses recursive calls to Automation.Find requesting the children each time. This returns valid results but is slower than requesting the subtree from the appropriate parent node - note that the root node is never the 'appropriate' node from which to request the subtree (see the note on MSDN). Since your example only requests the children once, that is not the issue.

Categories

Resources