Cómo realizar una aplicación en Borland Delphi 6 que utilice como motor de base de datos BDE (Borland Database Engine) y Paradox. Os explicamos cómo configurar adecuadamente el BDE y la propia aplicación para admitir conexiones de varios equipos de la red a estas tablas de forma simultánea (concurrente).
- Una aclaración inicial sobre Paradox en red.
- Preparar una aplicación Delphi para Paradox en red (múltiples usuarios conectados a las mismas tablas).
- Instalación y configuración del BDE.
- Compartir la carpeta de datos en el servidor para acceso de los clientes.
- Contenido del fichero de configuración INI en el equipo servidor y en los clientes.
Una aclaración inicial sobre Paradox en red
Por definición, Paradox es una base de datos de escritorio, semejante a Microsoft Access, no está diseñada para soportar multitud de conexiones de red de varios usuarios, aunque con lo que os explicaremos a continuación será posible que varios equipos de la red accedan a las mismas tablas Paradox. Hemos de decir que en determinadas circunstancias, sobre todo si no se siguen los pasos al pie de la letra, se pueden corromper los índices, aunque esto no sería un problema grabe pues se pueden volver a regenerar.
Para aplicaciones de gestión de las que necesitéis varios usuarios conectados a la vez a las mismas tablas recomendamos utilizar otros motores de bases de datos profesionales, más preparados para soportar cientos y miles de usuarios concurrentes, incluso conexiones a través de Internet. Por ejemplo MySQL, que además de soportar lo que os comentamos, es un motor de base de datos gratuito.
Aunque si no hay más remedio, por el motivo que sea, os explicaremos cómo realizar una aplicación en Borland Delphi 6 que utilice Paradox y permita conexión de varios usuarios en red a las mismas tablas.
Preparar una aplicación Delphi para Paradox en red (múltiples usuarios conectados a las mismas tablas)
Para realizar aplicaciones que utilicen tablas Paradox en red debemos seguir los siguientes pasos:
Añadiremos un DataModule a nuestra aplicación desde el menú «File» – «New» – «Data Module», a este DataModule le añadiremos los siguientes componentes:
- Session: con las siguientes propiedades:
- Active: False.
- AutoSessionName: False.
- KeepConnection: False.
- Name: Session1.
- NetFileDir: vacío, se modificará en tiempo de ejecución.
- PrivateDir: vacío, se modificará en tiempo de ejecución.
- SessionName: S1.
- SQLHourGlass: True.
- Tag: 0.
- Database: con las siguientes propiedades:
- AliasName: vacío.
- Connected: False.
- DatabaseName: ap
- DriverName: STANDARD.
- Exclusive: False.
- HandleShared: False.
- KeepConnection: False.
- LoginPrompt: False.
- Name: db.
- ReadOnly: False.
- SessionName: S1.
- Tag: 0.
- TransIsolation: tiReadCommitted.
- Table: con las propiedades importantes:
- Active: False.
- DatabaseName: ap.
- Name: Table1.
- SessionName: S1.
- TableName: datos.db (tabla de paradox que deberá existir en el directorio o carpeta de datos, donde queramos guardar las tablas de la aplicación)
- Datasource: con las propiedades:
- AutoEdit: False.
- DataSet: Table1.
- Enabled: True.
- Name: DataSource1.
Quedará algo así:
El Data Module (al cual le hemos puesto en la propiedad «Name» «MDatos») tendrá el siguiente código fuente:
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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
unit UnidadModuloDatos; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, DBTables, inifiles, DBIProcs; type TMDatos = class(TDataModule) db: TDatabase; Session1: TSession; Table1: TTable; Table1Id: TAutoIncField; Table1No: TStringField; Table1Si: TDateField; DataSource1: TDataSource; procedure AbrirBD(pw : String); procedure DataModuleCreate(Sender: TObject); procedure Table1AfterPost(DataSet: TDataSet); private { Private declarations } public hacer : boolean; { Public declarations } end; var MDatos: TMDatos; implementation {$R *.DFM} procedure TMDatos.AbrirBD(pw : String); var ruta, rutaprivate, rutanet : string; begin with tinifile.create (changefileext(paramstr(0),'.INI')) do try ruta := readstring ('Datos', 'Ruta', ''); rutanet := readstring ('Datos', 'Ruta net', ''); rutaprivate := readstring ('Datos', 'Ruta PrivateDir', ''); finally free; end; With Session1 do begin AddPassword (pw); End; With db do begin Params.Clear; Params.Add('PATH=' + RUTA); Params.Add('DEFAULT DRIVER=PARADOX'); Params.Add('ENABLE BCD=FALSE'); Connected := true; End; table1.Active := True; {session1.NetFileDir := rutanet; session1.PrivateDir := 'C:\TEMP'; Session1.Active := true; with db do begin Params.Clear; Params.Add('PATH=' + ruta); Params.Add('DEFAULT DRIVER=PARADOX'); Params.Add('ENABLE BCD=FALSE'); Connected := true; end; table1.active := true; } end; procedure TMDatos.DataModuleCreate(Sender: TObject); var ruta, servidor, rutaprivate, rutanet : string; begin with tinifile.create (changefileext(paramstr(0),'.INI')) do try ruta := readstring ('Datos', 'Ruta', ''); rutanet := readstring ('Datos', 'Ruta net', ''); rutaprivate := readstring ('Datos', 'Ruta PrivateDir', ''); servidor := readstring ('Datos', 'Es el servidor', '0'); if ruta = '' then begin while not (ruta <> '') do ruta := InputBox ('Ruta de los datos...', 'Introduzca la ruta de los ficheros *.db', 'c:\p\datos ó \\a\PRed\datos'); writestring ('Datos', 'Ruta', ruta); end; if rutaprivate = '' then begin while not (rutaprivate <> '') do rutaprivate := InputBox ('Ruta de los *.lck...', 'Introduzca el PrivateDir (ruta de los ficheros *.lck)', 'c:\p\temp'); try CreateDir(rutaprivate) except end; writestring ('Datos', 'Ruta PrivateDir', rutaprivate); end; if rutanet = '' then begin while not (rutanet <> '') do rutanet := InputBox ('Ruta net...', 'Introduce la Ruta de la carpeta NET', '\\a\PRed\datos\net'); writestring ('Datos', 'Ruta net', rutanet); END; if servidor = '' then begin while not (servidor <> '') do servidor := InputBox ('Servidor...', '¿Es este equipo el servidor?', 'no/sí'); if (UpperCase(servidor) = 'YES') or (UpperCase(servidor) = 'SÍ') or (UpperCase(servidor) = 'SI') then writestring ('Datos', 'Es el servidor', '1') else writestring ('Datos', 'Es el servidor', '0'); end; finally end; With mdatos.Session1 do begin NetFileDir := RUTANET; PrivateDir := rutaprivate; End; With db do begin Params.Clear; Params.Add('PATH=' + RUTA); Params.Add('DEFAULT DRIVER=PARADOX'); Params.Add('ENABLE BCD=FALSE'); //No testeado si esto funciona //si no funciona por código habrá que hacerlo manual //en la configuración del BDE if uppercase (servidor) <> 'NO' then Params.Add('LOCAL SHARE=TRUE') else Params.Add('LOCAL SHARE=FALSE'); Connected := true; End; table1.active := true; end; procedure TMDatos.Table1AfterPost(DataSet: TDataSet); begin Table1.FlushBuffers; end; end. |
Explicamos brevemente (pues es bastante sencillo) lo que hace el código anterior:
- Procedimiento AbrirBD: cuando sea llamado obtendrá los datos de conexión del fichero INI (configuración), configurará el componente Session (Session1) y el Database (db) con los valores leídos del fichero INI y abrirá las tablas de la aplicación.
- Procedimiento DataModuleCreate: este procedimiento se ejecutará una sola vez al abrir la aplicación, realizará las siguientes tareas:
- En primer lugar leerá los datos del fichero .ini de configuración de la aplicación (si existe):
- Leerá la «Ruta» carpeta donde estén alojados los ficheros de datos de la aplicación (las tablas Paradox, ficheros con extensión .db y los índices). Será conveniente que esta ruta esté en notación UNC (Universal Naming Convection) del tipo: \\nombre_servidor\recurso_compartido\Carpeta_Datos
- nombre_servidor: nombre de red (hostname) o IP del equipo que será el servidor, el que contenga la información (tablas paradox y demás).
- recurso_compartido: nombre de la carpeta que hemos de compartir, la que tendrá los datos de la aplicación.
- Carpeta_Datos: carpeta que crearemos dentro de la carpeta recurso_compartido para guardar los datos de la aplicación (ficheros .db e índices de Paradox).
- Leerá la «Ruta net» que será la carpeta donde se guardará el fichero PDOXUSRS.NET (contiene información de bloqueos y otras funciones importantes para tablas compartidas), este fichero debe ser EL MISMO para todos los equipos que accedan a la aplicación, por lo que debe estar en el servidor, en una carpeta compartida a la que todos los usuarios tengan acceso. Este valor del fichero INI («Ruta net») debe tener una notación UNC (Universal Naming Convection) del tipo:
\\nombre_servidor\recurso_compartido\Carpeta_Net
Donde:
- nombre_servidor: nombre de red (hostname) o IP del equipo que será el servidor, el que contenga la información (tablas paradox y demás).
- recurso_compartido: nombre de la carpeta que hemos de compartir, la que tendrá los datos de la aplicación.
- Carpeta_Net: carpeta que crearemos dentro de la carpeta recurso_compartido para guardar el fichero PDOXUSRS.NET
De esta forma, en todos los equipos configuraremos la misma ruta para el fichero PDOXUSRS.NET y será el mismo para todos.
- Leerá la «Ruta PrivateDir» que será la carpeta donde el BDE guarde archivos temporales del usuario actual (del tipo «Del1.MB» y ficheros *.lck de bloqueo de tablas), por lo que debe ser una ruta LOCAL (no de red) que sea única para cada equipo, por ejemplo: C:\Temp\Aplicacion_Paradox_Red
- También comprobará si el equipo actual es el servidor (valor «1») o es un equipo cliente (valor «0»).
- Leerá la «Ruta» carpeta donde estén alojados los ficheros de datos de la aplicación (las tablas Paradox, ficheros con extensión .db y los índices). Será conveniente que esta ruta esté en notación UNC (Universal Naming Convection) del tipo: \\nombre_servidor\recurso_compartido\Carpeta_Datos
- Si no existe alguno de los datos anteriores la aplicación mostrará una ventana pidiéndolos. En una aplicación comercial final es recomendable utilizar una ventana de configuración para todos estos datos, queda bastante más profesional que pedirlos uno a uno un InputBox.
- Configura los distintos componentes con los datos obtenidos.
- En primer lugar leerá los datos del fichero .ini de configuración de la aplicación (si existe):
- Para el caso del evento AfterPost del componente Table1, ejecutamos la función FlushBuffers que «fuerza» al BDE a guardar los cambios realizados en una tabla que hayan quedado en la caché, de esta forma se evitan incongruencias en los datos si varios usuarios han accedido a la misma tabla a la vez.
A continuación creamos un TForm que será el formulario principal de la aplicación, el que el usuario verá al iniciarla, donde estarán los datos de las tablas Paradox. En este formulario pondremos un TDBNavigator, TDBGrid, TListBox y un TButton, quedará de la siguiente forma:
El formulario tendrá el siguiente código:
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 |
unit UnidadPrincipal; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DBTables, BDE,Db, Grids, DBGrids, ExtCtrls, DBCtrls, StdCtrls, inifiles, Buttons, shellapi; type TFormPrincipal = class(TForm) DBNavigator1: TDBNavigator; Panel1: TPanel; Panel2: TPanel; Panel3: TPanel; ListBox1: TListBox; DBGrid1: TDBGrid; Splitter1: TSplitter; Panel4: TPanel; Image1: TImage; Panel5: TPanel; BInfo: TBitBtn; Panel6: TPanel; LWEB: TLabel; procedure BInfoClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure LWEBClick(Sender: TObject); procedure LWEBClick2(Sender: TObject); private { Private declarations } public { Public declarations } end; var FormPrincipal: TFormPrincipal; implementation uses UnidadModuloDatos; {$R *.DFM} procedure TFormPrincipal.BInfoClick(Sender: TObject); VAR TmpCursor: hDbiCur; rslt: dbiResult; UsrDesc: USERDesc; begin LISTBOX1.Items.Clear; LISTBOX1.Items.add('Usuarios conectados:'); Check(DbiOpenUserList(TmpCursor)); try repeat Rslt := DbiGetNextRecord(TmpCursor, dbiNOLOCK, @UsrDesc, nil); if Rslt <> DBIERR_EOF then begin LISTBOX1.Items.add(UsrDesc.szUserName); end; until Rslt <> DBIERR_NONE; finally Check(DbiCloseCursor(TmpCursor)); end; LISTBOX1.Items.add('------+++------'); LISTBOX1.Items.add('Ruta fichero NET: ' + mdatos.Session1.NetFileDir); LISTBOX1.Items.add('Ruta directorio privado (*.lck): ' + mdatos.Session1.PrivateDir); LISTBOX1.Items.add('Ruta directorio datos: ' + mdatos.Session1.Databases[0].Directory); LISTBOX1.Items.add('Nombre del DRIVER: ' + mdatos.Session1.Databases[0].DriverName); LISTBOX1.Items.add('Nombre del Alias del database: ' + mdatos.Session1.Databases[0].GetNamePath); end; procedure TFormPrincipal.FormClose(Sender: TObject; var Action: TCloseAction); begin mdatos.Session1.Close; mdatos.db.CloseDataSets; end; procedure TFormPrincipal.LWEBClick(Sender: TObject); begin ShellExecute(Handle, Nil, PChar('http://www.ajpdsoft.com'), Nil, Nil, SW_SHOWNORMAL); end; procedure TFormPrincipal.LWEBClick2(Sender: TObject); begin ShellExecute(Handle, Nil, PChar('http://www.ajpdsoft.com'), Nil, Nil, SW_SHOWNORMAL); end; end. |
En realidad sólo es necesario código fuente si queremos hacer alguna acción «fuera de lo normal», Delphi ya incluye componetes como el DBNavigator con botones para añadir, eliminar, refrescar, mover al primero, último, siguiente anterior, etc. que no necesitan código fuente, es suficiente con enlazar el DBNavigator con el DataSource correspondiente. El código fuente anterior sólo lo usamos para mostrar información de la base de datos (como los usuarios que hay conectados) en un ListBox, si no mostrásemos esto no haría falta código fuente.
Para el caso del fichero principal del proyecto o aplicación Delphi (el .dpr) tendrá el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
program P; uses Forms, UnidadPrincipal in 'UnidadPrincipal.pas' {FormPrincipal}, UnidadModuloDatos in 'UnidadModuloDatos.pas' {MDatos: TDataModule}; {$R *.RES} begin Application.Initialize; Application.Title := 'AjpdSoft - Paradox en red/Paradox int Net'; Application.CreateForm(TMDatos, MDatos); Application.CreateForm(TFormPrincipal, FormPrincipal); Application.Run; end. |
Que se creará de forma automática al ir agregando los formularios y módulos de datos, sólo tendremos que modificarlo si queremos realizar alguna comprobación o tarea antes de iniciar algún formulario.
Con lo anterior ya tendremos la aplicación preparada para ser ejecutada por varios usuarios (varios equipos) en la misma red utilizando las mismas tablas paradox.
La aplicación en funcionamiento:
Instalación y configuración del BDE
Lógicamente tendremos que instalar el BDE (Borland Database Engine) en todos los equipos donde queramos utilizar la aplicación que accederá a tablas Paradox. Lógicamente también todos los equipos deben estar conectados por red al servidor.
Tras instalar el BDE, deberemos configurarlo accediendo a «Inicio» – «Configuración» – «Panel de control», abriremos «BDE Administrator»:
Accederemos a la pestaña “Configuration”, seleccionaremos “System”, abriremos “INIT” y en la pestaña “Definition” cambiaremos el valor de la propiedad: “LOCAL SHARE” a False para el caso de los equipos clientes y True para el servidor. Es fundamental cambiar este valor en todos los equipos para que no haya problemas de corrupción de índices y demás:
Compartir la carpeta de datos en el servidor para acceso de los clientes
En el equipo servidor, el que contendrá las tablas Paradox de la aplicación, debemos compartir la carpeta donde se encuentren dichas tablas, con permisos de lectura y escritura para los usuarios de los equipos cliente. Por ejemplo, si la carpeta de la aplicación está en:
C:\AjpdSoft Paradox en Red
Pulsaremos con el botón derecho sobre la carpeta, en el menú emergente seleccionaremos «Compartir y seguridad». En la ventana de propiedades de la carpeta, en la pestaña «Compartir», marcaremos la opción «Compartir esta carpeta», en «Recurso compartido» introduciremos el nombre que tendrá la carpeta compartida, por ejemplo «paradoxred» (es recomendable no utilizar espacios ni nombres muy largos):
Nota: si utilizamos Windows 2000, 2003 ó 2008 o si tenemos la seguridad avanzada activada en Windows XP deberemos darle permisos a los usuarios que se conectarán, estos usuarios deberán estar dados de alta en el equipo servidor con el mismo nombre de usuario y la misma contraseña que tengan en los equipos cliente, para que se establezca relación de confianza.
Tras compartir la carpeta, si el equipo servidor se llama «pcservidor» ya podremos acceder a la carpeta compartida desde cualquier PC poniendo (en «Inicio» – «Ejecutar»):
\\pcservidor\paradoxred
En la carpeta
C:\AjpdSoft Paradox en Red
Crearemos las subcarpetas:
- «Datos»: que contendrá las tablas Paradox de la aplicación:
- «Net»: será la carpeta donde se guardará el fichero PDOXUSRS.NET. Puesto que este fichero debe ser el mismo para todos los equipos (tanto el servidor como los clientes), cuando la aplicación pida la ruta «Net» introduciremos:
\\pcservidor\paradoxred\Net
- «temp»: carpeta donde se guardarán los ficheros temporales, esta carpeta será local para cada equipo. En ella se guardarán los ficheros .lck, .MB, etc.
Nota importante: la carpeta temporal (PrivateDir) donde se alojarán los .lck no debe coincidir con la carpeta de los datos (tablas paradox).
Contenido del fichero de configuración INI en el equipo servidor y en los clientes
El fichero de configuración de la aplicación será diferente para el equipo servidor y para los equipos cliente.
Para el equipo servidor será algo así:
[Datos]
Ruta=\\pcservidor\paradoxred\Datos
Ruta PrivateDir=C:\AjpdSoft Paradox en Red\temp
Ruta net=\\pcservidor\paradoxred\Net
Es el servidor=1
En los equipos clientes:
[Datos]
Ruta=\\pcservidor\paradoxred\Datos
Ruta PrivateDir=C:\AjpdSoft Paradox en Red\temp
Ruta net=\\pcservidor\paradoxred\Net
Es el servidor=0
Nota importante: como se puede observar en el ejemplo la ruta del NET DIR se debe poner tanto en el servidor como en el cliente con la notación UNC (Ej. \\MiServidor\misDatos\net), ambos deben tener el NET DIR escrito así. Lo mismo ocurre con la ruta de la carpeta de los datos.