En el anterior artículo de Classic PowerBasic gratis, BiaNamaran me sugería hacer un pequeño tutorial tipo “Hello World” explicando la filosofía de trabajo de PowerBasic for Windows (PB/Win), y PowerBasic Forms (PB/Forms), unas herramientas que pese a estar valoradas en 80$, podéis conseguir de manera gratuita.

Asumiendo que ya hayáis instalado PBWin y PBForms, cosa que podrás hacer incluso en un equipo con Windows 2000 y 32 Mb. de memoria RAM, empezamos con esta pequeña guía paso a paso.

Abrimos PBFORMS.EXE, que es la herramienta de diseño de ventanas. Procedemos a dibujar visualmente nuestro diálogo, no tiene mucho misterio. Vamos colocando componentes, hasta disponer de un label y un botón, y listos.

Desde PB/Forms, asignamos las propiedades a cada componente, el diálogo en si, el botón, y el label o etiqueta, así ajustándolos a nuestras necesidades.

Al guardarlo, nos generará un código PowerBasic, que podemos abrir directamente con el editor de PB/Win (PBEDIT.EXE).

Podemos irnos directamente a Run > Compile and Execute (CTRL-E), para verificar que todo ha ido correctamente. Deberemos ver el diálogo que justo hemos creado.

Si pulsamos sobre el botón de Cerrar, veremos que el programa muestra un MessageBox. Esto es porque PB/Forms lo ha insertado en el código, que deberemos modificar a nuestro gusto.

En este punto, la primera virguería que vemos, es que fiel a sus orígenes, el ejecutable, ocupa tan solo 28.160 bytes. Hablamos de un ejecutable standalone, que no necesita ni de frameworks, ni de librerías externas, ni de DLL de terceros.

Podemos comprobar, que ha generado una enormidad de código, pero no os asustéis. Son más de 100 lineas, pero porque contienen muchos comentarios para guiarnos y separar secciones:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#PBFORMS Created v1.51
'------------------------------------------------------------------------------
' The first line in this file is a PB/Forms metastatement.
' It should ALWAYS be the first line of the file. Other   
' PB/Forms metastatements are placed at the beginning and 
' end of "Named Blocks" of code that should be edited     
' with PBForms only. Do not manually edit or delete these 
' metastatements or PB/Forms will not be able to reread   
' the file correctly.  See the PB/Forms documentation for 
' more information.                                       
' Named blocks begin like this:    #PBFORMS BEGIN ...     
' Named blocks end like this:      #PBFORMS END ...       
' Other PB/Forms metastatements such as:                  
'     #PBFORMS DECLARATIONS                               
' are used by PB/Forms to insert additional code.         
' Feel free to make changes anywhere else in the file.    
'------------------------------------------------------------------------------
 
#Compile Exe
#Dim All
 
'------------------------------------------------------------------------------
'   ** Includes **
'------------------------------------------------------------------------------
#PBFORMS Begin Includes 
#If NOT %DEF(%WINAPI)
	#Include "WIN32API.INC"
#EndIf
#PBFORMS END Includes
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Constants **
'------------------------------------------------------------------------------
#PBFORMS Begin Constants 
%IDD_DLGEJEMPLO      =  101
%IDC_LBLINSTRUCTIONS = 1003
%IDC_BTNCERRAR       = 1002
#PBFORMS END Constants
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Declarations **
'------------------------------------------------------------------------------
DECLARE CallBack FUNCTION ShowDLGEJEMPLOProc()
DECLARE FUNCTION ShowDLGEJEMPLO(BYVAL hParent AS Dword) AS LONG
#PBFORMS Declarations
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Main Application Entry Point **
'------------------------------------------------------------------------------
FUNCTION PbMain()
	ShowDLGEJEMPLO %HWND_DESKTOP
END FUNCTION
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** CallBacks **
'------------------------------------------------------------------------------
CallBack FUNCTION ShowDLGEJEMPLOProc()
 
	SELECT CASE AS LONG CbMsg
		CASE %WM_INITDIALOG
			' Initialization handler
 
		CASE %WM_NCACTIVATE
			STATIC hWndSaveFocus AS DWord
			IF IsFalse CbWParam THEN
				' Save control focus
				hWndSaveFocus = GetFocus()
			ELSEIF hWndSaveFocus THEN
				' Restore control focus
				SetFocus(hWndSaveFocus)
				hWndSaveFocus = 0
			END IF
 
		CASE %WM_COMMAND
			' Process control notifications
			SELECT CASE AS LONG CbCtl
				CASE %IDC_BTNCERRAR
					IF CbCtlMsg = %BN_CLICKED OR CbCtlMsg = 1 THEN
						MsgBox "%IDC_BTNCERRAR=" + Format$(%IDC_BTNCERRAR), _
							%MB_TASKMODAL
					END IF
 
				CASE %IDC_LBLINSTRUCTIONS
 
			END SELECT
	END SELECT
