Visión general
JLicenseManager es el módulo de licenciamiento offline de jdashboardUI para aplicaciones B4J/Windows. No es solamente un validador: reúne identificación de hardware, generación y validación de códigos, persistencia local, Trial, expiración, protección básica frente a retroceso del reloj, diálogo de activación, temas y callbacks de activación.
Una característica especialmente importante de la distribución es que Licensr Pro, el generador/administrador de licencias incluido con la librería, usa el mismo módulo JLicenseManager que usa la aplicación cliente. Además, Licensr Pro está construido con la propia jdashboardUI y se entrega con su código fuente, por lo que sirve como implementación de referencia real y como demostración completa de la librería.
Esto significa que la documentación debe entenderse en dos lados:
- Aplicación cliente: carga, valida y guarda la licencia, obtiene el HWID, muestra el diálogo de activación y puede bloquear la aplicación.
- Licensr Pro: administra programas y claves base y genera los códigos que recibirá el cliente utilizando el mismo sistema de licenciamiento.
Las capturas mostradas en esta página son capturas reales del diálogo cliente y de Licensr Pro.
Arquitectura: el mismo módulo en ambos lados
El punto central es que no existen dos motores de licencia incompatibles. JLicenseManager es el módulo común.
Licensr Pro se incluye para evitar que el desarrollador tenga que construir desde cero una herramienta administrativa o reproducir manualmente la lógica criptográfica. El código fuente incluido también permite personalizar la herramienta para el flujo de trabajo propio.
En las capturas reales de Licensr Pro se pueden ver:
- selección del programa;
- ingreso del HWID del cliente;
- selección del nivel de licencia;
- licencia perpetua o con fecha de expiración;
- generación del código;
- historial de licencias;
- registro de programas;
- Base Secret Key por programa;
- idiomas;
- ajustes y mantenimiento;
- información del sistema.
Recomendación: para emisión remota de licencias, toma el código fuente de Licensr Pro incluido como implementación de referencia. No dupliques el formato ni la lógica criptográfica en otro generador si no es necesario.
Plataforma y dependencias
La implementación actual de JLicenseManager está orientada a B4J sobre Windows porque GetHWID obtiene el número de serie de la placa base mediante PowerShell y usa WMIC como fallback.
El módulo utiliza internamente, entre otros:
B4XSerializator;B4XCipher/ jB4XEncryption;StringUtils;jShell;custom_form;JEditText;jButton;JTextFlow;JIconFont;CustomMessageBox;CSSUtils.
El proyecto de jdashboardUI incluye bcprov-jdk18on-1.85, necesario para la infraestructura criptográfica usada por jB4XEncryption.
Inicialización
La firma pública real es:
Dim LM As JLicenseManager
LM.Initialize("MY_BASE_SECRET", "MyApplication", Me, "LM")
Parámetros:
| Parámetro | Tipo | Uso |
|---|---|---|
| SecretBase | String | Base secreta usada por el motor de licencias |
| AppName | String | Nombre usado para File.DirData(AppName) |
| Callback | Object | Objeto que recibirá eventos |
| EventName | String | Prefijo de eventos |
La implementación crea automáticamente el directorio de datos si todavía no existe.
En un comentario antiguo del fuente aparece
Initialize2, pero el método público actual se llama Initialize.
Integración mínima recomendada
Una forma sencilla de proteger el inicio de una aplicación es utilizar CheckLicenseAndBlock:
Private LM As JLicenseManager
Sub AppStart (Form1 As Form, Args() As String)
MainForm = Form1
LM.Initialize("MY_BASE_SECRET", "MyApplication", Me, "LM")
LM.Theme = LM.THEME_DARKPRO
LM.ShowTrialButton = True
LM.DefaultTrialDays = 7
Wait For (LM.CheckLicenseAndBlock(MainForm)) Complete (Allowed As Boolean)
If Allowed = False Then
ExitApplication
Return
End If
MainForm.Show
End Sub
Si la licencia local es válida, devuelve True. Si está próxima a vencer puede mostrar una advertencia. Si no existe o no es válida, abre el diálogo de activación. Si el usuario no consigue activar, devuelve False.
LicenseResult
El módulo define:
Type LicenseResult ( _
IsValid As Boolean, _
Message As String, _
Level As String, _
ExpirationTicks As Long, _
ExpirationDate As String)
Campos:
| Campo | Descripción |
|---|---|
| IsValid | Resultado de validación |
| Message | Mensaje de estado |
| Level | Nivel de licencia |
| ExpirationTicks | Expiración en ticks; 0 representa sin expiración |
| ExpirationDate | Fecha formateada |
Obtener el HWID
Dim HardwareId As String = LM.GetHWID
Log(HardwareId)
La implementación intenta:
- PowerShell con
Get-CimInstance -ClassName Win32_BaseBoard. - Si falla, CMD +
wmic baseboard get serialnumber. - El valor queda cacheado durante la sesión.
Si ninguno de los dos métodos obtiene un valor, devuelve HWID_ERROR_O_NO_SOPORTADO.
Esto hace que la implementación actual de HWID sea específica de Windows.
Generación del código
El módulo expone:
Dim Code As String = LM.GenerateLicenseCode( _
ClientHWID, _
ExpirationTicks, _
"Pro")
La estructura interna serializada contiene:
hwid;exp;level;issued.
Luego se cifra y se convierte a Base64.
Licencia perpetua
La validación interpreta ExpirationTicks = 0 como licencia sin expiración.
Licencia temporal
Para una licencia con fecha:
Dim Exp As Long = DateTime.Add(DateTime.Now, 0, 1, 0)
Licensr Pro ofrece esta selección desde su interfaz y es el flujo recomendado cuando el código se genera en una máquina administrativa para un cliente remoto.
Validar manualmente un código
Dim Result As LicenseResult = LM.ValidateLicense(LicenseCode)
If Result.IsValid Then
Log("Level: " & Result.Level)
Log("Expires: " & Result.ExpirationDate)
Else
Log(Result.Message)
End If
La validación comprueba:
- que el código pueda descifrarse;
- que corresponda al HWID esperado;
- que no haya expirado.
Cuando es válida, actualiza también el estado público del módulo.
Guardar y cargar licencia local
SaveLocalLicense
LM.SaveLocalLicense(LicenseCode)
Guarda:
license.lic: código de licencia;sys_time.dat: control de tiempo cifrado.
Ambos se gestionan dentro de File.DirData(AppName).
LoadLocalLicense
Dim Result As LicenseResult = LM.LoadLocalLicense
Carga el archivo, comprueba manipulación del reloj y ejecuta ValidateLicense.
Protección frente a retroceso del reloj
El módulo mantiene sys_time.dat con la última hora conocida cifrada.
Si la hora actual retrocede más de una hora respecto de la almacenada, LoadLocalLicense considera que existe manipulación y bloquea la licencia.
Si el archivo de tiempo no se puede descifrar, la implementación actual lo regenera con la hora actual en lugar de bloquear automáticamente.
Esta protección es una medida local adicional; no sustituye una validación en servidor si tu modelo de seguridad requiere control online.
Trial
StartTrialMode
LM.StartTrialMode(7)
Solo crea el Trial si todavía no existe license.lic.
Internamente:
- calcula la fecha de expiración;
- genera una licencia con nivel
Trial; - la guarda con
SaveLocalLicense.
Trial desde el diálogo
LM.ShowTrialButton = True
LM.DefaultTrialDays = 7
El botón Trial del diálogo usa DefaultTrialDays.
Diálogo de activación
Dim Result As LicenseResult = LM.ShowActivationDialog(MainForm)
La captura real incluida en esta página corresponde a este diálogo.
El diálogo incorpora:
- estado de licencia;
- HWID de solo lectura;
- botón para copiar HWID;
- campo para código de activación;
- Activate / Update License;
- Trial opcional;
- Cancel;
- tema configurable;
- alertas visuales.
Una característica interesante es que el propio diálogo usa componentes de jdashboardUI, entre ellos JEditText, jButton, JTextFlow, CustomMessageBox y custom_form.
Eventos
El módulo declara cuatro eventos:
Private Sub LM_ActivationSuccess(Result As LicenseResult)
Log("Activated: " & Result.Level)
End Sub
Private Sub LM_ActivationFailed(Message As String)
Log("Failed: " & Message)
End Sub
Private Sub LM_TrialActivated(Result As LicenseResult)
Log("Trial expires: " & Result.ExpirationDate)
End Sub
Private Sub LM_ActivationCancelled
Log("Cancelled")
End Sub
Comportamiento confirmado del código actual:
ActivationSuccess: se dispara después de validar y guardar correctamente.ActivationFailed: se dispara si el código introducido no es válido.TrialActivated: se dispara cuando se activa correctamente el Trial.ActivationCancelled: está declarado, pero los handlers actuales de Cancel y cierre simplemente cierran el formulario y no realizanCallSub. No conviene depender de este callback hasta que esa implementación se ajuste.
SetCallback
Puedes cambiar posteriormente el receptor:
LM.SetCallback(Me, "License")
Estado público de la licencia
Después de cargar o validar puedes consultar:
Log(LM.LicenseIsValid)
Log(LM.LicenseLevel)
Log(LM.LicenseExpirationTicks)
Log(LM.LicenseExpirationDate)
Log(LM.LicenseMessage)
Campos disponibles:
LicenseIsValid As Boolean;LicenseLevel As String;LicenseExpirationTicks As Long;LicenseExpirationDate As String;LicenseMessage As String.
Aviso antes de expirar
LM.WarningDaysBeforeExpiration = 5
CheckLicenseAndBlock muestra una advertencia si una licencia válida vence dentro del número de días configurado, pero permite continuar.
Temas
Theme usa DarkPro por defecto.
LM.Theme = LM.THEME_OCEAN
LM.ApplyTheme(LM.Theme)
Constantes disponibles:
| Constante | Valor |
|---|---|
| THEME_DARKPRO | DarkPro |
| THEME_NORDICFROST | NordicFrost |
| THEME_FORESTGREEN | ForestGreen |
| THEME_MIDNIGHTBLUE | MidnightBlue |
| THEME_MATERIALINDIGO | MaterialIndigo |
| THEME_VAMPIRERED | VampireRed |
| THEME_CYBERPUNK | Cyberpunk |
| THEME_SLATEGRAY | SlateGray |
| THEME_DEEPPURPLE | DeepPurple |
| THEME_COFFEEBEANS | CoffeeBeans |
| THEME_OCEAN | Ocean |
| THEME_SUNSET | Sunset |
| THEME_ICE | Ice |
| THEME_DARKELEGANT | DarkElegant |
| THEME_LIGHTPRO | LightPro |
| THEME_ROSEGOLD | RoseGold |
| THEME_SAGEGREEN | SageGreen |
| THEME_TEALMINIMALIST | TealMinimalist |
| THEME_WARMCOPPER | WarmCopper |
| THEME_CYBERORANGE | CyberOrange |
| THEME_LAVENDER | Lavender |
| THEME_SOFTBLUE | SoftBlue |
Personalización y localización del diálogo
Prácticamente todos los textos importantes son públicos:
DialogTitle;DialogStatusExpired;DialogStatusInvalid;DialogStatusActive;DialogHWIDLabel;DialogLicenseLabel;DialogCopyButton;DialogActivateButton;DialogUpdateButton;DialogCancelButton;DialogTrialButton;DialogSuccessTitle;DialogSuccessMsg;DialogErrorTitle;DialogErrorMsg;DialogWarningTitle;DialogWarningMsg;DialogTrialSuccessMsg;DialogStatusTrial;DialogStatusAboutToExpire;DialogBlockMessage;DialogBlockTitle;DialogExpirationWarningTitle.
Ejemplo:
LM.DialogTitle = "Activación del producto"
LM.DialogHWIDLabel = "Código de hardware"
LM.DialogActivateButton = "Activar"
LM.DialogTrialButton = "Probar gratis"
LM.DialogCancelButton = "Cancelar"
Los placeholders también son configurables:
PlaceholderExpiration = "{EXP}";PlaceholderLevel = "{LEVEL}";PlaceholderDays = "{DAYS}".
Formato de fecha
LM.ExpirationDateFormat = "dd/MM/yyyy"
Log(LM.FormatDate(DateTime.Now))
Si ticks <= 0, FormatDate devuelve Never.
AlwaysOnTop
LM.AlwaysOnTop = True
Se aplica a los formularios y alertas creados por el módulo.
CheckLicenseAndBlock en detalle
CheckLicenseAndBlock(Parent) es la API de alto nivel más cómoda:
- Aplica el tema.
- Ejecuta
LoadLocalLicense. - Si es válida, comprueba proximidad de expiración.
- Si no es válida y existía archivo, informa del error.
- Abre
ShowActivationDialog. - Si se activa correctamente devuelve
True. - Si no, muestra Access Denied y devuelve
False.
Licensr Pro incluido con código fuente
La distribución de la librería incluye Licensr Pro y su código fuente.
Esto es relevante por tres motivos:
- Usa el mismo JLicenseManager. El generador no representa un formato de licencia distinto al que usa la aplicación cliente.
- Está creado con jdashboardUI. Sirve como ejemplo real de una aplicación de escritorio completa hecha con los controles de la librería.
- Es modificable. El desarrollador puede adaptar idiomas, flujo, niveles de licencia, interfaz y administración de programas.
Licensr Pro incluye cinco áreas principales visibles en las capturas y en el código fuente incluido:
Generator
Permite seleccionar programa, introducir HWID, seleccionar tipo/nivel, elegir licencia perpetua o con expiración, generar el código y copiarlo.
Programs
Administra programas registrados y su Base Secret Key.
Languages
Incluye una interfaz de selección multilenguaje.
Settings
Muestra información del sistema, ubicación de base de datos, estadísticas y acciones de mantenimiento.
About
Identifica la aplicación Licensr Pro y presenta la información del generador.
Flujo completo recomendado
En la aplicación cliente
LM.Initialize("MY_BASE_SECRET", "MyApplication", Me, "LM")
LM.Theme = LM.THEME_DARKPRO
Wait For (LM.CheckLicenseAndBlock(MainForm)) Complete (Allowed As Boolean)
If Allowed = False Then ExitApplication
Para emitir la licencia
- Registrar el programa en Licensr Pro.
- Configurar la misma Base Secret Key correspondiente a ese producto.
- Obtener el HWID mostrado por el cliente.
- Seleccionar nivel y expiración.
- Generar el código.
- Entregar el código al usuario.
- El usuario lo introduce en el diálogo de
JLicenseManager. - El módulo valida y guarda la licencia local.
API pública completa
| Método | Retorno | Función |
|---|---|---|
| Initialize(SecretBase, AppName, Callback, EventName) | - | Inicializa módulo |
| SetCallback(Callback, EventName) | - | Cambia receptor de eventos |
| GetHWID | String | Obtiene HWID |
| StartTrialMode(Days) | - | Inicia Trial si no existe licencia |
| SaveLocalLicense(LicenseCode) | - | Guarda licencia y control horario |
| LoadLocalLicense | LicenseResult | Carga y valida |
| GenerateLicenseCode(HWID, ExpirationTicks, MyLicenseLevel) | String | Genera código |
| ValidateLicense(LicenseCode) | LicenseResult | Valida código |
| ApplyTheme(ThemeName) | - | Aplica tema |
| ShowActivationDialog(Parent) | LicenseResult | Muestra activación modal |
| FormatDate(ticks) | String | Formatea expiración |
| CheckLicenseAndBlock(Parent) | ResumableSub | Flujo completo de protección |
Límites y consideraciones
- El HWID actual está diseñado para Windows.
- Un cambio de placa base puede cambiar el identificador y afectar una licencia ligada al hardware.
- La seguridad offline depende de proteger adecuadamente la Base Secret Key.
- Una licencia offline no puede ofrecer por sí sola revocación instantánea desde servidor.
- La protección de reloj es local y usa una tolerancia de una hora.
- Si necesitas emisión remota, usa Licensr Pro y su fuente incluida como referencia del lado administrativo.