implemented wrapping of common 'save' methods

This commit is contained in:
Ludwig Lehnert 2025-06-26 14:24:44 +02:00
parent 20c916077a
commit a93c772b90
Signed by: ludwig
SSH Key Fingerprint: SHA256:4vshH9GJ8TLO1RS2fY6rDDLnq7+KVvSClCY+uEhYYRA
24 changed files with 447 additions and 39 deletions

4
.gitignore vendored
View File

@ -1,4 +1,6 @@
*.o
.gitignore
.o
*.so
*.a
*.test

View File

@ -1,9 +0,0 @@
{
"files.associations": {
"cstdlib": "c",
"memory": "cpp",
"semaphore": "cpp",
"stop_token": "cpp",
"iostream": "cpp"
}
}

17
ABSTRACT.md Normal file
View File

@ -0,0 +1,17 @@
# Windows Binary Fuzzing
Modern-day fuzzing is one of the most efficient techniques to automatically detect bugs and vulnerabilities in software projects [1]. It works by automatically feeding pseudo-randomly generated input data to the target program under test. However, most fuzzing techniques are only possible for target programs with source code and without a GUI [2]. This is because closed-source programs and programs with a GUI cannot easily be harnessed by fuzzers. This means, that the fuzzer cannot automatically find a good way to feed input into the program. In the scope of this thesis/project, closed-source GUI programs on Windows should be fuzzed. Contrary to the Linux and open-source community, the Windows ecosystem has received little attention. Therefore, in the scope of this project, a tool should be developed, which automatically harnesses and fuzzes Windows applications. To achieve this, simple binary rewriting frameworks [3] may be used to replace GUI file opening dialoges with an automatic way to feed file input into a program. Then, well-established fuzzing tools [4,5,6] can be used to fuzz multiple Windows-only programs. This should provide a first look into, whether the applications in the Windows ecosystem are as vulnerable as expected.
References:
[1] https://github.com/google/oss-fuzz
[2] https://www.usenix.org/system/files/usenixsecurity24-schilling.pdf
[3] https://ieeexplore.ieee.org/abstract/document/8023154/
[4] https://github.com/googleprojectzero/winafl
[5] https://par.nsf.gov/servlets/purl/10308834
[6] https://www.usenix.org/system/files/sec21-nagy.pdf

View File

@ -4,12 +4,17 @@
- [x] `GetOpenFileNameA` (ANSI)
- [x] `GetOpenFileNameW` (UTF-16)
- [x] `GetSaveFileNameA` (ANSI)
- [x] `GetSaveFileNameW` (UTF-16)
- [x] `IFileOpenDialog`
- [x] `IFileSaveDialog`
- [x] `QFileDialog::getOpenFileName`
- [ ] `QFileDialog::getOpenFileNames`
- [x] `QFileDialog::getOpenFileUrl`
- [ ] `QFileDialog::getOpenFileUrls`
- [ ] `QFileDialog::getOpenFileContent`
- [x] `QFileDialog::getSaveFileName`
- [x] `QFileDialog::getSaveFileUrl`
- [ ] `wxFileDialog`
## Build scripts

View File

