How to append text to file in a UWP app - c++-cli

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.

Based on the answer by #Sunius, but for C++/WinRT in a UWP app, noting that the log file will be in the %appdata%\..\Local\Packages\<YOUR_UWP_APP PACKAGE>\LocalState folder:
// 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.");
}
And the 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;
};
Note that the broadFileSystemAccess capability is needed in your UWP Package.appxmanifest if the log file is to be stored in an arbitrary folder -- this capability must be set in code (the Appx GUI will not show it) as shown in this SO post.

Related

How to Serialize Custom Objects in Qt6

I have a custom class Dummy which I want to send as an object using Dynamic Replica in Qt6. I'm able to send the class but it's being transferred as QVariant which I'm not able to extract(or cast) from QVariabnt object.
Below is my implementation:
Dummy.h file
#ifndef CLIENT_DUMMY_H
#define CLIENT_DUMMY_H
#include <QtCore/qobject.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmap.h>
#include <QtCore/qmetatype.h>
#include <QtRemoteObjects/qremoteobjectnode.h>
#include <QtRemoteObjects/qremoteobjectsource.h>
#include <QtCore>
class Dummy {
Q_GADGET
Q_PROPERTY(QString m_name READ name WRITE setName)
public:
Dummy(){}
explicit Dummy(QString str) : m_name(str) {}
QString name() const {
return m_name;
}
void setName(QString str){
m_name = str;
}
~Dummy() = default;
private:
QString m_name;
};
Q_DECLARE_METATYPE(Dummy)
inline QDataStream &operator<<(QDataStream &ds, const Dummy &obj) {
QtRemoteObjects::copyStoredProperties(&obj, ds);
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, Dummy &obj) {
QtRemoteObjects::copyStoredProperties(ds, &obj);
return ds;
}
inline bool operator==(const Dummy &left, const Dummy &right) Q_DECL_NOTHROW {
return left.name() == right.name();
}
inline bool operator!=(const Dummy &left, const Dummy &right) Q_DECL_NOTHROW {
return !(left == right);
}
inline QDebug operator<<(QDebug dbg, const Dummy &obj) {
dbg.nospace() << "Dummy(" << "m_name: " << obj.name() << ")";
return dbg.maybeSpace();
}
The class is present at both sides server as well as client.
.rep file
class Interface {
PROP(Dummy dummy);
[...removed extra code]
}
Server method sending data to client:
void send() {
Dummy dummy("DummyString");
setDummy(dummy);
}
Client side file:
Inside constructor:
QObject::connect(reptr.data(), SIGNAL(dummyChanged(Dummy)), this, SLOT(receiveDummy(Dummy)));
void DynamicClient::receiveDummy(Dummy dummy) {
if(reptr.data()->isReplicaValid()){
QVariant variant = reptr.data()->property("dummy");
qDebug() << variant;
}
}
But when object from server to client is sent, qDebug() prints below:
QVariant(Dummy, QVariant(QString, "DummyString"))
I'm not able to extract Dummy Object from Qvariant object.
I've tried registering my custom type using qRegisterMetaType() as well but still it didn't work. Apart from that, I've used qvariant_cast and variant.value() but I printed the value I get some random character each time.
Thanks in advance. I can post more code if required.
Turns out I was not registering the Dummy class on client side due to which client was not able to recognize the type.
qRegisterMetaType needs to be used at both ends.

How to recieve a xml response from jazz server using qt?

