Microsoft Exchange oAuth - System.OutOfMemoryException - c#

I've written a console app thats running as a service (using TopShelf) and using a while loop it continuously polls an office 365 inbox every 30 seconds to check for new messages. I'm doing this using oAuth and the below Microsoft libary
Microsoft.Exchange.WebServices 2.2.0.
I'm using the reccomended approach from MS to get the Access Token silently AcquireTokenByUsernamePassword & AcquireTokenSilent.
After about 5 days of running perfectly I'm getting the below exception form Microsoft library:
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
at System.String.Concat(String str0, String str1)
at Microsoft.Exchange.WebServices.Data.OAuthCredentials..ctor(String token, Boolean verbatim) in \\REDMOND\EXCHANGE\BUILD\E15\15.00.0913.015\SOURCES\sources\dev\EwsManagedApi\src\EwsManagedApi\Credentials\OAuthCredentials.cs:line 79
at Microsoft.Exchange.WebServices.Data.OAuthCredentials..ctor(String token) in \\REDMOND\EXCHANGE\BUILD\E15\15.00.0913.015\SOURCES\sources\dev\EwsManagedApi\src\EwsManagedApi\Credentials\OAuthCredentials.cs:line 36
When I've traced this through its being generated by this section in my code:
exchangeService = new ExchangeService
{
Credentials = new OAuthCredentials(authResult.AccessToken),
Url = new Uri(rdr.O365ServiceURL)
};
I've got my exchangeService object declared outside of my while loop and then I'm instantiating it inside the loop so I can make use of the AcquireTokenSilent call, otherwise Microsoft refuse the connection and issue the below message back:
Error: Your app has been throttled by AAD due to too many requests. To avoid this, cache your tokens see https://aka.ms/msal-net-throttling.
Is this a bug in Microsoft code or can I do something better to manage the memory here?
Also the service that crashed out was upto 3.6gb memory footprint which is about 3.5gb too high.

There was nothing wrong with the code in question. Whilst not ideal (I'm going to look I to the notification stream approach instead of polling thanks #paulsm4) it still worked and the Exchange Service was not disposable, so could not be instantiated with a using block.
The issue was actually in the log4net framework where I had the memory appender enabled. I removed this from my logger setup and the memory footprint stays where it should be.
This project uses three separate loggers and they were all building up in memory even after it was written to the file.
The instantiating of the exchange object was just the straw that broke the camels back and a red herring.
The app is sitting at a health 14MB memory footprint now.
Thanks!

Related

Windows DPAPI in AWS AMI fails with Access is denied

We are using an AWS EC2 Windows AMI to do our builds from a Jenkins job.
Our libraries use the Windows Cryptography API: Next Generation (NG) (DPAPI) to protect sensitive data from C# and C++ components.
Our builds succeed without any issue, but our unit test trying to use this API in the AMI instance keeps on failing.
On the C# side we get the following exception (almost the same on C++ side):
Access is denied.
Source: System.Security
HRESULT: -2147024891
Stack:
at System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope)
We could get the Unit Tests passing with PsExec, but the output is lost and the step that normally takes about 20 minutes now takes more than 4 hours.
From some reading up it seems like the WinRM is the cause of the issue, the PsExec seems to verify that.
Are there any other options instead of PsExec we can try to allow the unit tests to use the DPAPI inside the AMI?
PS: We did try to sync the master keys with CryptProtectData(CRYP TPROTECT_CRED_SYNC) but that did not work.
Pass DataProtectionScope.LocalMachine to ProtectedData.Protect() for the scope. Seems like you are passing DataProtectionScope.CurrentUser which requires the same user for Protect and Unprotect. LocalMachine allows any admin user to UnProtect().

C# COM application crash debugging

