Dela via


How to connect to Windows Live using the C++ REST SDK

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

In a C++ Windows Runtime app, you use the Live SDK REST reference directly to connect to Windows Live services. C++ apps can't use the Live SDK (Microsoft.Live.DLL) because that SDK is only for .NET projects. However, the C++ REST SDK, which is included in Microsoft Visual Studio, provides a convenient native C++ API for making REST calls. This tutorial shows how to use the C++ REST SDK to sign into Microsoft OneDrive, which is one of the Live services, and then upload and download photos. It also demonstrates a best practice for connected apps—users can sign in, sign out, and know their connected status at all times.

For general guidelines about the online connection experience, see Guidelines for the Microsoft online experience. Although the code examples in that article use C# with the Live SDK, and Javascript with the WL object, the guidelines are still relevant for C++ apps that use the Live REST endpoints directly.

Important  The tutorial in this topic demonstrates a Windows Store app. You can also use the C++ REST SDK with a Windows Phone Store app.

 

What you need to know

Technologies

Prerequisites

  • Visual Studio
  • A Windows Store developer account.
  • This tutorial assumes you have a basic familiarity with Windows Store apps written in C++. If that's not the case, consider starting with Hello World in C++ before attempting to work through this topic.

Instructions

Step 1: Reserve an app name

Before your app can connect to Live, it must be registered with the Windows Store. To register your app, you need to complete the first step in the submission process, which is to reserve the app's name.

  1. Navigate to your dashboard in the Windows Developer Center. If you are not already signed in, you will be prompted to do so.
  2. Click on the "Submit an app" link.
  3. Follow the instructions to reserve a unique name for your app. When this step shows as complete you will be able to connect to Live.

Step 2: Associate the app with the Windows Store

  1. Create a new C++ Windows Store project using the Blank template and call it MyLiveApp. In the real world, you can give your project any name you like and use any project template.
  2. On the main menu, choose Store (or Project | Store depending on which edition of Visual Studio you are using) and then Associate App with the Store. Follow the wizard instructions to associate your app with the name you just reserved. Visual Studio will update the project's .appxmanifest file with the Package Display Name, Package Name, and other values based on your selection. It also gives your app the key it will need to connect to Live.

Step 3: Add a reference to the C++ REST SDK

  1. Under Project | Properties, click the Add New Reference button to show the Add Reference dialog.
  2. In the left pane, expand the Windows node and then select Extensions. In the middle pane, check the box next to C++ REST SDK for Windows Store apps.

Step 4: Add live_connect.h to the project

The live_connect.h header file provides a convenient wrapper for connecting to Windows Live services, and is part of a sample project in the C++ REST SDK. Currently it is not an official SDK header and so it is not located in the include folder. We have to add its contents manually to our new project and modify it just a little.

  1. On the main menu, select Project | Add new Item and then select Header File (.h). Name the file "live_connect_MyLiveApp.h" (or whatever you like) and click OK.

  2. Copy the contents of the live_connect.h file from the C++ REST SDK Code Gallery page here and paste them into the new file you just created, overwriting any existing content in the file.

  3. Delete all the include statements and replace them with these:

    #include <basic_types.h>
    #include <http_client.h>
    #include <streams.h>
    #include <filestream.h>
    

    Basically we removed collection.h and pplxtasks.h because we already include collection.h and ppltasks.h in pch.h. For the rest, we modified the search path so that they will be found in the C++ REST Extension SDK folder (under \Program Files (x86)\Microsoft SDKs\...).

  4. Add the following public member function to the live_client class:

    /// <summary>
    /// Indicates whether the user is able to sign out of the app.
    /// </summary>
    bool can_sign_out()
    {
        return m_authenticator->CanSignOut;
    }
    

Step 5: Create the data model

