Common Dialog/Save -- Selected file format?

In an application I'm making in VB 4 Pro, I use the CommonDialog's FileSave method to present a "save file" dialog box. The file type combo box has two choices, *.dwg and *.dxf.

Now, in Windows 95 the file type selection works as I thought it would; the appropriate (ie. the selected ) file suffix is added to the file name. But in Windows 3.1, it isn't. And I can't find any (other) way to check what file format the user selected. Can anyone help me out on this one?

Thanks in advance,

Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

The common dialog control is nto very restrictive.  You can basically enter any extention that you want into the dialog, and the dialog will just go on it's merry way, returning whatever the user typed.

Here's a solution.

After the Common dialog has been processed, use the FilterIndex to figure out what filter the user has selected.  Compare that with the FileTitle property to see if the user has indeed typed the same extention that they have selected.

'set you Common dialog stuff here
Select case commondlg1.FilterIndex
  Case 0 'DWG
    strCompare = ".DWG"
  Case 1 'DXF
    strCompare = ".DXF"
If RIGHT$(commondlg1.FileTitle,4) ,. strCompare THEN
  MSgbox "Invalid file name"
  'or fix the extention
  'Cancel Save

Hope this helps

MacSverreAuthor Commented:
I'm afraid this doesn't work. The filterindex property is just there for setting the default file type in a list of file types. If you read it, it returns whatever you set it to in code (or in the properties window). If you haven't set it at all, it returns zero.

Anyone else?
Why don't you just check the filename the dialog returns and check the terminal three characters?  cut them off with the "right" function, then you know which extension was chosen.  Hope this helps...
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

MacSverreAuthor Commented:
Sorry, but if you read my original question, you can see that my problem is that Win 3.1 doesn't add the extension of the selected file format. That makes it kinda hard to check the suffix...

Anyone else?
Interesting.  This works in VB 5 with out any problems.  VB4's help file says that FilterIndex "Returns or sets a default filter"

I did try it in VB 4/16 and it doesn't work.  

You could try DefaultEXT.  DefaultEXT will set a default extension, but ignores the filter, so you only get one default.

Obviously, you cannot change to VB5 if you're in a Win 3.x environment.

This is what I turned up in the MS KB on FilterIndex:

Article ID: Q106682
PRB: Default Extension Ignores File Type in VB Common Dialog

Essentially, MS says that it screwed up, and that File type does not override DefaultExt, and it's not like anything else in Windows.

It suggests writing a DLL to wrap around the CommonDialog functions in COMMDLG.DLL.

From Q106682:
Workaround for Windows API Programmers

Visual Basic's common dialog custom controls for Open and Save As pass their FilterIndex property to the Windows API function GetOpenFileName. GetOpenFileName is located in the Windows COMMDLG.DLL file. However, Visual Basic ignores the nFilterIndex value that the GetOpenFileName function returns. By design, your Visual Basic program cannot access the structure returned by the GetOpenFileName function, even by calling API routines.

You can write your own DLL routine in C to call the Windows common dialog routines located in COMMDLG.DLL. Then call this DLL from Visual Basic. The following documentation from the Windows Software Development Kit (SDK) explains how to use the nFilterIndex element of the structure passed to GetOpenFileName:

   Specifies an index into the buffer pointed to by the lpstrFiler member.
   The system uses the index value to obtain a pair of strings to use as
   the initial filter description and filter pattern for the dialog box.
   The first pair of strings has an index value of 1. When the user chooses
   the OK button to close the dialog box, the system copies the index of
   the selected filter strings into this location. If the nFilterIndex
   member is 0, the filter in the buffer pointed to by the
   lpstrCustomFilter member is used. If the nFilterIndex member is 0 and
   the lpstrCustomFilter member is NULL, the system uses the first filter
   in the buffer pointed to by the lpstrFilter member. If each of the three
   members is either 0 or NULL, the system does not use any filters and
   does not show any files in the File Name list box of the dialog box.

OK, Just because I like to figure out things that MS says can't be done, here is a set of API calls that will do what you want to do.  Bastardized it from an Access 2.0 project that I was doing.  

Option Explicit

' Global Declaration Section
     lStructSize As Long
     hwndOwner As Integer
     hInstance As Integer
     lpstrFilter As Long
     lpstrCustomFilter As Long
     nMaxCustFilter As Long
     nFilterIndex As Long
     lpstrFile As Long
     nMaxFile As Long
     lpstrFileTitle As Long
     nMaxFileTitle As Long
     lpstrInitialDir As Long
     lpstrTitle As Long
     Flags As Long
     nFileOffset As Integer
     nFileExtension As Integer
     lpstrDefExt As Long
     lCustData As Long
     lpfnHook As Long
     lpTemplateName As Long
End Type
Declare Function GetOpenFileName Lib "COMMDLG.DLL" (OPENFILENAME As tagOPENFILENAME) As Integer
Declare Function GetSaveFileName Lib "COMMDLG.DLL" (OPENFILENAME As tagOPENFILENAME) As Integer
Declare Function lstrcpy Lib "Kernel" (ByVal lpDestString As Any, ByVal lpSourceString As Any) As Long


