PowerShell : config auto Zebra GX420t (port TCP 9100, ZPL, taille d’étiquette)

Déployez une Zebra GX420t sans clics : création du port TCP 9100, ajout de la file d’impression et définition de la taille d’étiquette via ZPL/SGD, le tout en PowerShell, avec scripts prêts à l’emploi et bonnes pratiques de déploiement.

Sommaire

Contexte et objectif

Dans de nombreux environnements Windows, la configuration d’une imprimante d’étiquettes Zebra doit être reproductible, rapide et sans interface graphique. L’objectif ici est de :

  • Créer automatiquement un port TCP Raw 9100 et une file d’impression pour une Zebra GX420t.
  • Définir la taille des étiquettes (largeur et hauteur) sans passer par « Préférences de l’imprimante ».
  • Garantir un déploiement idempotent (relancer le script ne casse rien), avec journalisation et tests de connectivité.

Pourquoi PrintManagement ne suffit pas

Le module PowerShell intégré PrintManagement (Add-Printer, Add-PrinterPort, etc.) gère la création de ports et de files d’impression, mais ne sait pas écrire dans les Printer Preferences (format d’étiquette, vitesse, densité, etc.). Aucune cmdlet Microsoft ne couvre ces réglages avancés sur les Zebra. Pour cela, on s’appuie sur le langage d’impression Zebra (ZPL) et sur les variables SGD (Set‑Get‑Do), envoyés directement à l’imprimante.

Principe technique : ZPL & SGD

Les imprimantes Zebra interprètent le ZPL. Pour définir un format, deux commandes suffisent généralement :

  • ^PW{largeur_en_points} (Print Width)
  • ^LL{longueur_en_points} (Label Length)

Le tout est encapsulé entre ^XA (début) et ^XZ (fin). Exemple pour une étiquette 4″×6″ en 203 dpi : ^XA^PW812^LL1218^XZ.

Alternative lisible : SGD expose des paires clé‑valeur, par exemple :

! U1 setvar "media.type" "label"
! U1 setvar "zpl.label_length" "1218"
! U1 setvar "device.languages" "zpl"

Ces commandes (ZPL ou SGD) peuvent être envoyées :

  • En direct sur le port TCP 9100 de l’imprimante (Raw).
  • Via la file d’impression Windows, en écriture « raw » (utile en USB/COM).

Conversions de dimensions : mm, pouces et points

La largeur et la longueur attendues par ZPL/SGD sont en points (dots). La conversion est simple :

  • pouces → points : points = pouces × DPI
  • mm → points : points = (mm ÷ 25,4) × DPI

La GX420t existe typiquement en 203 dpi. Si votre modèle est différent, adaptez le DPI dans les scripts.

Fonctions PowerShell réutilisables

Les blocs ci‑dessous constituent une boîte à outils. Copiez‑les tels quels, puis utilisez le « Script complet » plus bas.

Conversion vers des points Zebra

function ConvertTo-ZebraDots {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][double]$Value,
        [ValidateSet('mm','in')][string]$Unit = 'mm',
        [ValidateRange(100,600)][int]$Dpi = 203
    )
    switch ($Unit) {
        'mm' { return [int][math]::Round(($Value / 25.4) * $Dpi) }
        'in' { return [int][math]::Round($Value * $Dpi) }
    }
}

Test de connectivité TCP 9100

function Test-ZebraTcp9100 {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$IPAddress,
        [int]$TimeoutMs = 2500
    )
    $client = [System.Net.Sockets.TcpClient]::new()
    $ar = $client.BeginConnect($IPAddress,9100,$null,$null)
    $ok = $ar.AsyncWaitHandle.WaitOne($TimeoutMs)
    if(-not $ok){ return $false }
    try { $client.EndConnect($ar); return $true } catch { return $false }
    finally { $client.Close() }
}

Création du port TCP et de la file d’impression

