UWP - DataPackage.OperationCompleted is EMPTY & gets called by nobody - c#

I'm writing a UWP file manager and I've come to a problem with drag&drop. I'm probably just beating my head against the wall since this is obviously another bug in the platform, but this time I can't find any workaround.
When dragging files and dropping them into File Explorer everything is fine. I fill up the DataPackage and listen to the OperationCompleted event, which happens when the files finished moving to another folder. When I drop them into another view within my app, I can call DataPackageView.ReportOperationCompleted, which does work (sort of). The problem is, it's also called AUTOMATICALLY at the same time the drop happens, even though the operation is not finished yet - and I can't do anything about it. The call stack is completely empty when I hit a breakpoint in the event handler.
On top of that, when I actually look into the arguments of OperationCompleted, the Operation in OperationCompletedEventArgs is ALWAYS None! It's None when File Explorer does the job, it's None when it gets called automatically, it's None when I call it manually, NO MATTER WHAT argument I pass in. Any explanation for this, Microsoft? I'm tired of fixing your bugs, especially when I can't actually do it since the platform is so limited.

one other "curiosity" with drag&dropping files in UWP is that if you get files dropped in your app and a requested operation set to move - you can't actually move them - the files are read-only. Try explaining that to the user.
Not sure how you to move files. In general, you should use StorageFile.CopyAsync method. You could use try/catch block to wrap this operation like the follwing:
try
{
var operation = appFile.File.CopyAsync(ApplicationData.Current.LocalFolder, appFile.File.DisplayName, NameCollisionOption.GenerateUniqueName);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("exception msg: "+ex.Message);
}
Then if the file is readonly, you will get exception message, you should use this message to notify user.
I wanted a notification for when the operation is actually done.
You should implemente AsyncOperationWithProgressCompletedHandler for your async operation, then you will get notification when it's completed.
operation.Completed = (tresult,tprogress) => { System.Diagnostics.Debug.WriteLine("progress msg: "+tprogress); };

Related

TransactionTooLargeException from Samsung's SmartClip service crashing my app - can it be intercepted?

