Deprecated: Array and string offset access syntax with curly braces is deprecated in /homepages/21/d38531796/htdocs/jose/smfforum/Sources/Subs.php on line 3825
Fred's Tutorial #5: Windows API Tutorial: The Open File Dialog Box

IT-Consultant: Frederick J. Harris > Fred's API (Application Programming Interface) Tutorials

Fred's Tutorial #5: Windows API Tutorial: The Open File Dialog Box

(1/2) > >>

Frederick J. Harris:
Form5.bas (For versions Of PB Win before v10.  See last post in this thread for a v10 translation)

    This is the fifth tutorial in a sequence of tutorials I have started to help introduce programmers to using the original Windows Application Programming Interface to code Windows programs.  The first is at…

and an index and links to the others can be found within the introduction in that one.  This tutorial is primarily about the Common Dialog Box Library  and  secondarily shows how to put a simple dialog box in a program through a resource script.  A zip file - - is attached at the end of this document.  It contains both source code files (Form5.bas and Form5.rc) and this document in .doc format.

     The Common Dialog Box Library, ComDlg32.dll, is something that you are going to have to take very seriously as a programmer.  It is an important component of Windows, and the standard interfaces it provides to users in terms of enabling them to access their files and resources has contributed greatly to the ascendency of graphical user interfaces in general and of the Windows operating system in particular.  In this tutorial I want to get you started with the Open File Dialog Box, which is certainly one of the most important ones.  Most of the others are just variations on this one.  If you followed along with my Tutorial #1, that is, Form1.bas, you'll find that you are working with the very same concepts here.  Specifically, back in Form1 we started out by trying to get the size of the desktop work area with the SystemParametersInfo() function, and in doing that we had to create a RECT variable rc, and pass that variable to the function by reference (a pointer, in other words) in order for the function to return the size of the desktop to us in that TYPE RECT variable.  

     Towards the end of Form1.bas we encountered a more complicated type, that is a WNDCLASSEX type, and we saw how we could fill out the various fields of this type, and pass the address of a variable of this type to the RegisterClassEx() function, and in that way create a general template for a class from which we could instantiate a real window through a CreateWindowEx() function call.

     In this tutorial we'll be doing just about the exact same thing, but we'll be dealing with different objects.  Instead of a simple RECT struct/type that only contains four fields, ie., nTop, nBottom, etc., we'll be working with the OPENFILENAME struct/type, and that entity contains 20 fields.  Its pretty big.  However, the underlying principles are exactly the same.  We'll pass the runtime address of a variable of the OPENFILENAME type to a function named GetOpenFileName().  When that function gets called it will present an Open File Dialog Box to the user.  This open file dialog box, as well as all the others, are a part of Windows.  We do not have to create them ourselves.  When we call that GetOpenFileName() function Windows will display the dialog box to the user.  When the user selects a file the function will return and indicate to us in a TRUE/FALSE return value whether it has succeeded in obtaining a file name from the user, and if it has that file name will be waiting for us in a buffer pointed to by one of the asciiz pointer members of the struct/type we passed to the function.  Whew!  That was a mouthfull.  I better slow down and fill in the details, right?

     We'll start with the API documentation on the GetOpenFileName() function, which is as follows...


The GetOpenFileName function creates an Open common dialog box that lets the user specify the drive, directory,
and the name of a file or set of files to open.

BOOL GetOpenFileName
  LPOPENFILENAME lpofn   // address of structure with initialization data

lpofn           Pointer to an OPENFILENAME structure that contains information used to initialize the dialog
                 box. When GetOpenFileName returns, this structure contains information about the user's file
Return Values

If the user specifies a filename and clicks the OK button, the return value is nonzero. The buffer pointed to by
the lpstrFile member of the OPENFILENAME structure contains the full path and filename specified by the user.

