winapi openfiledialog c++ - c++

I use mingw, c++
I would like to open multiple files, something like this:
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "TXT\0"
"*.txt\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT;
ofn.lpstrDefExt = "txt";
But how can I get the filenameS? If I check the szFileName variable, it only contains the folder name.

From the documentation:
If the user selects more than one file, the lpstrFile buffer returns the path to the current directory followed by the file names of the selected files. The nFileOffset member is the offset, in bytes or characters, to the first file name
the directory and file name strings are NULL separated, with an extra NULL character after the last file name.
From your question:
If I check the szFileName variable, it only contains the folder name.
Keep checking one character past the terminating '\0'.
Each time the following character isn't another '\0', that's the start of a new filename.

Related

How to get a path to file in c++?

I am making an app with ImGUI to choose pictures. So I need to call "OPENFILENAME" to call a dialog window, and there is my code to do it:
OPENFILENAME ofn;
::memset(&ofn, 0, sizeof(ofn));
TCHAR f1[MAX_PATH];
f1[0] = 0;
ofn.lStructSize = sizeof(ofn);
ofn.nFilterIndex = 2;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFile = f1;
ofn.Flags = OFN_FILEMUSTEXIST;
if (::GetOpenFileName(&ofn) != FALSE)
{
show_path = TRUE;
}
But later I need to have a path to image, which I chose in char type and lpstrFile is a THCAR type. I found anything to make THCAR into char.
Am I doing choosing file in right way, and if yes, how can I get in usual char format a path to file?

Call to GetOpenFileNameA causes common dialog error 2

I'm trying to open a dialog box using GetOpenFileNameA. However, the dialog does not open. Instead, I get a nice CommDlgError 2. Searching Google and StackOverflow for this error did not produce any helpful results.
Confusingly, this code works on a school computer that also uses Visual Studio, albeit a different version.
Note: All variables not declared in this block of code are "global" variables that are only accessible within the main code module.
void GetInputFile()
{
char szFileNameIN[MAX_PATH];
char szFileNameOUT[MAX_PATH];
// get the input file name
OPENFILENAME ofn;
ZeroMemory(&fInputPath, sizeof(fInputPath));
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = LPWSTR("Any File\0*.*\0");
ofn.lpstrFile = LPWSTR(fInputPath);
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFileTitle = LPWSTR(szFileNameIN);
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrTitle = LPWSTR("Select an input File");
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;
if (GetOpenFileNameA(LPOPENFILENAMEA(&ofn))) // user selected an input file
{
}
else {
// Get error
TCHAR error[MAX_LOADSTRING];
wsprintf(error,TEXT("%i"),CommDlgExtendedError());
MessageBox(NULL,error,TEXT("ERROR"),MB_OK);
}
}
Those awful (LPWSTR) casts tell me that you are compiling with UNICODE defined, so the OPENFILENAME struct you are using is actually OPENFILENAMEW; given that you are using GetOpenFileNameA, you have to use OPENFILENAMEA (or use straight GetOpenFileName with wide strings).
(and remember that, as a rule of thumb, if you have to cast pointers to/from anything different than void * and similar, you are probably doing something wrong; adding pointer casts will only silence the compiler, not make the error go away)
You are using the TCHAR version of OPENFILENAME. Since you are assigning Unicode string pointers to its fields, that means your project is being compiled with UNICODE defined, so TCHAR maps to wchar_t and OPENFILENAME maps to OPENFILENAMEW. But you are using ANSI character buffers and the ANSI version of GetOpenFileName(), and using incorrect type-casts all over the place.
So get rid of all the type-casts, and then either:
use proper TCHAR types and APIs for everything:
void GetInputFile()
{
TCHAR szFileNameIN[MAX_PATH];
TCHAR szFileNameOUT[MAX_PATH];
// get the input file name
OPENFILENAME ofn;
ZeroMemory(&fInputPath, sizeof(fInputPath));
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = TEXT("Any File\0*.*\0");
ofn.lpstrFile = fInputPath; // must be TCHAR[]...
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFileTitle = szFileNameIN;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrTitle = TEXT("Select an input File");
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&ofn)) // user selected an input file
{
}
else
{
// Get error
TCHAR error[MAX_LOADSTRING];
wsprintf(error, TEXT("%i"), CommDlgExtendedError());
MessageBox(NULL, error, TEXT("ERROR"), MB_OK);
}
}
use proper CHAR/WCHAR types and APIs for everything:
void GetInputFile()
{
WCHAR szFileNameIN[MAX_PATH];
WCHAR szFileNameOUT[MAX_PATH];
// get the input file name
OPENFILENAMEW ofn;
ZeroMemory(&fInputPath, sizeof(fInputPath));
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = L"Any File\0*.*\0";
ofn.lpstrFile = fInputPath; // must be WCHAR[]...
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFileTitle = szFileNameIN;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrTitle = L"Select an input File";
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&ofn)) // user selected an input file
{
}
else
{
// Get error
WCHAR error[MAX_LOADSTRING];
wsprintfW(error, L"%i", CommDlgExtendedError());
MessageBoxW(NULL, error, L"ERROR", MB_OK);
}
}
void GetInputFile()
{
CHAR szFileNameIN[MAX_PATH];
CHAR szFileNameOUT[MAX_PATH];
// get the input file name
OPENFILENAMEA ofn;
ZeroMemory(&fInputPath, sizeof(fInputPath));
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = "Any File\0*.*\0";
ofn.lpstrFile = fInputPath; // must be CHAR[]...
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFileTitle = szFileNameIN;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrTitle = "Select an input File";
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;
if (GetOpenFileNameA(&ofn)) // user selected an input file
{
}
else
{
// Get error
CHAR error[MAX_LOADSTRING];
wsprintfA(error, "%i", CommDlgExtendedError());
MessageBoxA(NULL, error, "ERROR", MB_OK);
}
}

