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.