If the user cancels or closes the Open dialog box or an error occurs, the return value is zero. To get extended
error information, call the CommDlgExtendedError function, which can return one of the following values...

     That looks pretty easy, doesn't it?  Well, it does until you look at the LPOPENFILENAME variable that is suppossed to be passed to the function.  Here it is reproduced below and I've done something you've probably never seen, and that is place the PowerBASIC TYPE declaration right beside the C declaration, for comparison purposes.

--- Code: ---  C struct OPENFILENAME                       PowerBASIC TYPE OPENFILENAME

typedef struct tagOFN {                     TYPE OPENFILENAME
 DWORD         lStructSize;                   lStructSize       AS DWORD
 HWND          hwndOwner;                     hWndOwner         AS DWORD
 HINSTANCE     hInstance;                     hInstance         AS DWORD
 LPCTSTR       lpstrFilter;                   lpstrFilter       AS ASCIIZ PTR
 LPTSTR        lpstrCustomFilter;             lpstrCustomFilter AS ASCIIZ PTR
 DWORD         nMaxCustFilter;                nMaxCustFilter    AS DWORD
 DWORD         nFilterIndex;                  nFilterIndex      AS DWORD
 LPTSTR        lpstrFile;                     lpstrFile         AS ASCIIZ PTR
 DWORD         nMaxFile;                      nMaxFile          AS DWORD
 LPTSTR        lpstrFileTitle;                lpstrFileTitle    AS ASCIIZ PTR
 DWORD         nMaxFileTitle;                 nMaxFileTitle     AS DWORD
 LPCTSTR       lpstrInitialDir;               lpstrInitialDir   AS ASCIIZ PTR
 LPCTSTR       lpstrTitle;                    lpstrTitle        AS ASCIIZ PTR
 DWORD         Flags;                         Flags             AS DWORD
 WORD          nFileOffset;                   nFileOffset       AS WORD
 WORD          nFileExtension;                nFileExtension    AS WORD
 LPCTSTR       lpstrDefExt;                   lpstrDefExt       AS ASCIIZ PTR
 DWORD         lCustData;                     lCustData         AS LONG
 LPOFNHOOKPROC lpfnHook;                      lpfnHook          AS DWORD
 LPCTSTR       lpTemplateName;                lpTemplateName    AS ASCIIZ PTR
}OPENFILENAME;                              END TYPE

--- End code ---
    Taking a high level view I think you would have to say they are pretty similiar.  In C the variable type comes first, then the name of the variable.  In PowerBASIC that's reversed.  In terms of the variable types themselves, some of them are even named the same.  Note the DWORD and WORD types.  Actually, the HWND and HINSTANCE types in Api are just redefinitions of more fundamental C data types such as unsigned int, which is four bytes or 32 bits on most present Windows systems.  These are further redefined  through what is known as a typedef in C, so the types are even more similiar than at first sight.  The major differences seem to be with the LPCTSTR and LPTSTR data types.  These too are data type redefinitions of character pointers, which, if you recall, are the way C represents strings, ie., as arrays of characters.  PowerBASIC represents this 'low-level' idea of a string as an Asciiz Ptr.  Interestingly enough, even though one might hope to somehow obtain what we in BASIC think of as a 'string' from this complicated contrivance, there really aren't any 'strings' in it.  There certainly are piles of pointers to strings but nowhere to be found is a good old fashioned BASIC string.  Actually, they are all either 32 bit or 16 bit integers (mostly 32 bit).  And don't think for a minute that pointers are strings.  They most definitely are not.  They hold (in 32 bit Windows) the 32 bit address of something else.  And it is that 'something else' - a file name string - that we want.

     So how does a type containing only 32 bit integers help us in obtaining a string of characters that represents a file name string?  The answer lies in what those 32 bit Asciiz Ptr or LPTSTR variables are pointing at.  If they can be safely pointed at memory that we have obtained somehow that has enough space to hold the number of characters in a string a user might select with the dialog box, then perhaps we can somehow get at that 'string' and use it in our program.  Providing space for string data is certainly something you have done before in whatever programming you have done to get you this far, but if that programming was in some version of BASIC using BASIC dynamic strings, it may not strictly apply very well to the situation that confronts us here in finding 'room' for a string the user has selected with the dialog box.  Bear with me while I elaborate.

     Suppose you define a variable like so...

     Dim strFileName As String

     By doing that havn't you provided 'space' where a string might be assigned?  Actually not.  Dim is an executable statement that will cause the PowerBASIC compiler to obtain a string handle from Windows that can later be used to allocate storage for a string if an assignment statement later assigns a string to the variable.  But as such, the above Dim statement won't provide any 'space' containing any 'room' for a string.  A dynamic string can only obtain memory space for a string as a result of an assignment statement where the right operand of the statement is a string to be assigned.  For example, if the following statement...

     strFileName = "C:\Code\TASM\bin\"

