PowerBuilder

Guía de Interoperabilidad WebBrowser - PowerBuilder 2025 R2

AutorLuis Avilan
Publicado
Construyendo un puente bidireccional entre WebBrowser y PowerBuilder, artículo de Luis Avilan
PB

0 visualizaciones

Arquitectura Hibrida HTML5 - PowerScript mediante WebView2 Chromium
Base de datos: PBDemoDB2025 | Vista: [Sales].[vSalesPerson]

1. Diagrama Conceptual de Arquitectura

1+---------------------------------------------------------+
2| VENTANA POWERBUILDER |
3| (w_demo_interop) |
4| +---------------------------------------------------+ |
5| | CONTROL WEBBROWSER (wb_1) | |
6| | | |
7| | +----------------------------------------------+ | |
8| | | PAGINA HTML | | |
9| | | (interop.html) | | |
10| | | | | |
11| | | [Tarjeta] -> postMessage(JSON) ----------+--+--+---> Dispara evento:
12| | | | | | WebMessageReceived
13| | | [Tabla HTML] <-- renderizar(JSON) <------+--+--+--- Inyectado via:
14| | | | | | EvaluateJavascriptAsync
15| | +----------------------------------------------+ | |
16| +---------------------------------------------------+ |
17+---------------------------------------------------------+

Flujo de Datos Bidireccional

| Direccion | Mecanismo | Descripcion | |-----------|-----------|-------------| | HTML -> PB | window.chrome.webview.postMessage() | El frontend envia un JSON serializado al evento WebMessageReceived | | PB -> HTML | wb1.EvaluateJavascriptAsync() | PowerScript inyecta una llamada JS con datos JSON empaquetados |

Conexion ODBC

1SQLCA.DBMS = "ODBC"
2SQLCA.AutoCommit = False
3SQLCA.DBParm = "ConnectString='DSN=PB SQLServer V2025R2;UID=sa;PWD=sa'"

2. El Frontend (`interop.html`)

El archivo interop.html se encuentra en el directorio raiz del proyecto. Contiene:

• Diseno premium dark-mode con glassmorphism y micro-animaciones

• 4 Tarjetas de Comando con efecto ripple y glow diferenciado por rol

• Toast notifications para retroalimentacion visual

• Funcion enviarComando() - Canal de salida HTML -> PowerBuilder

• Funcion renderizarTablaVista() - Canal de entrada PowerBuilder -> HTML

• Modo demo standalone - Simula datos de vSalesPerson cuando no hay canal WebView2

3. Implementacion en el Backend (PowerScript)

A. Objetos Stubs (Soporte del Demo)

Ventana Auxiliar: `w_auxiliar`

Ventana estandar vacia de tipo Main. Comprueba que HTML puede instanciar objetos graficos nativos del IDE.

Funcion Global: `f_global_demo`

| Propiedad | Valor | |-----------|-------| | Nombre | fglobaldemo | | Retorno | (None) |

1// Funcion Global: f_global_demo()
2MessageBox("Funcion Global Ejecutada", &
3 "La funcion global f_global_demo ha sido invocada con exito " + &
4 "desde el evento asincrono del WebBrowser.", &
5 Information!)

Objeto No Visual: `n_v_objeto_demo`

| Propiedad | Valor | |-----------|-------| | Metodo | ofprocesarmensaje | | Acceso | Public | | Retorno | Integer |

1// Metodo: n_v_objeto_demo.of_procesar_mensaje()
2MessageBox("Logica de NVO Activa", &
3 "Mensaje encapsulado recibido. El metodo of_procesar_mensaje() " + &
4 "del NVO ha tomado el control del flujo.", &
5 Information!)
6Return 1

B. Ventana Principal: `w_demo_interop`

Control WebBrowser llamado wb1 estirado para ocupar toda la ventana.

Evento `Open` de `w_demo_interop`

Conecta la ODBC y carga el HTML:

1// Evento: Open para w_demo_interop
2String ls_ruta_html
3
4// Conexion ODBC a SQL Server - PBDemoDB2025
5SQLCA.DBMS = "ODBC"
6SQLCA.AutoCommit = False
7SQLCA.DBParm = "ConnectString='DSN=PB SQLServer V2025R2;UID=sa;PWD=sa'"
8
9CONNECT USING SQLCA;
10
11If SQLCA.SQLCode <> 0 Then
12 MessageBox("Error de Conexion", &
13 "No se pudo conectar a PBDemoDB2025." + &
14 "~r~nCodigo: " + String(SQLCA.SQLCode) + &
15 "~r~nMensaje: " + SQLCA.SQLErrText, StopSign!)
16End If
17
18ls_ruta_html = "file:///" + GetCurrentDirectory() + "\interop.html"
19wb_1.Navigate(ls_ruta_html)

Evento `Close` de `w_demo_interop`

1DISCONNECT USING SQLCA;

Evento `WebMessageReceived` del Control `wb_1`

Firma del evento: WebMessageReceived(string uri, boolean isJson, string webmessage)

1// Evento: WebMessageReceived para el control wb_1
2// Firma: WebMessageReceived(string uri, boolean isJson, string webmessage)
3
4String ls_json_crudo, ls_accion, ls_objeto
5JsonParser lnv_json
6Long ll_root
7
8ls_json_crudo = webmessage
9
10If Trim(ls_json_crudo) = "" Then Return
11
12lnv_json = Create JsonParser
13lnv_json.LoadString(ls_json_crudo)
14ll_root = lnv_json.GetRootItem()
15
16ls_accion = lnv_json.GetItemString(ll_root, "accion")
17ls_objeto = lnv_json.GetItemString(ll_root, "objeto")
18
19Choose Case ls_accion
20
21 Case "ABRIR_VENTANA"
22 Window lw_ventana
23 Open(lw_ventana, ls_objeto)
24
25 Case "LLAMAR_FUNCION_GLOBAL"
26 f_global_demo()
27
28 Case "LLAMAR_NVO"
29 n_v_objeto_demo lnv_objeto
30 lnv_objeto = Create n_v_objeto_demo
31 lnv_objeto.of_procesar_mensaje()
32 Destroy lnv_objeto
33
34 Case "CONSULTAR_VISTA_SQL"
35 // Consulta real a [Sales].[vSalesPerson] de PBDemoDB2025
36 JsonGenerator lnv_gen
37 Long ll_root_gen, ll_array
38 String ls_json_resultado
39
40 lnv_gen = Create JsonGenerator
41 ll_root_gen = lnv_gen.CreateJsonObject()
42 ll_array = lnv_gen.AddItemArray(ll_root_gen, "datos")
43
44 String ls_first_name, ls_last_name, ls_job_title, ls_territory
45 Decimal ld_sales_ytd, ld_sales_last_year
46 Long ll_fila
47
48 DECLARE cur_sales CURSOR FOR
49 SELECT FirstName, LastName, JobTitle, TerritoryName,
50 SalesYTD, SalesLastYear
51 FROM Sales.vSalesPerson
52 ORDER BY SalesYTD DESC;
53
54 OPEN cur_sales;
55 FETCH cur_sales INTO :ls_first_name, :ls_last_name, :ls_job_title, &
56 :ls_territory, :ld_sales_ytd, :ld_sales_last_year;
57
58 Do While SQLCA.SQLCode = 0
59 ll_fila = lnv_gen.AddItemObject(ll_array)
60 lnv_gen.AddItemString(ll_fila, "FirstName", ls_first_name)
61 lnv_gen.AddItemString(ll_fila, "LastName", ls_last_name)
62 lnv_gen.AddItemString(ll_fila, "JobTitle", ls_job_title)
63 lnv_gen.AddItemString(ll_fila, "TerritoryName", ls_territory)
64 lnv_gen.AddItemNumber(ll_fila, "SalesYTD", Double(ld_sales_ytd))
65 lnv_gen.AddItemNumber(ll_fila, "SalesLastYear", Double(ld_sales_last_year))
66
67 FETCH cur_sales INTO :ls_first_name, :ls_last_name, :ls_job_title, &
68 :ls_territory, :ld_sales_ytd, :ld_sales_last_year;
69 Loop
70
71 CLOSE cur_sales;
72
73 ls_json_resultado = lnv_gen.GetJsonString()
74 wb_1.EvaluateJavascriptAsync("renderizarTablaVista('" + ls_json_resultado + "')")
75 Destroy lnv_gen
76
77 Case Else
78 MessageBox("Despachador Invalido", &
79 "Accion no mapeada: " + ls_accion, StopSign!)
80End Choose
81
82Destroy lnv_json

