Detect email move using Exchange Web Service Managed API? - c#

I'm using EWS Managed API and C#.
I want to know if it's possible to detect when an email is moved to another folder.
This is what I have so far:
static void SetPullNotifications(ExchangeService service)
{
PullSubscription subscription = service.SubscribeToPullNotificationsOnAllFolders(
5, null,
EventType.Moved, EventType.Deleted, EventType.Copied, EventType.Modified);
GetEventsResults events = subscription.GetEvents();
foreach (ItemEvent itemEvent in events)
{
switch (itemEvent.EventType)
{
case EventType.Moved:
MessageBox.Show("Item Moved :" + itemEvent.ItemId.UniqueId);
break;
case EventType.Deleted:
MessageBox.Show("Item deleted: " + itemEvent.ItemId.UniqueId);
break;
case EventType.Copied:
MessageBox.Show("Item Copied :" + itemEvent.ItemId.UniqueId);
break;
case EventType.Modified:
MessageBox.Show("Item Modified :" + itemEvent.ItemId.UniqueId);
break;
}
}
}
This works fine if I put a breakpoint on the method GetEvents(), then move an email. But without the breakpoint it does not work. The events contains no results.
Any ideas ?

your on the right way, but your missing something. Your code will only get the events which occour between creating the subscription and getting the Events, that's why it only works with a breakpoint.
To make your code work you should do 2 things.
At first: Create the subscrption when you start your application and keep a refernce on it.
At Second Store the Watermark you get from the Subscription and reload it on application startup. Maybe like this:
static PullSubscription s_Subscription;
static void Main()
{
ExchangeService service = CreateService();
CreateSubsciption(service);
//DoSomething;
GetEvents();
//DoSomething;
StoreWatermark(s_Subscription.Watermark);
}
static void CreateSubscription(ExchangeService Service)
{
string watermarkt = LoadWatermark();
s_Subscription = service.SubscribeToPullNotificationsOnAllFolders(
5, watermark,
EventType.Moved, EventType.Deleted, EventType.Copied, EventType.Modified);
}
static void GetEvents()
{
GetEventsResults events = subscription.GetEvents();
foreach (ItemEvent itemEvent in events)
{
switch (itemEvent.EventType)
{
case EventType.Moved:
MessageBox.Show("Item Moved :" + itemEvent.ItemId.UniqueId);
break;
case EventType.Deleted:
MessageBox.Show("Item deleted: " + itemEvent.ItemId.UniqueId);
break;
case EventType.Copied:
MessageBox.Show("Item Copied :" + itemEvent.ItemId.UniqueId);
break;
case EventType.Modified:
MessageBox.Show("Item Modified :" + itemEvent.ItemId.UniqueId);
break;
}
}
}

You can use Streaming Notifications with EWS to listen for changes to items on the Exchange Server. Here is an example on how to set up Streaming Notifications:
http://blogs.msdn.com/b/exchangedev/archive/2010/12/22/working-with-streaming-notifications-by-using-the-ews-managed-api.aspx
In your case you should handle the EventType.Moved event. When you are handling the events you are given an object of type ItemEvent (as shown in the above example) which has two properties OldParentFolderId and ParentFolderId which identify the folder the item was moved from and to.

The reason, why your code doesnt work, is just simple. there is no time that Events could happen. You create a subcription which will recognize only events from the Moment you create it, 'cause the watermark is null. Only one line later, so let's say a millisecond later you ask the subscription "hey were there any Event in the past millisecond?" and the answer is "no". Create your subscription at program startup and call getevents on a timer, maybe 5 minutes later. And if you had any event in the past five minutes, your messagebox will appear.

Related

C# Event Not Triggered Unless Invoke Is Run on Task