is encountered in a program, it will cause the compiler to generate code which will call OLE String Engine memory allocation functions that will request that at least 17 bytes of memory be allocated into which the text can be moved and stored.  What these functions will return is a pointer to the memory where the string can be placed. If later the program encounters another program line that causes the string to grow, such as...

     strFileName = strFileName & "Hello.asm"

then the compiler will generate more code to request of Windows that more memory be allocated for the extra bytes.  This further allocation may necessitate releasing the original memory for the first string, and the movement of both strings to a new memory address.  The reason I'm telling you this is that if you looked at the OPENFILENAME type above you might have noticed a particularly auspicious looking variable about midway down in the type like so...

     lpstrFile         AS ASCIIZ PTR

...that certainly looks like it might be a place where we could get a file name string from the GetOpenFileName() function after it returns.  This may lead you to think that somehow the file name string will be stored 'in' that variable, or that a dynamic string you create with a Dim statement can be assigned to that variable in the type like so...

     lpstrFile = strFileName

...and that somehow you can 'pry' a file name string out of it after the function returns.  Well, none of that will work.  If you don't provide memory to store the file name string before you call the GetOpenFileName() function, the program will compile but either crash or corrupt memory somewhere as soon as it tries to store a string of bytes at a wild pointer.  In the second case, if you try to set lpstrFile to strFileName and then compile that, you'll find it won't even compile.  You'll get this compile time error from PowerBASIC...

     The compiler has found a string operand in a position where a numeric operand should be, or a type
     mismatch has been detected.

     This should make it clear in your mind that a pointer to a string isn't a string.  So you may be wondering, "How are we going to use this thing (OPENFILENAME Type) to get a file name if all it contains are numbers?"  Well, if you refer back to the WNDCLASSEX type in Form1.bas you'll see that we were actually confronted with a similiar problem there.  One of the fields of that type was as follows...

     lpszClassName As Asciiz Ptr

     ...and what we did there was define a variable like so...

     Local szClassName As Asciiz*6

     ...and we assigned the name "Form1" to the asciiz string like this...

     szClassName = "Form1"

     ...which then allowed us to assign the address of the string to the Asciiz Ptr member of the type like so...

     wc.lpszClassName = VarPtr(szClassName) 'Simple, but very important!

     You may wish to review this material in Form1.bas if you need to, because it has to be clear in your mind what is happening here if you wish to continue with Api coding techniques.  Due to the importance of these concepts I spent considerable time with it in Form1.bas, even providing C and PowerBASIC versions of console programs showing memory allocation/addressing details.
     What we need to do then in preparation for a call to the GetOpenFileName() function is declare a number of asciiz strings of definite predefined lengths that by their very declaration will cause memory to be allocated into which the GetOpenFileName() function can safely place strings of characters without overwriting memory belonging to this or other processes.  And while you won't be any more successful in assigning these asciiz strings to the Asciiz Ptr OPENFILENAME members requiring addresses than you were with assigning dynamic strings to them, you most certainly will have no difficulties assigning the addresses of these string buffers to the members using the VarPtr() function, which function returns addresses of variables, not their contents.

     Now the amount of space required to store a file name string can't be absolutely known beforehand because you have no control over where the user of your program might navigate to in his/her quest for a file to open.  The only thing that can be done in terms of determining the size of the Asciiz string to declare is to make sure it is large enough to accomodate any string that may exist on the system.  Equates have been defined in the file for PowerBASIC and in Windows.h header file for C and they are generally used in specifying the size of Asciiz character string buffers to declare.  Here are the applicable equates from the PowerBASIC Include file...

