UWP Edition (Universal Windows, UAP 10.0)

With Windows 10 Fall Creators Update and newer, you can use the DLL based on .NET Standard 2.0 version of MailBee.NET Objects (C:\Program Files (x86)\MailBee.NET Objects\uap10.0.16299). This is the most recommended approach.

If your UWP app must target an older version of Windows, you can use the special UWP edition of MailBee.NET library. UWP edition mostly has .NET 4.5 feature set (including async/await methods). Still, a number of limitations do exist for that "old" UWP version:

However, in case if you can use the .NET Standard 2.0 version (the Min Target of your UWP project is Windows 10 Fall Creators Update), there will be much fewer limitations (and most of them are very specific and don't affect most apps):

What you need to run MailBee with UWP (Windows 10 Fall Creators Update or newer)

In Project properties, make sure that Target version and Min version is Windows 10 Fall Creators Update or newer. The MailBee.NET.dll resides in C:\Program Files (x86)\MailBee.NET Objects\uap10.0.16299 folder. Or, via NuGet, the required .DLL will be installed automatically:

Install-Package MailBee.NET

The above installs MailBee.NET.dll from lib\uap10.0.16299 folder of the package.

Install System.Text.Encoding.CodePages package:

Install-Package System.Text.Encoding.CodePages

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) must be called before accessing any MailBee classes. This initializes codepages support in the runtime.

What you need to run MailBee with UWP (older Windows)

The MailBee.NET.dll for UWP resides in C:\Program Files (x86)\MailBee.NET Objects\uap10.0 folder. Or, via NuGet, the required .DLL will be installed automatically. Just run this command:

Install-Package MailBee.NET

The above installs MailBee.NET.dll from lib\uap10.0 folder of the package (assuming that Min Target for the project is Windows version older than Fall Creators Update).

You must update Microsoft.NETCore.UniversalWindowsPlatform NuGet package to 5.2.2 (for Visual Studio 2015) or higher version (for Visual Studio 2017).

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) must be called before accessing any MailBee classes. This initializes codepages support in .NET runtime.

Socket support is different in UWP therefore methods like Imap.GetSocket return StreamSocket, not Socket. Socket class was not supported by UWP prior to Windows 10 Fall Creators Update.

You must use SocketCreating event instead of ClientServerCertificates.AutoValidation to configure which SSL certificate errors can be tolerated (useful if you need to connect to a mail server with self-signed certificate). See code snippet in Imap.SocketCreating topic for example. ClientServerCertificates.AutoValidation property is not supported.

In case of Windows 10 Fall Creators Update being Min Target (so that .NET Standard 2.0 version of MailBee.NET.dll is used instead of "old" UWP version), Imap.GetSocket will return Socket (not StreamSocket). And you can use ClientServerCertificates.AutoValidation property.

Filesystem access

Filesystem access in UWP (paths to locations accessible for reading or writing files) is limited to the app's folders like ApplicationData.Current.LocalFolder.Path. If you just need some place where to save the log file for debugging, Path.Combine(ApplicationData.Current.LocalFolder.Path, "log.txt") path can be used. For example:

msg.Parser.WorkingFolder = ApplicationData.Current.LocalFolder.Path (assuming msg is MailMessage instance).