I want to get a response from the server. I am making a rest api request using "Qt C++" framework. I am able to connect to the server but not able to get the response. The jazz server supports only xml format. I am a beginner in qt.
.pro
QT += widgets
TEMPLATE += app
CONFIG += c++17 console
CONFIG -= app_bundle
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated
before Qt 6.0.0
SOURCES += \
main.cpp
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
main.h
main.h
#ifndef MAIN_H
#define MAIN_H
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QApplication>
class MyObject : public QObject {
Q_OBJECT
public:
explicit MyObject(QApplication* application);
void TestConnection() const;
static void ReplyFinished(QNetworkReply *reply);
QNetworkAccessManager *manager;
};
#endif // MAIN_H
main.cpp
#include <iostream>
#include "main.h"
MyObject::MyObject(QApplication* app) {
manager = new QNetworkAccessManager(app);
}
void MyObject::TestConnection() const {
auto status = connect(manager, &QNetworkAccessManager::finished,
this, &MyObject::ReplyFinished);
qDebug() << "Connection status:" << status;
manager->get(QNetworkRequest(QUrl("www.jazz.net")));
}
void MyObject::ReplyFinished(QNetworkReply *reply) {
QString answer = reply->readAll();
qDebug() << answer;
QApplication::quit();
}
int main(int argc, char *argv[]) {
auto *app = new QApplication(argc, argv);
auto myObject = new MyObject(app);
myObject->TestConnection();
return QApplication::exec();
}
The output is :
Connection status: true
qt.tlsbackend.ossl: Failed to load libssl/libcrypto.
"" //The response is empty
Note: I am looking for a specific request-response to the jazz server and the URL format is
{format: https://:/}

C++/WinRT handling exceptions from StorageFolder::GetFileAsync

When no file exists the app throws an exception which I am unable to catch
In my example code below I create a directory "MyFolder" and save a file "" and save a file "sample.txt" to it.
I then call the function FindFileAsync twice - first using the already created file "sample.txt" which works fine and then using a non-existing file "nofile.txt" which fails with the error
Exception thrown at 0x00007FF94E39D759 in FindFile.exe: Microsoft C++ exception: winrt::hresult_error at memory location.
Is there any solution to this issue.
MainPage.h
#pragma once
#include "MainPage.g.h"
namespace winrt::FindFile::implementation
{
struct MainPage : MainPageT<MainPage>
{
MainPage();
Windows::Foundation::IAsyncAction FindFileAsync(hstring value);
Windows::Foundation::IAsyncAction GetFolderAsync(hstring const& value);
Windows::Foundation::IAsyncAction CreateFileAsync(hstring const& fname);
Windows::Foundation::IAsyncAction DeletefileAsync(Windows::Storage::StorageFile const & value);
void ClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args);
private:
hstring folderDir{ L"MyFolder" };
};
}
namespace winrt::FindFile::factory_implementation
{
struct MainPage : MainPageT<MainPage, implementation::MainPage>
{
};
}
MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::UI::Xaml;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Popups;
namespace winrt::FindFile::implementation
{
MainPage::MainPage()
{
InitializeComponent();
}
Windows::Foundation::IAsyncAction MainPage::FindFileAsync(hstring value)
{
Windows::Storage::StorageFolder storageFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
StorageFolder sampleFolder{ co_await storageFolder.CreateFolderAsync(folderDir, CreationCollisionOption::OpenIfExists) };
try
{
Windows::Storage::StorageFile manifest{ co_await sampleFolder.GetFileAsync(value) };
}
catch (winrt::hresult_error msg)
{
hstring ms{ msg.message() };
MessageDialog dlg(ms, L"File Error");
dlg.ShowAsync();
}
}
Windows::Foundation::IAsyncAction MainPage::GetFolderAsync(hstring const& value)
{
Windows::Storage::StorageFolder storageFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
auto sampleFolder{ co_await storageFolder.CreateFolderAsync(value, CreationCollisionOption::OpenIfExists) };
}
IAsyncAction MainPage::CreateFileAsync(hstring const& fname)
{
Windows::Storage::StorageFolder storageFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
auto sampleFolder{ co_await storageFolder.CreateFolderAsync(folderDir, CreationCollisionOption::OpenIfExists) };
auto sampleFile{ co_await sampleFolder.CreateFileAsync(L"sample.txt", Windows::Storage::CreationCollisionOption::ReplaceExisting) };
co_await Windows::Storage::FileIO::WriteTextAsync(sampleFile, L"Swift as a shadow");
}
IAsyncAction MainPage::DeletefileAsync(StorageFile const& value)
{
co_await value.DeleteAsync();
}
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
hstring fname{ L"sample.txt" };
GetFolderAsync(folderDir);
CreateFileAsync(fname);
// The following statement is successful as the file exists
FindFileAsync(fname);
// The following ststement throws an exception error
FindFileAsync(L"nofile.txt");
}
}
In C++/WinRT, some exceptions, but not all, force the debugger to pause program execution despite the exception being handled (thrown inside a try block). However, you can resume it by pressing F5 or launching the app without a debugger (CTRL+F5 or via the start menu). I'm not sure whenever this is a bug or intended behavior.
I have found a solution that worked for me.
When the debugger triggers the exception the message includes a checked checkbox. If unchecked and the program is continued to the end the problem is resolved. When run again the error is not triggered and the catch can handle the "file not found". Not sure why the default setting is to trigger the exception.