' Open File Constants
Global Const OFN_READONLY = &H1
Global Const OFN_NOCHANGEDIR = &H8
Global Const OFN_SHOWHELP = &H10
Global Const OFN_ENABLEHOOK = &H20
Global Const OFN_NOVALIDATE = &H100
Global Const OFN_PATHMUSTEXIST = &H800
Global Const OFN_FILEMUSTEXIST = &H1000
Global Const OFN_CREATEPROMPT = &H2000
Global Const OFN_SHAREAWARE = &H4000
Global Const OFN_SHARENOWARN = 1
Global Const OFN_SHAREWARN = 0
' SAveCommDlg - Save pen File Common Dialog Function - used to call the COMMDLG.DLL
'   to create a file for saving...that file.
'   Usage:
'  strFilter = "Ext 1" & chr$(0) & ".EX1" & chr$(0) & "Ext 2" & chr$(0) & ".EX2" & chr$(0) & 
'     strString = SaveCommDlg(Title,Filter,DefaultFile, DefaultEXT, FilterIndex)
'     If Len(strString) = 0 Then
'       Error
'     Else
'       Do something with this file name
'     End if
'  Filterindex = 1,2 etc based off of FileType selected
'   creatively pilfered from MS tech net!
Function SaveCommDlg(strParmTitle As String, strParmFilter As String, strParmFileName As String, strParmDefaultEXT As String, ByRef nRetFilterIndex As Long) As String
  Dim strFilter As String, strFileName As String, strFileTitle As String, strDefExt As String
  Dim strTitle As String, strCurDir As String
  Dim APIResults As Integer

  On Error GoTo SaveCommDlg_Err
  SaveCommDlg = ""
  'Define the strFilter string and allocate space in the "c" string
  strFilter = strParmFilter
  'Allocate string space for the returned strings.
  strFileName = strParmFileName & Chr$(0) & Space$(255) & Chr$(0)
  strFileTitle = Space$(255) & Chr$(0)
  ' Give the dialog a caption title.
  strTitle = strParmTitle & Chr$(0)
  strDefExt = strParmDefaultEXT & Chr$(0)
  ' Set up the default directory
  strCurDir = CurDir & Chr$(0)
  ' Set up the data structure before you call the GetOpenFileName
  'If the OpenFile Dialog box is linked to a form use this line.
  'It will pass the forms window handle.
  'OPENFILENAME.hwndOwner = Screen.ActiveForm.hWnd
  'If the OpenFile Dialog box is not linked to any form use this line.
  'It will pass a null pointer.
  'OPENFILENAME.hwndOwner = 0&
  'No... use this...
  'Get Window Handle
  OPENFILENAME.hwndOwner = Form1.hWnd

  OPENFILENAME.lpstrFilter = lstrcpy(strFilter, strFilter)
  OPENFILENAME.nFilterIndex = 1
  OPENFILENAME.lpstrFile = lstrcpy(strFileName, strFileName)
  OPENFILENAME.nMaxFile = Len(strFileName)
  OPENFILENAME.lpstrFileTitle = lstrcpy(strFileTitle, strFileTitle)
  OPENFILENAME.nMaxFileTitle = Len(strFileTitle)
  OPENFILENAME.lpstrTitle = lstrcpy(strTitle, strTitle)
  "NOTE: These flags are set for the original OPENFILE calls, but they seem to work now...
  OPENFILENAME.lpstrDefExt = lstrcpy(strDefExt, strDefExt)
  OPENFILENAME.hInstance = 0
  OPENFILENAME.lpstrCustomFilter = 0
  OPENFILENAME.nMaxCustFilter = 0
  OPENFILENAME.lpstrInitialDir = lstrcpy(strCurDir, strCurDir)
  OPENFILENAME.nFileOffset = 0
  OPENFILENAME.nFileExtension = 0
  OPENFILENAME.lCustData = 0
  OPENFILENAME.lpfnHook = 0
  OPENFILENAME.lpTemplateName = 0
  '* This will pass the desired data structure to the Windows API,
  '* which will in turn it uses to display the Open Dialog form.
  APIResults = GetSaveFileName(OPENFILENAME)
  If APIResults <> 0 Then
    ' Note that FileName$ will have an embedded Chr$(0) at the end.
    strFileName = Left$(strFileName, InStr(strFileName, Chr$(0)) - 1)
    strFileName = ""
  End If
  'Return Filter index  
  nRetFilterIndex = OPENFILENAME.nFilterIndex
  SaveCommDlg = strFileName

  Exit Function

  MsgBox Error
  SaveCommDlg = ""
  Resume SaveCommDlg_Exit
End Function


Form with one Command button on it:
Private Sub Command1_Click()
  Dim strFile As String
  Dim strFilter As String
  Dim strCompare as String
  Dim lngFilterIndex As Long
  strFilter = "DWG Files" & Chr$(0) & "*.DWG" & Chr$(0) & "DXF Files" & Chr$(0) & "*.DXF" & Chr$(0)
  strFile = SaveCommDlg("Save file as...", strFilter, "TEST.DWG", "DWG", lngFilterIndex)

Select case lngFilterIndex
 Case 1 'DWG
  strCompare = ".DWG"
 Case 2 'DXF
  strCompare = ".DXF"

If RIGHT$(strFile ,4) <> strCompare THEN
  MSgbox "File extention and File type do not match"
  'or fix the extention
  'Cancel Save

End Sub

This does work in VB 4/16 , but you're going to have to still check to see if the filterIndex and the ext are matching.  The CommonDialog DLL doesn't force the defaultExt and FileType to match. But you should be able to trap the filename and FilterIndex at this point and insure correctness.

Hope this helps, and sorry the first one didn't work


Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
MacSverreAuthor Commented:
Thank you very much! I've done a "work-a-round" that I will use in my current project, but I _will_ use this in later projects.

About the extension: It doesn't really matter to me if the user types in a different extension, as long a I can check which file format was selected.

Again, thanks!

It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.