The C# console application running .NET 4.5.2 (app1) opens a COM application (app2) and does some work with that app2's API. So far all of the work is successful, but sometimes when app1 attempts to close app2, app2 hangs permanently.
If the process for app2 is ended with task manager then app1 reports access denied. Does that occur because the terminated process is no longer available or does it occur because it was blocking a thread in app1 and it was unable to report the error until the thread was allowed to continue?
The code used to terminate app2 is
private static void CloseSW(SldWorks swApp, Process sw_proces)
{
// Close with API call
if (Task.Run(() => { swApp.CloseAllDocuments(true); swApp.ExitApp(); }).Wait(TimeSpan.FromSeconds(20)))
return;
// Kill process if API call failed
if (Task.Run(() => { SWHelper.CloseSW(sw_proces); }).Wait(TimeSpan.FromSeconds(20)))
return;
// Unable to close SolidWorks, ignore error and continue
// This will eventually cause SolidWorks to crash and the crash handler will take over
}
This code should never take much more than 40 seconds to complete, but maybe the COM interop is causing some unexpected behaviour?
I am unable to reproduce this error on a development machine. What it the best way to trace the exact point of failure? It is possible that the failure is not in CloseSW but some point before this. Is there a better way to trace the error than to write each line to a log file?
It is also worth noting that this code works for 60 - 150 runs before it has any errors and both applications are closed between each run.
I have control of the remote environment so remote debugging is an option, but I've never set that up before.
Typically with COM interops causing issues is that IIS is having issues with the object using the current ISAPI.dll. Please verify that your permissions are configured within your assembly to work with your current version of IIS>
A few questions to help assist would be, which framework version are you using, which version of IIS and what is your Application Pool using for a framework.
HTH

Converting Microsoft EWS StreamingNotification Example to a service