Qt5 QNetworkAccessManager finished signal never emits

I have a very confusing problem.
I had a simple project that was downloading files from some ftp servers. It worked very good.
Then, I tried implementing that same code into a larger project (first one was a Console App, and the second one is GUI, but I don't think that changes anything..).
After doing some debugging it seems to me that finished() signal from QNetworkAccessManager somehow never gets emitted (or received).
Again, the exact same lines of code work as a separate project.
downloader.h
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QFile>
#include <QDebug>
class Downloader : public QObject
{
Q_OBJECT
public:
explicit Downloader(QObject *parent = 0);
signals:
void dloadend();
void printed();
public slots:
void replyFinished (QNetworkReply *reply);
void doDownload(QUrl url);
void printDLend();
private:
QNetworkAccessManager *manager;
};
#endif // DOWNLOADER_H
downloader.cpp
#include "downloader.h"
Downloader::Downloader(QObject *parent) :
QObject(parent)
{
}
void Downloader::doDownload(QUrl url)
{
qDebug()<<"entry: doDownload\n";
QNetworkRequest req(url);
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),this,SLOT(printDLend()));//SLOT(replyFinished(QNetworkReply*)));
manager -> get(req);
qDebug()<<"exit: doDownload\n";
}
void Downloader::replyFinished (QNetworkReply *reply)
{
qDebug()<<"entry: reply\n";
if(reply->error()) {
qDebug() << "ERROR!";
qDebug() << reply->errorString();
}
else
{
qDebug() << "Download finished!";
QFile *file = new QFile("C:/users/jelicicm/Desktop/test1.hex");
if(file->open(QFile::Append))
{
file->write(reply->readAll());
file->flush(); file->close();
qDebug() <<"Downloaded file size:" <<file->size() <<"Bytes";
qDebug() <<"File name: "<< file->fileName();
;
}
delete file;
}
reply->deleteLater();
manager->deleteLater();
emit dloadend();
}
mainwindow.cpp (important part)
void MainWindow::on_actionDownloadFirmwareImage_triggered()
{
Downloader d;
QUrl url("ftp://ftp.xenbase.org/pub/Genomics/JGI/Xenla6.0/Xenla_6.0_JGI_Gene_Models.fasta.tgz");
qDebug() << "url, debug";
ui->plainTextEdit->appendPlainText(url.toDisplayString());
d.doDownload(url);
QObject::connect(&d,SIGNAL(dloadend()),this, SLOT(printDLend()));
}
Can't get my head around this.
Any help is welcome,
Thanks!
EDIT> More info:
Debugger posts this>
url, debug
entry: doDownload
exit: doDownload
You create object Downloader on stack and it is deleted right after your function exits. You must create object using new, and provide MainWindow object as parent, so after you close MainWindow, the object will be destroyed.
If the download finish, you still need to destroy the object, so simply connect the dloadend() signal to deleteLater() slot, Qt loop will delete your object right after all signal are processed.
void MainWindow::on_actionDownloadFirmwareImage_triggered()
{
Downloader *d = new Downloader(this);
QUrl url("ftp://example.com/some.file.tgz");
qDebug() << "url, debug";
ui->plainTextEdit->appendPlainText(url.toDisplayString());
d->doDownload(url);
QObject::connect(d,SIGNAL(dloadend()),this, SLOT(printDLend()));
QObject::connect(d,SIGNAL(dloadend()), d, SLOT(deleteLater()));
}