Android Samsung phones have a SmartClip service that is invoked when a screenshot is taken. It launches an interface to immediately allow the screenshot to be edited.
My app will often crash when this action is performed, specifically when a large number of objects are drawn on the screen. This is a Xamarin.Forms app.
When the crash occurs, two threads are reported as responsible, with the following stack traces:
android.os.BinderProxy.transactNative BinderProxy.java
android.os.BinderProxy.transact BinderProxy.java:605
com.samsung.android.content.smartclip.ISpenGestureService$Stub$Proxy.sendSmartClipRemoteRequestResult ISpenGestureService.java:910
com.samsung.android.content.smartclip.SpenGestureManager.sendSmartClipRemoteRequestResult SpenGestureManager.java:77
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher.sendResult SmartClipRemoteRequestDispatcher.java:654
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher.dispatchScrollableAreaInfo SmartClipRemoteRequestDispatcher.java:313
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher.access$100 SmartClipRemoteRequestDispatcher.java:59
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher$2.run SmartClipRemoteRequestDispatcher.java:154
android.os.Handler.handleCallback Handler.java:938
android.os.Handler.dispatchMessage Handler.java:99
android.os.Looper.loop Looper.java:246
android.app.ActivityThread.main ActivityThread.java:8512
java.lang.reflect.Method.invoke Method.java
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:602
com.android.internal.os.ZygoteInit.main ZygoteInit.java:1130
com.samsung.android.content.smartclip.SpenGestureManager.sendSmartClipRemoteRequestResult SpenGestureManager.java:81
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher.sendResult SmartClipRemoteRequestDispatcher.java:654
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher.dispatchScrollableAreaInfo SmartClipRemoteRequestDispatcher.java:313
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher.access$100 SmartClipRemoteRequestDispatcher.java:59
com.samsung.android.content.smartclip.SmartClipRemoteRequestDispatcher$2.run SmartClipRemoteRequestDispatcher.java:154
android.os.Handler.handleCallback Handler.java:938
android.os.Handler.dispatchMessage Handler.java:99
android.os.Looper.loop Looper.java:246
android.app.ActivityThread.main ActivityThread.java:8512
java.lang.reflect.Method.invoke Method.java
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run RuntimeInit.java:602
com.android.internal.os.ZygoteInit.main ZygoteInit.java:1130
So... what do I do? This is a crash originating in third party code that I'm not including myself. Is there a hook somewhere that I can prevent this action from executing, or to handle the exception without crashing my app?
The only other solution would be to reduce the number of objects displayed so it comes under the 1 Mb limit, but I'd like to avoid that.
EDIT to add: Unfortunately, it looks like UnhandledException handling is not going to suffice, it was only meant for logging information about the crash.
Thanks to #ToolmakerSteve I have a workaround.
I used the information in the links to detect this exception and restart the activity:
# in MainApplication.cs
private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if ((e.ExceptionObject as RuntimeException)?.InnerException is TransactionTooLargeException)
{
Intent intent = new Intent(ApplicationContext, typeof(MainActivity));
intent.AddFlags(ActivityFlags.NewTask);
StartActivity(intent);
}
}
}
Trying to do any state-saving action in this method fails. You have to rely on state that's saved prior to the crash. Luckily I already had such a mechanism.
So I just needed to detect a crash on startup (I used AppCenter's Crashes.HasCrashedInLastSessionAsync() and run my state-restoring method. Though for some reason I could only run this check after populating MainPage. Doing it before risked a blank screen.
In addition to this, I implemented limits on the number of objects drawn on the screen, so that the crash happens less frequently.
This isn't the best answer because it would be better to stop the third party code from crashing apps, but I hope it helps someone else.

IGeolocator's PositionChanged firing twice?

Context
I'm developing a traffic management app using C# (Xamarin Forms) which requires a constant feed of the user's location.
Plugin
I'm using the Geolocator plugin by James Montemagno and the PositionChanged event on the IGeolocator interface seems to be triggering twice when a position change occurs.
Device
I'm currently debugging on an Android Emulator on Visual Studio Enterprise v15.5.3
Steps to reproduce the behaviour
1) After instantiating the locator object:
IGeolocator locator = CrossGeolocator.Current
2) Some code for when the locator's PositionChanged is triggered:
locator.PositionChanged += (sender,e) => {
// Testing its frequency
System.Diagnostics.Debug.WriteLine("Position Changed Triggered.");
}
3) Start listening in an async Task function
locator.DesiredAccuracy = 100;
if(!locator.isListening)
await locator.StartListeningAsync(TimeSpan.FromMilliseconds(500), 1, true);
4) Send coordinates via the emulator
Expected Results
The output window should display one message saying "Position Changed Triggered".
Actual Results
The output window has two identical messages printed, saying: "Position Changed Triggered".
Why is this happening? and how can I make it so that the event is ONLY triggered/handled ONCE for every time the position is actually changed.
What I've tried so far
Googled the issue, not many identical situations found.
Saw an explanation here which makes me believe it's the emulator has something to do with it, although I found the solution a bit ambiguous and am not sure what they're referring to with "Live/Pin Mode"
Created a separate function and assigned the event to it, then removed it after calling my code. This does cause it to execute once, but it never executes again unless I reassign in. And if I do, sure enough the code will run for the second time, resulting in the same initial problem.
Tried setting a boolean to check if it has already run once, but realised not long after how that's illogical.
Tried to set a DateTime object to make sure no more than 1 event occurs in a given time frame; this was also no good.
Help would be appreciated. Thanks.
Please note that the location service might deliver a location event more than one time (more here).
Even if the location service is not delivering the location event more than one time, usage and/or implementation of the API (Geolocator plugin) might cause reception of the location event more than one time from the API.
In the app you might check the location event properties (accuracy, location timestamp, order of event received, etc.). Use the "best" location event with "best" meaning preferred accuracy, most recent timestamp, or the first received location event if timestamp and accuracy are equal.

Making file picker asynchronous - Windows Phone 8.1