I've been working to try and convert Microsoft's EWS Streaming Notification Example to a service
( MS source http://www.microsoft.com/en-us/download/details.aspx?id=27154).
I tested it as a console app. I then used a generic service template and got it to the point it would compile, install, and start. It stops after about 10 seconds with the ubiquitous "the service on local computer started and then stopped."
So I went back in and upgraded to C# 2013 express and used NLog to put a bunch of log trace commands to so I could see where it was when it exited.
The last place I can find it is in the example code, SynchronizationChanges function,
public static void SynchronizeChanges(FolderId folderId)
{
logger.Trace("Entering SynchronizeChanges");
bool moreChangesAvailable;
do
{
logger.Trace("Synchronizing changes...");
//Console.WriteLine("Synchronizing changes...");
// Get all changes since the last call. The synchronization cookie is stored in the
// _SynchronizationState field.
// Only the the ids are requested. Additional properties should be fetched via GetItem
//calls.
logger.Trace("Getting changes into var changes.");
var changes = _ExchangeService.SyncFolderItems(folderId, PropertySet.IdOnly, null, 512,
SyncFolderItemsScope.NormalItems,
_SynchronizationState);
// Update the synchronization cookie
logger.Trace("Updating _SynchronizationState");
the log file shows the trace message ""Getting changes into var changes." but not the "Updating _SynchronizationState" message.
so it never gets past var changes = _ExchangeService.SyncFolderItems
I cannot for the life figure out why its just exiting. There are many examples of EWS streaming notifications. I have 3 that compile and run just fine but nobody as far as I can tell has posted an example of it done as a service.
If you don't see the "Updating..." message it's likely the sync threw an exception. Wrap it in a try/catch.
OK, so now that I see the error, this looks like your garden-variety permissions problem. When you ran this as a console app, you likely presented the default credentials to Exchange, which were for your login ID. For a Windows service, if you're running the service with one of the built-in accounts (e.g. Local System), your default credentials will not have access to Exchange.
To rectify, either (1) run the service under the account you did the console app with, or (2) add those credentials to the Exchange Service object.

Push Notifications with PushSharp - the basics

I need to push notifications to tens of thousands of iOS devices that my app installed. I'm trying to do it with PushSharp, but I'm missing some fundamental concepts here. At first I tried to actually run this in a Windows service, but couldn't get it work - getting null reference errors coming from _push.QueueNotification() call. Then I did exactly what the documented sample code did and it worked:
PushService _push = new PushService();
_push.Events.OnNotificationSendFailure += new ChannelEvents.NotificationSendFailureDelegate(Events_OnNotificationSendFailure);
_push.Events.OnNotificationSent += new ChannelEvents.NotificationSentDelegate(Events_OnNotificationSent);
var cert = File.ReadAllBytes(HttpContext.Current.Server.MapPath("..pathtokeyfile.p12"));
_push.StartApplePushService(new ApplePushChannelSettings(false, cert, "certpwd"));
AppleNotification notification = NotificationFactory.Apple()
.ForDeviceToken(deviceToken)
.WithAlert(message)
.WithSound("default")
.WithBadge(badge);
_push.QueueNotification(notification);
_push.StopAllServices(true);
Issue #1:
This works perfectly and I see the notification pop up on the iPhone. However, since it's called a Push Service, I assumed it would behave like a service - meaning, I instantiate it and call _push.StartApplePushService() within a Windows service perhaps. And I thought to actually queue up my notifications, I could do this on the front-end (admin app, let's say):
PushService push = new PushService();
AppleNotification notification = NotificationFactory.Apple()
.ForDeviceToken(deviceToken)
.WithAlert(message)
.WithSound("default")
.WithBadge(badge);
push.QueueNotification(notification);
Obviously (and like I already said), it didn't work - the last line kept throwing a null reference exception.
I'm having trouble finding any other kind of documentation that would show how to set this up in a service/client manner (and not just call everything at once). Is it possible or am I missing the point of how PushSharp should be utilized?
Issue #2:
Also, I can't seem to find a way to target many device tokens at once, without looping through them and queuing up notifications one at a time. Is that the only way or am I missing something here as well?
Thanks in advance.
#baramuse explained it all, if you wish to see a service "processor" you can browse through my solution on https://github.com/vmandic/DevUG-PushSharp where I've implemented the workflow you seek for, i.e. a win service, win processor or even a web api ad hoc processor using the same core processor.
From what I've read and how I'm using it, the 'Service' keyword may have mislead you...
It is a service in a way that you configure it once and start it.
From this point, it will wait for you to push new notifications inside its queue system and it will raise events as soon as something happens (delivery report, delivery error...). It is asynchronous and you can push (=queue) 10000 notifications and wait for the results to come back later using the event handlers.
But still it's a regular object instance you will have to create and access as a regular one. It doesn't expose any "outside listener" (http/tcp/ipc connection for example), you will have to build that.
In my project I created a small selfhosted webservice (relying on ServiceStack) that takes care about the configuration and instance lifetime while only exposing the SendNotification function.
And about the Issue #2, there indeed isn't any "batch queue" but as the queue function returns straight away (enqueue and push later) it's just a matter of a looping into your device tokens list...
public void QueueNotification(Notification notification)
{
if (this.cancelTokenSource.IsCancellationRequested)
{
Events.RaiseChannelException(new ObjectDisposedException("Service", "Service has already been signaled to stop"), this.Platform, notification);
return;
}
notification.EnqueuedTimestamp = DateTime.UtcNow;
queuedNotifications.Enqueue(notification);
}

Problem with calling Console application (WCF Service) from webform

I am using a ASP.net webform application to run an existing console application which get all records from DB and send them through a third party WCF service. Locally everything is working fine. When I run the application it opens the console, gets the records and sends them. But now I pushed my files over to Test server along with the exe file and related config files. But when I access the application through the browser (test url) I get the same error message time and again and I don't see the console window. Sometimes everything works fine but never two times in a row.
The error message is:
"There was no end point listening at '.....svc' that could accept message. This is often caused by incorrect address or soap action.
System.net.webexception. Remote name could not be resolved
at System.Net.HttpWebRequest.GetRequestStream
at System.ServiceModel.Channels.HttpOutput.Webrequest.HttpOutput.GetOutputStream()
The code I have used in the webform to call console application is:
ProcessStartInfo p = new ProcessStartInfo();
p.Arguments = _updateNow.ToString();
p.FileName="something";
p.UseShellExecute = false;// tried true too without luck
Process.Start(p);
Error message denotes "there is no end point" and sounds like there is problem with the WCF service but if I double click the executable in Test there is no problem. What could be the possible problem or should I redo the console application functionality to my main webform application?
Update: After adding Thread.Sleep(3000) after Process.Start(p), I'm having no problem. So seems like main application is not waiting for the batch process to complete. How to solve this problem?
It seems like there is a short delay between starting the console application and the WCF web service becoming initialise and available to use - this is to be expected.
You could either:
Work around the issue using Thread.Sleep() and possibly with a couple of catch - retry blocks.
You could have the console application report to the creating process when it is ready to recieve requests (for example by having it write to the standard output and using redirected streams).
However at this point I'd probably reconsider the architecutre slightly - starting a new process is relativley costly, and on top of that initialising a WCF serice is also relatively costly too. If this is being done once per request then as well as the above timing issues you are also incurring performance penalties.
Is it not possible to change the architecutre slightly so that a single external process (for example a Windows service) is used for all requests instead of spawning a new process each time?

Categories