I have a custom event handler that is supposed to post events that happen due to network events such as a device connecting or disconnecting etc. In the code for handling TCPClient Exceptions I run this code
case SocketError.HostUnreachable:
connectionStatus = NetworkingEventType.Error;
networkEvent = new GUINetworkEventArgs(connectionStatus, ipAddress, DateTime.Now, e.SocketErrorCode);
//GlobalVariables.raiseGUINetworkEvent(this, networkEvent);
Task.Run(() => GlobalVariables.raiseGUINetworkEvent(this, networkEvent));
ServerClientMessageCarrier.BroadcastToClients(networkEvent.ServerClientMsg, ServerClientMessageType.GUISettings);
boardStatus = connectionStatusList.Where(status => status.ipAddress.Equals(ipAddress)).FirstOrDefault();
if (boardStatus != null)
{
boardStatus.status = connectionStatus;
}
connectionStatusList.Remove(boardStatus);
Debug.WriteLine("NetworkCore.cs connectToControllerBoard(): Socket Exception for " + ipAddress + ": No code to handle (" + e.SocketErrorCode.ToString() + ")");
Log.Logger.Fatal("NetworkCore.cs Unexpected SocketErrorCode Exception " + ipAddress + "with Error Code:" + e.SocketErrorCode.ToString() + "\n" + e.Message.ToString());
//GlobalVariables.pauseAutoReconnectList.Add(ipAddress);
MessageBox.Show("Fatal Network Error Detected. Host is unreachable. This could be caused by an unplugged network cable. Please reconnect and restart afterwards. Pausing Reconnecting until reset.","Fatal Network Error");
//connectToControllerBoard(ipAddress, port, frequency);
return;
public static void raiseGUINetworkEvent(object sender, GUINetworkEventArgs args)
{
GUINetworkNotification?.Invoke(sender, args);
Debug.WriteLine("GlobalVariables.cs: raiseGUINetworkEvent - " + args.networkStatusEvent.ToString() + " from " + args.ipAddress);
}
What is kind of strange is if I do not put this line inside of a Task. It does not work, none of the other parts of code that subscribe to the event receive any notification.
Task.Run(() => GlobalVariables.raiseGUINetworkEvent(this, networkEvent));
"GlobalVariables" is a static class I use to house all my Event Handlers and such so any part of the program can subscribe and invoke events globally.
I was wondering why this is? My guess is that since this Exception Handling code is done on it's own Task and it has a return line a few lines down its shutting down the Task and the function to invoke the event is cancelled? But as far as I can tell Invoke is not a cancellable async function? It should just run synchronously but apparently this is not the case from my experimentation.
I would appreciate if anyone has some insight on why this works the way it does because it is quite confusing to me. I want to learn why, so I can correct any other part of my code that might be affected by the nature of how this works.

Session Triggers (Log on/off) in a Windows 7 Service with C#

the problem I have here might have a quick workaround but I've been stuck for a while.
I am writing a service that starts a process when a user logs in and stops it when the user logs out. In my previous tries I have tried using a loop which checked the presence of explorer.exe but it didn't work very well as the infinite loop meant the service was stuck at "starting". So I have looked into the C# on session change. Here is the code I currently have, creating text files to check if it actually works.
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
CanPauseAndContinue = true;
CanHandleSessionChangeEvent = true;
ServiceName = "Service1";
}
public void onDebug()
{
OnStart(null);
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
EventLog.WriteEntry("SimpleService.OnSessionChange", DateTime.Now.ToLongTimeString() +
" - Session change notice received: " +
changeDescription.Reason.ToString() + " Session ID: " +
changeDescription.SessionId.ToString());
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "sessionchange.txt");
switch (changeDescription.Reason)
{
case SessionChangeReason.SessionLogon:
string[] lines = { DateTime.Now.ToShortTimeString() };
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "logged in.txt");
System.IO.File.WriteAllLines(#"C:\Users\Public\test\loggedin.txt", lines);
break;
case SessionChangeReason.SessionLogoff:
string[] lines2 = { DateTime.Now.ToShortTimeString() };
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "logged out.txt");
System.IO.File.WriteAllLines(#"C:\Users\Public\test\loggedout.txt", lines2);
break;
}
}
protected override void OnStart(string[] args)
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "onstart.txt");
}
I expected this to work, but apparently not. The on start text file is being created, but out of the 4 logged on/off ones that should appear (somewhere) only loggedout.txt appears in the Public user directory after a log out. No matter how many times I shut down, log in or out the other text files for login are not being created. The time in the only loggedout.txt file create also won't update.
If it helps, I'm running this in a Virtual Machine. I have also read somewhere that SessionChange might only apply to remote log ins but I'm not sure. I'm not sure how to proceed, wether to keep trying or there is another way for services to "listen" to session changes?
Thanks for taking the time to read through.

