I have the following code (extract) to display Save As dialog:
char FileName[MAX_PATH] = "TestImage.jpg"
...
lpofn.lStructSize = sizeof(OPENFILENAME);
lpofn.hwndOwner = hWnd;
lpofn.hInstance = GetWindowInstance (hWnd);
lpofn.lpstrFilter = "JPG - JPEG File\0*.JPG\0TIF - TIFF File\0*.TIF\0PNG File\0*.PNG\0BMP - Bitmat File\0*.BMP\0";
lpofn.lpstrCustomFilter = NULL;
lpofn.nMaxCustFilter = NULL;
lpofn.nFilterIndex = 0;
lpofn.lpstrFile = FileName;
lpofn.nMaxFile = MAX_PATH;
lpofn.lpstrFileTitle = NULL;
lpofn.nMaxFileTitle = NULL;
lpofn.lpstrInitialDir = NULL;
lpofn.lpstrTitle = NULL;
lpofn.Flags = OFN_HIDEREADONLY | OFN_ENABLEHOOK | OFN_EXPLORER;
lpofn.nFileOffset = 0;
lpofn.nFileExtension = 0;
lpofn.lpstrDefExt = NULL;
lpofn.lCustData = NULL;
lpofn.lpfnHook = &UpdateFilename;
lpofn.lpTemplateName = NULL;
if(!GetSaveFileName(&lpofn)) return;
...
eg.
- User does save as, default File name = "TestImage.jpg", default Files of Type = JPG
- User changes Files of Type to PNG, File name control remains at "TestImage.jpg" instead of changing to "TestImage.png"
Am I doing something wrong? Is it possible to instruct GetSaveFileName() to change the extension, or do I have to have a custom save as dialog (any examples?)
I'm using Win32 API, VC6.
Update: here is the Hook function:
UINT CALLBACK UpdateFilename(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_NOTIFY:
// Check for CDN_TYPECHANGE etc
return FALSE;
}
return FALSE;
}
Note that the hook function does stop on breakpoints. I've purposely not proceeded further with handling CDN_TYPECHANGE until I can figure out why the look of the dialog changes when the hook is enabled and how to fix it.
In order to update the dialog while it is still running, you need to provide a pointer to an lpfnHook
callback in the OPENFILENAME
struct, and have the callback handle the CDN_TYPECHANGE
notification. It can send the dialog a CDM_GETFILEPATH
or CDM_GETSPEC
message to get the current filename, tweak it as needed, and then send a CDM_SETCONTROLTEXT
message to update the edit field (the ID of the filename edit field is 0x442
) with the new value.
Update: There is nothing wrong with your hook code. GetSaveFileName()
is deprecated starting in Windows Vista, replaced by (and becoming a wrapper around) the Common Item Dialog. The GSFN dialog UI is not altered by a hook in XP, so you must be using Vista+, in which case enabling the hook simply causes the wrapper to use different settings when invoking the CID internally. A lot of the new CID features are based on IShellItem
, not filename strings, so the wrapper removes anything that cannot be represented as a old-style filename, and makes the dialog look like the old-style GSFN dialog in XP and earlier. So what you are seeing is normal behavior for GetSaveFileName()
under Vista+! If you do not like it, then do not use GetSaveFileName()
anymore. Use the new IFileSaveDialog
interface instead. In fact, it natively changes the file extension for you if you configure multiple file types, designate one of them as the default extension, and then set an initial filename that matches the default extension. But if you wanted to, you can alternatively implement the IFileDialogEvents
interface in your code to receive OnTypeChange
notifications and then use the IFileDialog::SetFileName()
method to update the displayed filename as neded.
Use a buffer for your suggested file name, not a static string.
char szFile[MAX_PATH];
szFile[0] = '\0';
lpofn.lpstrFile= szFile;
lpofn.nMaxFile = sizeof(szFile)/ sizeof(*szFile);
Besides that, it is a default Windows behaviour going well back to Win95 days. As you are using VS6.0, you stil have these DLLs. What I did then was to use:
lpofn.lpstrDefExt = (LPSTR)NULL
That prevents any extension from being added. I then checked lpofn.nFileExtension upon return to find out which extension was selected.
if (lpofn.nFileExtension == 0)
{
// add default extension, no extension was selected/entered by user
}
else
{
// there is an extension, save as entered.
}