how to save file with GetSaveFileName in win32?

Hamed picture Hamed · May 4, 2012 · Viewed 14k times · Source

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.

Answer

Adrian McCarthy picture Adrian McCarthy · May 4, 2012

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.