@ -21,8 +21,11 @@ const char *MANGLED_QFileDialog_getOpenFileNames = "?getOpenFileNames@QFileDialo
const char *MANGLED_QFileDialog_getOpenFileUrl = "?getOpenFileUrl@QFileDialog@@SA?AVQUrl@@PEAVQWidget@@AEBVQString@@AEBV2@1PEAV4@V?$QFlags@W4Option@QFileDialog@@@@AEBV?$QList@VQString@@@@@Z";
const char *MANGLED_QFileDialog_getOpenFileUrls = "?getOpenFileUrls@QFileDialog@@SA?AV?$QList@VQUrl@@@@PEAVQWidget@@AEBVQString@@AEBVQUrl@@1PEAV4@V?$QFlags@W4Option@QFileDialog@@@@AEBV?$QList@VQString@@@@@Z";
const char *MANGLED_QFileDialog_getOpenFileContent = "?getOpenFileContent@QFileDialog@@SAXAEBVQString@@AEBV?$function@$$A6AXAEBVQString@@AEBVQByteArray@@@Z@std@@PEAVQWidget@@@Z";
const char *MANGLED_QFileDialog_getSaveFileName = "?getSaveFileName@QFileDialog@@SA?AVQString@@PEAVQWidget@@AEBV2@11PEAV2@V?$QFlags@W4Option@QFileDialog@@@@@Z";
const char *MANGLED_QFileDialog_getSaveFileUrl = "?getSaveFileUrl@QFileDialog@@SA?AVQUrl@@PEAVQWidget@@AEBVQString@@AEBV2@1PEAV4@V?$QFlags@W4Option@QFileDialog@@@@AEBV?$QList@VQString@@@@@Z";
char *CUSTOM_FILE = "C:\\Users\\vboxuser\\Downloads\\spoofed_number.txt";
char *OPEN_FILE = "C:\\Users\\vboxuser\\Downloads\\spoofed_number.txt";
char *SAVE_FILE = "C:\\Users\\vboxuser\\Downloads\\spoofed_save.txt";
// Function pointers for functions we need to call ourselves
static void (*fn_QString_const)(void *qstring, const char *str) = NULL;
@ -46,9 +49,11 @@ static void pre_IFileOpenDialog_GetResult(void *wrapctx, void **user_data);
static void pre_IFileOpenDialog_Show(void *wrapctx, void **user_data);
static HRESULT custom_IFileDialog_Show(void *, void *);
static HRESULT custom_IFileDialog_GetResult(void *, IShellItem **ppsitem);
static HRESULT custom_IFileOpenDialog_GetResult(void *, IShellItem **ppsitem);
static HRESULT custom_IFileSaveDialog_GetResult(void *, IShellItem **ppsitem);
struct IFileOpenDialogVtbl customFODVtbl = {};
struct IFileSaveDialogVtbl customFSDVtbl = {};
// --- drwrap Callbacks for WinAPI Functions ---
@ -57,7 +62,7 @@ static void pre_GetOpenFileNameA(void *wrapctx, void **user_data)
OPENFILENAMEA *ofn = (OPENFILENAMEA *)drwrap_get_arg(wrapctx, 0);
if (ofn && ofn->lpstrFile)
{
strncpy(ofn->lpstrFile, CUSTOM_FILE, ofn->nMaxFile - 1);
strncpy(ofn->lpstrFile, OPEN_FILE, ofn->nMaxFile - 1);
ofn->lpstrFile[ofn->nMaxFile - 1] = '\0';
}
@ -69,7 +74,31 @@ static void pre_GetOpenFileNameW(void *wrapctx, void **user_data)
OPENFILENAMEW *ofn = (OPENFILENAMEW *)drwrap_get_arg(wrapctx, 0);
if (ofn && ofn->lpstrFile)
{
mbstowcs(ofn->lpstrFile, CUSTOM_FILE, ofn->nMaxFile - 1);
mbstowcs(ofn->lpstrFile, OPEN_FILE, ofn->nMaxFile - 1);
ofn->lpstrFile[ofn->nMaxFile - 1] = L'\0';
}
drwrap_skip_call(wrapctx, (void *)1, 0);
}
static void pre_GetSaveFileNameA(void *wrapctx, void **user_data)
{
OPENFILENAMEA *ofn = (OPENFILENAMEA *)drwrap_get_arg(wrapctx, 0);
if (ofn && ofn->lpstrFile)
{
strncpy(ofn->lpstrFile, SAVE_FILE, ofn->nMaxFile - 1);
ofn->lpstrFile[ofn->nMaxFile - 1] = '\0';
}
drwrap_skip_call(wrapctx, (void *)1, 0);
}
static void pre_GetSaveFileNameW(void *wrapctx, void **user_data)
{
OPENFILENAMEW *ofn = (OPENFILENAMEW *)drwrap_get_arg(wrapctx, 0);
if (ofn && ofn->lpstrFile)
{
mbstowcs(ofn->lpstrFile, SAVE_FILE, ofn->nMaxFile - 1);
ofn->lpstrFile[ofn->nMaxFile - 1] = L'\0';
}
@ -84,7 +113,7 @@ static void pre_QFileDialog_getOpenFileName(void *wrapctx, void **user_data)
return;
// The return value is passed as a pointer in the first argument (sret)
void *qstring_ret_ptr = (void *)drwrap_get_arg(wrapctx, 0);
fn_QString_const(qstring_ret_ptr, CUSTOM_FILE);
fn_QString_const(qstring_ret_ptr, OPEN_FILE);
drwrap_skip_call(wrapctx, NULL, 0);
}
@ -95,7 +124,46 @@ static void pre_QFileDialog_getOpenFileUrl(void *wrapctx, void **user_data)
const char *prefix = "file:///";
char urlbuf[MAX_PATH + 10];
snprintf(urlbuf, sizeof(urlbuf), "%s%s", prefix, CUSTOM_FILE);
snprintf(urlbuf, sizeof(urlbuf), "%s%s", prefix, OPEN_FILE);
// Replace backslashes for URL format
for (char *p = urlbuf; *p; ++p)
{
if (*p == '\\')
*p = '/';
}
// Create a temporary QString for the URL
char local_qstring[24]; // Allocate space for a QString object on the stack
fn_QString_const(local_qstring, urlbuf);
// Get the return QUrl object pointer (sret)
void *qurl_ret_ptr = (void *)drwrap_get_arg(wrapctx, 0);
fn_QUrl_const(qurl_ret_ptr, local_qstring, 0); // 0 is TolerantMode
drwrap_skip_call(wrapctx, NULL, 0);
}
static void pre_QFileDialog_getSaveFileName(void *wrapctx, void **user_data)
{
if (fn_QString_const == NULL)
return;
// The return value is passed as a pointer in the first argument (sret)
void *qstring_ret_ptr = (void *)drwrap_get_arg(wrapctx, 0);
fn_QString_const(qstring_ret_ptr, SAVE_FILE);
drwrap_skip_call(wrapctx, NULL, 0);
}
static void pre_QFileDialog_getSaveFileUrl(void *wrapctx, void **user_data)
{
if (fn_QString_const == NULL || fn_QUrl_const == NULL)
return;
const char *prefix = "file:///";
char urlbuf[MAX_PATH + 10];
snprintf(urlbuf, sizeof(urlbuf), "%s%s", prefix, SAVE_FILE);
// Replace backslashes for URL format
for (char *p = urlbuf; *p; ++p)
{
@ -116,25 +184,54 @@ static void pre_QFileDialog_getOpenFileUrl(void *wrapctx, void **user_data)
// --- COM Wrapping Logic ---
const int TYPE_IFileOpenDialog = 1;
const int TYPE_IFileSaveDialog = 2;
struct CoCreateInstance_data {
int type;
void *dialog;
};
static void pre_CoCreateInstance(void *wrapctx, void **user_data)
{
*user_data = NULL;
// Arguments: rclsid, pUnkOuter, dwClsContext, riid, ppv
const IID *riid = (const IID *)drwrap_get_arg(wrapctx, 3);
const CLSID *rclsid = (const CLSID *)drwrap_get_arg(wrapctx, 0);
if (!rclsid || !riid) return;
// We are only interested in the creation of a FileOpenDialog
if (rclsid != NULL && IsEqualIID(rclsid, &CLSID_FileOpenDialog) &&
riid != NULL && IsEqualIID(riid, &IID_IFileOpenDialog))
int type = 0;
void *dialog = NULL;
if (IsEqualIID(rclsid, &CLSID_FileOpenDialog) && IsEqualIID(riid, &IID_IFileOpenDialog))
{
dr_printf("drwrap: Intercepting CoCreateInstance for FileOpenDialog\n");
// Pass the pointer to the output interface pointer (ppv) to the post-hook
*user_data = drwrap_get_arg(wrapctx, 4);
dialog = drwrap_get_arg(wrapctx, 4);
type = TYPE_IFileOpenDialog;
}
else
if (IsEqualIID(rclsid, &CLSID_FileSaveDialog) && IsEqualIID(riid, &IID_IFileSaveDialog))
{
*user_data = NULL;
dr_printf("drwrap: Intercepting CoCreateInstance for FileSaveDialog\n");
// Pass the pointer to the output interface pointer (ppv) to the post-hook
dialog = drwrap_get_arg(wrapctx, 4);
type = TYPE_IFileSaveDialog;
}
if (!type || !dialog) return;
struct CoCreateInstance_data *m_user_data = malloc(sizeof(*m_user_data));
if (!m_user_data) {
perror("malloc");
dr_exit_process(1);
}
m_user_data->type = type;
m_user_data->dialog = dialog;
*user_data = (void *)m_user_data;
}
static void post_CoCreateInstance(void *wrapctx, void *user_data)
@ -143,31 +240,76 @@ static void post_CoCreateInstance(void *wrapctx, void *user_data)
if (!user_data)
return;
struct CoCreateInstance_data m_user_data;
m_user_data = *(struct CoCreateInstance_data *)user_data;
free(user_data);
HRESULT hr = (HRESULT)drwrap_get_retval(wrapctx);
if (SUCCEEDED(hr))
{
// The user_data is the ppv argument from the pre-hook
IFileOpenDialog **ppv = (IFileOpenDialog **)user_data;
if (ppv && *ppv)
{
if (m_user_data.type == TYPE_IFileOpenDialog) {
IFileOpenDialog **ppv = (IFileOpenDialog **)m_user_data.dialog;
if (!ppv || !*ppv) return;
IFileOpenDialog *fod = *ppv;
WCHAR wfile[MAX_PATH];
mbstowcs(wfile, OPEN_FILE, MAX_PATH - 1);
wfile[MAX_PATH - 1] = L'\0';
fod->lpVtbl->SetFileName(fod, wfile);
customFODVtbl = *fod->lpVtbl;
customFODVtbl.Show = custom_IFileDialog_Show;
customFODVtbl.GetResult = custom_IFileDialog_GetResult;
customFODVtbl.GetResult = custom_IFileOpenDialog_GetResult;
fod->lpVtbl = &customFODVtbl;
}
if (m_user_data.type == TYPE_IFileSaveDialog) {
IFileSaveDialog **ppv = (IFileSaveDialog **)m_user_data.dialog;
if (!ppv || !*ppv) return;
IFileSaveDialog *fsd = *ppv;
WCHAR wfile[MAX_PATH];
mbstowcs(wfile, SAVE_FILE, MAX_PATH - 1);
wfile[MAX_PATH - 1] = L'\0';
customFSDVtbl = *fsd->lpVtbl;
customFSDVtbl.Show = custom_IFileDialog_Show;
customFSDVtbl.GetResult = custom_IFileSaveDialog_GetResult;
fsd->lpVtbl = &customFSDVtbl;
}
}
}
static HRESULT custom_IFileDialog_Show(void *self, void *b) {
static HRESULT custom_IFileDialog_Show(void *self, void *b)
{
return S_OK;
}
static HRESULT custom_IFileDialog_GetResult(void *self, IShellItem **ppsitem) {
static HRESULT custom_IFileSaveDialog_GetResult(void *self, IShellItem **ppsitem)
{
WCHAR file[MAX_PATH];
mbstowcs(file, CUSTOM_FILE, MAX_PATH);
mbstowcs(file, SAVE_FILE, MAX_PATH);
file[MAX_PATH - 1] = L'\0';
HANDLE handle = CreateFileA(SAVE_FILE, GENERIC_ALL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
CloseHandle(handle);
return fn_SHCreateItemFromParsingName(file, NULL, &IID_IShellItem, (void **)ppsitem);
}
static HRESULT custom_IFileOpenDialog_GetResult(void *self, IShellItem **ppsitem)
{
WCHAR file[MAX_PATH];
mbstowcs(file, OPEN_FILE, MAX_PATH);
file[MAX_PATH - 1] = L'\0';
return fn_SHCreateItemFromParsingName(file, NULL, &IID_IShellItem, (void **)ppsitem);
@ -203,18 +345,21 @@ static void event_module_load(void *drcontext, const module_data_t *mod, bool lo
// WinAPI Hooks
WRAP_FUNCTION("GetOpenFileNameA", pre_GetOpenFileNameA, NULL);
WRAP_FUNCTION("GetOpenFileNameW", pre_GetOpenFileNameW, NULL);
WRAP_FUNCTION("GetSaveFileNameA", pre_GetSaveFileNameA, NULL);
WRAP_FUNCTION("GetSaveFileNameW", pre_GetSaveFileNameW, NULL);
WRAP_FUNCTION("CoCreateInstance", pre_CoCreateInstance, post_CoCreateInstance);
// Qt Hooks
WRAP_FUNCTION(MANGLED_QFileDialog_getOpenFileName, pre_QFileDialog_getOpenFileName, NULL);
WRAP_FUNCTION(MANGLED_QFileDialog_getOpenFileUrl, pre_QFileDialog_getOpenFileUrl, NULL);
WRAP_FUNCTION(MANGLED_QFileDialog_getSaveFileName, pre_QFileDialog_getSaveFileName, NULL);
WRAP_FUNCTION(MANGLED_QFileDialog_getSaveFileUrl, pre_QFileDialog_getSaveFileUrl, NULL);
// Get pointers to functions we need to call ourselves
GET_FUNCTION_POINTER(fn_QString_const, MANGLED_QString_const);
GET_FUNCTION_POINTER(fn_QUrl_const, MANGLED_QUrl_const);
GET_FUNCTION_POINTER(fn_CoTaskMemAlloc, "CoTaskMemAlloc");
GET_FUNCTION_POINTER(fn_SHCreateItemFromParsingName, "SHCreateItemFromParsingName");
}
static void event_exit(void)

View File

@ -1,10 +1,11 @@
cl /Fe:compiled\Foo.exe source\Foo.c /Zi /Od /link /MACHINE:X64
cl /Fe:compiled\GetOpenFileNameA.exe comdlg32.lib source\GetOpenFileNameA.c /Zi /Od /link /MACHINE:X64
cl /Fe:compiled\GetOpenFileNameW.exe comdlg32.lib source\GetOpenFileNameA.c /Zi /Od /link /MACHINE:X64
cl /Fe:compiled\IFileDialog.exe uuid.lib ole32.lib source\IFileDialog.c /Zi /Od /link /MACHINE:X64
cl /Fe:compiled\GetSaveFileNameA.exe comdlg32.lib source\GetSaveFileNameA.c /Zi /Od /link /MACHINE:X64
cl /Fe:compiled\GetSaveFileNameW.exe comdlg32.lib source\GetSaveFileNameA.c /Zi /Od /link /MACHINE:X64
cl /Fe:compiled\IFileOpenDialog.exe uuid.lib ole32.lib source\IFileOpenDialog.c /Zi /Od /link /MACHINE:X64
cl /Fe:compiled\IFileSaveDialog.exe uuid.lib ole32.lib source\IFileSaveDialog.c /Zi /Od /link /MACHINE:X64
qmake source\QT_getOpenFileContent.pro
nmake
@ -21,5 +22,11 @@ nmake
qmake source\QT_getOpenFileUrls.pro
nmake
qmake source\QT_getSaveFileName.pro
nmake
qmake source\QT_getSaveFileUrl.pro
nmake
qmake source\QT_TestLib.pro
nmake -f Makefile.Debug

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,42 @@
#include <windows.h>
#include <commdlg.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
char file[MAX_PATH] = {0};
OPENFILENAME ofn = {
.lStructSize = sizeof(ofn),
.lpstrFilter = "Text Files\0*.txt\0All Files\0*.*\0",
.lpstrFile = file,
.nMaxFile = MAX_PATH,
.lpstrTitle = "Save File As",
.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT
};
int res = GetSaveFileNameA(&ofn);
if (!res) {
puts("File selection cancelled or failed!");
exit(1);
}
FILE *f = fopen(ofn.lpstrFile, "w");
if (!f) {
perror("Error opening file for writing");
exit(1);
}
if (fprintf(f, "foo") < 0) {
perror("Error writing to file");
fclose(f);
exit(1);
} else {
printf("Successfully wrote \"foo\" to '%s'.\n", ofn.lpstrFile);
}
fclose(f);
return 0;
}

View File

@ -0,0 +1,42 @@
#include <windows.h>
#include <commdlg.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
WCHAR file[MAX_PATH] = {0};
OPENFILENAMEW ofn = {
.lStructSize = sizeof(ofn),
.lpstrFilter = L"Text Files\0*.txt\0All Files\0*.*\0",
.lpstrFile = file,
.nMaxFile = MAX_PATH,
.lpstrTitle = L"Select File to Write To",
.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST
};
int res = GetOpenFileNameW(&ofn);
if (!res) {
_putws(L"File selection cancelled or failed!");
exit(1);
}
FILE *f = _wfopen(ofn.lpstrFile, L"w");
if (!f) {
_wperror(L"Error opening file for writing");
exit(1);
}
if (fwprintf(f, L"foo") < 0) {
_wperror(L"Error writing to file");
fclose(f);
exit(1);
} else {
wprintf(L"Successfully wrote \"foo\" to '%s'.\n", ofn.lpstrFile);
}
fclose(f);
return 0;
}

View File

@ -1,5 +1,5 @@
#include <windows.h>
#include <shobjidl.h> // Required for IFileDialog and related interfaces
#include <shobjidl.h>
#include <stdio.h>
#include <stdlib.h>

View File

@ -0,0 +1,82 @@
#include <windows.h>
#include <shobjidl.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr)) {
fprintf(stderr, "CoInitializeEx failed: 0x%lx\n", hr);
exit(1);
}
char file[MAX_PATH] = {0};
IFileSaveDialog *pFileSave = NULL;
IShellItem *pItem = NULL;
PWSTR pszFilePath = NULL;
hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, CLSCTX_ALL,
&IID_IFileSaveDialog, (void**)&pFileSave);
if (SUCCEEDED(hr)) {
COMDLG_FILTERSPEC rgSpec[] = {{L"All Files", L"*.*"}};
pFileSave->lpVtbl->SetFileTypes(pFileSave, ARRAYSIZE(rgSpec), rgSpec);
pFileSave->lpVtbl->SetTitle(pFileSave, L"Save File As");
DWORD dwFlags;
pFileSave->lpVtbl->GetOptions(pFileSave, &dwFlags);
pFileSave->lpVtbl->SetOptions(pFileSave, dwFlags | FOS_OVERWRITEPROMPT | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR);
hr = pFileSave->lpVtbl->Show(pFileSave, NULL);
if (SUCCEEDED(hr)) {
printf("GetResult...\n");
hr = pFileSave->lpVtbl->GetResult(pFileSave, &pItem);
if (SUCCEEDED(hr)) {
printf("GetDisplayName...\n");
hr = pItem->lpVtbl->GetDisplayName(pItem, SIGDN_FILESYSPATH, &pszFilePath);
if (SUCCEEDED(hr)) {
wcstombs(file, pszFilePath, MAX_PATH);
printf("Selected file: %s\n", file);
}
}
}
}
if (pszFilePath) {
CoTaskMemFree(pszFilePath);
}
if (pItem) {
pItem->lpVtbl->Release(pItem);
}
if (pFileSave) {
pFileSave->lpVtbl->Release(pFileSave);
}
CoUninitialize();
if (FAILED(hr) || file[0] == '\0') {
puts("File selection cancelled or failed.");
exit(1);
}
FILE *f = fopen(file, "w");
if (!f) {
perror("Error opening file for writing");
exit(1);
}
if (fprintf(f, "foo") < 0) {
perror("Error writing to file");
fclose(f);
exit(1);
} else {
puts("Successfully wrote \"foo\" to the file.");
}
fclose(f);
return 0;
}

View File

@ -0,0 +1,34 @@
#include <QApplication>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <iostream>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QString fileName = QFileDialog::getSaveFileName(nullptr, "Select File", "", "All Files (*.*)");
if (fileName.isEmpty()) {
qDebug() << "File selection cancelled.";
return 0;
}
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
std::cerr << "Failed to open file for writing: " << file.errorString().toStdString() << std::endl;
return 1;
}
QTextStream out(&file);
out << "foo";
file.close();
qDebug() << "Successfully wrote \"foo\" to:" << fileName;
return 0;
}

View File

@ -0,0 +1,5 @@
QT += core widgets
CONFIG += console
SOURCES += QT_getSaveFileName.cpp
TARGET = QT_getSaveFileName
DESTDIR = compiled

View File

@ -0,0 +1,31 @@
#include <QApplication>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <iostream>
#include <QUrl>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QUrl url = QFileDialog::getSaveFileUrl(nullptr, "Select File", QUrl(), "All Files (*.*)");
if (!url.isValid() || !url.isLocalFile()) {
qDebug() << "File selection cancelled.";
return 1;
}
QFile file(url.toLocalFile());
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
std::cerr << "Failed to open file for writing: " << file.errorString().toStdString() << std::endl;
return 1;
}
QTextStream out(&file);
out << "foo";
file.close();
qDebug() << "Successfully wrote \"foo\" to:" << url.toLocalFile();
return 0;
}

View File

@ -0,0 +1,5 @@
QT += core widgets
CONFIG += console
SOURCES += QT_getSaveFileUrl.cpp
TARGET = QT_getSaveFileUrl
DESTDIR = compiled