Files
nsis-plugin-nsparser/README_IT.md
T
2026-04-30 00:13:24 +02:00

9.1 KiB

nsParser NSIS Plugin

Plugin NSIS nativo in C per estrarre velocemente valori da file di grandi dimensioni, bypassando i limiti di NSIS_MAX_STRLEN (1KB).

Problema Risolto

NSIS ha un limite di 1024 byte per le stringhe (NSIS_MAX_STRLEN). File HTML/JSON con linee molto lunghe (>1KB) non possono essere processati con FileRead o operazioni su stringhe native NSIS.

Esempio: Il file HTML di Remote Desktop Manager contiene una linea di 39.830 caratteri (135KB totali) che causa il troncamento di FileRead a 1KB, impedendo l'estrazione della versione.

Soluzioni fallite:

  • FileRead → Tronca a 1KB
  • Concatenazione stringhe → Limite 1023 byte totali
  • NScurl /MEMORY → Limite ~8KB
  • Lettura byte-by-byte → Troppo lenta (10-30 secondi per 135KB)

Soluzione nsParser: Plugin nativo in C che legge file in chunk di 8KB con buffer sliding window di 16KB, parsing in <1 secondo.

Compilazione

Requisiti

  • Visual Studio 2022 (MSBuild 17.x, PlatformToolset v143)
  • Python 3.x (per build automation)

Build Automatica

cd nsParser
python build_plugin.py

Compila tutte le configurazioni e copia i DLL finali in ../plugins/:

  • x86-ansi → 85-87 KB
  • x86-unicode → 85-87 KB
  • x64-ansi → 104 KB
  • amd64-unicode → 104 KB

File intermedi (.lib, .exp, .obj) rimangono in nsParser/Plugins/{platform}/ per debugging.

Opzioni build

python build_plugin.py --config x86-unicode      # Solo un'architettura (x86-ansi|x86-unicode|amd64-unicode|all)
python build_plugin.py --toolset 2026            # Toolset specifico (2022|2026|auto)
python build_plugin.py --jobs 4                  # Numero di job MSBuild paralleli (default: CPU count)
python build_plugin.py --clean                   # Pulizia dist/ prima della build
python build_plugin.py --install-dir "C:\NSIS\Plugins"  # Copia in directory NSIS aggiuntiva
python build_plugin.py --verbose                 # Output MSBuild esteso
python build_plugin.py --version                 # Stampa versione ed esce

Build Manuale

cd nsParser
build.cmd

O con MSBuild direttamente:

msbuild nsParser.vcxproj /p:Configuration="Release" /p:Platform="Win32"
msbuild nsParser.vcxproj /p:Configuration="Release Unicode" /p:Platform="Win32"
msbuild nsParser.vcxproj /p:Configuration="Release" /p:Platform="x64"
msbuild nsParser.vcxproj /p:Configuration="Release Unicode" /p:Platform="x64"

Utilizzo in NSIS

!addplugindir "plugins\x86-unicode"

Section
  ; Download HTML file
  NScurl::http GET "https://devolutions.net/..." "$PLUGINSDIR\rdm.html" /END
  Pop $0
  
  ; Extract version from HTML
  nsParser::Extract "$PLUGINSDIR\rdm.html" "RDM7zX64.Version"
  Pop $0  ; Status: "OK" or "ERROR"
  Pop $1  ; Version string (es: "2025.3.21.0")
  
  ${If} $0 == "OK"
    DetailPrint "Version found: $1"
  ${Else}
    DetailPrint "Version not found"
  ${EndIf}
SectionEnd

Esempio Reale: RDM_Functions.nsh

!macro _GetLatestVersion
  NScurl::http GET "https://devolutions.net/..." "$R8" /END
  Pop $0
  
  ${If} $0 == "OK"
    nsParser::Extract "$R8" "RDM7zX64.Version"
    Pop $0  ; Status
    Pop $1  ; Version: "2025.3.21.0"
    
    ${If} $0 == "OK"
      StrCpy $Latest_Version "$1"
      StrCpy $DOWNLOAD_LINK "https://cdn.devolutions.net/download/Devolutions.RemoteDesktopManager.win-x64.$Latest_Version.7z"
    ${EndIf}
  ${EndIf}
!macroend

API

nsParser::Extract

nsParser::Extract <filepath> <pattern>

Parametri:

  • <filepath>: Percorso assoluto del file da analizzare
  • <pattern>: Pattern da cercare (es: "RDM7zX64.Version")

Stack Output:

  1. Status: "OK" o "ERROR"
  2. Value: Stringa estratta (o vuota se errore)

Logica di estrazione:

  1. Cerca <pattern> nel file
  2. Trova il primo : dopo il pattern
  3. Trova il primo " dopo i due punti
  4. Estrae tutto fino al " di chiusura
  5. Restituisce il valore trovato

Esempio: Nel file con contenuto {"RDM7zX64.Version":"2025.3.21.0"}, il pattern "RDM7zX64.Version" estrae "2025.3.21.0".

Vantaggi

  • Veloce: ~1000x più veloce del byte-reading NSIS (<1s vs 10-30s per 135KB)
  • No Limiti: Bypassa NSIS_MAX_STRLEN, gestisce file di qualsiasi dimensione
  • Efficiente: Lettura in chunk di 8KB con sliding window buffer di 16KB
  • Pattern Spanning: Buffer sliding window preserva pattern tra chunk (ultimi 2KB)
  • Multi-Arch: 4 configurazioni (x86/x64, ANSI/Unicode)
  • Semplice: Singola funzione Extract parametrica
  • Generico: Non specifico per RDM, usabile per qualsiasi estrazione pattern