If your UWP app must load some additional files (let's use the bounce database for BounceMail component as an example), it should store them in Assets folder:

BounceMail is not available in UWP prior to Windows 10 Fall Creators Update.

Capabilities section of Package.appxmanifest must have Internet (Client) and Private Networks (Client & Server) options checked. The latter is needed if you want to connect to localhost mail servers. Otherwise, you may get UnauthorizedAccessException or MailBeeSocketTimeoutException. You may also need more settings (Package Manifest / Declarations / Protocol / Name) in case if you want to add OAuth 2.0 authentication, see OAuth 2.0 for Universal Windows apps guide.

In our tests of Windows authentication, login with SASL NTLM method didn't work with UWP apps while SASL GSSAPI does. See AuthenticationMethods topic for more info on the authentication mechanisms available in MailBee.

UWP example

This is the UWP example for older Windows. In case of Windows 10 Fall Creators Update or newer, see OAuth 2.0 for Universal Windows apps guide.

This C#/VB example logs in your Gmail inbox via OAuth 2.0 and displays the body of the last e-mail, including embedded pictures if any (the pictures will be base64 embedded directly in HTML). The sample also demonstrates logging IMAP session into a file, excepting handling, enabling self-signed certificates (the latter is not needed for Gmail but can be useful if you need to use a local mail server). To make this sample work, do the following:

C#

using System;
using System.IO;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Popups;
using Windows.Storage;
using Windows.Networking.Sockets;
using Windows.Security.Cryptography.Certificates;
using System.Text;
using System.Threading;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Oauth2.v2;
using Google.Apis.Oauth2.v2.Data;
using Google.Apis.Services;
using MailBee;
using MailBee.Mime;
using MailBee.ImapMail;

namespace UniApp1
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        static void Imp_SocketCreating(object sender, SocketCreatingEventArgs e)
        {
            // Demonstrate how we can allow self-signed or expired certificates.
            e.NewSocket = new StreamSocket();
            e.NewSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
            e.NewSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
            e.NewSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired);
        }

        private async void button_Click(object sender, RoutedEventArgs e)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            MailBee.Global.LicenseKey = "MN1XX-XXXXXXXX-XXXX";

            Imap imp = new Imap();
            imp.Log.Filename = Path.Combine(ApplicationData.Current.LocalFolder.Path, "log.txt");
            imp.Log.Enabled = true;
            await imp.Log.ClearAsync();

            // Request Gmail IMAP/SMTP scope and the e-mail address scope.
            string[] scopes = new string[] { "https://mail.google.com/", "https://www.googleapis.com/auth/userinfo.email" };

            System.Diagnostics.Debug.WriteLine("Requesting authorization");
            Uri SecretsUri = new Uri("ms-appx:///Assets/client_secret.json", UriKind.Absolute);
            UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(SecretsUri, scopes, "user", CancellationToken.None);
            System.Diagnostics.Debug.WriteLine("Authorization granted or not required (if the saved access token already available)");

            if (credential.Token.IsExpired(credential.Flow.Clock))
            {
                System.Diagnostics.Debug.WriteLine("The access token has expired, refreshing it");
                if (await credential.RefreshTokenAsync(CancellationToken.None))
                {
                    System.Diagnostics.Debug.WriteLine("The access token is now refreshed");
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("The access token has expired but we can't refresh it :(");
                    return;
                }
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("The access token is OK, continue");
            }

            System.Diagnostics.Debug.WriteLine("Requesting the e-mail address of the user from Google");

            // Sometimes, you may also need to set Initializer.ApplicationName property.
            // In our tests, setting just Initializer.HttpClientInitializer was enough for Google.
            Oauth2Service oauthService = new Oauth2Service(
                new BaseClientService.Initializer() { HttpClientInitializer = credential });

            // Userinfo.Get may crash if you run the app under debugger (a bug in Google API).
            // If this happens, use "Start without debugging" instead.
            Userinfoplus userInfo = await oauthService.Userinfo.Get().ExecuteAsync();
            string userEmail = userInfo.Email;
            System.Diagnostics.Debug.WriteLine("E-mail address is " + userEmail);

            // Build XOAUTH2 token. Can be used with Gmail IMAP or SMTP.
            string xoauthKey = OAuth2.GetXOAuthKeyStatic(userEmail, credential.Token.AccessToken);

            try
            {
                await imp.ConnectAsync("imap.gmail.com", 993);
                await imp.LoginAsync(null, xoauthKey, AuthenticationMethods.SaslOAuth2);
                await imp.SelectFolderAsync("Inbox");
                MailMessage msg = await imp.DownloadEntireMessageAsync(imp.MessageCount, false);
                await imp.DisconnectAsync();
                string html = msg.GetHtmlWithBase64EncodedRelatedFiles();
                web.NavigateToString(html);
            }
            catch (MailBeeException ex)
            {
                if (imp.IsConnected)
                {
                    await imp.DisconnectAsync();
                }
                System.Diagnostics.Debug.WriteLine(ex.ToString());
                MessageDialog dialog = new MessageDialog(ex.ToString());
                await dialog.ShowAsync();
            }
        }
    }
}

VB