function New-ZebraTcpPort {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$IPAddress
    )
    # Le paramètre -PrinterHostAddress crée un port Standard TCP/IP Raw (par défaut 9100)
    if(-not (Get-PrinterPort -ErrorAction SilentlyContinue | Where-Object {$_.PrinterHostAddress -eq $IPAddress})) {
        Write-Verbose "Création du port TCP/IP pour $IPAddress (Raw 9100)"
        Add-PrinterPort -PrinterHostAddress $IPAddress | Out-Null
    }
    # Retourne le nom du port (généralement 'IP_x.x.x.x')
    (Get-PrinterPort | Where-Object {$_.PrinterHostAddress -eq $IPAddress}).Name
}

function Get-ZebraDriverName {
[CmdletBinding()]
param(
[string]$ModelPattern = 'GX420t'
)
$drv = Get-PrinterDriver -ErrorAction SilentlyContinue | Where-Object { $_.Name -match $ModelPattern } | Select-Object -First 1
if(-not $drv){
throw "Pilote pour '$ModelPattern' introuvable. Installez au préalable le pilote Zebra (ex. 'ZDesigner GX420t')."
}
$drv.Name
}

function New-ZebraPrinter {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$PrinterName,
[Parameter(Mandatory)][string]$PortName,
[Parameter(Mandatory)][string]$DriverName,
[string]$Location,
[string]$Comment
)
$existing = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue
if($existing){
Write-Verbose "La file '$PrinterName' existe déjà. Aucune création."
return $existing
}
Add-Printer -Name $PrinterName -DriverName $DriverName -PortName $PortName -Location $Location -Comment $Comment
Get-Printer -Name $PrinterName
} 

Envoi de ZPL : TCP direct ou file d’impression

function Send-ZebraZplTcp {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$IPAddress,
        [Parameter(Mandatory)][string]$Zpl
    )
    $bytes = [System.Text.Encoding]::ASCII.GetBytes($Zpl)
    $client = [System.Net.Sockets.TcpClient]::new($IPAddress,9100)
    try {
        $stream = $client.GetStream()
        $stream.Write($bytes,0,$bytes.Length)
        $stream.Flush()
    } finally {
        if($stream){ $stream.Close() }
        $client.Close()
    }
}

# Envoi "raw" via la file Windows (utile en USB/COM)

Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Runtime.InteropServices;
public class RawPrinterHelper {
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public class DOCINFOA { public string pDocName; public string pOutputFile; public string pDataType; }
[DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="StartDocPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint="EndDocPrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="StartPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="EndPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="WritePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
public static bool SendStringToPrinter(string printerName, string zpl) {
IntPtr pPrinter = IntPtr.Zero;
DOCINFOA di = new DOCINFOA();
di.pDocName = "RAW ZPL";
di.pDataType = "RAW";
if (!OpenPrinter(printerName, out pPrinter, IntPtr.Zero)) return false;
if (!StartDocPrinter(pPrinter, 1, di)) { ClosePrinter(pPrinter); return false; }
if (!StartPagePrinter(pPrinter)) { EndDocPrinter(pPrinter); ClosePrinter(pPrinter); return false; }
IntPtr pBytes = Marshal.StringToCoTaskMemAnsi(zpl);
int dwWritten = 0;
bool ok = WritePrinter(pPrinter, pBytes, zpl.Length, out dwWritten);
Marshal.FreeCoTaskMem(pBytes);
EndPagePrinter(pPrinter); EndDocPrinter(pPrinter); ClosePrinter(pPrinter);
return ok;
}
}
"@ -ErrorAction SilentlyContinue

function Send-ZebraZplQueue {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$PrinterName,
[Parameter(Mandatory)][string]$Zpl
)
$null = [RawPrinterHelper]::SendStringToPrinter($PrinterName, $Zpl)
} 

Appliquer la taille d’étiquette et enregistrer

function Set-ZebraLabelSize {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][int]$WidthDots,
        [Parameter(Mandatory)][int]$LengthDots,
        [switch]$Persist,       # ^JUS
        [string]$Orientation = 'N' # N=Normal, R=L90, I=Inversé, B=L270 (si besoin)
    )
    # Ajoutez ^PO$Orientation si l’orientation doit être forcée
    $jus = $Persist.IsPresent ? '^JUS' : ''
    "^XA^PW$WidthDots^LL$LengthDots$jus^XZ"
}