4. Pruebas de Verificacion

Matriz de Pruebas

| # | Tarjeta | Accion Esperada | Validacion | |---|---------|-----------------|------------| | 1 | Abrir Ventana Nativa | Se despliega wauxiliar | HTML instancia objetos graficos del IDE | | 2 | Invocar Funcion Global | MessageBox de fglobaldemo | Canal HTML ejecuta funciones globales | | 3 | Ejecutar Metodo de NVO | MessageBox de ofprocesarmensaje | HTML delega logica a clases encapsuladas | | 4 | Consultar Vista SQL | Tabla con vendedores de vSalesPerson | Flujo completo HTML -> PB -> SQL Server -> JSON -> HTML |

Flujo Detallado de la Consulta SQL (Tarjeta #4)

1sequenceDiagram
2 participant HTML as Frontend HTML
3 participant WV2 as WebView2 Channel
4 participant PB as PowerScript
5 participant SQL as SQL Server
6
7 HTML->>WV2: postMessage({"accion":"CONSULTAR_VISTA_SQL"})
8 WV2->>PB: WebMessageReceived(webmessage)
9 PB->>PB: JsonParser - Extraer "accion"
10 PB->>SQL: DECLARE CURSOR Sales.vSalesPerson
11 SQL-->>PB: Registros (FirstName, LastName, SalesYTD...)
12 PB->>PB: JsonGenerator - Empaquetar datos
13 PB->>WV2: EvaluateJavascriptAsync("renderizarTablaVista('...')")
14 WV2->>HTML: Ejecutar JS en el DOM
15 HTML->>HTML: JSON.parse - forEach - Render tabla

5. Columnas de [Sales].[vSalesPerson]

| Columna | Tipo | Uso en el Demo | |---------|------|----------------| | FirstName | nvarchar(50) | Nombre del vendedor | | LastName | nvarchar(50) | Apellido del vendedor | | JobTitle | nvarchar(50) | Cargo profesional | | TerritoryName | nvarchar(50) | Territorio de ventas | | SalesYTD | money | Ventas del anio actual | | SalesLastYear | money | Ventas del anio anterior |

6. Objetos Requeridos (Checklist)

• [ ] wdemointerop - Ventana principal con control wb1 (WebBrowser)

• [ ] wauxiliar - Ventana stub vacia

• [ ] fglobaldemo - Funcion global con MessageBox

• [ ] nvobjetodemo - NVO con metodo ofprocesarmensaje()

• [x] interop.html - Creado en el directorio raiz del proyecto

• [ ] DSN ODBC PB SQLServer V2025R2 configurado apuntando a PBDemoDB2025

7. Notas de Produccion

[!IMPORTANT]
El DSN ODBC PB SQLServer V2025R2 debe estar configurado en el sistema apuntando a la base de datos PBDemoDB2025 con las credenciales sa/sa.
[!TIP]
El archivo interop.html incluye un modo demo standalone: si se abre directamente en un navegador (sin PowerBuilder), la tarjeta SQL Server simulara 6 registros de vendedores automaticamente.
[!WARNING]
Todos los archivos PowerScript usan caracteres ASCII puros (sin acentos) para evitar problemas de codificacion al importar en el IDE de PowerBuilder.