END FUNCTION
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Dialogs **
'------------------------------------------------------------------------------
FUNCTION ShowDLGEJEMPLO(BYVAL hParent AS Dword) AS LONG
	LOCAL lRslt AS LONG
 
#PBFORMS Begin Dialog %IDD_DLGEJEMPLO->->
	LOCAL hDlg  AS Dword
 
	Dialog New hParent, "Ejemplo PBForms", 188, 216, 201, 121, TO hDlg
	Control Add Button, hDlg, %IDC_BTNCERRAR, "&Cerrar", 75, 96, 50, 15
	Control Add Label,  hDlg, %IDC_LBLINSTRUCTIONS, "Click en Cerrar para " + _
		"cerrar", 0, 0, 200, 80
#PBFORMS END Dialog
 
	Dialog Show Modal hDlg, CALL ShowDLGEJEMPLOProc TO lRslt
 
#PBFORMS Begin CleanUp %IDD_DLGEJEMPLO
#PBFORMS END CleanUp
 
	FUNCTION = lRslt
END FUNCTION
'------------------------------------------------------------------------------

Ahora añadiremos algunas metainstrucciones, que se encargan en el caso de PowerBasic de modificar el compilador, y el preprocesador, agregaremos estas tres, con el fin de generar el código más veloz posible:

#OPTIMIZE SPEED
#REGISTER ALL
#ALIGN 16

El primer nos dice que queremos que se optimice el código generado, en este caso para velocidad (SPEED), pero podría ser también para tamaño (SIZE). La segunda induce al compilador a utilizar registros del procesador para almacenar variables siempre que sea posible, de forma automática. Los registros, son mucho más rápidos que la memoria. Por último, forzamos que todas las estructuras estén alineadas en memoria usando párrafos (16 bytes). Esto hace que su acceso sea más veloz en procesadores modernos.

Después haremos una tontería, que consiste en cambiar el texto del label en tiempo de ejecución. Lo suyo hubiera sido generarlo ya con ese texto desde PB/Forms, pero así vemos la operativa básica con los componentes o controles. En la función ShowDLGEJEMPLO, que es la que se invoca al mostrar el diálogo, fuera del código generado por PBFORMS, añadiremos lo siguiente:

CONTROL SET TEXT hDlg, %IDC_LBLINSTRUCTIONS, "Bienvenido a PB/Forms"

Por último, nos queda cerrar efectivamente la ventana o diálogo cuando el usuario pulse el botón. Para ello, donde estaba el MessageBox original, agregamos el código:

DIALOG END CBHNDL, 0

Volvemos a ejecutar nuestro programa, y obtenemos el resultado esperado. El texto del label ha cambiado, y pulsando en Cerrar, cerramos la ventana, y el programa.

El resultado, de toda esta funcionalidad de ejemplo, es un .EXE de 27.136 bytes, y del mismo modo que cuando empezamos, totalmente autocontenido, que ha quedado de esta manera:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#PBFORMS Created v1.51
'------------------------------------------------------------------------------
' The first line in this file is a PB/Forms metastatement.
' It should ALWAYS be the first line of the file. Other
' PB/Forms metastatements are placed at the beginning and
' end of "Named Blocks" of code that should be edited
' with PBForms only. Do not manually edit or delete these
' metastatements or PB/Forms will not be able to reread
' the file correctly.  See the PB/Forms documentation for
' more information.
' Named blocks begin like this:    #PBFORMS BEGIN ...
' Named blocks end like this:      #PBFORMS END ...
' Other PB/Forms metastatements such as:
'     #PBFORMS DECLARATIONS
' are used by PB/Forms to insert additional code.
' Feel free to make changes anywhere else in the file.
'------------------------------------------------------------------------------
 
#COMPILE EXE
#OPTIMIZE SPEED
#REGISTER ALL
#ALIGN 16
#DIM ALL
 
'------------------------------------------------------------------------------
'   ** Includes **
'------------------------------------------------------------------------------
#PBFORMS Begin Includes
#IF NOT %DEF(%WINAPI)
    #INCLUDE "WIN32API.INC"