GetSaveFileName can't open user's temp-dir

I use a similar function to get the path and the filename, which I want to use to save a file on a Windows 8.1 machine:
void TestFunc()
{
OPENFILENAME ofn;
WCHAR szFileName[MAX_PATH] = L"C:\\Users\\biber\\AppData\\Local\\temp\\test.txt";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = (LPWSTR)szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = (LPCWSTR)L"txt";
GetSaveFileName(&ofn);
return ;
}
The problem is, that the SaveFileDialog doesn't show me initial the Temp-Path. If I change the szFileName to another path than the %TEMP%, it works.
It doesn't work with the temp path.

Handle multiple file selection

I have the following code:
void OpenJpgFile(HWND hWnd)
{
OPENFILENAME ofn;
wchar_t szFileName[17*MAX_PATH] = L"";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrTitle = L"Selecione as fotos que deseja adicionar...";
ofn.lpstrFilter = L"Arquivos JPEG (*.jpg)\0*.jpg\0Todos os Arquivos (*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT;
ofn.lpstrDefExt = L"jpg";
if(GetOpenFileName(&ofn))
{
//Test
MessageBox(hWnd,ofn.lpstrFileTitle,L"TESTE",NULL);
if(gpBitmap)
{
delete gpBitmap;
}
gpBitmap = new Bitmap(szFileName);
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
}
What I wanted to know is how can I get the name of all files the user has selected...
All I can get is the path to the folder... is there an array that keeps the file names?
Per the documentation of OFN_ALLOWMULTISELECT when OFN_EXPLORER is also specified, lpstrFile will contain the directory followed by the names of the files, separated by null characters, and terminated with two adjacent null characters. When you call new Bitmap(szFileName) you are treating szFileName incorrectly - that is you pass it to a constructor that expects a standard single-null terminated string. Instead you have to process it more carefully to read past the initial null, and probably recreate your full file paths.
This string format happens to be the same format as used for REG_MULTI_SZ so, aside from not having to worry about missing final terminators, advice related to handling values of type REG_MULTI_SZ can help you here.
Note that to be fully general, you may also have to handle cases that require a larger buffer. See GetOpenFileName() with OFN_ALLOWMULTISELECT flag set for another angle on that

how to save file with GetSaveFileName in win32?

I write this code to get fileName to save my file :
#include "stdafx.h"
#include <windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
OPENFILENAME ofn;
char szFileName[MAX_PATH] = "";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = (LPCWSTR)L"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = (LPWSTR)szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = (LPCWSTR)L"txt";
GetSaveFileName(&ofn);
printf("the path is : %s\n", ofn.lpstrFile);
getchar();
return 0;
}
But the output is :
the path is : H
why ? Am I doing something wrong ?
I'm using Visual Studio 2008 on Windows 7.
This line:
printf("the path is : %s\n", ofn.lpstrFile);
should use the wide char version of printf.
wprintf(L"the path is : %s\n", ofn.lpstrFile);
The root problem is in these lines:
char szFileName[MAX_PATH] = "";
...
ofn.lpstrFile = (LPWSTR)szFileName;
ofn.nMaxFile = MAX_PATH;
This creates a buffer of MAX_PATH characters, but it tells the GetSaveFileName function that it's a buffer of MAX_PATH wide characters. This is likely to crash (or silently trample memory) when someone chooses a long path name.
The giveaway is the cast. Don't lie to the compiler or the libraries. They don't like that, and they'll always get their revenge in the end. Replace those lines with this:
WCHAR szFileName[MAX_PATH] = L"";
...
ofn.lpstrFile = szFileName; // no cast needed
ofn.nMaxFile = MAX_PATH;
Now the selected filename will be returned as a string of wide characters. Tony The Lion's answer is correct in that that you need to use wprintf rather than printf to print strings of wide characters:
wprintf(L"the path is : %s\n", ofn.lpstrFile); // though I'd use szFileName at this point
If you need the string in 8-bit characters instead of wide characters, you can use WideCharToMultiByte. But I would just stick with the wide character APIs in general.
Never cast unless you know exactly what it does and why it's necessary in your particular case.
You're both wrong, it's a simple C pointer/stack issue.
// WRONG:
char szFileName[MAX_PATH] = "";
This confuses arrays and pointers, you declare an array on the stack, but then change its memory address to point to an empty string in the data section. In other words, a buffer overflow.
// RIGHT:
char szFileName[MAX_PATH];
ZeroMemory(szFileName, MAX_PATH);
This declares a character array on the stack and initializes all elements to null terminator.
Hope that helps!