Paul Squires's FireFly 3 can be used to create this BoxVolume/Form3 application and the
use of it for that purpose is really interesting.  Essentially you have a Visual Basic
like experience where very many things are handled by the FireFly Visual Designer/IDE; 
especially those things related to building the interface and making everything work; 
what I've been referring to as the internal plumbing or wiring of the application.  
Specificaly, and with reference to our BoxVolume program, the types of things that you
don't have to do when making the project in FireFly are as follows;

1)  You don't have to do any registering of Window Classes or CreateWindow() calls;

2)  You don't have to connect message handlers to Window Procedures using any kind
    of function pointer or switch/select case logic.  In fact, you don't have to
    deal with WinMain() or WndProc() at all if you don't want to;

What is left that you do have to do (after you've used the FireFly Visual Designer to
create your user interface) is write the code for the event procedures that calculate
and display the box's volume.  In fact, here is the totality of the code I wrote to
create an exact duplicate of our little project in FireFly.  You should be able to 
easily figure out how the auto-generated procedure names relate to my C++ and PowerBASIC
project's procedure names...

First BoxVolume.inc
[CODE]
Type CBox
  dblLength  As Double
  dblWidth   As Double
  dblHeight  As Double
  dblVolume  As Double
  szBoxType  As Asciiz*16
End Type
[/CODE]


Then the main program module - frmBoxVolume.frm
[CODE]
Function frmBoxVolume_WM_CREATE(hWndForm As Dword, ByVal UserData As Long) As Long
  Local szBuffer() As Asciiz*16
  Register i As Long
  
  ReDim szBuffer(4) As Asciiz*16
  szBuffer(0)="Ordinary Box"
  szBuffer(1)="Box Of Junk"
  szBuffer(2)="Beer Crate"
  szBuffer(3)="Wine Box"
  szBuffer(4)="Candy Box"
  For i=0 To 4
    Call SendMessage(HWND_FRMBOXVOLUME_CBOTYPE,%CB_INSERTSTRING,-1,VarPtr(szBuffer(i)))
  Next i                                
  SendMessage(HWND_FRMBOXVOLUME_CBOTYPE,%CB_SETCURSEL,0,0)
  
  Function=0
End Function


Function frmBoxVolume_btnCalculate_BN_CLICKED(ControlIndex As Long, hWndForm As Dword, hWndControl As Dword,idButtonControl As Long) As Long
  Local szBuffer As Asciiz*64, szBoxType As Asciiz*16
  Local bx As CBox
  
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTLENGTH),szBuffer,64)
  bx.dblLength=Val(szBuffer)
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTWIDTH),szBuffer,64)
  bx.dblWidth=Val(szBuffer)
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTHEIGHT),szBuffer,64)
  bx.dblHeight=Val(szBuffer)
  bx.dblVolume = bx.dblLength * bx.dblWidth * bx.dblHeight
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_CBOTYPE),szBoxType,16)
  szBuffer="The Volume Of Your " & szBoxType & " Is " & Str$(bx.dblVolume) & "."
  Call SetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTVOLUME),szBuffer)                                              

  Function=0
End Function


Function frmBoxVolume_btnDisplay_BN_CLICKED(ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idButtonControl As Long) As Long
  Local szBuffer As Asciiz*64, szBoxType As Asciiz*16
  Local bx As CBox
  
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTLENGTH),szBuffer,64)
  bx.dblLength=Val(szBuffer)
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTWIDTH),szBuffer,64)
  bx.dblWidth=Val(szBuffer)
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTHEIGHT),szBuffer,64)
  bx.dblHeight=Val(szBuffer)
  bx.dblVolume = bx.dblLength * bx.dblWidth * bx.dblHeight
  Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_CBOTYPE),bx.szBoxType,16)
  Call frmOutput_Show(0,%FALSE,VarPtr(bx))
  
  Function=0
End Function


Function frmBoxVolume_WM_CLOSE(hWndForm As Dword) As Long
  Local hOutput As Dword
  
  Do 
     hOutput=FindWindowEx(0,0,"FORM_BOXVOLUME_FRMOUTPUT_CLASS","Output Screen")
     If hOutput Then
        Call SendMessage(hOutput,%WM_CLOSE,0,0)
     Else
        Exit Do
     End If   
  Loop
  
  Function=0
