I want to start a Task when my Main Page is loaded, which get my position using Xamarin.Mobile in background. And the difficult point is to wait, if this task isn't finished, when the user clicked on a button.
On Xamarin iOS, I managed to do it but when I try to do exactly the same on Windows Phone 8.0 I get an AggregateException with as message : "One or more errors occurred".
There is the code i use :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Diagnostics;
using System.IO;
using Microsoft.Phone.Scheduler;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel;
using Xamarin.Geolocation;
namespace Application.WinPhone
{
public partial class Connexion : PhoneApplicationPage
{
static Task w;
// Constructor
public Connexion()
{
InitializeComponent();
w = new Task (() =>
{
Debug.WriteLine("Start");
Geolocator geolocator = null;
geolocator = new Geolocator() { DesiredAccuracy = 50};
var t = geolocator.GetPositionAsync(8000).ContinueWith(x =>
{
Debug.WriteLine(string.Format("Latitude : {0} Longitude : {1}", x.Result.Latitude, x.Result.Longitude)); //Visual Studio's debugger indicate this line with the exception
});
t.Wait();
Debug.WriteLine("Finished");
});
w.Start();
}
private void Connexion_Click(object sender, RoutedEventArgs e)
{
w.Wait();
//Here use the position find by the task to know on which page send the user
NavigationService.Navigate(new Uri("/Inscription.xaml", UriKind.RelativeOrAbsolute));
}
}
}
If there are some grammatical errors in my post Sorry i'm French. :)
Thanks in advance for help.
First of all, you shouldn't do any heavy lifting in the constructor. That's a design flaw. Second, Windows Phone expects your app to start in a limited amount of time, i.e. 10 seconds max. So, starting the app and waiting 8 seconds on the Geolocator is probably taking too much time, hence the canceled task.
What you could do is create the Geolocator in the constructor of the page and get the position in the OnNavigatedTo event.
One thing you should really do is to rearange it with asnyc/await as you really don't need creating tasks in this case, i.e. (out of my head):
public Connexion()
{
Connexion.IsEnabled = false;
var ignore = InitAsync();
}
private async Task InitAsync()
{
Debug.WriteLine("Start");
Geolocator geolocator = null;
geolocator = new Geolocator() { DesiredAccuracy = 50};
var result = await geolocator.GetPositionAsync(8000);
Debug.WriteLine(string.Format("Latitude : {0} Longitude : {1}", result.Latitude, result.Longitude)); //Visual Studio's debugger indicate this line with the exception
Connexion.IsEnabled = true;
}
Note that button should be disabled unless action succeeds. You should also add try/catch handler in there and you'll get clearer exception (perhaps Geolocator can't be created in a non UI thread?)
Besides what Geolocator class are you actually using - Forms Labs one?
Related
I have a windows service that communicates with a DB residing on a different server.The service is installed and always running.The service will look out if there any new records in the DB Table and get 1000 records at a time via stored proc and process records (updates/creates in the CRM system).
Logic is working perfectly alright but the problem is with service going to idle state(service is in running state but won't execute the method 'ProcessNewOtranRecords' that calls stored proc) after few hours. When the service is restarted it again works as expected for few more hours.
Please suggest me if there is any good approach to keep service active all the time.
Here is the code :
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Security.Permissions;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using CRM.Objects.BusinessLogic;
namespace CRM
{
partial class CrmProcessOtran : ServiceBase
{
OtranBL _otranBL = new OtranBL();
private System.Timers.Timer mainTimer;
int eventID = 0;
public CrmProcessOtran()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("Service Start");
mainTimer = new System.Timers.Timer(30000); // 30 seconds
mainTimer.Elapsed += PerformOtranOperations;
mainTimer.AutoReset = false;
mainTimer.Start();
}
protected override void OnStop()
{
eventLog1.WriteEntry("Service Stopped");
mainTimer.Stop();
mainTimer.Dispose();
mainTimer = null;
}
public void PerformOtranOperations(object sender, EventArgs e)
{
eventID++;
eventLog1.WriteEntry(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss") + " - checking for new otran records", EventLogEntryType.Information, eventID);
// Check for new otran records
int otranRecords = _otranBL.GetOtranRecordCount(eventLog1);
if (otranRecords == 0)
{
eventLog1.WriteEntry("0 new otran records", EventLogEntryType.Information, eventID);
return;
}
eventLog1.WriteEntry(otranRecords.ToString("N0") + " new otran records found with proc_status = 0", EventLogEntryType.Information, eventID);
// Process new records
eventLog1.WriteEntry(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss") + " - Begin processing new otran records", EventLogEntryType.Information, eventID);
int processedrecords = _otranBL.ProcessNewOtranRecords(eventLog1);
eventLog1.WriteEntry(processedrecords.ToString() + " processed records", EventLogEntryType.Information, eventID);
mainTimer.Start();
}
}
}
You should add some Try/Catch/Finally logic into PerformOtranOperations. You're propably experiencing some exception after some iterations and your mainTimer.Start(); is never called from that moment.
So the service is running but the trigger is not active any more.
The Timer component catches and suppresses all exceptions thrown by event handlers
for the Elapsed event; see MSDN.
Because of this you probably don't see any errors in the Windows event viewer.
Can you rework your code to use a System.Threading.Timer instance instead, which doesn't swallow the exception if something goes wrong.
As Pavlinll suggest, you should implement some error handling in order to keep your service running.
Here is my code, a simplified version
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using RDSCOMMUNICATORLib;
using System.Timers;
using System.Threading;
namespace RDSConsoleApplication
{
class Program
{
static public RDSComClass oObj = new RDSComClass();
static void Main(string[] args)
{
try
{
oObj.Host = "127.0.0.1";
oObj.Port = 2902;
oObj.LoadPiece(); // OK HERE
IConnectionEvents_OnPieceEventHandler PieceArraved = new IConnectionEvents_OnPieceEventHandler(oObj_OnPiece);
oObj.OnPiece += PieceArraved;
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
}
} // end main
static public void oObj_OnPiece(int lLSCRef, string strLSCName, int lPieceNumber, int bWithScans)
{
try
{
// HERE WE START GETTING EXCEPTION "Unable to cast COM object of type.....
// The application called an interface that was marshalled for a different thread"
oObj.LoadPiece();
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
}
}
} // end class Program
} // end namespace
I am referencing a COM object inside C# console application that serves as a gateway to connect to the back end and periodically receive some "piece" objects.
As a test, when I try from within the main method all works fine: I can connect, receive "piece" object and access its properties. The problem is that I need to receive and process that same "piece" object from within oObj_OnPiece callback method, and it throws the above mentioned exception. I browsed other similar posts, I understand it's a threading issue, but not sure how to resolve it. Any help is appreciated.
You try to query an interface, which is already in use in a different thread in your application. In your case you have queried by your call the interface in your main-thread first. I guess this is the first thread.
Is it possible that the eventhandler is opening a different thread to deal with the event? If this is the case ( just check this by adding one breakpoint in your event-handler before trying to access the interface , start your program and check if there a 2 threads running ).
What you have to do is: ensure that you query your interface only in one thread by removing the first call to your COM-object.
I have made a Background Taks demo. It is a 95% % copy of the SOLUTION in this Question:
Windows Phone 8.1 Background Task - Can't Debug and won't fire
The complete example OF THE SOLUTION can be download here : http://1drv.ms/1qCPLMY
The problem is when my event fires the program terminates. "my" solution can be downloaded here: http://1drv.ms/1x3z7Mp
So here is "my" code :
First the class implementing IBackgroundTask
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
namespace Tasks
{
public sealed class Upload : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Hello IBackgroundTask");
//return;
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var textElements = toastXml.GetElementsByTagName("text");
var networkStateChangeEventDetails = (taskInstance.TriggerDetails as Windows.Networking.Connectivity.NetworkStateChangeEventDetails);
if (networkStateChangeEventDetails == null)
return;
textElements[0].AppendChild(toastXml.CreateTextNode("I'm message from your task!"));
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
}
}
And here is the code for registering the Background taks:
private async void Button_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("Registering task");
var taskRegistered = false;
var exampleTaskName = "UploadTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == exampleTaskName)
{
//taskRegistered = true;
task.Value.Unregister(true);
// break;
}
}
await BackgroundExecutionManager.RequestAccessAsync();
if (!taskRegistered)
{
Debug.WriteLine("Registering task inside");
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "Tasks.Upload";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.NetworkStateChange, false));
BackgroundTaskRegistration task = builder.Register();
//task.Completed += new BackgroundTaskCompletedEventHandler(NetworkStateChangeTaskOnCompleted);
//task.Trigger += new BackgroundTaskCompletedEventHandler(NetworkStateChangeTaskOnCompleted);
await new MessageDialog("Task registered!").ShowAsync();
}
}
private void NetworkStateChangeTaskOnCompleted(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args)
{
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var textElements = toastXml.GetElementsByTagName("text");
textElements[0].AppendChild(toastXml.CreateTextNode("NetworkStateChangeTaskOnCompleted() =>"));
textElements[0].AppendChild(toastXml.CreateTextNode("I'm message from your task!"));
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
I get no exception, and no error mesage. The program just terminates, when the event fires. Same on both Device and Emulator.
I've checked your project there are couple of things you need to improve:
first and most important - your BackgroundTask must be a Windows Runtime Componenet not a Class library (as it is now) - open properties of the Background Task and change it. BackgroundTask must be a runtime component - that's why your program terminates.
you will also need to change the namespace to the project's (file's) name - in this case you will have Task.Upload (instead of Tasks.Upload). Remember also to change entry in Declarations in package.appxmanifest file.
As I've tried after this changes your app should work fine.
I followed the instructions in the marked answer here to create a service. The service gets installed correctly. When I start the service after a while it throws a message
"Windows could not start the xxx service on Local Computer.
Error 1053: The service did not respond to the start or control request in a timely fashion.
"
After I click ok, its status stays at "Starting" for ever. When I checked the application and system logs, there were no errors.
When I check the SQL trace, the service is actually running correctly and doing what its supposed to do. So why does its status stay at "Starting" ?
Update:
This is the code in OnStart method
protected override void OnStart(string[] args)
{
Loader loader = new Loader();
loader.StartProcess();
}
Update 2:
based on WiktorZychla's comment I did this and it worked :)
protected override void OnStart(string[] args)
{
Loader loader = new Loader();
ThreadStart threadDelegate = new ThreadStart(loader.StartProcess);
Thread newThread = new Thread(threadDelegate);
newThread.Start();
}
Based on WiktorZychla's comment this is what I did
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using IndexLoader;
using System.Threading;
namespace myNameSpace
{
public partial class LoaderService : ServiceBase
{
Thread newThread;
public LoaderService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Loader loader = new Loader();
ThreadStart threadDelegate = new ThreadStart(loader.StartProcess);
newThread = new Thread(threadDelegate);
newThread.Start();
}
protected override void OnStop()
{
if ((newThread != null) && (newThread.IsAlive))
{
Thread.Sleep(5000);
newThread.Abort();
}
}
}
}
It would be instructive to know exactly what is in your OnStart() method. The OnStart() method is a callback from the OS that is used to start your service, but it must return within 30 seconds or so (I remember reading that somewhere). Otherwise, the OS gives the message you're seeing. In short, limit the OnStart() method to getting things initialized, and defer the actual work your service is to perform to a thread of some kind.
In your Program.cs file in the Main() function make sure that you have:
ServiceBase.Run(new ServiceClassHere());
I've been guilty many times when creating a windows form app of keeping
Application.Run(new Class()); in my Main() function
I have written an Application Desktop Toolbar (a.k.a AppBar), it works great except for the fact that if I kill the process, the AppBar code never gets a chance to cleanup by sending an ABM_REMOVE. The problem is that this basically screws the users desktop up. The AppBar is written in .NET using interop code.
Does anyone know of a way to clean this resource up, even in the case of a process kill from TaskManager?
When a process is killed from Task Manager, no events are raised within that application. It's common to use a seperate helper application that listens for the Win32_ProcessStopTrace event for your process. You can use the WqlEventQuery, which is part of System.Management for this.
Here is some example code for this from a MegaSolutions post.
using System;
using System.Collections.Generic;
using System.Text;
using System.Management;
class ProcessObserver : IDisposable
{
ManagementEventWatcher m_processStartEvent = null;
ManagementEventWatcher m_processStopEvent = null;
public ProcessObserver(string processName, EventArrivedEventHandler onStart, EventArrivedEventHandler onStop)
{
WqlEventQuery startQuery = new WqlEventQuery("Win32_ProcessStartTrace", String.Format("ProcessName='{0}'", processName));
m_processStartEvent = new ManagementEventWatcher(startQuery);
WqlEventQuery stopQuery = new WqlEventQuery("Win32_ProcessStopTrace", String.Format("ProcessName='{0}'", processName));
m_processStopEvent = new ManagementEventWatcher(stopQuery);
if (onStart != null)
m_processStartEvent.EventArrived += onStart;
if (onStop != null)
m_processStopEvent.EventArrived += onStop;
}
public void Start()
{
m_processStartEvent.Start();
m_processStopEvent.Start();
}
public void Dispose()
{
m_processStartEvent.Dispose();
m_processStopEvent.Dispose();
}
}