PowerBuilder
Guía de Interoperabilidad WebBrowser - PowerBuilder 2025 R2

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| | | | | | WebMessageReceived13| | | [Tabla HTML] <-- renderizar(JSON) <------+--+--+--- Inyectado via:14| | | | | | EvaluateJavascriptAsync15| | +----------------------------------------------+ | |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 = False3SQLCA.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_interop2String ls_ruta_html34// Conexion ODBC a SQL Server - PBDemoDB20255SQLCA.DBMS = "ODBC"6SQLCA.AutoCommit = False7SQLCA.DBParm = "ConnectString='DSN=PB SQLServer V2025R2;UID=sa;PWD=sa'"89CONNECT USING SQLCA;1011If SQLCA.SQLCode <> 0 Then12 MessageBox("Error de Conexion", &13 "No se pudo conectar a PBDemoDB2025." + &14 "~r~nCodigo: " + String(SQLCA.SQLCode) + &15 "~r~nMensaje: " + SQLCA.SQLErrText, StopSign!)16End If1718ls_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_12// Firma: WebMessageReceived(string uri, boolean isJson, string webmessage)34String ls_json_crudo, ls_accion, ls_objeto5JsonParser lnv_json6Long ll_root78ls_json_crudo = webmessage910If Trim(ls_json_crudo) = "" Then Return1112lnv_json = Create JsonParser13lnv_json.LoadString(ls_json_crudo)14ll_root = lnv_json.GetRootItem()1516ls_accion = lnv_json.GetItemString(ll_root, "accion")17ls_objeto = lnv_json.GetItemString(ll_root, "objeto")1819Choose Case ls_accion2021 Case "ABRIR_VENTANA"22 Window lw_ventana23 Open(lw_ventana, ls_objeto)2425 Case "LLAMAR_FUNCION_GLOBAL"26 f_global_demo()2728 Case "LLAMAR_NVO"29 n_v_objeto_demo lnv_objeto30 lnv_objeto = Create n_v_objeto_demo31 lnv_objeto.of_procesar_mensaje()32 Destroy lnv_objeto3334 Case "CONSULTAR_VISTA_SQL"35 // Consulta real a [Sales].[vSalesPerson] de PBDemoDB202536 JsonGenerator lnv_gen37 Long ll_root_gen, ll_array38 String ls_json_resultado3940 lnv_gen = Create JsonGenerator41 ll_root_gen = lnv_gen.CreateJsonObject()42 ll_array = lnv_gen.AddItemArray(ll_root_gen, "datos")4344 String ls_first_name, ls_last_name, ls_job_title, ls_territory45 Decimal ld_sales_ytd, ld_sales_last_year46 Long ll_fila4748 DECLARE cur_sales CURSOR FOR49 SELECT FirstName, LastName, JobTitle, TerritoryName,50 SalesYTD, SalesLastYear51 FROM Sales.vSalesPerson52 ORDER BY SalesYTD DESC;5354 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;5758 Do While SQLCA.SQLCode = 059 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))6667 FETCH cur_sales INTO :ls_first_name, :ls_last_name, :ls_job_title, &68 :ls_territory, :ld_sales_ytd, :ld_sales_last_year;69 Loop7071 CLOSE cur_sales;7273 ls_json_resultado = lnv_gen.GetJsonString()74 wb_1.EvaluateJavascriptAsync("renderizarTablaVista('" + ls_json_resultado + "')")75 Destroy lnv_gen7677 Case Else78 MessageBox("Despachador Invalido", &79 "Accion no mapeada: " + ls_accion, StopSign!)80End Choose8182Destroy 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)
1sequenceDiagram2 participant HTML as Frontend HTML3 participant WV2 as WebView2 Channel4 participant PB as PowerScript5 participant SQL as SQL Server67 HTML->>WV2: postMessage({"accion":"CONSULTAR_VISTA_SQL"})8 WV2->>PB: WebMessageReceived(webmessage)9 PB->>PB: JsonParser - Extraer "accion"10 PB->>SQL: DECLARE CURSOR Sales.vSalesPerson11 SQL-->>PB: Registros (FirstName, LastName, SalesYTD...)12 PB->>PB: JsonGenerator - Empaquetar datos13 PB->>WV2: EvaluateJavascriptAsync("renderizarTablaVista('...')")14 WV2->>HTML: Ejecutar JS en el DOM15 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.