WNS PushNotificationReceived does not intercept toast push notification

I'm writing a windows desktop app that relies on notifications to work. However, the event handler code, PushNotificationReceived on the channel does not seem to actually fire when I receive a notification. The following code is called to get the channel before its uri is sent to my server:
internal async Task<PushNotificationChannel> GetChannel()
{
PushNotificationChannel pnc;
try
{
pnc = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
if (_channel == null || !pnc.Uri.Equals(_channel.Uri))
{
_channel = pnc;
_channel.PushNotificationReceived += OnPushNotificationReceived;
Debug.WriteLine(_channel.Uri);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
_channel = null;
}
dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
return _channel;
}
Such that anytime the channel is created or updated (via a different channel uri), it should assign the new channel's PushNotificationReceived event to the following (which is basically lifted from msdn's example):
void OnPushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs e)
{
string typeString = String.Empty;
string notificationContent = String.Empty;
switch (e.NotificationType)
{
//
//other notification types omitted for brevity
//
case PushNotificationType.Toast:
notificationContent = e.ToastNotification.Content.GetXml();
typeString = "Toast";
// Setting the cancel property prevents the notification from being delivered. It's especially important to do this for toasts:
// if your application is already on the screen, there's no need to display a toast from push notifications.
e.Cancel = true;
break;
}
Debug.WriteLine("Received notification, with payload: {0}", notificationContent);
string text = "Received a " + typeString + " notification, containing: " + notificationContent;
var ignored = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MainPage.Current.ClearBanner();
});
}
Importantly, "MainPage.Current" is a reference to the app's main page as a static variable. The clear banner line simply removes a pink banner from the main page (just trying to get something simple working to start).
However, the code never seems to fire (no debug statement, pink banner remains). I am successfully getting the toast notification, and clicking on it will set focus to my app, so it's definitely not going to the wrong place.
Is there something I am doing wrong or some way to debug the notifications themselves?

SQLDependency.Stop() - called but when QN Subscription times out, a message is dropped on the queue