Implementazione Tecnica

Algoritmo

1. CreateFile()  Apre file in lettura
2. Loop:
   a. ReadFile()  Legge 8KB chunk
   b. Append a search buffer (16KB max)
   c. strstr()  Cerca pattern
   d. Se trovato:
      - Trova ':' dopo pattern
      - Trova '"' dopo ':'
      - Estrae fino a '"' chiusura
      - Return value
   e. Se buffer pieno:
      - Copia ultimi 2KB all'inizio (sliding window)
      - Azzera resto buffer
      - Continue reading
3. CloseHandle()

Caratteristiche

  • Buffer Size: 16KB totale (8KB chunk + 8KB overlap)
  • Sliding Window: Preserva ultimi 2KB per pattern spanning
  • String Search: Native strstr() su dati ANSI
  • Unicode Support: MultiByteToWideChar() per output Unicode
  • Windows API: CreateFile, ReadFile, CloseHandle
  • No CRT: Runtime library statica (MultiThreaded)

Configurazioni Visual Studio

Configuration Platform CharacterSet Output Size Plugin Dir
Release Win32 MultiByte 85-87 KB x86-ansi
Release Unicode Win32 Unicode 85-87 KB x86-unicode
Release x64 MultiByte 104 KB x64-ansi
Release Unicode x64 Unicode 104 KB amd64-unicode

Struttura File

nsParser/
├── nsParser.c              # Implementazione plugin (140 righe)
├── nsParser.h              # Header NSISFUNC macro
├── nsParser.def            # Export definitions
├── nsParser.vcxproj        # Visual Studio project (4 configs)
├── build_plugin.py         # Build automation + DLL copy
├── build.cmd               # Batch wrapper
├── .gitignore              # Ignora Plugins/*, .user, __pycache__
├── README.md               # Questa documentazione
└── Plugins/                # Build intermedi (gitignored)
    ├── x86-ansi/          # DLL + .lib + .exp + .obj
    ├── x86-unicode/
    ├── x64-ansi/
    └── amd64-unicode/

../plugins/                 # Output finale (copiato da Python)
├── x86-ansi/nsParser.dll
├── x86-unicode/nsParser.dll
├── x64-ansi/nsParser.dll
└── amd64-unicode/nsParser.dll

Note Tecniche

NSIS_MAX_STRLEN Limit

Il limite di 1KB è assoluto in NSIS Unicode e non può essere aumentato senza ricompilare NSIS stesso. Questo plugin è l'unica soluzione pratica per processare file con linee >1KB.

VersionCompare Bug

ATTENZIONE: ${VersionCompare} ha un bug noto con versioni a 4 parti. Esempio:

${VersionCompare} "2024.3.18.0" "2025.3.21.0" $R0
; $R0 = "2" (SBAGLIATO! Dovrebbe essere "1")

Workaround (usato in RDM_Installer.nsi):

${VersionCompare} "$Current_Version" "$Latest_Version" $R0

${If} $R0 == "2"
  ; Estrai anno e confronta numericamente
  ${WordFind} "$Current_Version" "." "+1{" $R1  ; Anno corrente
  ${WordFind} "$Latest_Version" "." "+1{" $R2   ; Anno remoto
  
  ${If} $R1 < $R2
    ; Anno vecchio → forzare selezione
    !insertmacro SelectSection 0
  ${Else}
    !insertmacro UnselectSection 0
  ${EndIf}
${EndIf}

Nota: In RDM.nsi (launcher) il workaround non serve perché si verifica solo uguaglianza ($R0 == "0"), non maggiore/minore.

Performance

Benchmark: File HTML 135KB (linea 39.830 caratteri)

Metodo Tempo Note
NSIS FileRead Fallisce Tronca a 1KB
NSIS Byte-reading 10-30 secondi Troppo lento
NScurl /MEMORY Fallisce Limite ~8KB
nsParser plugin <1 secondo Soluzione ottimale

Perché è così veloce?

  1. Native C: Compiled code vs NSIS interpreted
  2. Bulk Reading: 8KB chunks vs 1-byte iterations
  3. Efficient Search: strstr() CPU-optimized vs NSIS string ops
  4. No String Limits: Direct memory access vs NSIS_MAX_STRLEN

Casi d'Uso

  • Estrazione versioni da HTML/JSON con linee lunghe
  • Parsing file di configurazione con valori embedded
  • Lettura metadata da file generati (build info, manifests)
  • Download + parsing di risorse remote in un unico passaggio
  • Qualsiasi pattern "key":"value" o key: "value"

Licenza

Sviluppato per uso interno nel workspace Launchers.

Changelog

v1.0 (2025-11-10)

  • Rinominato da RDMVersion a nsParser (generico)
  • Supporto multi-architettura (4 configurazioni)
  • Build automation con Python
  • Struttura directory pulita (Plugins/ + ../plugins/)
  • Fixed amd64-unicode configuration bug
  • Integrato in RDM_Installer.nsi e RDM_Functions.nsh
  • Performance validated: <1s per 135KB file

v0.1 (Initial)

  • Plugin iniziale RDMVersion specifico per Remote Desktop Manager