Implementing Office 365 into our WinRT application with Visual C++ (Part I)

Hi Community,

The following post is about how we can leverage Office 365 (SharePoint in our case) from our WinRT application. WWSAPI will provide the infrastructure required to interact with the Web Services. There’s a blog post I wrote a couple of years ago on WWSAPI that can be found here. The very first thing we have to be aware of is that SharePoint Online relies on Claims-Based authentication which uses FedAuth cookies that are written with an HTTPOnly flag, said that now you must be wondering, how can we retrieve these cookies then?

Before we start delving into the code, I’d like to mention that most of this functionality is encapsulated in a Win32 DLL that’s consumed from our WinRT application. The main reason for this is that WinRT supports only a subset of Win32 and I’d like this functionality to be available to different clients.

Ok… Back to the task of retrieving the FedAuth cookies, we can accomplish this by calling the InternetGetCookieEx function  as shown in the ReadSharePointCookies method below, please note two things:

  1. The InternetGetCookieEx function accepts as an argument the type of cookie to retrieve
  2. Our function is an exported function which returns an HRESULT (for success/failure) and it returns a map with all of the HTTPOnly cookies.
 1: SPCLIENT_API HRESULT ReadSharePointCookies(map<basic_string<wchar_t>, basic_string<wchar_t>>& cookies) {
 2:     auto retval = S_FALSE;
 3:     DWORD size = CookieBufferLength;
 4:     unique_ptr<wchar_t> buffer(new wchar_t[size]);
 5:
 6:     if (!InternetGetCookieEx(MicrosoftOnlineUrl, NULL, buffer.get(), &size, INTERNET_COOKIE_HTTPONLY, NULL))  {
 7:         if (size > 0) {
 8:             buffer.reset();
 9:             buffer = unique_ptr<wchar_t>(new wchar_t[size]);
 10:             if (InternetGetCookieEx(MicrosoftOnlineUrl, NULL, buffer.get(), &size, INTERNET_COOKIE_HTTPONLY, NULL))  {
 11:                 auto cookieStr = basic_string<wchar_t>(buffer.get());
 12:                 int next = cookieStr.find_first_of(';');
 13:
 14:                 while(next != string::npos) {
 15:                     auto cookie = cookieStr.substr(0, next);
 16:                     cookieStr = cookieStr.erase(0, cookie.length() + 1);
 17:                     auto name = cookie.substr(0, cookie.find('='));
 18:                     std::remove(name.begin(), name.end(), ' ');
 19:                     cookies[name] = cookie;
 20:                     next = cookieStr.find_first_of(';');
 21:                 }
 22:
 23:                 retval = S_OK;
 24:             }
 25:         }
 26:     }
 27:
 28:     return retval;
 29: }

The exported function declaration looks like this

 1: extern "C"
 2: {
 3:     SPCLIENT_API HRESULT ReadSharePointCookies(map<basic_string<wchar_t>, basic_string<wchar_t>>& cookies);
 4: }

And we do it to prevent name mangling and preserve compatibility with C.  Remember that this is a Win32 DLL consumed from our WinRT application.

For the login process then we leverage/re-use what Office 365 provides us with – A login page

image

We’re not responsible for the authentication process, but how can we get the cookies after signing in to the site? Well, if you’ve ever used the WebBrowser control (whether it’s WinForms or WPF) then fear not because WinRT provides us with the WebView control, so it’s a pretty straightforward thing to do as shown in the XAML/Code below

 1: <UserControl x:Class="MyMetroSync.Login"
 2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6:     mc:Ignorable="d"
 7:              d:DesignHeight="768" d:DesignWidth="1366">
 8:
 9:     <Grid x:Name="LayoutRoot" Background="#FF0C0C0C">
 10:         <WebView x:Name="webContent" Margin="27,31,24,36"  LoadCompleted="webContent_LoadCompleted"  />
 11:
 12:
 13:     </Grid>
 14: </UserControl>
 1: void MyMetroSync::Login::webContent_LoadCompleted(Platform::Object^ sender, Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)
 2: {
 3:     if (e->Uri->Path->Equals(LandingPage)) {
 4:         App::Properties->Insert(CookieName, ReadSharePointCookie());
 5:
 6:         //Let's call getwebid
 7:         auto spHelper = ref new Core();
 8:         auto result = spHelper->GetWebId(((IMap<String^, String^>^) App::Properties->Lookup(CookieName)), L"http://bonafideideas.sharepoint.com/_vti_bin/webs.asmx");
 9:         App::ShowSplit(nullptr);
 10:     }
 11: }

As you can see we got a property bag called Properties to store our cookies and it’s not the same property found in WPF

 1: namespace MyMetroSync
 2: {
 3:     ref class App
 4:     {
 5:     private:
 6:         static Map<String^, Object^>^ properties;
 7:
 8:     public:
 9:         virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ pArgs);
 10:         static void ShowSplit(Expression::Blend::SampleData::SampleDataSource::SampleDataCollection^ collection);
 11:         static void ShowLoginPage();
 12:
 13:         static property Map<String^, Object^>^ Properties {
 14:             Map<String^, Object^>^ get();
 15:         }
 16:     };
 17: }

But hang on… Where do you retrieve the cookies in your application?

 1: typedef HRESULT (*ptrFun) (map<basic_string<wchar_t>, basic_string<wchar_t>>& cookies);
 2:
 3: IMap<String^, String^>^ Core::ReadSharePointCookie() {
 4:     HRESULT result;
 5:     HINSTANCE hInstance;
 6:     auto retval = ref new Map<String^, String^>();
 7:     auto cookies = map<basic_string<wchar_t>, basic_string<wchar_t>>();
 8:
 9:     if ((hInstance = LoadPackagedLibrary(SP_HELPER_LIBRARY, 0)) != NULL) {
 10:         auto functor = (ptrFun) GetProcAddress(hInstance, "ReadSharePointCookies");
 11:
 12:         if (functor != nullptr && (functor(cookies)) == S_OK) {
 13:             for_each(cookies.begin(), cookies.end(), [&] (pair<basic_string<wchar_t>, basic_string<wchar_t>> cookie) {
 14:                 retval->Insert(ref new String(cookie.first.c_str()), ref new String(cookie.second.c_str()));
 15:             });
 16:         }
 17:
 18:         FreeLibrary(hInstance);
 19:     }
 20:
 21:     return retval;
 22: }

Our Win32 DLL must’ve been added to the solution and since every Metro Style application is “packaged” then the application is able to find it without any issues. Another interesting point is the function LoadPackagedLibrary that’s responsible for loading the specified module and its dependencies into the address space of the calling process, then similarly to how it’s done natively, once we have a HINSTANCE to the loaded module, we call GetProcAddress and its return value is cast into a callback to call the desired function (in this case, ReadSharePointCookies).

See you soon!

Angel

0 thoughts on “Implementing Office 365 into our WinRT application with Visual C++ (Part I)”

  1. Great!
    I’m working on an idea for SharePoint integration using WWSAPI. This will help. Just waiting for part 2.

  2. Great!
    I’m working on an idea for SharePoint integration using WWSAPI. This will help. Just waiting for part 2.

Leave a Reply

Your email address will not be published. Required fields are marked *