GetSaveFileName() how to update the file extension

2019-09-20 12:25发布

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.

标签: c winapi vc6
2条回答
混吃等死
2楼-- · 2019-09-20 13:20

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.

查看更多
一纸荒年 Trace。
3楼-- · 2019-09-20 13:21

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.
}
查看更多
登录 后发表回答