JustPaste
HomeCategoriesAboutDonateContactTerms of UsePrivacy Policy
JustPaste

Free online notepad — write and share instantly

Navigate

  • Home
  • Timeline
  • Categories

Info

  • About
  • Donate
  • Contact

Legal

  • Terms of Use
  • Privacy Policy

© 2026 JustPaste.app. All rights reserved.

Made with ♥ by JustPaste

zebra print relay | JustPaste.app
about 2 months ago0 views
👨‍💻Programming

zebra print relay

# ═══════════════════════════════════════════════════════════════════

# ZEBRA ZD421 PRINT RELAY — NO ADMIN REQUIRED

# Run on the Windows PC with the USB printer.

#

# HOW TO USE:

# 1) Right-click this file → "Run with PowerShell"

# 2) Windows may ask for firewall permission — click "Allow"

# 3) Leave it running. The WLMT0 scanner sends labels here.

# 4) Press Ctrl+C to stop.

#

# CONFIGURATION: Change $printerName below to match YOUR printer.

#   To find the exact name run in PowerShell: Get-Printer | Select Name

# ═══════════════════════════════════════════════════════════════════

 

# ─── CHANGE THIS to your printer's exact Windows name ───

$printerName = "ZDesigner ZD421-203dpi ZPL"

 

# ─── Port (must match the URL entered in the Knack module) ───

$port = 3000

 

# ─── Raw Printer Helper (Win32 API — sends ZPL bytes to USB printer) ───

Add-Type @"

using System;

using System.Runtime.InteropServices;

 

public class RawPrinterHelper

{

    [StructLayout(LayoutKind.Sequential)]

    public struct DOCINFOA

    {

        [MarshalAs(UnmanagedType.LPStr)] public string pDocName;

        [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;

        [MarshalAs(UnmanagedType.LPStr)] public string pDataType;

    }

 

    [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)]

    public static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);

 

    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true)]

    public static extern bool ClosePrinter(IntPtr hPrinter);

 

    [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)]

    public static extern bool StartDocPrinter(IntPtr hPrinter, int level, [In] ref DOCINFOA di);

 

    [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true)]

    public static extern bool EndDocPrinter(IntPtr hPrinter);

 

    [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true)]

    public static extern bool StartPagePrinter(IntPtr hPrinter);

 

    [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true)]

    public static extern bool EndPagePrinter(IntPtr hPrinter);

 

    [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true)]

    public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);

 

    public static bool SendRawData(string printerName, byte[] data)

    {

        IntPtr hPrinter;

        if (!OpenPrinter(printerName, out hPrinter, IntPtr.Zero)) return false;

 

        DOCINFOA di = new DOCINFOA();

        di.pDocName = "ZPL Label";

        di.pDataType = "RAW";

 

        if (!StartDocPrinter(hPrinter, 1, ref di)) { ClosePrinter(hPrinter); return false; }

        if (!StartPagePrinter(hPrinter)) { EndDocPrinter(hPrinter); ClosePrinter(hPrinter); return false; }

 

        IntPtr pBytes = Marshal.AllocCoTaskMem(data.Length);

        Marshal.Copy(data, 0, pBytes, data.Length);

        int written;

        bool ok = WritePrinter(hPrinter, pBytes, data.Length, out written);

        Marshal.FreeCoTaskMem(pBytes);

 

        EndPagePrinter(hPrinter);

        EndDocPrinter(hPrinter);

        ClosePrinter(hPrinter);

        return ok;

    }

}

"@

 

# ─── Helper: build an HTTP response string ───

function Send-Response ($client, [int]$statusCode, [string]$statusText, [string]$body) {

    $bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($body)

    $header  = "HTTP/1.1 $statusCode $statusText`r`n"

    $header += "Access-Control-Allow-Origin: *`r`n"

    $header += "Access-Control-Allow-Methods: POST, GET, OPTIONS`r`n"

    $header += "Access-Control-Allow-Headers: Content-Type`r`n"

    $header += "Content-Type: text/plain; charset=utf-8`r`n"

    $header += "Content-Length: $($bodyBytes.Length)`r`n"

    $header += "Connection: close`r`n"

    $header += "`r`n"

    $headerBytes = [System.Text.Encoding]::UTF8.GetBytes($header)

    $stream = $client.GetStream()

    $stream.Write($headerBytes, 0, $headerBytes.Length)

    if ($bodyBytes.Length -gt 0) { $stream.Write($bodyBytes, 0, $bodyBytes.Length) }

    $stream.Flush()

    $client.Close()

}

 