Script complet d’installation

Ce script crée le port, ajoute la file d’impression, pousse le ZPL de format et, en option, enregistre la configuration en mémoire permanente de l’imprimante.

param(
    [Parameter(Mandatory)][string]$PrinterName,
    [Parameter(Mandatory)][string]$IPAddress,
    [ValidateRange(100,600)][int]$Dpi = 203,
    [ValidateSet('mm','in')][string]$Unit = 'mm',
    [double]$LabelWidth = 101.6,   # 4 pouces par défaut (en mm)
    [double]$LabelHeight = 152.4,  # 6 pouces par défaut (en mm)
    [switch]$Persist,              # ^JUS
    [switch]$UseSpoolerRaw,        # Envoi via la file au lieu du TCP 9100 direct
    [string]$Location,
    [string]$Comment = "Zebra GX420t - déploiement automatisé"
)

Write-Host "== Vérification de la connectivité TCP 9100 ==" -ForegroundColor Cyan
if(-not (Test-ZebraTcp9100 -IPAddress $IPAddress)){
Write-Warning "Impossible d’atteindre $IPAddress:9100. Vérifiez réseau/pare‑feu/alimentation."
}

Write-Host "== Création du port TCP/IP ==" -ForegroundColor Cyan
$portName = New-ZebraTcpPort -IPAddress $IPAddress
Write-Host "Port utilisé : $portName"

Write-Host "== Recherche du pilote Zebra ==" -ForegroundColor Cyan
$driverName = Get-ZebraDriverName -ModelPattern 'GX420t'
Write-Host "Pilote retenu : $driverName"

Write-Host "== Création de la file d’impression ==" -ForegroundColor Cyan
$printer = New-ZebraPrinter -PrinterName $PrinterName -PortName $portName -DriverName $driverName -Location $Location -Comment $Comment
Write-Host "File : $($printer.Name)"

Write-Host "== Conversion des dimensions ==" -ForegroundColor Cyan
$pw = ConvertTo-ZebraDots -Value $LabelWidth -Unit $Unit -Dpi $Dpi
$ll = ConvertTo-ZebraDots -Value $LabelHeight -Unit $Unit -Dpi $Dpi
Write-Host "Largeur (points): $pw  |  Longueur (points): $ll"

$zpl = Set-ZebraLabelSize -WidthDots $pw -LengthDots $ll -Persist:$Persist
Write-Host "== Envoi du ZPL de configuration ==" -ForegroundColor Cyan
if($UseSpoolerRaw){
Send-ZebraZplQueue -PrinterName $PrinterName -Zpl $zpl
} else {
Send-ZebraZplTcp -IPAddress $IPAddress -Zpl $zpl
}

# Optionnel : imprimer une étiquette de test

$test = "^XA^FO30,30^A0N,40,40^FDTest $PrinterName^FS^FO30,90^GB$pw,$ll,2^FS^XZ"
if($UseSpoolerRaw){
Send-ZebraZplQueue -PrinterName $PrinterName -Zpl $test
} else {
Send-ZebraZplTcp -IPAddress $IPAddress -Zpl $test
}

Write-Host "Configuration terminée." -ForegroundColor Green 

Variante avec SGD (Set‑Get‑Do)

SGD permet des réglages lisibles et ciblés. Vous pouvez générer un « paquet » de variables et l’envoyer tel quel au port 9100 ou via la file.

function New-ZebraSgdPayload {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][int]$WidthDots,
        [Parameter(Mandatory)][int]$LengthDots,
        [switch]$Persist
    )
@"
! U1 setvar "device.languages" "zpl"
! U1 setvar "media.type" "label"
! U1 setvar "zpl.print_width" "$WidthDots"
! U1 setvar "zpl.label_length" "$LengthDots"
$( if($Persist){'! U1 setvar "power.on_reset" "last_state"'} )
"@
}