Immediate Access Violation when debugging Windows.Devices.Sensors project in Windows 7

I have a large solution with 50+ unmanaged projects in it. I have recently added a project with managed code in it to the solution. The managed code accesses Windows.Devices.Sensors in a .NET dll. This dll is eventually wrapped by unmanaged code and called from another unmanaged project.
My problem is that I get the following access violation before main() even executes.
Unhandled exception at 0x744b8ea0 in myApplication.exe: 0xC0000005: Access violation.
Managed code:
#using <Windows.winmd>
using namespace Windows::Devices::Sensors;
#include <math.h>
namespace TabletSensors
{
namespace NET
{
public ref class DotNetDllClass
{
public:
DotNetDllClass()
{
Initialization();
}
~DotNetDllClass()
{
}
float* GetQuaternion()
{
OrientationSensorReading^ reading = _orientation->GetCurrentReading();
if( reading != nullptr )
{
float* quat = new float[4];
quat[0] = reading->Quaternion->X;
quat[1] = reading->Quaternion->Y;
quat[2] = reading->Quaternion->Z;
quat[3] = reading->Quaternion->W;
return quat;
}
else
{
return NULL;
}
}
private:
void Initialization()
{
_orientation = OrientationSensor::GetDefault();
if( _orientation != nullptr )
{
_orientation->ReportInterval = 16;
}
else
{
// not good ... throw exception or something
}
}
OrientationSensor^ _orientation;
};
}
}
Wrapper header file:
namespace TabletSensors
{
namespace NETWrapper
{
class DLLEXPORT_SENSORS WrapperClass
{
public:
__stdcall WrapperClass();
__stdcall ~WrapperClass();
float* __stdcall GetQuaternion();
};
}
}
Wrapper cpp file:
#define MIXSENSORS_BUILD
#include <gcroot.h>
#include "DotNetWrapper.h"
#include "DotNetDll.h"
using namespace TabletSensors::NETWrapper;
using namespace TabletSensors::NET;
static gcroot<TabletSensors::NET::DotNetDllClass^> Sensors = nullptr;
static System::UInt16 refCount = 0;
#pragma managed
inline TabletSensors::NET::DotNetDllClass^ GetSensors(void)
{
return (TabletSensors::NET::DotNetDllClass^)Sensors;
}
void Init()
{
++refCount;
if(GetSensors() == nullptr)
{
Sensors = gcnew TabletSensors::NET::DotNetDllClass();
}
}
void CleanUp()
{
if( refCount > 0 )
{
--refCount;
}
}
float* GetQuaternion_()
{
return Sensors->GetQuaternion();
}
#pragma unmanaged
TabletSensors::NETWrapper::WrapperClass::WrapperClass()
{
Init();
}
TabletSensors::NETWrapper::WrapperClass::~WrapperClass()
{
CleanUp();
}
float* TabletSensors::NETWrapper::WrapperClass::GetQuaternion()
{
float* x = new float[4];
return GetQuaternion_();
}
#pragma managed
Unmanaged project referencing my wrapper class:
#include "DotNetWrapper.h"
.
.
.
void UnmanagedProject::Update()
{
// if this line is present, I get an access violation without hitting any breakpoints.
TabletSensors::NETWrapper::WrapperClass _tabletSensors;
.
.
.
}
Since the managed code is trying to access Tablet Sensors I understand why it doesn't work on my Windows 7 desktop. What I don't understand it why it won't even allow me to debug my code at all. No breakpoints are hit before the Access Violation occurs.
What I would really like to figure out is how to use exception handling or #ifdefs to keep this crash from happening. But I have had very little luck.
Any ideas?
The fix is to Delay Load the managed DLL. The allows the application to run until that DLL is explicitly called. Thanks to Ben Voight for his answer here: https://stackoverflow.com/a/28467701/1454861