DirectXTutorial.com
The Ultimate DirectX Tutorial
Sign In
Lesson 4: The Application Lifecycle
Summary

In the old days of Windows, we could just write our game and be done. It was assumed that if the user didn't close the program, it would just continue running. Even if the user launched another game or application, our game would just go on, ignoring the other applications. We placed the responsibility of resource and power management on the user.

But these days are not like the old. We live in an era of tablets, smartphones, and users who don't know what they're doing. On these devices, power, memory and processor share are not necessarily free for the taking. As a result, operating systems have started to take it upon themselves to manage the lifecycle of an application. This means the OS has total control over when your application is running and when it is paused.

In this lesson, I want to talk about how exactly Windows manages your app and why. Then I want to talk about what we need to do in our game's code to go along with this.

Suspension and Termination

A Windows 8 app has three states of execution. They are Running, Suspended, and NotRunning.

Running, Suspended, and NotRunning

Running, Suspended, and NotRunning

The application always starts in NotRunning. This is exactly what it sounds like. It's not running.

When the user is ready to play the game, he will Activate it. This can be done in many ways, but for games it will usually be done by tapping on the game's tile. Once it has been activated, the game is in the Running state. This is also self-explanatory. It's running.

At some point, the user will leave the game. He will either go to the Start screen or switch to another app. After a short time, the app will Suspend. When suspended, the game is still stored in memory, but all execution has been paused. This comsumes very little battery, and improves performance of other apps. If the user decides to switch back to the game, it is immediately resumed to its Running state.

However, the user does not always switch back to the game right away. Windows may at any time decide the game needs to close to make more memory available for other apps. The user may also close the app directly. At this point, the game is Terminated, and is returned to the NotRunning state.

When your game terminates, it is not notified, and is given no opportunity to save data. If your game doesn't need to save its status, that's all very well. However, most games need to save their state in some way.

To save your game data, you don't ask the player to explicitly save the game data. Instead, Windows gives you about five seconds before suspension to save any information you want to storage before the game is fully suspended.

Save Your Game State Before Suspension

Save Your Game State Before Suspension

The Suspending Event

If you are expected to save your game data when the game suspends, it's necessary for there to be a Suspending event. This event triggers when your app goes into Suspended state.

The interesting thing about this event is that it is not a window event, it's an application event.

Application events are coded very similar to window events. Here's how you define the event:

virtual void Initialize(CoreApplicationView^ AppView)
{
    ...

    CoreApplication::Suspending +=
        ref new EventHandler<SuspendingEventArgs^>(this, &App::Suspending);

}

Let's take a closer look at this.

CoreApplication

This and one other event (the next one below) are called from CoreApplication, not the CoreWindow like we did last lesson. This is because in a multiple-window setup, the entire application must handle suspension in one place, not from each window.

EventHandler

EventHandler is similar to TypedEventHandler. It lacks the first template parameter, the one we were passing CoreWindow^ into before.

SuspendingEventArgs^

The only template parameter this event uses is SuspendingEventArgs. It has only one member, which is useful when saving data. I'll talk about it below.

Now for the function itself. It looks like this:

void Suspending(Object^ Sender, SuspendingEventArgs^ Args)
{
}

The parameters of the function are WinRT's root Object class, and the SuspendingEventArgs class. In this function, we would place code that saves the state of our game. This is a bit beyond the scope of this tutorial (it is a DirectX tutorial), so we'll pass over it for now. It's something you should look into before publishing your game however.


The Resuming Event

Like suspension, resuming a game has its own event. There isn't much to this event.

virtual void Initialize(CoreApplicationView^ AppView)
{
    ...
    ...

    CoreApplication::Resuming +=
        ref new EventHandler<Object^>(this, &App::Resuming);

}

The function itself is easy too.

void Resuming(Object^ Sender, Object^ Args)
{
}

There isn't much to the parameters either. You won't use them very often.


Good management of the application lifecycle is crucial to a good user experience, and the Suspending and Resuming events are a key part of this.

This isn't nearly enough information to actually correctly save the state of your game, but it's a start. It'll be important for you to know at least this going forward.

Before we move on, let's take a look at our App class with the new events included

