Creating a log file in Windows Runtime using C++

If you have a native C++ project running on Windows Runtime AND you want to do some logging then you are in for a surprise. You cannot simply open a file "C:\log.txt" and dump all your logs there. You have to use Windows Runtime APIs to create files and write to them. Since Windows Runtime APIs are mostly Async you will jump through different hoops to get there.

This class will allow you to create a temp file and it will allow you write formatted output to the file. You can do things like ".Write( L"param %d", parameter1 );"

It's very easy to use this class, simply create an instance and call Register in the constructor of your App/Class. Then simply call Write/WriteLine to write to the log file. When you are done call UnRegister to close the file. Register/UnRegister functions are there to provide your class ref counting. This will be helpful when you have one global instance of this Logger but multiple classes are using it. In this scenario you only want to close & flush the file when all classes using this logger are gone. So make sure to call Register/UnRegister in Constructor/Destructor respectively.

 DebugLogger logger;
logger.Register();
logger.Write( L"param %d", 123 );
logger.UnRegister();

Full Source code:

 #pragma once
/******************************************************************************
*
* Author: Asim Goheer
*
* DebugLogger will allow you to write logs to local folder in 
* Windows Runtime C++ projects.
*
* This class will allow you to log everything to isolated storage(or local folder). Once all the 
* logging is done it will do proper clean-up and release the file.
* 
******************************************************************************/


#include <wrl/async.h>
#include "HStringOwner.h"
#include "CSOwner.h"
#include <time.h>

class DebugLogger
{
public:
   DebugLogger() : m_csNewLine( L"\r\n" )
   {
      InitializeCriticalSectionEx( &m_critSec, 0, 0 );
   }

   virtual ~DebugLogger()
   {
      DeleteCriticalSection( &m_critSec );
   }

   void Register()
   {
      CRITICAL_SECTION_OWNER( m_critSec );
      if( m_registerCount == 0 )
         Init();

      m_registerCount++;
   }

   void UnRegister()
   {
      if( !m_pDataWriter )
         return;

      Microsoft::WRL::ComPtr< ABI::Windows::Foundation::IAsyncOperation< UINT32 > > pAsyncAction;
      m_pDataWriter->StoreAsync( &pAsyncAction );

      pAsyncAction->put_Completed( Microsoft::WRL::Callback< ABI::Windows::Foundation::IAsyncOperationCompletedHandler< UINT32 > >(
         [ this ] ( ABI::Windows::Foundation::IAsyncOperation< UINT32 >* pHandler, AsyncStatus status )
      {
         if( !pHandler || status != AsyncStatus::Completed )
            return S_OK;

         Microsoft::WRL::ComPtr< ABI::Windows::Foundation::IAsyncOperation< bool > > pAsyncAction;
         m_pIIStream->FlushAsync( &pAsyncAction );

         pAsyncAction->put_Completed( Microsoft::WRL::Callback< ABI::Windows::Foundation::IAsyncOperationCompletedHandler< bool > >(
            [ this ] ( ABI::Windows::Foundation::IAsyncOperation< bool >* pHandler, AsyncStatus status )
         {
            if( !pHandler || status != AsyncStatus::Completed )
               return S_OK;

            CRITICAL_SECTION_OWNER( m_critSec );
            m_registerCount--;
            if( m_registerCount == 0 )
            {
               {
                  // close the file
                  Microsoft::WRL::ComPtr< ABI::Windows::Foundation::IClosable > pICloseable;
                  HRESULT hr = m_pDataWriter.As< ABI::Windows::Foundation::IClosable >( &pICloseable );
                  if( SUCCEEDED( hr ) )
                     pICloseable->Close();
               }

               {
                  // close the file
                  Microsoft::WRL::ComPtr< ABI::Windows::Foundation::IClosable > pICloseable;
                  HRESULT hr = m_pIIStream.As< ABI::Windows::Foundation::IClosable >( &pICloseable );
                  if( SUCCEEDED( hr ) )
                     pICloseable->Close();
               }

               {
                  // close the file
                  Microsoft::WRL::ComPtr< ABI::Windows::Foundation::IClosable > pICloseable;
                  HRESULT hr = m_randomAccessStream.As< ABI::Windows::Foundation::IClosable >( &pICloseable );
                  if( SUCCEEDED( hr ) )
                     pICloseable->Close();
               }
            }

            return S_OK;
         } ).Get() );

         return S_OK;
      } ).Get() );
   }