--- Code: ---%MAX_PATH  = 260  ' max. length of full pathname
%MAX_DRIVE = 3    ' max. length of drive component
%MAX_DIR   = 256  ' max. length of path component
%MAX_FNAME = 256  ' max. length of file name component
%MAX_EXT   = 256  ' max. length of extension component

--- End code ---

     So the idea is to dimension an Asciiz string buffer like so to hold the file name...

     Local szFileName As Asciiz * %MAX_PATH

     ...and then assign the address of this buffer to the ofn.lpstrFile member like so...

     ofn.lpstrFile=VarPtr(szFileName).   ''''Note the VarPtr() function!!!

     Not wanting to keep you in suspense much longer, reproduced below is the procedure OnOpen(), which routine does the necessary preliminary work to set up the call to GetOpenFileName(), and finally places the chosen file name into a text box on the form (if the user selected a filename and didn't cancel)...

--- Code: ---Sub OnOpen(wea As WndEventArgs)     'Handles File >> Open... From WM_COMMAND
  Local szTitleName As Asciiz*(%MAX_FNAME+%MAX_EXT)
  Local szFileName As Asciiz*%MAX_PATH
  Local szIniDir As Asciiz*256
  Local strFilter As String
  strFilter= _   'Dynamic strings work best here with PowerBASIC as you can embed NULLs.
  "bas files (*.bas), inc files (*.inc), rc files (*.rc)"+Chr$(0)+"*.bas;*.inc;*.rc"+Chr$(0)+ _
  "c files (*.c), cpp files (*.cpp), h files (*.h)"+Chr$(0)+"*.c;*.cpp;*.h"+Chr$(0)+ _
  "txt files (*.txt)"+Chr$(0)+"*.txt"+Chr$(0)+Chr$(0)
  szIniDir=CurDir$                                     'Initial directory
  ofn.lStructSize=SizeOf(OPENFILENAME)                 'Size of structure/type
  ofn.hWndOwner=Wea.hWnd                               'Owned by Main Program Window
  ofn.hInstance=GetWindowLong(wea.hWnd,%GWL_HINSTANCE) 'The instance handle can be gotten many ways
  ofn.lpstrFilter=StrPtr(strFilter)                    'Filters file extensions shown in dialog box
  ofn.lpstrInitialDir = VarPtr(szIniDir)
  ofn.lpstrFile=VarPtr(szFileName)                     'This is finally the file name string you want
  ofn.lpstrFileTitle=VarPtr(szTitleName)               'This is as above, but without the full path
  If GetOpenFileName(ofn) Then                         'Function returns non-zero if valid file chosen
     Call SetWindowText(GetDlgItem(Wea.hWnd,%IDC_FILENAME),szFileName)
     MsgBox("You Must Have Cancelled!")
  End If
End Sub

--- End code ---

     Now that I've gone to such great lengths to impress upon you the necessity of using Asciiz strings of predefined lengths so as to provide buffers into which the GetOpenFileName() function can copy strings, I'd better do some fast explaining about strFilter in the above procedure.  You are no doubt aware of how the filters work in the 'Files of Type' ComboBox in the Open File Dialog Box.  The way they are actually stored in preparation to assignment in the ofn.lpstrFilter member of the OPENFILENAME type is as two dimensional arrays of null terminated strings, with a double null terminator at the end.  In the above example you can think of it something like this...

--- Code: ---     "bas files (*.bas), inc files (*.inc), rc files (*.rc)" + Chr$(0) ,       "*.bas;*.inc;*.rc" + Chr$(0)
     "c files (*.c), cpp files (*.cpp), h files (*.h)"       + Chr$(0) ,       "*.c;*.cpp;*.h"    + Chr$(0)
     "txt files (*.txt)"                                     + Chr$(0) ,       "*.txt"            + Chr$(0) + Chr$(0)

--- End code ---
    Using PowerBASIC I don't seem to have success in storing these things in Asciiz string buffers because PowerBASIC ends the assignment to the buffer upon encountering the first null byte after the 'rc files (*.rc)' above in the first line. The end result is that the Open File Dialog Box works OK except there is no file filter and all the files are displayed.  Perhaps there may be other ways to solve this but what I do is assign the filter strings to a dynamic string (which can hold null bytes) and use the StrPtr() function to assign the address of that string buffer to the lpstrFilter pointer member of the Type.  This is satisfactory because the assignment of the strings to the dynamic string variable strFilter triggers a memory allocation into which the strings can comfortably be stored.  And the StrPtr() function will return the address of that string.  I seem to vaguely recall using another technique with PowerBASIC years ago that somehow involved the Remove$ statement, but the details now escape me.  In any case, this technique I am showing you is workable and easy.

     As you can see above one of the fields of the Type is the initial directory that is to be shown when the dialog box opens.  That can be set to the directory in which the program is first run by using the PowerBASIC CurDir$ keyword.

     As you can see toward the bottom of the function, if the GetOpenFileName() function returns a non zero number the file name string is copied to the text box in the middle of the Form.  The SetWindowText() function has two parameters, the first being the handle of the window into which the text is to be 'Set', and the second is a pointer to a buffer containing the desired text.  They don't let me use global variables around here so when I created the text box in fnWndProc_OnCreate() the handle of the text box was stored in a local variable which got thrown away and lost just as soon as the text box was created and the function exited.  So the only way we can get the handle of the text box back is to call GetDlgItem() and pass to it the handle to the text box's parent and the control identifier of the text box, which luckily we do know (there's more than one way to skin a cat!).  So all is not lost.  With the handle to the text box reclaimed we can set the file name in it.
     One final point I might make is that those buffers we painstakingly created above to create space for the GetOpenFileName() function to place strings into won't last very long.  They are all local variables stored on the stack, so as soon as the Open File Dialog closes and OnOpen() exits they become totally invalid and are gone.  However, at the very, very last moment of their existance their content is copied into the buffer provided by the text box - and so the string lives on!  It will safely exist there in the memory provided by the text box until you replace it with something else or you end the program.  Although I can't do it here without censure, it is not uncommon in real life applications to dimension the OPENFILENAME variable globally, or at least statically, so that avoids these problems.

     Here is the full text of Form5.bas.  First is the resource file, Form5.rc, then the source code file for Form5.bas.  If you need assistance compiling the resource file please refer to directions at the top of Form4.bas at

In a nut shell though, all you have to do is paste Form5.rc into a blank PBEdit window and compile it.  The compiler will first generate a Form5.res and then a Form5.pbr file.  The Form5.pbr file is then compiled into the executable when you compile Form5.bas.  All these files are attached.

--- Code: ---//Form5.rc
#include "resource.h"
#define IDM_OPEN        1000
#define IDM_SAVE        1002
#define IDM_EXIT        1004
#define IDM_SHOWDLG     2000
#define IDC_NAME        2100
#define IDM_HELP        3000

Form5 MENU
 POPUP "&File"
  MENUITEM "&Open...",               IDM_OPEN
  MENUITEM "&Save...",               IDM_SAVE
  MENUITEM "E&xit",                  IDM_EXIT
 POPUP "O&ptions"
  MENUITEM "Sho&w Dialog",           IDM_SHOWDLG
 POPUP "&Help"
  MENUITEM "&Help...",               IDM_HELP

dlgBox DIALOG DISCARDABLE 30,90,340,40
CAPTION "What Do You Want To Do With Your Life?"
FONT 8, "MS Sans Serif"
  DEFPUSHBUTTON  "OK",IDOK,200,20,40,14
  PUSHBUTTON     "Cancel",IDCANCEL,145,20,40,14
  LTEXT          "In 80 Characters Or Less",IDC_STATIC,3,6,110,8

--- End code ---

And here is Form5.bas...

--- Code: ---#Compile Exe
#Include ""     'Main Windows Include File
#Include ""     'Include file for the Common Dialog Box Library
#Resource "Form5.pbr"       'PowerBASIC resource file
%IDM_OPEN      =    1000    'Equate for File    >> Open...
%IDM_SAVE      =    1002    '                   >> Save...
%IDM_EXIT      =    1004    '                   >> Exit
%IDM_SHOWDLG   =    2000    'Equate for Options >> Show Dialog
%IDM_HELP      =    3000    'Equate for Help    >> Help...          
%IDC_FILENAME  =    4000    'Equate for Control ID of main text box on center of Form
%IDC_EXIT      =    4002    'Equate for Exit Button on Form
%IDC_NAME      =    2100    'Equate for Textbox on Dialog Box

Type WndEventArgs           'Type for passing window procedure parameters.  Allows me
  wParam As Long            'to shorten parameter list.  .NET does this all the time.
  lParam As Long            'See, for example pea for PaintEventArgs in the OnPaint()
  hWnd   As Dword           'Message Handler.
End Type

Function fnWndProc_OnCreate(wea As WndEventArgs) As Long   'Handles WM_CREATE message
  Local hCtrl As Dword

  'Text Box For Filename or Dialog Box Text
  hCtrl= _
  CreateWindowEx _
  ( _
    %WS_EX_CLIENTEDGE, _     'This gives a text box a nice 3D look
    "Edit", _                'Predefined 'edit' window class
    "", _                    'start out blank
    5,20,505,26, _           'edit controls have piles of styles
    Wea.hWnd, _              'parent of edit control, that is main window
    %IDC_FILENAME, _         'equate for control identifier
    GetWindowLong(wea.hWnd,%GWL_HINSTANCE), _
    Byval 0 _                'lpCreateParams -- we're not using any here

  'Exit Button
  CreateWindowEx _
  ( _
    0, _                     'Plain old button will work for me
    "Button", _              'Predefined 'edit' window class
    "Exit", _                'Let's make an exit button
    %WS_CHILD Or %WS_VISIBLE, _     'This works for simple buttons
    205,60,110,26, _                'coordinates & sizes of button
    Wea.hWnd, _                     'Parent of button
    %IDC_Exit, _                    'Equate for button (ctrl id)
    GetWindowLong(wea.hWnd,%GWL_HINSTANCE), _   'Get instance handle
    Byval 0 _                       'No Creation Data

  fnWndProc_OnCreate=0              'Return 0 to Windows
End Function

Sub OnOpen(wea As WndEventArgs)     'Handles File >> Open... From WM_COMMAND
  Local szTitleName As Asciiz*(%MAX_FNAME+%MAX_EXT)
  Local szFileName As Asciiz*%MAX_PATH
  Local szIniDir As Asciiz*256
  Local strFilter As String
  strFilter= _   'Dynamic strings work best here with PowerBASIC as you can embed NULLs.
  "bas files (*.bas), inc files (*.inc), rc files (*.rc)"+Chr$(0)+"*.bas;*.inc;*.rc"+Chr$(0)+ _
  "c files (*.c), cpp files (*.cpp), h files (*.h)"+Chr$(0)+"*.c;*.cpp;*.h"+Chr$(0)+ _
  "txt files (*.txt)"+Chr$(0)+"*.txt"+Chr$(0)+Chr$(0)
  szIniDir=CurDir$                                     'Initial directory
  ofn.lStructSize=SizeOf(OPENFILENAME)                 'Size of structure/type
  ofn.hWndOwner=Wea.hWnd                               'Owned by Main Program Window
  ofn.hInstance=GetWindowLong(wea.hWnd,%GWL_HINSTANCE) 'The instance handle can be gotten many ways
  ofn.lpstrFilter=StrPtr(strFilter)                    'Filters file extensions shown in dialog box
  ofn.lpstrInitialDir = VarPtr(szIniDir)
  ofn.lpstrFile=VarPtr(szFileName)                     'This is finally the file name string you want
  ofn.lpstrFileTitle=VarPtr(szTitleName)               'This is as above, but without the full path
  If GetOpenFileName(ofn) Then                         'Function returns non-zero if valid file chosen
     Call SetWindowText(GetDlgItem(Wea.hWnd,%IDC_FILENAME),szFileName)
     MsgBox("You Must Have Cancelled!")
  End If
End Sub

Sub OnSave(wea As WndEventArgs)                         'Handles File >> Save... From WM_COMMAND
  MsgBox("You Clicked File >> Save...")
End Sub

Sub OnExit(wea As WndEventArgs)                         'Handles File >> Exit From WM_COMMAND
  MsgBox("Send A Message To Windows To Close The Application.")
  Call SendMessage(wea.hWnd,%WM_CLOSE,0,0)
End Sub

Function ModalDlgProc(ByVal hwndDlg As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  Local szLocalBuffer As Asciiz*80
  Local hwndEdit As Dword

  Select Case As Long wMsg
      Call SetFocus(hwndEdit)                                 'Set focus to text box when
      Function=%FALSE                                         'dialog box opens
    Case %WM_COMMAND
      Select Case Lowrd(wParam)
        Case %IDOK
          hwndEdit=GetDlgItem(hwndDlg,%IDC_NAME)              'GetDlgItem() is a really useful function that plays
          Call GetWindowText(hwndEdit,szLocalBuffer,80)       'a big part in allowing you to eliminate globals
          Call SetWindowText _
          ( _
            GetDlgItem(GetParent(hwndDlg),%IDC_FILENAME), _   'Get parent of dialog box, then
            szLocalBuffer _                                   'control id of text box on main
          )                                                   'form into which to place text.
          Call EndDialog(hwndDlg,%TRUE)                       'EndDialog() ends the dialog box.  If %TRUE is
          Function=%TRUE                                      'returned the user hit the [ENTER] button or clicked
          Exit Function                                       'OK.  If the user cancels or x's out return %FALSE
        Case %IDCANCEL                                        'as the 2nd parameter of EndDialog().
          Call EndDialog(hwndDlg,%FALSE)
          Exit Function
      End Select
  End Select

End Function

Sub OnShowDialog(wea As WndEventArgs)
  If DialogBox(GetWindowLong(wea.hWnd,%GWL_HINSTANCE),"dlgBox",wea.hWnd,CodePtr(ModalDlgProc)) Then
     MsgBox _                                                     'DialogBox() is the function that
     ( _                                                          'causes a dialog box to be shown.
       "You Clicked The 'OK' Button Or Pressed The [ENTER] Key!" _'The Function takes four parameters.
     )                                                            'The first (retrieved here by
  Else                                                            'GetWindowLong() Is the program
     MsgBox _                                                     'instance handle.  The second Is
     ( _                                                          'the String which names the Dialog
       "You Cancelled Out!" _                                     'Box In Form5.rc. The third is the
     )                                                            'parent window handle, and the
  End If                                                          'fourth is a Pointer To the Dialog
End Sub                                                           'Box procedure.

Sub OnHelp(wea As WndEventArgs)
  MsgBox("There Is No Help!")
End Sub

Function fnWndProc_OnCommand(wea As WndEventArgs) As Long     'Handles WM_COMMAND message
  Select Case LoWrd(wea.wParam)
    Case %IDM_OPEN
      Call OnOpen(wea)
    Case %IDM_SAVE
      Call OnSave(wea)
    Case %IDM_EXIT
      Call OnExit(wea)
    Case %IDC_EXIT
      Call OnExit(wea)
      Call OnShowDialog(wea)
    Case %IDM_HELP
      Call OnHelp(wea)
  End Select

End Function

Function fnWndProc_OnClose(wea As WndEventArgs) As Long       'Handles WM_CLOSE message
  MsgBox _
  ( _
    "fnWndProc_OnClose() Is A Good Place To Prompt User If Changes" & Chr$(13) & Chr$(10) & _
    "Should Be Saved And To Clean Stuff Up." _
  Call DestroyWindow(wea.hWnd)

End Function

Function fnWndProc_OnDestroy(wea As WndEventArgs) As Long     'Handles WM_DESTROY message
  Call PostQuitMessage(0)
End Function

Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
  Local wea As WndEventArgs

  Select Case wMsg
    Case %WM_CREATE
      wea.wParam=wParam: wea.lParam=lParam: wea.hWnd=hWnd
      Exit Function
    Case %WM_COMMAND
      wea.wParam=wParam: wea.lParam=lParam: wea.hWnd=hWnd
      Exit Function
    Case %WM_CLOSE
      wea.wParam=wParam: wea.lParam=lParam: wea.hWnd=hWnd
      Exit Function
    Case %WM_DESTROY
      wea.wParam=wParam: wea.lParam=lParam: wea.hWnd=hWnd
      Exit Function
  End Select

End Function

Function WinMain(ByVal hIns As Long, ByVal hPrev As Long, ByVal lpCmdLn As Asciiz Ptr, ByVal iShow As Long) As Long
  Local winclass As WndClassEx
  Local szAppName As Asciiz*8
  Local hMainWnd As Dword
  Local Msg As tagMsg

  szAppName="Form5"                                       : winclass.cbSize=SizeOf(winclass)
  winclass.lpszClassName=VarPtr(szAppName)                : winclass.lpfnWndProc=CodePtr(fnWndProc) Or %CS_VREDRAW               : winclass.hInstance=hIns
  winclass.cbClsExtra=0                                   : winclass.cbWndExtra=0
  winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)  : winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)
  winclass.hbrBackground=%COLOR_BTNFACE+1                 : winclass.lpszMenuName=VarPtr(szAppName)
  Call RegisterClassEx(winclass)
  hMainWnd=CreateWindowEx(0,szAppName,"Form5",%WS_OVERLAPPEDWINDOW,350,300,525,155,0,0,hIns,ByVal 0)
  Call ShowWindow(hMainWnd,iShow)
  Call UpdateWindow(hMainWnd)
  While GetMessage(Msg,%NULL,0,0)
    Call TranslateMessage(Msg)
    Call DispatchMessage(Msg)

End Function

--- End code ---

Kent Sarikaya:
Fred thanks you for these really fine articles. Now that I am getting a little familiar with PowerBasic and able to dive deeper into areas, I am finding your articles along with all the other great content on this forum to be invaluable. Thanks!

Frederick J. Harris:
Your welcome Kent.  Glad you're finding them useful!

Paul Breen:
Very nicely done, and even though I've been able to get the open file dialog to work, I still learned something. Any chance of showing how one "hooks" into this common dialog to customize it?
It has taken me a long time to come around, but the straight sdk mode is the best way to go especially with help like this. Showing the c and the basic declaration code was a nice touch.

Frederick J. Harris:
I don't have anything written up yet on hooking the dialog boxes.  Perhaps that would be a good topic.  However, over the years I've seen that topic come up occasionally on the PB Forum.  I expect a search there would bring up some posts/demos of that.


[0] Message Index

[#] Next page

Go to full version