# ─── Start TCP Listener (does NOT require admin) ───

try {

    $listener = New-Object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Any, $port)

    $listener.Start()

} catch {

    Write-Host ""

    Write-Host "ERROR: Could not bind to port $port." -ForegroundColor Red

    Write-Host "Another program may be using that port. Try changing `$port above." -ForegroundColor Yellow

    Write-Host ""

    Read-Host "Press Enter to exit"

    exit 1

}

 

Write-Host ""

Write-Host "=====================================================" -ForegroundColor Green

Write-Host "  ZEBRA ZD421 PRINT RELAY RUNNING" -ForegroundColor Green

Write-Host "  ** NO ADMIN REQUIRED **" -ForegroundColor Green

Write-Host "=====================================================" -ForegroundColor Green

Write-Host ""

Write-Host "  Listening on port : $port"

Write-Host "  Printer           : $printerName"

Write-Host ""

 

# Show local IP addresses so user knows what to enter in the module

$ips = Get-NetIPAddress -AddressFamily IPv4 |

       Where-Object { $_.IPAddress -ne "127.0.0.1" -and $_.PrefixOrigin -ne "WellKnown" } |

       Select-Object -ExpandProperty IPAddress

foreach ($ip in $ips) {

    Write-Host "  URL for module    : http://${ip}:${port}/print" -ForegroundColor Cyan

}

 

Write-Host ""

Write-Host "  Enter this URL in the Knack module Printer Settings."

Write-Host "  Press Ctrl+C to stop the relay."

Write-Host ""

 

# ─── Main Loop ───

try {

while ($true) {

    $client = $listener.AcceptTcpClient()

    $stream = $client.GetStream()

    $stream.ReadTimeout = 5000

 

    # Read the full HTTP request

    $buffer = New-Object byte[] 65536

    $total = 0

    try {

        do {

            $n = $stream.Read($buffer, $total, $buffer.Length - $total)

            $total += $n

        } while ($stream.DataAvailable -and $total -lt $buffer.Length)

    } catch {}

 

    if ($total -eq 0) { $client.Close(); continue }

 

    $raw = [System.Text.Encoding]::UTF8.GetString($buffer, 0, $total)

 

    # Parse first line: METHOD /path HTTP/1.x

    $firstLine = ($raw -split "`r`n")[0]

    $parts = $firstLine -split ' '

    $method = if ($parts.Length -ge 1) { $parts[0].ToUpper() } else { "" }

 

    # OPTIONS → CORS preflight

    if ($method -eq "OPTIONS") {

        Send-Response $client 204 "No Content" ""

        continue

    }

 

    # GET → health check

    if ($method -eq "GET") {

        Send-Response $client 200 "OK" "RELAY_OK|$printerName"

        continue

    }

 

    # POST → print ZPL

    if ($method -eq "POST") {

        # Extract body (after the blank line)

        $bodyStart = $raw.IndexOf("`r`n`r`n")

        $zpl = ""

        if ($bodyStart -ge 0) { $zpl = $raw.Substring($bodyStart + 4) }

 

        if ([string]::IsNullOrWhiteSpace($zpl)) {

            Send-Response $client 400 "Bad Request" "EMPTY_PAYLOAD"

            continue

        }

 

        $zplBytes = [System.Text.Encoding]::UTF8.GetBytes($zpl)

        $ok = [RawPrinterHelper]::SendRawData($printerName, $zplBytes)

 

        $ts = Get-Date -Format "HH:mm:ss"

        if ($ok) {

            Write-Host "  $ts | OK    | $($zplBytes.Length) bytes sent to printer" -ForegroundColor Green

            Send-Response $client 200 "OK" "OK"

        } else {

            Write-Host "  $ts | FAIL  | Could not send to '$printerName'" -ForegroundColor Red

            Send-Response $client 500 "Server Error" "PRINT_FAILED"

        }

        continue

    }

 

    # Anything else

    Send-Response $client 405 "Method Not Allowed" "METHOD_NOT_ALLOWED"

}

} finally {

    $listener.Stop()

}
← Back to timeline