# Exemple d’envoi

$payload = New-ZebraSgdPayload -WidthDots $pw -LengthDots $ll -Persist:$Persist
Send-ZebraZplTcp -IPAddress $IPAddress -Zpl $payload 

Astuce : stockez le contenu SGD/ZPL dans un simple .txt et déployez‑le via GPO (Préférences > Fichier) vers un partage, puis utilisez PowerShell pour l’expédier au moment opportun.

Tableau pratique : formats courants en points

Référence rapide pour les tailles les plus utilisées. Les valeurs sont arrondies au point le plus proche.

Format (pouces)Largeur x Longueur203 dpi
(^PW / ^LL)
300 dpi
(^PW / ^LL)
Remarques
4″ × 6″101,6 mm × 152,4 mm812 / 12181200 / 1800Expédition standard
4″ × 4″101,6 mm × 101,6 mm812 / 8121200 / 1200Inventaire
4″ × 2″101,6 mm × 50,8 mm812 / 4061200 / 600Codes‑barres
3″ × 2″76,2 mm × 50,8 mm609 / 406900 / 600Petites étiquettes
2″ × 1″50,8 mm × 25,4 mm406 / 203600 / 300Actifs/IT

Bonnes pratiques de déploiement

  • Idempotence : testez l’existence du port et de la file avant de créer. Le script ci‑dessus le fait.
  • Drivers : pré‑installez les pilotes Zebra (package signé) sur vos masters ou via un outil de gestion.
  • Réseau : ouvrez TCP 9100 sur le pare‑feu des imprimantes et segmentez (VLAN) si nécessaire.
  • Journalisation : consignez les paramètres envoyés (PW/LL, DPI) pour diagnostiquer facilement.
  • Group Policy : distribuez les files aux postes via GPP « Imprimantes » et poussez ZPL/SGD via GPP « Fichier » + Script.
  • Firmware : gardez vos imprimantes à jour. Certaines fonctions (ex. auto‑sizing type ^SZ) n’existent qu’à partir d’un certain microprogramme.

Vérifications et tests

Après configuration, validez :

  1. La file imprime bien en « RAW » sans transformation (évitez le rendu GDI pour le ZPL pur).
  2. La largeur et la longueur correspondent à la réalité (pas de rognage, pas de saut intempestif).
  3. La détection de support est correcte (gap vs black mark). Si besoin, lancez une calibration.

Étiquette de test simple :

^XA
^CF0,40
^FO20,20^FDZebra GX420t^FS
^FO20,70^GB200,2,2^FS
^FO20,90^A0N,28,28^FDTaille: ^PW=812 ^LL=1218^FS
^XZ

Calibration et réglages complémentaires

  • Calibration : certaines séries acceptent ^XA^JUS^XZ (sauvegarde), ^XA^JC^XZ (calibration). Lancer une calibration peut résoudre les détections de fin d’étiquette.
  • Orientation : si vous devez tourner l’impression, ajoutez ^PO (N, R, I, B). Cela n’affecte pas la largeur/longueur physique.
  • Densité/Vitesse : réglez‑les via ZPL (^MD pour la densité, ^PR pour la vitesse) ou via des variables SGD ciblées.

Troubleshooting

Quelques symptômes fréquents et leurs pistes :

  • Étiquette tronquée en bas : augmentez ^LL (longueur), vérifiez le DPI utilisé dans la conversion.
  • Impression décalée : ajustez l’offset haut ^LT ou la position de départ (^FO dans vos modèles).
  • Deux étiquettes pour un seul job : l’imprimante peut considérer ^XZ comme fin et passer à l’étiquette suivante. Vérifiez la longueur et la détection de gap/mark.
  • Caractères imprimés au lieu de code‑barres : vous n’êtes pas en mode ZPL. Forcer ! U1 setvar "device.languages" "zpl".
  • Pas de sortie : testez TCP 9100, câble, alimentation. Vérifiez que le pilote n’interprète pas le flux (préférez RAW).
  • Modèle 300 dpi : recalculer PW/LL avec DPI=300. Des valeurs 203 dpi donnent un format erroné.