#ENDIF
#PBFORMS END Includes
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Constants **
'------------------------------------------------------------------------------
#PBFORMS Begin Constants
%IDD_DLGEJEMPLO      =  101
%IDC_LBLINSTRUCTIONS = 1003
%IDC_BTNCERRAR       = 1002
#PBFORMS END Constants
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Declarations **
'------------------------------------------------------------------------------
DECLARE CALLBACK FUNCTION ShowDLGEJEMPLOProc()
DECLARE FUNCTION ShowDLGEJEMPLO(BYVAL hParent AS DWORD) AS LONG
#PBFORMS Declarations
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Main Application Entry Point **
'------------------------------------------------------------------------------
FUNCTION PBMAIN()
    ShowDLGEJEMPLO %HWND_DESKTOP
END FUNCTION
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** CallBacks **
'------------------------------------------------------------------------------
CALLBACK FUNCTION ShowDLGEJEMPLOProc()
 
    SELECT CASE AS LONG CBMSG
        CASE %WM_INITDIALOG
            ' Initialization handler
 
        CASE %WM_NCACTIVATE
            STATIC hWndSaveFocus AS DWORD
            IF ISFALSE CBWPARAM THEN
                ' Save control focus
                hWndSaveFocus = GetFocus()
            ELSEIF hWndSaveFocus THEN
                ' Restore control focus
                SetFocus(hWndSaveFocus)
                hWndSaveFocus = 0
            END IF
 
        CASE %WM_COMMAND
            ' Process control notifications
            SELECT CASE AS LONG CBCTL
                CASE %IDC_BTNCERRAR
                    IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                        DIALOG END CBHNDL, 0
                    END IF
 
                CASE %IDC_LBLINSTRUCTIONS
            END SELECT
    END SELECT
END FUNCTION
'------------------------------------------------------------------------------
 
'------------------------------------------------------------------------------
'   ** Dialogs **
'------------------------------------------------------------------------------
FUNCTION ShowDLGEJEMPLO(BYVAL hParent AS DWORD) AS LONG
    LOCAL lRslt AS LONG
 
#PBFORMS Begin Dialog %IDD_DLGEJEMPLO->->
    LOCAL hDlg  AS DWORD
 
    DIALOG NEW hParent, "Ejemplo PBForms", 188, 216, 201, 121, TO hDlg
    CONTROL ADD BUTTON, hDlg, %IDC_BTNCERRAR, "&Cerrar", 75, 96, 50, 15, %BS_DEFAULT OR %WS_TABSTOP
    CONTROL ADD LABEL,  hDlg, %IDC_LBLINSTRUCTIONS, "Click en Cerrar para " + _
        "cerrar", 0, 0, 200, 80
#PBFORMS END Dialog
 
    CONTROL SET TEXT hDlg, %IDC_LBLINSTRUCTIONS, "Bienvenido a PB/Forms"
    DIALOG SHOW MODAL hDlg, CALL ShowDLGEJEMPLOProc TO lRslt
 
#PBFORMS Begin CleanUp %IDD_DLGEJEMPLO
#PBFORMS END CleanUp
 
    FUNCTION = lRslt
END FUNCTION
'------------------------------------------------------------------------------

PB/Forms, viene a ser como las Microsoft Foundation Classes (MFC) de Microsoft. Una ayuda para crear entornos gráficos, va más allá de un editor de recursos, pero no es una herramienta RAD propiamente dicha. Como habéis visto, genera código BASIC, que sigue el esquema de mensajes de Windows. Funciona muy bien, y el códido terminado, podemos pasarlo nuevamente a PB/Forms, y cambiar los componentes que queramos, todo ello, sin alterar ni romper el código que hayamos escrito a mano.

Es una evolución de lo que antes conocíamos como DDT (Dynamic Dialog Tools), una buena ayuda en aplicaciones sencillas, pero que se queda corta en desarrollos más complejos. En caso de duda a la hora de seguir el artículo, podéis echar mano de la documentación en linea.

Como muestras más avanzadas, tenemos CSV_Edit.exe, un completo ejemplo incluido en PB/Forms, que parte de más de 60 Kb. de código fuente (cerca de 2.000 lineas), pero que se compilan a un compacto ejecutable de apenas 55 Kb.

Pongo a vuestra disposición el ejecutable y el binario aquí (15 Kb. en formato ZIP).