I tried to make File open picker asynchronous using TaskComplectionSource however sometimes I get my application closed with -1 return value, sometimes I get exception like:
[System.Runtime.InteropServices.COMException] = {System.Runtime.InteropServices.COMException (0x80004005): Unspecified error
Unspecified error
at Windows.Storage.Pickers.FileOpenPicker.PickSingleFileAndContinue()
at PhotosGraphos.Mobile.Common.StorageFileExtensions.<PickSingleFileAsyncMobile..
Code:
public static class StorageFileExtensions
{
private static TaskCompletionSource<StorageFile> PickFileTaskCompletionSource;
private static bool isPickingFileInProgress;
public static async Task<StorageFile> PickSingleFileAsyncMobile(this FileOpenPicker openPicker)
{
if (isPickingFileInProgress)
return null;
isPickingFileInProgress = true;
PickFileTaskCompletionSource = new TaskCompletionSource<StorageFile>();
var currentView = CoreApplication.GetCurrentView();
currentView.Activated += OnActivated;
openPicker.PickSingleFileAndContinue();
StorageFile pickedFile;
try
{
pickedFile = await PickFileTaskCompletionSource.Task;
}
catch (TaskCanceledException)
{
pickedFile = null;
}
finally
{
PickFileTaskCompletionSource = null;
isPickingFileInProgress = false;
}
return pickedFile;
}
private static void OnActivated(CoreApplicationView sender, IActivatedEventArgs args)
{
var continuationArgs = args as FileOpenPickerContinuationEventArgs;
sender.Activated -= OnActivated;
if (continuationArgs != null && continuationArgs.Files.Any())
{
StorageFile pickedFile = continuationArgs.Files.First();
PickFileTaskCompletionSource.SetResult(pickedFile);
}
else
{
PickFileTaskCompletionSource.SetCanceled();
}
}
}
What's weird - this bug is hardly reproduced while debugging. Does anyone have any idea what could be reason of that?
Don't do that (don't try to turn Continuation behaviour into async). Why?
Normally when your app is put into the background (for example when you call file picker), it's being suspended, and here is one small pitfall - when you have a debugger attached, your app will work without being suspended. Surely that can cause some troubles.
Note also that when you normally run your app and you fire a picker, then in some cases your app can be terminated (low resources, user closes it ...). So you need here two things which are added by VS as a template: ContinuationManager and SuspensionManager. More you will find at MSDN. At the same link you will find a good procedure to debug your app:
Follow these steps to test the case in which your app is terminated after calling the AndContinue method. These steps ensure that the debugger reattaches to your app after completing the operation and continuing.
In Visual Studio, right-click on your project and select Properties.
In Project Designer, on the Debug tab under Start action, enable Do not launch, but debug my code when it starts.
Run your app with debugging. This deploys the app, but does not run it.
Start your app manually. The debugger attaches to the app. If you have breakpoints in your code, the debugger stops at the breakpoints. When your app calls the AndContinue method, the debugger continues to run.
If your app calls a file picker, wait until you have opened the file provider (for example, Phone, Photos, or OneDrive). If your app calls an online identity provider, wait until the authentication page opens.
On the Debug Location toolbar, in the Process dropdown list, select the process for your app. In the Lifecycle Events dropdown list, select Suspend and Shutdown to terminate your app but leave the emulator running.
After the AndContinue operation completes, the debugger reattaches to your app automatically when the app continues.
I've changed file picker to standard way provided by #Romasz - it still was crashing. I've been debugging it for hours and I get same COMException but sometimes with information provided:
"GetNavigationState doesn't support serialization of a parameter type which was passed to Frame.Navigate"
It seems that code with TaskCompletionSource works and there is nothing wrong with that. I found out in msdn documentation for Frame
Note: The serialization format used by these methods is for internal use only. Your app should not form any dependencies on it. Additionally, this format supports serialization only for basic types like string, char, numeric and GUID types.
And I was passing my model-class object in navigation parameter - so it was kept in navigation stack therefore it couldn't be serialized. The lesson is: do not use non-primitive types for navigation parameter - Frame.Navigate should disallow such navigation and throw exception - but it doesn't..
EDIT:
Another bug - if you bind tapped (let say button tapped) or event like that to command which launch FileOpenPicker you need to check if picker.PickFile.. was called before - otherwise when you tap fast on that button you'll get few calls to picker.PickFile.. and UnauthorizedAccessException will be thrown.

Check If File Is In Use By Other Instances of Executable Run

