implemented wrapping of common 'save' methods
This commit is contained in:
parent
20c916077a
commit
a93c772b90
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
*.o
|
||||
.gitignore
|
||||
|
||||
.o
|
||||
*.so
|
||||
*.a
|
||||
*.test
|
||||
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -1,9 +0,0 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"cstdlib": "c",
|
||||
"memory": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"iostream": "cpp"
|
||||
}
|
||||
}
|
17
ABSTRACT.md
Normal file
17
ABSTRACT.md
Normal 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
|
@ -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
|
||||
|
@ -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);
|
||||
@ -190,11 +332,11 @@ static void event_module_load(void *drcontext, const module_data_t *mod, bool lo
|
||||
} \
|
||||
}
|
||||
|
||||
#define GET_FUNCTION_POINTER(var, name) \
|
||||
#define GET_FUNCTION_POINTER(var, name) \
|
||||
{ \
|
||||
if (var == NULL) \
|
||||
{ \
|
||||
var = (void *)dr_get_proc_address(mod->handle, name); \
|
||||
var = (void *)dr_get_proc_address(mod->handle, name); \
|
||||
if (var != NULL) \
|
||||
dr_printf("Found function pointer %s in %s\n", name, dr_module_preferred_name(mod)); \
|
||||
} \
|
||||
@ -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)
|
||||
|
@ -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.
BIN
programs/compiled/GetSaveFileNameA.exe
Normal file
BIN
programs/compiled/GetSaveFileNameA.exe
Normal file
Binary file not shown.
BIN
programs/compiled/GetSaveFileNameW.exe
Normal file
BIN
programs/compiled/GetSaveFileNameW.exe
Normal file
Binary file not shown.
BIN
programs/compiled/IFileOpenDialog.exe
Normal file
BIN
programs/compiled/IFileOpenDialog.exe
Normal file
Binary file not shown.
BIN
programs/compiled/IFileSaveDialog.exe
Normal file
BIN
programs/compiled/IFileSaveDialog.exe
Normal file
Binary file not shown.
Binary file not shown.
BIN
programs/compiled/QT_getSaveFileName.exe
Normal file
BIN
programs/compiled/QT_getSaveFileName.exe
Normal file
Binary file not shown.
BIN
programs/compiled/QT_getSaveFileUrl.exe
Normal file
BIN
programs/compiled/QT_getSaveFileUrl.exe
Normal file
Binary file not shown.
42
programs/source/GetSaveFileNameA.c
Normal file
42
programs/source/GetSaveFileNameA.c
Normal 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;
|
||||
}
|
42
programs/source/GetSaveFileNameW.c
Normal file
42
programs/source/GetSaveFileNameW.c
Normal 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;
|
||||
}
|
@ -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>
|
||||
|
82
programs/source/IFileSaveDialog.c
Normal file
82
programs/source/IFileSaveDialog.c
Normal 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;
|
||||
}
|
34
programs/source/QT_getSaveFileName.cpp
Normal file
34
programs/source/QT_getSaveFileName.cpp
Normal 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;
|
||||
}
|
5
programs/source/QT_getSaveFileName.pro
Normal file
5
programs/source/QT_getSaveFileName.pro
Normal file
@ -0,0 +1,5 @@
|
||||
QT += core widgets
|
||||
CONFIG += console
|
||||
SOURCES += QT_getSaveFileName.cpp
|
||||
TARGET = QT_getSaveFileName
|
||||
DESTDIR = compiled
|
31
programs/source/QT_getSaveFileUrl.cpp
Normal file
31
programs/source/QT_getSaveFileUrl.cpp
Normal 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;
|
||||
}
|
5
programs/source/QT_getSaveFileUrl.pro
Normal file
5
programs/source/QT_getSaveFileUrl.pro
Normal file
@ -0,0 +1,5 @@
|
||||
QT += core widgets
|
||||
CONFIG += console
|
||||
SOURCES += QT_getSaveFileUrl.cpp
|
||||
TARGET = QT_getSaveFileUrl
|
||||
DESTDIR = compiled
|
Loading…
x
Reference in New Issue
Block a user