The app retrieves user, album and photo data, so we create two classes to represent those objects. We also need data members to store the connection status. The data classes are declared as Bindable because the ListView objects are data-bound to them. We encapsulate the data classes as members in a single DataModel class, which we will then store as a global resource, and set as the DataContext for the MainPage.

  1. Add the following data classes to the MyLiveApp namespace in app.xaml.h:

        [Windows::UI::Xaml::Data::Bindable]
        public ref class Album sealed
        {
        public:
            property Platform::String^ Name;
            property Platform::String^ ID;
            property int Count;
            property Windows::Foundation::Uri^ Link;
        };
    
        [Windows::UI::Xaml::Data::Bindable]
        public ref class SkyDrivePhoto sealed
        {
        public:
            property Platform::String^ Name;
            property Platform::String^ ID;
            property Platform::String^ ParentID;
            property Platform::String^ PhotoSource;
        };
    
        [Windows::UI::Xaml::Data::Bindable]
        public ref class Me sealed
        {
        public:
            property Platform::String^ Id;
            property Platform::String^ Name;
            property Platform::String^ FirstName;
            property Platform::String^ LastName;
            property Platform::String^ Gender;
            property Platform::String^ Locale;
            property Platform::String^ UpdatedTime;
        };
    
        [Windows::UI::Xaml::Data::Bindable]
        public ref class DataModel sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged
        {
        public:
            DataModel()
            {
                Status = "You're not signed in.";
                AlbumDataSource = ref new Platform::Collections::Vector<Album^>();
                PhotoDataSource = ref new Platform::Collections::Vector<SkyDrivePhoto^>();
            }
    
            property  Windows::Foundation::Collections::IVector<Album^>^ AlbumDataSource;
            property  Windows::Foundation::Collections::IVector<SkyDrivePhoto^>^ PhotoDataSource;
    
            property Platform::String^ Status
            {
                void set(Platform::String^ str)
                {
                    _status = str;
                    PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Status"));
                }
    
                Platform::String^ get(){ return _status; }
            }
    
            property Platform::String^ UserName
            {
                void set(Platform::String^ str)
                {
                    _userName = str;
                    PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("UserName"));
                }
    
                Platform::String^ get(){ return _userName; }
            }
    
            virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
    
        internal:
            property web::live::live_client& LiveClient
            {
                web::live::live_client& get()
                {
                    return _liveClient;
                }
            }
        private:
            Platform::String^ _status;
            Platform::String^ _userName;
            web::live::live_client _liveClient;
        };
    

    Note that we also store an instance of the web::live::live_client class (defined in live_connect.h) here in the app class, so that it remains in memory for the lifetime of the app. This design is not required but it makes sense in this case because the only purpose of this app is to connect to OneDrive.

  2. Add this code to app.xaml, just before the closing </Application> tag. This markup causes the DataModel class to be instantiated when the app starts up:

     <Application.Resources>
        <local:DataModel x:Key="dataModel"></local:DataModel>
    </Application.Resources>
    

    No changes to app.xaml.cpp are required.

Step 6: Create the user interface main page