End Function
[/CODE]


Then the code for the Output Screen - frmOutput.frm
[CODE]
Function frmOutput_WM_CREATE (hWndForm As Dword, ByVal UserData As Long) As Long
  Local ptrBox As CBox Ptr, ptrOutputBox As CBox Ptr
      
  ptrBox=UserData
  ptrOutputBox=GlobalAlloc(%GPTR,SizeOf(CBox))
  If ptrOutputBox Then
     Type Set @ptrOutputBox = @ptrBox 
     Call SetProp(hWndForm,"Our Box",ptrOutputBox) 
  Else
     Function=-1   
  End If  
  
  Function=0
End Function


Function frmOutput_WM_PAINT(hWndForm As Dword) As Long
  Local szBuffer As Asciiz*64
  Local strField As String*8
  Local hFont,hTmp As Dword
  Local ptrBox As CBox Ptr
  Local ps As PAINTSTRUCT
  Local hDC As Dword
  
  hDC=BeginPaint(hWndForm,ps)
  hFont=CreateFont _
  ( _
    20,0,0,0,%FW_BOLD,0,0,0,%ANSI_CHARSET, _
    %OUT_DEFAULT_PRECIS,%CLIP_DEFAULT_PRECIS, _
    %PROOF_QUALITY,%DEFAULT_PITCH,"Courier New" _
  )
  hTmp=SelectObject(hDC,hFont)
  ptrBox=GetProp(hWndForm,"Our Box")
  szBuffer="Box Type     = " & @ptrBox.szBoxType
  TextOut(hDC,40,20,szBuffer,Len(szBuffer))
  RSet strField=Format$(@ptrBox.dblLength,"###0.#0")
  szBuffer="Length       = " & strField
  TextOut(hDC,40,40,szBuffer,Len(szBuffer))
  RSet strField=Format$(@ptrBox.dblWidth,"###0.#0")
  szBuffer="Width        = " & strField
  TextOut(hDC,40,60,szBuffer,Len(szBuffer))
  RSet strField=Format$(@ptrBox.dblHeight,"###0.#0")
  szBuffer="Height       = " & strField
  TextOut(hDC,40,80,szBuffer,Len(szBuffer))
  RSet strField=Format$(@ptrBox.dblVolume,"####0.#0")
  szBuffer="Volume       = " & strField
  TextOut(hDC,40,100,szBuffer,Len(szBuffer))
  Call SelectObject(hDC,hTmp) 
  Call DeleteObject(hFont)
  Call EndPaint(hWndForm,ps)
                            
  Function=0
End Function


Function frmOutput_WM_CLOSE(hWndForm As Dword) As Long
  Local pBox As CBox Ptr
  
  pBox=RemoveProp(hWndForm,"Our Box")
  If pBox Then
     Call GlobalFree(pBox)
  End If       
    
  Function=0
End Function
[/CODE]

That's it!  Compare Function frmBoxVolume_WM_CREATE() with either the fnWndProc_OnCreate()
of the PowerBASIC or C++ version and you'll see how much shorter it is.  All the 
CreateWindow() and RegisterClassEx() calls are gone.  That is taken care of by designer,
which, by the way, does create/auto-generate the underlying Api code in a form you can 
examine if you wish.  However, the point is I suppose - you don't have to. 

Just a few quick comments on naming objects.  When I created the user interface in FireFly
I named the Length text box txtLength - which would have been a typical VB usage.  The main
program Window/Dialog/Form I named frmBoxVolume.  So if you look in...

Function frmBoxVolume_btnCalculate_BN_CLICKED()

...which is the event procedure FireFly auto-generated for clicking the 'Calculate' button
on the main form, you'll see this code to get the box length out of the txtLength text box...

Call GetWindowText(GetDlgItem(hWndForm,IDC_FRMBOXVOLUME_TXTLENGTH),szBuffer,64)

What you see there is that FireFly created the equate with the IDC_ prefix, then added the
name of the form to that - frmBoxVolume, then finally added the txtLength name of the text
box to produce the whole equate. FireFly has a dialog 'Handles And Control Ids' on its View
Menu where you can easily get your equates/handles for everything in your application and
get them inserted into your code where you need them.

This is a really nice way to program.  I'll attach all the FireFly 3 files for this project.