ref class App sealed : public IFrameworkView
{
public:
    virtual void Initialize(CoreApplicationView^ AppView)
    {
        AppView->Activated += ref new TypedEventHandler
            <CoreApplicationView^, IActivatedEventArgs^>(this, &App::OnActivated);
        CoreApplication::Suspending +=
            ref new EventHandler<SuspendingEventArgs^>(this, &App::Suspending);
        CoreApplication::Resuming +=
            ref new EventHandler<Object^>(this, &App::Resuming);

    }
    virtual void SetWindow(CoreWindow^ Window)
    {
        Window->PointerPressed += ref new TypedEventHandler
            <CoreWindow^, PointerEventArgs^>(this, &App::PointerPressed);
    }
    virtual void Load(String^ EntryPoint) {}
    virtual void Run()
    {
        // Obtain a pointer to the window
        CoreWindow^ Window = CoreWindow::GetForCurrentThread();

        // Run ProcessEvents() to dispatch events
        Window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
    }
    virtual void Uninitialize() {}
        
    void OnActivated(CoreApplicationView^ CoreAppView, IActivatedEventArgs^ Args)
    {
        CoreWindow^ Window = CoreWindow::GetForCurrentThread();
        Window->Activate();
    }

    void Suspending(Object^ Sender, SuspendingEventArgs^ Args) {}
    void Resuming(Object^ Sender, Object^ Args) {}

};
The Event Loop

In the previous lesson, it was enough for us to call events with a single call to ProcessEvents(). In this section, we'll talk about an alternate way to use ProcessEvents(), ProcessAllIfPresent, and how it differs from its evil twin, ProcessUntilQuit.

Actually, there's nothing wrong with ProcessUntilQuit, the way it works just doesn't have spectacular results on games and their continuous activity.

Using ProcessUntilQuit as the parameter for ProcessEvents() creates a near-endless loop in which events are called. The function never returns until the Closed event is triggered. As soon as the Closed event is triggered, ProcessEvents() returns and the program exits.

The Flow of a Program Using ProcessUntilQuit

The Flow of a Program Using ProcessUntilQuit

This is perfectly logical for Windows programming, because generally speaking Windows applications, Word for example, tend to sit and do nothing until you make a move.

However, this doesn't work well for us. While all this waiting is going on, we need to be creating thirty to sixty fully-rendered 3D images per second and putting them on the screen without any delay at all. And so we are presented with a rather interesting problem, because Windows, if it sends any events, will most definitely not be sending thirty of them per second.

What we will do to solve this dilema is replace the current ProcessUntilQuit parameter with the new parameter ProcessAllIfPresent (process all if present). This value makes ProcessEvents() do essentially the same thing, but with one important difference: it returns as soon as all events have been handled. Once it returns, we are free to execute any code we like.

The Flow of a Program Using ProcessAllIfPresent

The Flow of a Program Using ProcessAllIfPresent

So how do we implement this into our program? Following is the new App class focusing on the new setup.

ref class App sealed : public IFrameworkView
{
    bool WindowClosed;    // change to true when it's time to close the window
public:
    virtual void Initialize(CoreApplicationView^ AppView)
    {
        AppView->Activated += ref new TypedEventHandler
            <CoreApplicationView^, IActivatedEventArgs^>(this, &App::OnActivated);

        WindowClosed = false;    // initialize to false
    }
    virtual void SetWindow(CoreWindow^ Window)
    {
        Window->PointerPressed += ref new TypedEventHandler
            <CoreWindow^, PointerEventArgs^>(this, &App::PointerPressed);
    }
    virtual void Load(String^ EntryPoint) {}
    virtual void Run()
    {
        CoreWindow^ Window = CoreWindow::GetForCurrentThread();

        // repeat until window closes
        while(!WindowClosed)
        {

            Window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

            // Run game code here
            // ...
            // ...
        }

    }
    virtual void Uninitialize() {}
        
    void OnActivated(CoreApplicationView^ CoreAppView, IActivatedEventArgs^ Args)
    {
        CoreWindow^ Window = CoreWindow::GetForCurrentThread();
        Window->Activate();
    }
};

bool WindowClosed;

First we create a bool to keep track of when the window is closed. We'll initialize it to false and change it later.

while(!WindowClosed)

