how to save file with GetSaveFileName in win32? - c++

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!

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

Windows API: Unicode variable metamorphasis

I created a function that reads from a file, and fills a data structure based on that file. The function works well alone, so I decided to build an GUI around its function. I have a function that calls the Windows GUI and returns a LPOPENFILENAME structure, to which I copy the LPOPENFILENAME.lpstrFile over to a LPCWSTR.
Next, I initialize a wstring to hold the LPCWSTR value. Here's where things get a bit strange.
LPCWSTR File = FileOpenOBJ(hWnd, hInst).lpstrFile;
MessageBox(hWnd, File, L"Warning 1: File/lpstrFile assignment ", MB_ICONEXCLAMATION | MB_OK);
wstring Fileformatting(File);
Which does a runtime display of something like this:
Which is fine, since LPCWSTR File now holds the value from the LPOPENFILENAME structure, but when a wstring is initialized with the value of File, both variables seemingly go out of range or pull from a pointer location that is no longer accurate and their values appear to be something like:
The goal is to hold the file name in a string so that I can (PHP-like) escape the "\" characters to insert into my function. As such:
C:\Users\InfinityMachine\Documents\Test.obj -> C:\\Users\\InfinityMachine\\Documents\\Test.obj
In which I:
while (Fileformatting.find(L"\\") != wstring::npos){
Fileformatting.replace(Fileformatting.find(L"\\"), 2, L"\\\\");
}
How can I get the correct value of LPCWSTR File into wstring Fileformatting?
You have probably initialized lpstrFile to a variable that is local to the FileOpenOBJ function, and when that function returns you get undefined behavior when you try to use it. It appears to work the first time because the stack hasn't been exercised yet, but that changes when you make the call to MessageBox.
Moving the initialization of the wstring would mask the problem but wouldn't fix it, you're still relying on undefined behavior. The fix would be to have the structure contain a pointer that doesn't go out of scope.
In addition to what others have said about dangling pointers and such, for this kind of situation, you are better off simply having FileOpenOBJ() return a wstring instead of a copy of the original OPENFILENAME struct:
wstring FileOpenOBJ(HWND hWnd, HINSTANCE hInst)
{
WCHAR szFileName[MAX_PATH+1] = {0};
OPENFILENAME ofn = {0};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.hInstance = hInst;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
...
GetOpenFileName(&ofn);
return szFileName;
}
wstring File = FileOpenOBJ(hWnd, hInst);
MessageBoxW(hWnd, File.c_str(), L"Warning 1: File/lpstrFile assignment ", MB_ICONEXCLAMATION | MB_OK);
If you need to return other values from the OPENFILENAME, you should define your own struct to hold what you need, for example:
struct FileInfo
{
wstring FullPath;
wstring FileName;
wstring Ext;
int FilterIndex;
...
};
FileInfo FileOpenOBJ(HWND hWnd, HINSTANCE hInst)
{
WCHAR szFileName[MAX_PATH+1] = {0};
OPENFILENAME ofn = {0};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.hInstance = hInst;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
...
GetOpenFileName(&ofn);
FileInfo fi;
fi.FullPath = szFileName;
fi.FileName = &szFileName[ofn.nFileOffset];
if (ofn.nFileExtension != 0)
fi.Ext = &szFileName[ofn.nFileExtension];
fi.FilterIndex = ofn.nFilterIndex;
...
return fi;
}
FileInfo fi = FileOpenOBJ(hWnd, hInst);
MessageBoxW(hWnd, fi.FullPath.c_str(), L"Warning 1: File/lpstrFile assignment ", MB_ICONEXCLAMATION | MB_OK);
Don't expose direct access to the OPENFILENAME struct. It should be left as a private implementation detail of FileOpenOBJ()'s internal logic.

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

winapi openfiledialog 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.