   void WriteLine( wchar_t* wzString )
   {
      if( !m_pDataWriter )
         return;

      CRITICAL_SECTION_OWNER( m_critSec );

      HStringOwner csString( wzString );

      UINT32 length = 0;
      HRESULT hr = m_pDataWriter->WriteString( csString, &length );
      if( FAILED( hr ) )
         return;

      length = 0;
      hr = m_pDataWriter->WriteString( m_csNewLine, &length );
      if( FAILED( hr ) )
         return;
   }

   void Write( wchar_t* fmt, ... )
   {

      wchar_t szBuf[ 512 ];
      szBuf[ 0 ] = '\0';

      try
      {
         int nSize = 0;

         va_list args;
         va_start( args, fmt );
         nSize = _vsnwprintf_s( szBuf, _countof( szBuf ), _TRUNCATE, fmt, args ); // C4996
         va_end( args );

         WriteLine( szBuf );
      }
      catch( ... )
      {
         WriteLine( L"Exception" );
      }
   }

private:
   void Init()
   {
      {
         CRITICAL_SECTION_OWNER( m_critSec );

         if( initDone )
            return;

         initDone = true;
      }

      m_logFileCreatedEvent.Attach( CreateEventEx( nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS ) );

      if( SUCCEEDED( CreateLogFile() ) )
      {
         WaitForSingleObjectEx( m_logFileCreatedEvent.Get(), INFINITE, FALSE );
      }
   }

   void CreateLogFileError( HRESULT hr )
   {
      SetEvent( m_logFileCreatedEvent.Get() );
   }