Naturally, this creates a near-infinite loop. We will escape out of it later when it comes time to close the window and quit the game.

CoreProcessEventOption::ProcessAllIfPresent

This causes ProcessEvents() to return almost immediately, allowing us to run our game's code.


The Closed Event

ProcessUntilQuit tells ProcessEvents() to run until the Closed event occurs. When the event is called, ProcessEvents() returns and the Run() function completes. It's important that this happen because Windows will usually be waiting for the function to complete.

This means we can't have an actual infinite loop running ProcessEvents() on repeat. We will sometimes need to break out of the loop, and that sometimes is whenever the Closed event is called.

The closed event is just like any other window event. Its argument parameter is CoreWindowEventArgs.

virtual void SetWindow(CoreWindow^ Window)
{
    ...

    Window->Closed += ref new TypedEventHandler
        <CoreWindow^, CoreWindowEventArgs^>(this, &App::Closed);

}

...

void Closed(CoreWindow^ sender, CoreWindowEventArgs^ args)
{
    WindowClosed = true;    // Time to end the endless loop
}

The only new thing to talk about here is CoreWindowEventArgs. And all I'm going to say is that it isn't relevant to us right now. That should make things easy. :)

Summary

Let's look at what we just learned.

First, we learned about suspending, resuming, and termination. We added the Suspending and Resuming events to our code.

We then learned about adjusting ProcessEvents() to have it return control of the app to us.

At this point, the new program should look like this. I've marked all the new cold in bold.

// Include the precompiled headers
#include "pch.h"

// Use some common namespaces to simplify the code
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::UI::Core;
using namespace Windows::UI::Popups;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Platform;


// the class definition for the core "framework" of our app
ref class App sealed : public IFrameworkView
{
    bool WindowClosed;    // change to true when it's time to close the window
public:
    virtual void Initialize(CoreApplicationView^ AppView)
    {
        AppView->Activated += ref new TypedEventHandler
            <CoreApplicationView^, IActivatedEventArgs^>(this, &App::OnActivated);
        CoreApplication::Suspending +=
            ref new EventHandler<SuspendingEventArgs^>(this, &App::Suspending);
        CoreApplication::Resuming +=
            ref new EventHandler<Object^>(this, &App::Resuming);

        WindowClosed = false;    // initialize to false

    }
    virtual void SetWindow(CoreWindow^ Window)
    {
        Window->Closed += ref new TypedEventHandler
            <CoreWindow^, CoreWindowEventArgs^>(this, &App::Closed);

    }
    virtual void Load(String^ EntryPoint) {}
    virtual void Run()
    {
        CoreWindow^ Window = CoreWindow::GetForCurrentThread();

        // repeat until window closes
        while(!WindowClosed)
        {

            Window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

            // Run game code here
            // ...
            // ...
        }

    }
    virtual void Uninitialize() {}
        
    void OnActivated(CoreApplicationView^ CoreAppView, IActivatedEventArgs^ Args)
    {
        CoreWindow^ Window = CoreWindow::GetForCurrentThread();
        Window->Activate();
    }

    void Closed(CoreWindow^ sender, CoreWindowEventArgs^ args)
    {
        WindowClosed = true;    // time to end the endless loop
    }

    void Suspending(Object^ Sender, SuspendingEventArgs^ Args) {}
    void Resuming(Object^ Sender, Object^ Args) {}

};


// the class definition that creates our core framework
ref class AppSource sealed : IFrameworkViewSource
{
public:
    virtual IFrameworkView^ CreateView()
    {
        return ref new App();    // create the framework view and return it
    }
};

[MTAThread]    // define main() as a multi-threaded-apartment function

// the starting point of all programs
int main(Array<String^>^ args)
{
    CoreApplication::Run(ref new AppSource());    // run the framework view source
    return 0;
}

If you run this program, it shouldn't look much different than it did before. It should appear as a simple blank screen. The color will match your Start screen.

The New Program Running

The New Program Running

I'd say we're doing a fairly good job. You now have under your belt all the Windows programming tools you will need for game programming. With what you know, you can create a window, prepare it for a game, and have everything ready for the DirectX code.

So, now the real fun begins! Let's dive into DirectX and build ourselves a 3D game!

Next Lesson: Understanding Graphics Concepts

GO! GO! GO!