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…
http://www.jose.it-berater.org/smfforum/index.php?topic=1243.0and 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 - Form5.zip - 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...
GetOpenFileName
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
);
Parameters
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
selection.
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.
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
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 Win32Api.inc 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...
%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
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)...
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
Local ofn As OPENFILENAME
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.nMaxFile=%MAX_PATH
ofn.nMaxFileTitle=%MAX_FNAME+%MAX_EXT
ofn.lpstrInitialDir = VarPtr(szIniDir)
ofn.lpstrDefExt=%NULL
ofn.Flags=%OFN_HIDEREADONLY Or %OFN_CREATEPROMPT
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
ofn.lpstrTitle=%NULL
If GetOpenFileName(ofn) Then 'Function returns non-zero if valid file chosen
Call SetWindowText(GetDlgItem(Wea.hWnd,%IDC_FILENAME),szFileName)
Else
MsgBox("You Must Have Cancelled!")
End If
End Sub
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...
"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)
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
http://www.jose.it-berater.org/smfforum/index.php?topic=1251.0In 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.
//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 SEPARATOR
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
STYLE WS_POPUP | WS_CAPTION | WS_DLGFRAME
CAPTION "What Do You Want To Do With Your Life?"
FONT 8, "MS Sans Serif"
BEGIN
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
EDITTEXT IDC_NAME,85,4,250,12,ES_AUTOHSCROLL
END
And here is Form5.bas...
#Compile Exe
#Include "Win32api.inc" 'Main Windows Include File
#Include "Comdlg32.inc" '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
%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER Or %ES_AUTOHSCROLL, _
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
hCtrl=_
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
Local ofn As OPENFILENAME
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.nMaxFile=%MAX_PATH
ofn.nMaxFileTitle=%MAX_FNAME+%MAX_EXT
ofn.lpstrInitialDir = VarPtr(szIniDir)
ofn.lpstrDefExt=%NULL
ofn.Flags=%OFN_HIDEREADONLY Or %OFN_CREATEPROMPT
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
ofn.lpstrTitle=%NULL
If GetOpenFileName(ofn) Then 'Function returns non-zero if valid file chosen
Call SetWindowText(GetDlgItem(Wea.hWnd,%IDC_FILENAME),szFileName)
Else
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
Case %WM_INITDIALOG
hwndEdit=GetDlgItem(hwndDlg,%IDC_NAME)
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)
Function=%TRUE
Exit Function
End Select
End Select
Function=%FALSE
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)
Case %IDM_SHOWDLG
Call OnShowDialog(wea)
Case %IDM_HELP
Call OnHelp(wea)
End Select
fnWndProc_OnCommand=0
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)
fnWndProc_OnClose=0
End Function
Function fnWndProc_OnDestroy(wea As WndEventArgs) As Long 'Handles WM_DESTROY message
Call PostQuitMessage(0)
fnWndProc_OnDestroy=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
fnWndProc=fnWndProc_OnCreate(wea)
Exit Function
Case %WM_COMMAND
wea.wParam=wParam: wea.lParam=lParam: wea.hWnd=hWnd
fnWndProc=fnWndProc_OnCommand(wea)
Exit Function
Case %WM_CLOSE
wea.wParam=wParam: wea.lParam=lParam: wea.hWnd=hWnd
fnWndProc=fnWndProc_OnClose(wea)
Exit Function
Case %WM_DESTROY
wea.wParam=wParam: wea.lParam=lParam: wea.hWnd=hWnd
fnWndProc=fnWndProc_OnDestroy(wea)
Exit Function
End Select
fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
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)
winclass.style=%CS_HREDRAW 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)
Wend
Function=msg.wParam
End Function