In this example, we'll create a simple user interface with a single page to display the user's OneDrive photo albums, photos, their connection status, and a SettingsFlyout to enable the user to sign in or out, and to view the app's Privacy policy. The flyout is invoked from the Settings charm. In the main page, one ListView displays each album in the user's OneDrive, and another displays the photos in the selected album.

  1. Replace the existing markup in mainpage.xaml with this markup that declares two side-by-side ListViews and some buttons:

        <Page
        x:Class="MyLiveApp.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:MyLiveApp"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <StackPanel Grid.ColumnSpan="2" Margin="50,10,10,10">
                <TextBlock Margin="20,10,10,10" Style="{StaticResource TitleTextBlockStyle}" FontSize="48">C++ REST SDK and OneDrive</TextBlock>
                <TextBlock x:Name="Status" Text="{Binding Path=Status}" FontSize="16" HorizontalAlignment="Right" Margin="10,40,10,10"></TextBlock>
                <TextBlock TextWrapping="Wrap" Margin="10,20,20,20" FontSize="16">Sign in to your OneDrive account by invoking the Settings pane and choosing Account. 
                    The greeting in the upper right clearly shows the user that they are connected.</TextBlock>
                <Button Click="GetAlbums_Click">Show my OneDrive albums</Button>
                <Button Click="Upload_Click">Upload a photo to the selected album</Button>
                <Button Click="Download_Click">Download the currently selected photo.</Button>
            </StackPanel>
            <ListView
                    x:Name="ItemsListView"
                Grid.Row="1"
                    ItemsSource="{Binding Path=AlbumDataSource}"
                SelectionMode="Single"
                SelectionChanged="ItemsListView_SelectionChanged"
                    IsItemClickEnabled="True"
                    ItemClick="ItemsListView_ItemClick">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Name}" FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding Count}" FontSize="16" Margin="15,0,0,0"/>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <ListView
                Grid.Row="1"
                        Grid.Column="1"
                    x:Name="ItemsDetailListView"
                SelectionMode="Single"
                SelectionChanged="ItemDetails_SelectionChanged"
                    ItemsSource="{Binding Path=PhotoDataSource}"
                    IsItemClickEnabled="True"
                        ScrollViewer.VerticalScrollBarVisibility="Visible"
                    ItemClick="ItemsDetailListView_ItemClick"
                        Margin="10,10,10,10">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Name}" FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding ID}" FontSize="16" Margin="15,0,0,0"/>
                            <TextBlock Text="{Binding ParentID}" FontSize="16" Margin="15,0,0,0"/>
                            <Image Height="150" Width="225" Source="{Binding PhotoSource}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>
    </Page>
    

    This page has two Windows::UI::Xaml::Controls::TextBlock elements that show the user's name and connected status, and a Windows::UI::Xaml::Controls::ListView that displays the high-level folders in the user's Albums collection. Each ListView item is an Album object, which we define in app.xaml.h. We bind to the Name and Count properties. When an album is selected, the first 10 photos in it are downloaded and displayed in the ListView in Column 1. The user can select a photo and click the button to download it.

  2. In MainPage.xaml.h, inside the namespace block, replace the class definition with this code:

        public ref class MainPage sealed
        {
        public:
            MainPage();
    
        protected:
            virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
            virtual void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
    
        private:
            void GetAlbums_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
            Windows::Foundation::EventRegistrationToken commandsRequestedEventRegistrationToken;
            void OnCommandsRequested(Windows::UI::ApplicationSettings::SettingsPane^ settingsPane, Windows::UI::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs^ e);
            void OnSettingsCommand(Windows::UI::Popups::IUICommand^ command);
            void ItemsListView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e);
            void ItemsDetailListView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e);
            void Upload_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
            void Download_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
            void ItemsListView_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);
            void ItemDetails_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);
            int m_selectedAlbumIndex;
            int m_selectedPhotoIndex;
        };
    
  3. In MainPage.xaml.cpp, replace the entire file contents with this code:

    //
    // MainPage.xaml.cpp
    // Implementation of the MainPage class.
    //
    
    #include "pch.h"
    #include "App.xaml.h"
    #include "MainPage.xaml.h"
    #include "SettingsFlyout.xaml.h"
    #include <string>
    
    using namespace MyLiveApp;
    using namespace concurrency;
    using namespace std;
    using namespace web;
    using namespace web::http;
    using namespace web::http::client;
    using namespace web::live;
    
    
    using namespace Windows::UI::ApplicationSettings;
    using namespace Windows::UI::Popups;
    
    using namespace Platform;
    using namespace Platform::Collections;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::Storage::Pickers;
    using namespace Windows::Storage;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    
    // The Blank Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkId=234238
    
    MainPage::MainPage()
    {
        InitializeComponent();
    
        // Initialize the DataContext for this page to
        // enable the ListViews to data-bind to our collections.
        DataContext = App::Current->Resources->Lookup("dataModel");
    }
    
    #pragma region settingsflyout
    
    void MainPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)
    {
        this->commandsRequestedEventRegistrationToken = SettingsPane::GetForCurrentView()->CommandsRequested += ref new TypedEventHandler<SettingsPane^, SettingsPaneCommandsRequestedEventArgs^>(this, &MainPage::OnCommandsRequested);
    }
    
    void MainPage::OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)
    {
        SettingsPane::GetForCurrentView()->CommandsRequested -= this->commandsRequestedEventRegistrationToken;
    }
    
    void MainPage::OnCommandsRequested(SettingsPane^ settingsPane, SettingsPaneCommandsRequestedEventArgs^ e)
    {
        UICommandInvokedHandler^ handler = ref new UICommandInvokedHandler(this, &MainPage::OnSettingsCommand);
        SettingsCommand^ accountCommand = ref new SettingsCommand("account", "Account", handler);
        e->Request->ApplicationCommands->Append(accountCommand);
    }
    
    void MainPage::OnSettingsCommand(Windows::UI::Popups::IUICommand^ command)
    {
        if (command->Label == "Account")
        {
            auto mySettings = ref new MyLiveApp::SettingsFlyout();
            mySettings->Show();
        }
    }
    
    #pragma endregion settingsflyout
    
    
    // Invoked when the user clicks on the "Show my OneDrive albums" button.
    // This function populates the ListView in Column 0.
    void MainPage::GetAlbums_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        // The DataModel instance was created when the app started. Here we 
        // retrieve it from the app resources Map.
        DataModel^ dm = safe_cast<DataModel^>(App::Current->Resources->Lookup("dataModel"));
    
        // Don't try getting albums if we're not signed in. You could
        // modify this code to show a useful message to the user.
        if (dm->LiveClient.authentication_token().length() == 0)
        {
            dm->Status == "Not signed in.";
            return;
        }
    
        // Make an asynchronous call to OneDrive's REST interface to get the
        // user's album info, which is formatted as JSON objects, then parse
        // the JSON, create local objects and store them in the data model for this page.
        dm->LiveClient.get(L"me/albums")
            .then([this, dm](web::json::value v) 
            {
                for (const auto& it : v[L"data"])
                {
                    const  auto& album = it.second;
                    Album^ a = ref new Album();
                    a->Name = ref new String(album[L"name"].as_string().c_str());
                    a->ID = ref new String(album[L"id"].as_string().c_str());
                    a->Count = album[L"count"].as_integer();
                    a->Link = ref new Uri(ref new String(album[L"link"].as_string().c_str()));
                    dm->AlbumDataSource->Append(a);
                }
            // Perform updates on the UI thread
            // to avoid the E_RPC_WRONG_THREAD exception.
            }, task_continuation_context::use_current())
            .then([dm](task<void> t)
        {
            try
            {
                // Handle exceptions in task chain.
                t.get();
            }
    
            catch (const http_exception& e)
            {
                wostringstream str;
                str << hex << e.error_code();
                dm->Status = L"Not connected. HTTP error code: " + ref new String(str.str().c_str());
            }
    
            catch (const exception& e)
            {
                wostringstream str;
                str << e.what();
                dm->Status = L"Not connected. Error: " + ref new String(str.str().c_str());
            }
        }, task_continuation_context::use_current());
    }
    
    // When the user clicks on an album in the ItemsListView in Column 0, the ItemsDetailListView in Column 1
    // loads the first 10 photos from that album. This is an asynchronous operation and the photos
    // may take a few seconds, or longer, to appear.
    void MainPage::ItemsListView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e)
    {
        Album^ a = static_cast<Album^>(e->ClickedItem);
        auto lv = static_cast<ListView^>(sender);
        lv->SelectedItem = a;
        DataModel^ dm = safe_cast<DataModel^>(App::Current->Resources->Lookup("dataModel"));
    
        // Remove photo data from the last selected album
        dm->PhotoDataSource->Clear();
    
        // Limit the number of photos to retrieve to 10
        String^ str = a->ID + L"/photos?limit=10";
    
        // Call the REST API and retrieve the photo data
        dm->LiveClient.get(str->Data()).then([this, dm](web::json::value v)
        {
            for (const auto& it : v[L"data"])
            {
                const auto& photo = it.second;
                SkyDrivePhoto^ p = ref new SkyDrivePhoto();
    
                // Picture metadata:
                p->Name = ref new String(photo[L"name"].as_string().c_str());
                p->ID = ref new String(photo[L"id"].as_string().c_str());
                p->ParentID = ref new String(photo[L"parent_id"].as_string().c_str());
    
                // The picture data:
                p->PhotoSource = ref new String(photo[L"source"].as_string().c_str());
    
                // Add the photo to the data model for this page. The ListView in Column 1
                // data binds to PhotoDataSource and automatically displays the 
                // photos as they are appended.
                dm->PhotoDataSource->Append(p);
            }
        }, task_continuation_context::use_current()).then([dm](task<void> t)
        {
            try
            {
                // Handle exceptions in task chain.
                t.get();
            }
    
            catch (const http_exception& e)
            {
                wostringstream str;
                str << hex << e.error_code();
                dm->Status = L"Not connected. HTTP error code: " + ref new String(str.str().c_str());
            }
    
            catch (const exception& e)
            {
                wostringstream str;
                str << e.what();
                dm->Status = L"Not connected. Error: " + ref new String(str.str().c_str());
            }
        });
    }
    
    // Enables the user to select a photo from their local pictures library and upload it
    // to the specified OneDrive folder.
    void MyLiveApp::MainPage::Upload_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        auto openPicker = ref new FileOpenPicker();
        openPicker->ViewMode = PickerViewMode::Thumbnail;
        openPicker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;
        openPicker->FileTypeFilter->Append(".jpg");
        openPicker->FileTypeFilter->Append(".jpeg");
        openPicker->FileTypeFilter->Append(".png");
    
        DataModel^ dm = safe_cast<DataModel^>(App::Current->Resources->Lookup("dataModel"));
        String^ path = dm->AlbumDataSource->GetAt(m_selectedAlbumIndex)->ID + L"/files/";
        StorageFile^ file;
        create_task(openPicker->PickSingleFileAsync()).then(
            [this, dm, file, path](StorageFile^ f)
        {
            String^ s = path + f->Name;
            return dm->LiveClient.upload(s->Data(), f);
        }).then([](task<web::json::value> t)
        {
            try
            {
                auto a = t.get();
            }
    
            catch (Platform::Exception^ e)
            {
                auto v = e->Message;
            }
    
            catch (std::exception& e)
            {
                auto x = e.what();
            }
        });
    }
    
    // Enables the user to select a photo from the ItemsDetailListView
    // and download it to the local Pictures folder.
    void MyLiveApp::MainPage::Download_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
    
        DataModel^ dm = safe_cast<DataModel^>(App::Current->Resources->Lookup("dataModel"));
    
        if (dm->PhotoDataSource->Size == 0)
        {
            // If no photos have been loaded yet, do nothing.
            return;
        }
        String^ path = dm->PhotoDataSource->GetAt(0)->ID;
        String^ name = dm->PhotoDataSource->GetAt(0)->Name;
        create_task(KnownFolders::PicturesLibrary->CreateFileAsync(name,
            CreationCollisionOption::GenerateUniqueName))
            .then([dm, path](StorageFile^ f)
        {
            dm->LiveClient.download(path->Data(), f);
        });
    }
    
    void MyLiveApp::MainPage::ItemsDetailListView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e)
    {
        auto lv = static_cast<ListView^>(sender);
        lv->SelectedItem = e->ClickedItem;
    }
    
    void MyLiveApp::MainPage::ItemsListView_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
    {
        auto lv = static_cast<ListView^>(sender);
        m_selectedAlbumIndex = lv->SelectedIndex;
    }
    
    void MyLiveApp::MainPage::ItemDetails_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
    {
        auto lv = static_cast<ListView^>(sender);
        m_selectedPhotoIndex = lv->SelectedIndex;
    }
    

    The http_client.h header contains the C++ REST SDK functions and type definitions and they are defined in the web::* namespaces. Most of the code in this file is infrastructure to make the Settings flyout work. Only the GetAlbums_Click method is related to the REST call to OneDrive. This method requires that the user is already logged in. If that is not the case, the method returns and does nothing. The code for signing in is in SettingsFlyout.xaml.cpp.

