如何在 UWP 应用程序中将文本附加到文件

How to append text to file in a UWP app

我需要在 UWP 应用程序中创建一个简单的日志 class(只是一个 "save to file" 方法)用于调试目的,但 AppendTextAsync 与 ofstream 有很大不同,我不知道如何使用. 这是我的 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;
};

这是我为了理解文档而写的乱七八糟的东西

#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?
    });
}

不要使用 StorageFile API 将数据保存到本地文件夹。仅在需要时使用它:对于需要将它们传递到存储文件中的 API,或者访问没有真实路径的图片库之类的地方。与传统 API 相比,StorageFile API 非常慢。最糟糕的是,所有文件操作都是异步的,这意味着它们很难处理,甚至更难调试。

对于您的场景,如果您熟悉的话,我会使用 std::wofstream:

#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.");
}

或者,您可以使用 Win32 文件 API:

#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()));
}

请注意,出于示例目的,我使用抛出 std::runtime_error 进行错误处理:您可能希望以不同的方式进行处理。

基于@Sunius 的回答,但对于 UWP 应用程序中的 C++/WinRT,请注意日志文件将位于 %appdata%\..\Local\Packages\<YOUR_UWP_APP PACKAGE>\LocalState 文件夹中:

// WinRtLog.cpp
#include "WinRtLog.h"
#include "winrt/Windows.Storage.h"

using namespace winrt::Windows;

winRTLog::winRTLog(std::wstring fileName) :
    m_OutStream(std::wstring(Storage::ApplicationData::Current().LocalFolder().Path().data()) + L"\" + fileName, std::ios::app)
{
    if (!m_OutStream.is_open())
        throw std::runtime_error("Failed to open the log file.");
}

void winRTLog::save(std::string log)
{
    m_OutStream << log.data();
    if (m_OutStream.fail())
        throw std::runtime_error("Failed to write to the log file.");
}

和 header:

// WinRtLog.h
#pragma once
#include <fstream>
#include "winrt/Windows.ApplicationModel.Core.h"

class winRTLog
{
    public:
        winRTLog(std::wstring fileName);
        void save(std::string log);

    private:
        std::wofstream m_OutStream;
};

请注意,如果要将日志文件存储在任意文件夹中,您的 UWP Package.appxmanifest 中需要 broadFileSystemAccess 功能——必须在代码中设置此功能(Appx GUI 将不显示)如图所示 in this SO post.