I need to create a simple log class (just a "save to file" method) in a UWP app for debugging purpose but AppendTextAsync is much different from ofstream and I don't know how to use.
This is my class
#pragma once
ref class winRTLog sealed
{
public:
winRTLog();
void save(Platform::String^ log);
private:
Platform::String^ myFilename = L"myLog.txt";
Windows::Storage::StorageFolder^ myFolder;
Windows::Storage::StorageFile^ myFile;
Windows::Foundation::IAsyncOperation<Windows::Storage::StorageFile^>^ createMyFile;
concurrency::task<Windows::Storage::StorageFile^> myFileTask;
};
and here's the mess I've written so far trying to understand the documentation
#include "pch.h"
#include <ppltasks.h>
#include "winRTLog.h"
winRTLog::winRTLog()
{
myFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
createMyFile = myFolder->CreateFileAsync(myFilename, Windows::Storage::CreationCollisionOption::OpenIfExists);
myFileTask = concurrency::create_task(myFolder->GetFileAsync(myFilename));
//how to make myFile point the file I've created?
}
void winRTLog::save(Platform::String^ log)
{
//??
myFileTask.then([&]()
{
//how to use Windows::Storage::FileIO::AppendTextAsync
//with myFile and log?
});
}
Don't use StorageFile APIs for saving data into local folder. Only use it if you need to: for APIs that require you passing them in a storage file or to access a place like pictures library which doesn't have a real path. StorageFile APIs are terribly slow compared to traditional APIs. Worst of all, all of the file operations are asynchronous, which means they're a pain to work with and even a bigger pain to debug.
For your scenario, I'd just use std::wofstream if you're familiar with it:
#include <fstream>
class winRTLog
{
public:
winRTLog(Platform::String^ fileName);
void save(Platform::String^ log);
private:
std::wofstream m_OutStream;
};
winRTLog::winRTLog(Platform::String^ fileName) :
m_OutStream(std::wstring(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data()) + L"\\" + fileName->Data(), std::ios::app)
{
if (!m_OutStream.is_open())
throw std::runtime_error("Failed to open the log file.");
}
void winRTLog::save(Platform::String^ log)
{
m_OutStream << log->Data();
if (m_OutStream.fail())
throw std::runtime_error("Failed to write to the log file.");
}
Alternatively, you can use Win32 file APIs:
#include <memory>
#include <string>
#include <windows.h>
class winRTLog
{
public:
winRTLog(Platform::String^ fileName);
~winRTLog();
void save(Platform::String^ log);
private:
HANDLE m_LogHandle;
};
winRTLog::winRTLog(Platform::String^ fileName)
{
auto filePath = std::wstring(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data()) + L"\\" + fileName->Data();
m_LogHandle = CreateFile2(filePath.c_str(), GENERIC_WRITE, 0, OPEN_ALWAYS, nullptr);
if (m_LogHandle == INVALID_HANDLE_VALUE)
throw std::runtime_error("Failed to open the log file: error code " + std::to_string(GetLastError()));
if (SetFilePointer(m_LogHandle, 0, nullptr, FILE_END) == INVALID_SET_FILE_POINTER)
throw std::runtime_error("Failed to set file pointer to the end of file: error code " + std::to_string(GetLastError()));
}
winRTLog::~winRTLog()
{
if (m_LogHandle != INVALID_HANDLE_VALUE)
CloseHandle(m_LogHandle);
}
void winRTLog::save(Platform::String^ log)
{
// Convert to UTF8
std::string utf8;
utf8.resize(4 * log->Length());
auto utf8Length = WideCharToMultiByte(CP_UTF8, 0, log->Data(), static_cast<int>(log->Length()), &utf8[0], static_cast<int>(4 * log->Length()), nullptr, nullptr);
if (utf8Length == 0)
throw std::runtime_error("Failed to convert log message to UTF8: error code " + std::to_string(GetLastError()));
utf8.resize(utf8Length);
// Write to actual log
DWORD bytesWritten;
auto writeResult = WriteFile(m_LogHandle, utf8.data(), static_cast<DWORD>(utf8.length()), &bytesWritten, nullptr);
if (writeResult == FALSE || bytesWritten != utf8.length())
throw std::runtime_error("Failed to write log message to the log file: error code " + std::to_string(GetLastError()));
}
Note, I used throwing std::runtime_error for error handling for example purposes: you might want to do it differently.