Before I go into too detail, my program is written in Visual Studio 2010 using C# .Net 4.0.
I wrote a program that will generate separate log files for each run. The log file is named after the time, and accurate up at millisecond (for example, 20130726103042375.log). The program will also generate a master log file for the day if it has not already exist (for example, *20130726_Master.log*)
At the end of each run, I want to append the log file to a master log file. Is there a way to check if I can append successfully? And retry after Sleep for like a second or something?
Basically, I have 1 executable, and multiple users (let's say there are 5 users).
All 5 users will access and run this executable at the same time. Since it's nearly impossible for all user to start at the exact same time (up to millisecond), there will be no problem generate individual log files.
However, the issue comes in when I attempt to merge those log files to the master log file. Though it is unlikely, I think the program will crash if multiple users are appending to the same master log file.
The method I use is
File.AppendAllText(masterLogFile, File.ReadAllText(individualLogFile));
I have check into the lock object, but I think it doesn't work in my case, as there are multiple instances running instead of multiple threads in one instance.
Another way I look into is try/catch, something like this
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch {}
But I don't think this solve the problem, because the status of the masterLogFile can change in that brief millisecond.
So my overall question is: Is there a way to append to masterLogFile if it's not in use, and retry after a short timeout if it is? Or if there is an alternative way to create the masterLogFile?
Thank you in advance, and sorry for the long message. I want to make sure I get my message across and explain what I've tried or look into so we are not wasting anyone's time.
Please let me know if there's anymore information I can provide to help you help me.
Your try/catch is the way to do things. If the call to File.Open succeeds, then you can write to to the file. The idea is to keep the file open. I would suggest something like:
bool openSuccessful = false;
while (!openSuccessful)
{
try
{
using (var writer = new StreamWriter(masterlog, true)) // append
{
// successfully opened file
openSuccessful = true;
try
{
foreach (var line in File.ReadLines(individualLogFile))
{
writer.WriteLine(line);
}
}
catch (exceptions that occur while writing)
{
// something unexpected happened.
// handle the error and exit the loop.
break;
}
}
}
catch (exceptions that occur when trying to open the file)
{
// couldn't open the file.
// If the exception is because it's opened in another process,
// then delay and retry.
// Otherwise exit.
Sleep(1000);
}
}
if (!openSuccessful)
{
// notify of error
}
So if you fail to open the file, you sleep and try again.
See my blog post, File.Exists is only a snapshot, for a little more detail.
I would do something along the lines of this as I think in incurs the least overhead. Try/catch is going to generate a stack trace(which could take a whole second) if an exception is thrown. There has to be a better way to do this atomically still. If I find one I'll post it.

App.Current.Shutdown not letting DataSet.WriteXml complete results in corrupt config file

Environment - C#, .NET 4.0, WPF, VS2010
I have an app that uses a keyboard hook to detect when a combination of four keys is pressed. When this occurs it calls Configuration.Save(), which has a call to myConfigDataSet.WriteXml(myConfigFile). And then on the next line it calls App.Current.Shutdown().
About half the time it works as expected. But many times it would insert XML content right into the middle of a previously existing configuration file, resulting in corrupt data.
I was able to fix the above issue by using...
if(File.Exists(myConfigFile)) { File.Delete(myConfigFile) }
...on the line just above the call to myConfigDataSet.WriteXml(myConfigFile)
But now many times it just writes a 0KB size file. I am pretty sure that all of my problems are being caused by App.Current.Shutdown() not waiting for the call to myConfigDataSet.WriteXml(myConfigFile) to finish.
Shouldn't this call block execution until the file has been written to disk? ...apparently not!!!
As a workaround I've already tried inserting Thread.Sleep(1000) to create a 1 second delay just before App.Current.Shutdown. But now sometimes the app errors out with a "StackOverFlow" exception...!!!
What would you guys recommend as a fix or workaround for this? What am I doing wrong?
You can't stop a App.Current.Shutdown
Instead, use Application.Current.MainWindow.Close().
In this case, you can intercept in the Close event, process what you need to process, and then you could call App.Current.Shutdown
You can try to put the call to myConfigDataSet.WriteXml(myConfigFile) in a try/finally block. The finally block is executed completely even when the thread is aborted (see here). Not sure if it works with App.Current.Shutdown though.
So instead of:
myConfigDataSet.WriteXml(myConfigFile)
Do the following:
try {
// Empty...
} finally {
myConfigDataSet.WriteXml(myConfigFile)
}

Categories