Imports Windows.UI.Popups
Imports Windows.Storage
Imports Windows.Networking.Sockets
Imports Windows.Security.Cryptography.Certificates
Imports System.Text
Imports System.Threading
Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Oauth2.v2
Imports Google.Apis.Oauth2.v2.Data
Imports Google.Apis.Services
Imports MailBee
Imports MailBee.Mime
Imports MailBee.ImapMail

Public NotInheritable Class MainPage
    Inherits Page

    Sub Imp_SocketCreating(sender As Object, e As SocketCreatingEventArgs)
        e.NewSocket = New StreamSocket()
        e.NewSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName)
        e.NewSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted)
        e.NewSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired)
    End Sub

    Private Async Sub button_Click(sender As Object, e As RoutedEventArgs) Handles button.Click
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)

        MailBee.Global.LicenseKey = "MN1XX-XXXXXXXX-XXXX"

        Dim imp As New Imap()

        imp.Log.Filename = Path.Combine(ApplicationData.Current.LocalFolder.Path, "log.txt")
        imp.Log.Enabled = True
        Await imp.Log.ClearAsync()

        ' Request Gmail IMAP/SMTP scope and the e-mail address scope.
        Dim scopes As String() = New String() {"https://mail.google.com/", "https://www.googleapis.com/auth/userinfo.email"}

        System.Diagnostics.Debug.WriteLine("Requesting authorization")
        Dim SecretsUri As New Uri("ms-appx:///Assets/client_secret.json", UriKind.Absolute)
        Dim credential As UserCredential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(SecretsUri, scopes, "user", CancellationToken.None)
        System.Diagnostics.Debug.WriteLine("Authorization granted or not required (if the saved access token already available)")

        If credential.Token.IsExpired(credential.Flow.Clock) Then
            System.Diagnostics.Debug.WriteLine("The access token has expired, refreshing it")
            If Await credential.RefreshTokenAsync(CancellationToken.None) Then
                System.Diagnostics.Debug.WriteLine("The access token is now refreshed")
            Else
                System.Diagnostics.Debug.WriteLine("The access token has expired but we can't refresh it :(")
                Return
            End If
        Else
            System.Diagnostics.Debug.WriteLine("The access token is OK, continue")
        End If

        System.Diagnostics.Debug.WriteLine("Requesting the e-mail address of the user from Google")

        ' Sometimes, you may also need to set Initializer.ApplicationName property.
        ' In our tests, setting just Initializer.HttpClientInitializer was enough for Google.
        Dim oauthService As New Oauth2Service(New BaseClientService.Initializer() With {
            .HttpClientInitializer = credential
        })

        ' Userinfo.Get may crash if you run the app under debugger (a bug in Google API).
        ' If this happens, use "Start without debugging" instead.
        Dim userInfo As Userinfoplus = Await oauthService.Userinfo.Get().ExecuteAsync()
        Dim userEmail As String = userInfo.Email
        System.Diagnostics.Debug.WriteLine(Convert.ToString("E-mail address is ") & userEmail)

        ' Build XOAUTH2 token. Can be used with Gmail IMAP or SMTP.
        Dim xoauthKey As String = OAuth2.GetXOAuthKeyStatic(userEmail, credential.Token.AccessToken)

        Dim savedEx As MailBeeException = Nothing ' Unlike C#, we can't await in Catch in VB14 (although this will change in VB17).
        Try
            Await imp.ConnectAsync("imap.gmail.com", 993)
            Await imp.LoginAsync(Nothing, xoauthKey, AuthenticationMethods.SaslOAuth2)
            Await imp.SelectFolderAsync("Inbox")
            Dim msg As MailMessage = Await imp.DownloadEntireMessageAsync(imp.MessageCount, False)
            Await imp.DisconnectAsync()
            Dim html As String = msg.GetHtmlWithBase64EncodedRelatedFiles()
            web.NavigateToString(html)
        Catch ex As MailBeeException
            savedEx = ex
            System.Diagnostics.Debug.WriteLine(ex.ToString())
        End Try
        If imp.IsConnected Then
            Await imp.DisconnectAsync()
        End If
        If savedEx IsNot Nothing Then
            Dim dialog As New MessageDialog(savedEx.ToString())
            Await dialog.ShowAsync()
        End If
    End Sub
End Class

Send feedback to AfterLogic

Copyright © 2006-2020 AfterLogic Corporation. All rights reserved.