   HRESULT CreateLogFile()
   {
      Microsoft::WRL::ComPtr< ABI::Windows::Storage::IApplicationDataStatics > applicationDataStatics;
      HRESULT hr = Windows::Foundation::GetActivationFactory( Microsoft::WRL::Wrappers::HStringReference( RuntimeClass_Windows_Storage_ApplicationData ).Get(), &applicationDataStatics );
      if( FAILED( hr ) )
      {
         return hr;
      }

      Microsoft::WRL::ComPtr< ABI::Windows::Storage::IApplicationData > applicationData;
      hr = applicationDataStatics->get_Current( &applicationData );
      if( FAILED( hr ) )
      {
         return hr;
      }

      Microsoft::WRL::ComPtr< ABI::Windows::Storage::IStorageFolder > storageFolder;
      hr = applicationData->get_LocalFolder( &storageFolder );
      if( FAILED( hr ) )
      {
         return hr;
      }

      HStringOwner logFileName( L"log.txt" );

      Microsoft::WRL::ComPtr< ABI::Windows::Foundation::IAsyncOperation< ABI::Windows::Storage::StorageFile* > > pAsyncAction;
      hr = storageFolder->CreateFileAsync( logFileName,  ABI::Windows::Storage::CreationCollisionOption::CreationCollisionOption_ReplaceExisting, &pAsyncAction );
      if( FAILED( hr ) )
      {
         return hr;
      }

      pAsyncAction->put_Completed( Microsoft::WRL::Callback< ABI::Windows::Foundation::IAsyncOperationCompletedHandler< ABI::Windows::Storage::StorageFile* > >(
         [ this ] ( ABI::Windows::Foundation::IAsyncOperation< ABI::Windows::Storage::StorageFile* >* pHandler, AsyncStatus status )
         {
            if( pHandler == nullptr || status != AsyncStatus::Completed )
            {
               CreateLogFileError( E_ABORT );
               return S_OK;
            }

            ABI::Windows::Storage::IStorageFile* pStorageFile = nullptr;
            HRESULT hr = pHandler->GetResults( &pStorageFile );
            if( FAILED( hr ) || !pStorageFile )
            {
               CreateLogFileError( hr );
               return S_OK;
            }

            Microsoft::WRL::ComPtr< ABI::Windows::Foundation::IAsyncOperation< ABI::Windows::Storage::Streams::IRandomAccessStream* > > pAsyncAction;
            hr = pStorageFile->OpenAsync(  ABI::Windows::Storage::FileAccessMode::FileAccessMode_ReadWrite, &pAsyncAction );
            if( FAILED( hr ) )
            {
               CreateLogFileError( hr );
               return S_OK;
            }

            pAsyncAction->put_Completed( Microsoft::WRL::Callback< ABI::Windows::Foundation::IAsyncOperationCompletedHandler< ABI::Windows::Storage::Streams::IRandomAccessStream* > >(
               [ this ] ( ABI::Windows::Foundation::IAsyncOperation< ABI::Windows::Storage::Streams::IRandomAccessStream* >* pHandler, AsyncStatus status )
               {
                  if( pHandler == nullptr || status != AsyncStatus::Completed )
                  {
                     CreateLogFileError( E_ABORT );
                     return S_OK;
                  }

                  HRESULT hr = pHandler->GetResults( &m_randomAccessStream );
                  if( FAILED( hr ) || !m_randomAccessStream )
                  {
                     CreateLogFileError( hr );
                     return S_OK;
                  }

                  hr = m_randomAccessStream->GetOutputStreamAt( 0, &m_pIIStream );
                  if( FAILED( hr ) || !m_pIIStream )
                  {
                     CreateLogFileError( hr );
                     return S_OK;
                  }

                  CreateDataWriter( m_pIIStream );

                  wchar_t buffer[ 256 ];
                  GetCurrentDateTime( buffer, sizeof( buffer ) / sizeof( wchar_t ) );
                  Write( L"Log file created %s", buffer );

                  return S_OK;
               } ).Get() );

            return S_OK;

         } ).Get() );

      return S_OK;
   }

   void CreateDataWriter( Microsoft::WRL::ComPtr< ABI::Windows::Storage::Streams::IOutputStream >& pStream )
   {
      Microsoft::WRL::ComPtr< ABI::Windows::Storage::Streams::IDataWriter > dataWriter;

      Microsoft::WRL::ComPtr< ABI::Windows::Storage::Streams::IDataWriterFactory > data_writer_factory;
      HRESULT hr = Windows::Foundation::GetActivationFactory( Microsoft::WRL::Wrappers::HStringReference( RuntimeClass_Windows_Storage_Streams_DataWriter ).Get(), &data_writer_factory );
      if( FAILED( hr ) )
      {
         CreateLogFileError( hr );
         return;
      }

      hr = data_writer_factory->CreateDataWriter( *pStream.GetAddressOf(), &m_pDataWriter );
      if( FAILED( hr ) )
      {
         CreateLogFileError( hr );
         return;
      }

      SetEvent( m_logFileCreatedEvent.Get() );
   }

   void GetCurrentDateTime( wchar_t* wzBuffer, int wcBufferLen )
   {
      time_t now = time( 0 );
      struct tm tstruct;
      localtime_s( &tstruct, &now );
      wcsftime( wzBuffer, wcBufferLen, L"%Y-%m-%d.%X", &tstruct );
   }


private:
   CRITICAL_SECTION  m_critSec;
   HStringOwner m_csNewLine;

   Microsoft::WRL::Wrappers::Event m_logFileCreatedEvent;
   Microsoft::WRL::ComPtr< ABI::Windows::Storage::Streams::IDataWriter > m_pDataWriter;
   Microsoft::WRL::ComPtr< ABI::Windows::Storage::Streams::IRandomAccessStream > m_randomAccessStream;
   Microsoft::WRL::ComPtr< ABI::Windows::Storage::Streams::IOutputStream > m_pIIStream;

   int m_registerCount { 0 };
   bool initDone { false };
};