Exemple : déploiement massif

Pour déployer sur des dizaines de sites :

  1. Centralisez un CSV Nom;IP;Largeur;Hauteur;Unite;DPI.
  2. Créez un script qui boucle sur les lignes, appelle New-ZebraTcpPort, New-ZebraPrinter, puis envoie ZPL/SGD.
  3. Exécutez depuis un compte disposant des droits d’ajout de pilotes et d’imprimantes.
Import-Csv .\zebra.csv -Delimiter ';' | ForEach-Object {
    $pw = ConvertTo-ZebraDots -Value $_.Largeur -Unit $_.Unite -Dpi $_.DPI
    $ll = ConvertTo-ZebraDots -Value $_.Hauteur -Unit $_.Unite -Dpi $_.DPI
    $port = New-ZebraTcpPort -IPAddress $_.IP
    $drv  = Get-ZebraDriverName -ModelPattern 'GX420t'
    $prn  = New-ZebraPrinter -PrinterName $_.Nom -PortName $port -DriverName $drv
    $zpl  = Set-ZebraLabelSize -WidthDots $pw -LengthDots $ll -Persist
    Send-ZebraZplTcp -IPAddress $_.IP -Zpl $zpl
}

Sécurité, gouvernance et réversibilité

  • Traçabilité : journalisez le ZPL envoyé (sans données sensibles). Conservez un hash/version.
  • Change management : validez en pré‑production avec quelques imprimantes avant un déploiement global.
  • Réversibilité : conservez un « profil d’usine » (jeu de commandes) pour revenir aux valeurs par défaut si nécessaire.

FAQ rapide

Faut‑il envoyer le ZPL via le port 9100 ou via la file ? En réseau, 9100 est simple et direct. En USB/COM, utilisez la file avec envoi RAW (fonction Send-ZebraZplQueue ci‑dessus).

Où définir la détection du média (gap/mark) ? Utilisez SGD media.type (label, continuous) et lancez une calibration si besoin.

Le format change après redémarrage ? Ajoutez la sauvegarde ^JUS ou les variables SGD pertinentes, et vérifiez que l’imprimante conserve son « last state ».

Conclusion

Sous Windows, la méthode réellement scriptable pour fixer la taille d’étiquette d’une Zebra GX420t consiste à envoyer des commandes ZPL/SGD directement à l’imprimante. Combinez Add-PrinterPort et Add-Printer pour créer la file, puis poussez le ZPL de format (^PW, ^LL) — éventuellement persisté avec ^JUS. Le résultat : un déploiement rapide, reproductible, documenté et compatible avec les déploiements massifs.


Annexe : snippets prêts à copier

Envoi ZPL minimal en TCP

$zpl  = "^XA^PW812^LL1218^XZ"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($zpl)
$client = [System.Net.Sockets.TcpClient]::new("192.0.2.10",9100)
$stream = $client.GetStream()
$stream.Write($bytes,0,$bytes.Length)
$stream.Close(); $client.Close()

Bloc SGD type

! U1 setvar "device.languages" "zpl"
! U1 setvar "media.type" "label"
! U1 setvar "zpl.print_width" "812"
! U1 setvar "zpl.label_length" "1218"

Création simple d’un port + file

Add-PrinterPort -PrinterHostAddress 192.0.2.10  # Raw 9100 par défaut
Add-Printer -Name "Zebra-GX420t-Expé" -DriverName "ZDesigner GX420t" -PortName "IP_192.0.2.10"

Étiquette de test encadrée

^XA^FO20,20^A0N,30,30^FDHello Zebra^FS^FO10,10^GB780,1180,2^FS^XZ

Note firmware : selon la révision, certaines options comme l’auto‑dimensionnement (^SZ) peuvent ne pas être disponibles. Vérifiez la version et harmonisez‑la lors des déploiements.

Sommaire