Scenario
Static Service Broker Queue and Service
Use these static queues for SQLDependency subscription
Rough outline of code
Using this blog post as a template the code roughly follows this pattern
SqlDependency.Start(this.dbConnectionString, this.notificationQueueName);
Configure Dependency targeting specific service (see code below)
private async void ConfigureDependencyUsingStoreProcedureAndSpecificQueue()
{
if (null != this.sampleSqlDependency)
{
this.sampleSqlDependency.OnChange -= null;
}
if (null != this.sampleSqlCommand)
{
this.sampleSqlCommand.Dispose();
}
if (null != this.sampleSqlConnection)
{
this.sampleSqlConnection.Dispose();
}
this.sampleSqlDependency = null;
this.sampleSqlCommand = null;
this.sampleSqlConnection = null;
//// Create connection.
this.sampleSqlConnection = new SqlConnection(this.dbConnectionString);
//// Create command.
this.sampleSqlCommand = new SqlCommand { Connection = this.sampleSqlConnection };
this.sampleSqlCommand.CommandType = CommandType.StoredProcedure;
this.sampleSqlCommand.CommandText = this.notificationStoredProcedure;
this.sampleSqlCommand.Notification = null;
//// Create Sql Dependency.
this.sampleSqlDependency = new SqlDependency(this.sampleSqlCommand, "service=" + this.notificationServiceName +"; Local database=" + this.databaseName, this.notificationTimeout);
this.sampleSqlDependency.OnChange += this.SqlDependencyOnChange;
await this.sampleSqlCommand.Connection.OpenAsync();
await this.sampleSqlCommand.ExecuteReaderAsync(CommandBehavior.CloseConnection);
if (null != this.sampleSqlCommand)
{
this.sampleSqlCommand.Dispose();
}
if (null != this.sampleSqlConnection)
{
this.sampleSqlConnection.Dispose();
}
Handle SqlDependencyOnChange event as below. Calling the ConfigureDependency code again
private void SqlDependencyOnChange(object sender, SqlNotificationEventArgs eventArgs)
{
if (eventArgs.Info == SqlNotificationInfo.Invalid)
{
Console.WriteLine("The above notification query is not valid.");
}
else
{
Console.WriteLine("\nNotification Time: {0}", DateTime.Now);
Console.WriteLine("\nNotification Info: " + eventArgs.Info);
Console.WriteLine("Notification source: " + eventArgs.Source);
Console.WriteLine("Notification type: " + eventArgs.Type + "\n");
}
switch (optionSelected)
{
case "1":
this.ConfigureDependencyUsingStoreProcedureAndDefaultQueue();
break;
case "2":
this.ConfigureDependencyUsingStoreProcedureAndSpecificQueue();
break;
case "3":
this.ConfigureDependencyUsingTextQueryAndDefaultQueue();
break;
case "4":
this.ConfigureDependencyUsingTextQueryAndSpecificQueue();
break;
}
}
Upon app shutdown call SqlDependency.Stop(this.dbConnectionString, this.notificationQueueName);. This returns true which according to the documentation means the listener was completely stopped.
Issue Faced
What I then see is that when the subscription reaches it's timeout period, it fires and drops a message onto the dependency queue waiting to be consumed.
If these messages stay in the queue, on next startup the app throws The given key was not present in the dictionary.
Also if I call SQLDependency.Stop() and leave the app running, it still consumes the QN fires for timeouts.
What step am I missing here as I am likely to face issues if messages are getting dropped on the static queue causing the The given key was not present in the dictionary exception.
Thanks
This returns true which according to the documentation means the listener was completely stopped.
This makes no promise with regard to the server state, nor the queue state. These will outlive your volatile application state, and at the next startup, you will find notifications from when your app was offline/shut down. You will have to code accordingly, with this expectation in place (eg. ignore keys that are not in the dictionary).
Note that even if SqlDependency.Stop() would attempt to stop the pending subscribed notifications, it is impossible to guarantee success as there could be notifications in transit (ie. pending delivery via Service Broker, notification may be in sys.transmission_queue). Waiting for all in transit notifications to drain delivery is also not feasible.
Additionally, your application would not handle backup-restore scenarios, as it is now. A restored backup may contain pending notification requests which will be invalidated upon restore and fire the notification message. When the application connects, it will find those unexpected notifications.
And ultimately, the application would not be able to handle its own disconnects/crashes. If you are relying on SqlDependency.Stop() to succeed before you can start successfully next time, you won't be able to start at all if you could not call SqlDependency.Stop() (=> any non-graceful stop).
For all these reasons, you must be able to handle keys not in the dictionary.

Reliably identifying and tracking Asterisk calls using C# and Aster.NET

I have been building a WinForms desktop application using C# that interfaces with Asterisk using Aster.NET (formerly/forked from Asterisk.NET). We're having real trouble reliably identifying and tracking calls that are related to an individual extension/user.
The problem we're having is due to the unpredictable/fuzzy nature of the events fired/triggered by Asterisk, with them being massively variable depending on how the call is routed before it hits an extension.
For example, the event sequence/format is different when: a call hits an IVR before getting blind transferred; if a call hits an IVR before it is attended transferred; if a call goes direct to the user's extension.
This is further hampered by the way that Asterisk tracks each side of the call using a different Unique ID (e.g. the incoming side of the call has a different UID than the received side of the call). Whilst we've managed to account for that in the (subsequently ugly!) code, we're still hitting issues with accounting for the different routing paths the call can take.
As such, I'm looking for any advice on how we can do the following:
Reliably identify an incoming call to a user's extension
We need to be able to identify the extension being called and the originating caller ID (after either a blind or attended transfer and direct call from external)
Reliably track the Unique ID for that incoming call as it's used to link to the call recording
Reliably identify an outgoing call from a user's extension
With the same caveats as above in mind
As it stands at the minute we have an extremely complex chain of event handlers that operate differently dependent on the 'current state' of the app.
To give one example: if we detect a NewStateEvent with a ChannelState of 6 ('Up'), we check if there is an ongoing call in process and that the UIDs match, and if so then the current call has been answered. If the UIDs don't match, but other factors do (e.g. channel, connectedlinenum, etc), then we pick this up as being the 'other side' of the call (i.e. the receiving or incoming side).
I'm not sure if the problem lies with the API or with AMI - but whichever it is it's causing us some real headaches.
Any advice greatly appreciated.
Is it possible for you to update to Asterisk 12? The channel names in AMI are now stable in Asterisk 12.
https://wiki.asterisk.org/wiki/display/AST/AMI+v2+Specification
i'm using package Aster.NET in c# . firstly install latest package of aster.net
than check that code .this code work perfect for me .
manager = new ManagerConnection(address, port, user, password);
manager.UnhandledEvent += new ManagerEventHandler(manager_Events);
manager.NewState += new NewStateEventHandler(Monitoring_NewState);
try
{
// Uncomment next 2 line comments to Disable timeout (debug mode)
// manager.DefaultResponseTimeout = 0;
// manager.DefaultEventTimeout = 0;
manager.Login();
if (manager.IsConnected())
{
Console.WriteLine("user name : " + manager.Username);
Console.ReadLine();
}
}
catch (Exception ex)
{
Console.WriteLine("Error connect\n" + ex.Message);
manager.Logoff();
Console.ReadLine();
}
void manager_Events(object sender, ManagerEvent e)
{
Console.WriteLine("Event : " + e.GetType().Name);
}
void Monitoring_NewState(object sender, NewStateEvent e)
{
string state = e.State;
string callerID = e.CallerId;
Console.WriteLine("caller num ...", e.CallerIdNum);
//Console.WriteLine("state =", state);
//Console.WriteLine("callerID =", callerID);
if ((state == "Ringing") | (e.ChannelState == "5"))
{
Console.WriteLine("hello rining your phone now ...");
String connectedLineNum;
String connectedLineName;
Dictionary<String, String> attributes = e.Attributes;
attributes.TryGetValue("connectedlinenum", out connectedLineNum);
attributes.TryGetValue("connectedlinename", out connectedLineName);
// "callerID" - called phone number
// "connectedLineNum" - calling phone number
// CallIn. Incoming call
}
else if ((state == "Ring") | (e.ChannelState == "4"))
{
Console.WriteLine("hello out going your call ...");
// CallOut. Outcoming call
}
else if ((state == "Up") | (e.ChannelState == "6"))
{
String connectedLineNum;
String connectedLineName;
Dictionary<String, String> attributes = e.Attributes;
attributes.TryGetValue("connectedlinenum", out connectedLineNum);
attributes.TryGetValue("connectedlinename", out connectedLineName);
// "callerID" - called phone number
// "connectedLineNum" - calling phone number
// human lifted up the phone right no
Console.WriteLine("human lifted up the phone...");
}
}

Categories