Step 7: Add the SettingsFlyout and the login logic

For connected apps, there are strong guidelines on how the UI should expose information about the state of the connection, and how it should enable users to sign in and sign out, and how it should expose the privacy statement. If your app enables user to sign in manually, then that UI belongs in a SettingsFlyout, as does your privacy statement. You should also enable users to sign out from Live, but you need to be aware that if a user is signed into Windows with a domain account that is linked to a Microsoft account, they will not be able to sign out of Live from your app. For example, assume a user has a domain account JoeUser@Contoso.com and he linked that account to his Microsoft account Joe1234@hotmail.com. Joe can use the app when signed in to his computer on the domain account, but he is not allowed to sign out of the app unless he logs out of the computer and logs in under his Microsoft account. We handle that case in this app by hiding the Sign out button if the user is not able to sign out.

  1. In Solution Explorer, right-click on the project node and choose Add New Item. In the dialog, scroll to the bottom and select SettingsFlyout. Accept the default name and click OK.

  2. In SettingsFlyout.xaml, replace the outermost StackPanel with this code:

        <!-- This StackPanel acts as a root panel for vertical layout of the content sections -->
    
        <StackPanel x:Name="content1">
            <StackPanel Style="{StaticResource SettingsFlyoutSectionStyle}">
                <TextBlock Margin="0,0,0,25" x:Name="userName" Text="{Binding Path=UserName}" Style="{StaticResource BodyTextBlockStyle}"/>
                <Button x:Name="signInBtn" Margin="-3,0,0,0" Content="Sign In" Click="SignInClick" Visibility="Collapsed"/>
                <Button x:Name="signOutBtn" Margin="-3,0,0,0" Content="Sign Out" Click="SignOutClick" Visibility="Collapsed"/>
                <TextBlock x:Name="signOutMessage" Visibility="Collapsed" Margin="10,10,10,10" TextWrapping="WrapWholeWords">"You are signed in with a domain account that is linked to your Microsoft account. To sign out from this app, you must unlink the Microsoft account from the domain account. Go to Settings, Change PC Settings, Accounts, and click Disconnect."</TextBlock>
            </StackPanel>
            <StackPanel Style="{StaticResource SettingsFlyoutSectionStyle}">
                <Button Padding="-5,0,0,0" Content="View privacy statement" Click="Privacy_Click" HorizontalAlignment="Left"/>
            </StackPanel>
        </StackPanel>
    
  3. In SettingsFlyout.xaml.h, replace the entire page contents with this:

    //
    // SettingsFlyout.xaml.h
    // Declaration of the SettingsFlyout class
    //
    
    #pragma once
    #include "live_connect.h"
    #include "SettingsFlyout.g.h"
    
    namespace MyLiveApp
    {
    
        [Windows::Foundation::Metadata::WebHostHidden]
        public ref class SettingsFlyout sealed
        {
        public:
            SettingsFlyout();
        private:
            void SignInClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
            void SignOutClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
            void backClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::BackClickEventArgs^ e);
            void Privacy_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
            Windows::Foundation::EventRegistrationToken backClickEventRegistrationToken;
            bool _isSecondContentLayer;
        };
    }
    
    
  4. In SettingsFlyout.xaml.cpp, replace the entire file with this code:

    
    //
    // SettingsFlyout.xaml.cpp
    // Implementation of the SettingsFlyout class
    //
    
    #include "pch.h"
    #include "SettingsFlyout.xaml.h"
    #include "MainPage.xaml.h"
    #include <http_client.h>
    
    #include <string>
    
    using namespace std;
    using namespace concurrency;
    
    using  namespace web;
    using  namespace web::http;
    using  namespace web::http::client;
    using  namespace web::live;
    using namespace Windows::Security::Authentication::OnlineId;
    
    
    using namespace MyLiveApp;
    
    using namespace Platform;
    using namespace Platform::Collections;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    using namespace Windows::UI::Popups;
    
    
    // The Settings Flyout item template is documented at https://go.microsoft.com/fwlink/p/?LinkId=273769
    
    MyLiveApp::SettingsFlyout::SettingsFlyout()
    {
        InitializeComponent();
        DataModel^ dm = safe_cast<DataModel^>(Application::Current->Resources->Lookup("dataModel"));
        DataContext = dm;
    
        if (dm->LiveClient.can_sign_out())
        {
            // We are signed in. Show sign out button only.
            signOutBtn->Visibility = Windows::UI::Xaml::Visibility::Visible;
            signInBtn->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
        }
        else
        {
            if (dm->LiveClient.authentication_token() == L"")
            {
                // We are signed out. Show the sign in button.
                signOutBtn->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
                signInBtn->Visibility = Windows::UI::Xaml::Visibility::Visible;
            }
            else
            {
                // We are signed in but can't sign out from here because we are
                // signed in with a linked domain account. In this case we need 
                // to display just the sign out message.
                signOutBtn->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
                signInBtn->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
                signOutMessage->Visibility = Windows::UI::Xaml::Visibility::Visible;
            }
        }
    }
    
    /// <summary>
    /// This is the handler for the SettingsFlyout2 BackClick event. Original content is restored 
    /// and the event args are marked as handled.
    /// This handler is only attached when the second content layer is visible.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void MyLiveApp::SettingsFlyout::backClick(Platform::Object^ sender, BackClickEventArgs^ e)
    {
        // Return to previous content and remove BackClick handler
        e->Handled = true;
        this->_isSecondContentLayer = false;
        this->Content = this->content1;
        this->BackClick -= this->backClickEventRegistrationToken;
    }
    
    void MyLiveApp::SettingsFlyout::Privacy_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        Button^ b = dynamic_cast<Button^>(sender);
        if (b != nullptr)
        {
            // Attach BackClick handler to override default back button behavior
            this->backClickEventRegistrationToken = this->BackClick += ref new BackClickEventHandler(this, &SettingsFlyout::backClick);
    
            // Create second layer of content
            TextBlock^ header = ref new TextBlock();
            header->Text = "MainPage Privacy Statement";
            header->Style = static_cast<Windows::UI::Xaml::Style^>(Application::Current->Resources->Lookup("TitleTextBlockStyle"));
            TextBlock^ tb = ref new TextBlock();
            tb->Text = "This app is not capable of performing any data mining of any kind on your private data, nor does it contain any functionality to access your data after you have stored it on your local machine. For the Microsoft privacy statement, see the Microsoft web site.";
            tb->Style = static_cast<Windows::UI::Xaml::Style^>(Application::Current->Resources->Lookup("BodyTextBlockStyle"));
    
            StackPanel^ sp = ref new StackPanel();
            sp->Children->Append(header);
            sp->Children->Append(tb);
            this->Content = sp;
    
            this->_isSecondContentLayer = true;
        }
    }
    
    
    void MyLiveApp::SettingsFlyout::SignInClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        DataModel^ model = safe_cast<DataModel^>(App::Current->Resources->Lookup("dataModel"));
        model->Status = "Signing in...";
        signInBtn->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
    
        // Specify the scopes we require. The method returns true if login is successful.
        model->LiveClient.login(L"wl.signin wl.basic wl.photos wl.skydrive_update")
        .then([this, model](bool isLoggedIn)
        {        
            if (!isLoggedIn)
            {
                throw std::exception();
            }
            // Request the "me" object from OneDrive
            // and pass the return value to the next continuation
            return model->LiveClient.get(L"me");      
        })
        // Parse the JSON-formatted response
        .then([this, model](web::json::value v)
            {
                // This shows how to create objects from JSON data.
                // In this demo we only use the Name string.
                auto p = ref new Me();
                p->Name = ref new String(v[L"name"].as_string().c_str());
                p->FirstName = ref new String(v[L"first_name"].as_string().c_str());
                p->LastName = ref new String(v[L"last_name"].as_string().c_str());
    
                // Display the user's name to indicated a status of "connected".
                model->Status = p->Name;
    
                // Store the name in case we get disconnected and then are restored
                // without signing in again, and need to update the status.
                model->UserName = p->Name;
    
                // Perform UI updates on the UI thread
                // to avoid the E_RPC_WRONG_THREAD exception.
            }, task_continuation_context::use_current())
        // Trap exceptions thrown in the task chain.    
        .then([](task<void> t)
        {
            try
            {
                t.get();
            }
            catch (exception e)
            {
                auto s = e.what();
                throw e;
            }
        });
    }
    
    void MyLiveApp::SettingsFlyout::SignOutClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        DataModel^ dm = safe_cast<DataModel^>(Application::Current->Resources->Lookup("dataModel"));
        dm->Status = "Signing out...";
    
        // Proceed to sign out. The button can 
        // only be clicked if CanSignOut is true.
        dm->LiveClient.logout().then([dm](bool signedOut)
        {
            if (signedOut)
            {
                dm->Status = "Signed out.";
            }
        }, task_continuation_context::use_current());
    }
    

    To sign in to Live, C++ apps use the OnlineIdAuthenticator and OnlineIdServiceTicketRequest classes from the Windows::Security::Authentication::OnlineId namespace. The classes enable the user of your app to authenticate their identity and log in to their personal Live account. the OnlineIdServiceTicketRequest is initialized with Windows Live scopes that specify what Live services the app is requesting. For more information about scopes, see Scopes. All this functionality is encapsulated in the live_client class that is defined in live_connect.h.

    The first time the user attempts to sign in, they will see a user interface from Windows that prompts them for their Microsoft account credentials, and asks them to specify synchronization preferences and to explicitly grant the app permission to log into their account. The user is only asked to grant this permission and specify preferences on the first login, or if they explicitly sign out. If a user is signed into Windows with a domain account that is linked to a Microsoft account (e.g. a Hotmail account) then they cannot sign out from Live without disconnecting the Microsoft account from the domain account. You do this by invoking the Settings pane, then choosing Change PC Settings, Accounts, then selecting the Microsoft account and clicking on "Disconnect" under the account information.

    In the MyLiveApp::SettingsFlyout::SignInClick method, we login and request the "me" object, which has basic information about the user who is logging in.

  5. Now you should be able to build and run your app by pressing F5.

Complete example