How to Use IronPrint with Claude Code
A hospital laboratory prints three kinds of documents from one service. Specimen labels go to the label printer at the collection desk, result reports go to the office printer, and courier manifests go to the dispatch room. A routing file names the printer for each document type, and a single wrong character in that file can send patient results to another room without an error. Claude Code, Anthropic's agentic coding tool, edits files and runs commands in a repository. It is available as a command-line tool, as extensions for VS Code and JetBrains editors, in the Claude desktop app, and at claude.ai/code.
IronPrint prints PDFs and images from .NET, either silently or through the operating system dialog. The PrintSettings.PrinterName property selects the printer for a job, and Printer.GetPrinterNames lists the printers installed on the machine. The four tested prompts below have Claude Code build a printer router for that laboratory, and the sections between them record the IronPrint behavior each prompt relies on.
Start claude in a terminal opened at the project folder and switch the model with /model opusplan before sending the first prompt. Typing @ before a path includes the content of that file in the request.
Add the IronPrint NuGet package, version 2026.10.1, to LabPrintRouter.csproj.
Then have Program.cs check the printer names in @config/printer-routes.json.
The program does this, in order:
1. Applies the license key and prints License.IsLicensed.
2. Prints each name from Printer.GetPrinterNames on its own line, in quotes.
3. Prints each route's document type, its printer name in quotes, and installed or missing.
A route's printer counts as installed only on an exact match, case and spaces included.
Don't send any print job. Every printer on this machine opens a Save dialog.
My key is in the IRONPRINT_LICENSE_KEY environment variable.
Program.cs reads it at startup and assigns it to License.LicenseKey.
Don't print its value, and don't dump the environment to find it.
If you check the variable, a yes or no on whether it's set is enough.
The quotes in the output make a trailing space visible.
Minimal Workflow (5 steps)
- Download the C# printing library from NuGet
- Open Claude Code from the project folder and set the model with /model
- Record IronPrint's printer name rules in CLAUDE.md so that every session loads them
- Request a router that sends a job only when the configured name matches an installed printer exactly
- Review the routing report, then send one test page to each routed printer before release
What does Claude Code do in a .NET printing project?
The agent writes a routing service that reads the routing file, compares each configured printer name with the installed printers, and records a decision for every document. Claude Code can also run the program and its tests and fix the failures they report. The developer decides which printer serves each document type.
State the package name and version in the first prompt. In the attack known as slopsquatting, attackers register harmful packages under names that language models hallucinate. An agent that picks a package name by itself can install one of those packages.
How do you set up Claude Code?
The install and the first sign-in follow Anthropic's quickstart. Setup is done once claude opens a session in a terminal and answers a prompt.
How do you set up a project Claude Code can work in?
Put the inputs in the repository before the first prompt. The sample project for this guide holds a .NET 9 console app for net9.0-windows, a routing file for four document types, and an inbox of seven PDFs whose names begin with their document type. One route uses the exact name of an installed printer. The other three use a printer that is not installed, an installed name in a different case, and an installed name with a trailing space.
Claude Code reads CLAUDE.md when a session starts, so its rules apply in every later session. Once the Quickstart has added the package, the second prompt writes that file and defines a read-only subagent that reviews the printing code.
Add a Printing section to a new CLAUDE.md at the repository root.
Later sessions should follow these rules from it:
- All printing goes through IronPrint. Add no other printing library.
- The package is IronPrint, at the version LabPrintRouter.csproj references now.
- Look up IronPrint member names in docs/IronPrint.xml.
- config/ and inbox/ are inputs. Leave them unchanged.
- The program sends no print job unless it runs with --send.
- Never run it with --send on this machine.
- Read the key from IRONPRINT_LICENSE_KEY. Never print it, save it, or dump the environment.
Copy IronPrint.xml to docs/ now, from the ironprint 2026.10.1 folder of the local NuGet cache.
Use the one under lib/net7.0-windows7.0.
If it isn't there, tell me, and still add the subagent below.
Then create a subagent at .claude/agents/printer-name-auditor.md.
Limit its tools to Read, Grep and Glob.
It reviews Services/ and returns three lists:
- IronPrint types and members in use, flagging any that docs/IronPrint.xml lacks.
- Types under IronSoftware.*, listed apart, since they ship in other assemblies.
- Every place a PrinterName gets set, and whether that name passed an exact installed check first.
Claude Code watches .claude/agents and loads a newly added subagent definition with no restart, but only when that folder existed at the start of the session. The sample repository therefore holds the folder, with an empty .gitkeep file, before the session opens. IronPrint's llms.txt indexes its how-to guides, its agent skill, and its other documentation.
Apply the license key before the first print call. Without a key, Printer.Print and Printer.PrintAsync throw, and no job is queued. The IronPrint License Keys page offers a trial key and shows both the code assignment and the configuration-file setting.
Which IronPrint printer rules belong in CLAUDE.md?
We probed the behavior behind each line on IronPrint 2026.10.1, once licensed and once unlicensed. Paste the block under the Printing section.
These printer rules come from probes on IronPrint 2026.10.1:
- PrintSettings.PrinterName must equal a Printer.GetPrinterNames() entry, character for character.
- Any other name sends the job to the Windows default printer, and nothing throws.
- That covers a missing printer, a different case, a trailing space and an empty string.
- Decide whether to print with StringComparison.Ordinal.
- A trimmed or case-blind match may label a near miss, but it never sends a job.
- Printer.GetPrinterTrays() returns an empty list for a name that isn't installed.
- An empty tray list is also normal for some real printers, so it can't test a name.
- Printer.GetPrinterNames() works without a key, so a printer list proves nothing about licensing.
- IronPrint doesn't read IRONPRINT_LICENSE_KEY by itself. Assign License.LicenseKey at startup.
- Assigning a null key doesn't throw. License.IsLicensed just stays false.
- Without a key, Printer.Print throws AggregateException with a LicensingException inside.
- Without a key, Printer.PrintAsync throws LicensingException directly.
- LicensingException is IronSoftware.Exceptions.LicensingException, internal to IronPrint.dll.
- Print and PrintAsync both wait on the driver, and a Save dialog keeps them waiting.
The two exception shapes affect error handling. A handler checks the exception from PrintAsync but the inner exception from Print. Neither can use catch (LicensingException), because the type is not public and that clause failed with CS0122 in the probe.
Does IronPrint reject a printer name that does not match?
According to the specify printer name guide, the name comparison is case-sensitive, and a wrong name either fails silently or raises an error. Our probe sent a two-page report under four names that matched no installed printer. The names were an uninstalled printer name, "adobe pdf" for the installed "Adobe PDF", the installed name followed by a space, and an empty string. None of the four calls threw, and every job was sent to the default queue, which on the test machine is Microsoft Print to PDF.
The router therefore has to compare each configured name with the installed names itself, before it calls the print method.
How does IronPrint build a print job?
IronPrint sends each PDF page to the driver as an image at the resolution in PrintSettings.Dpi. The probe job to Microsoft Print to PDF produced a two-page A4 PDF in which each page was a single 1239 by 1754 pixel image with no text layer, which is A4 at 150 DPI. The sample outputs on the guide follow the same pattern at the default 300 DPI, with 2479 by 3508 pixels per A4 page. A check of a PDF printer's output must therefore compare page counts or images, because the printed text cannot be searched.
To inspect jobs without printing them, the test setup hooked the WPF print call that IronPrint uses on Windows. The hook writes every job to disk in XPS format and spools nothing, and the saved page images measured 1239 by 1754 pixels, the same as the probe job. IronPrint writes the other settings into the job's print ticket. With Grayscale on, the ticket requested monochrome output. The page images were stored as RGB and held only gray pixels. The ticket's copy count came from NumberOfCopies, and the job held each page once.
The label route shows what a fallback does to a job. The input is specimen-label__S-031.pdf, a 100 by 50 mm label from the inbox, which the routing file sends to LAB-ZEBRA-01 at 300 DPI with PaperSize.PrinterDefault.
Output
The label route's settings were sent without the name check, so the job fell back to Microsoft Print to PDF. The printer default paper size then resolved to that printer's A4 sheet, and the label became a 1179 by 591 pixel image on an A4 landscape page. The job scaled that image to 297 by 149 mm, the full width of the sheet.
Specimen label S-031 as IronPrint built the job without the name check. One A4 landscape page at 300 DPI, addressed to Microsoft Print to PDF and captured to a file in place of a printer.
How do you prompt for IronPrint code that compiles?
Send the router and its report as one prompt in plan mode, so the design is agreed before any file changes. Press Shift+Tab until the status bar shows plan mode, then send the prompt. Under opusplan, the plan comes from Opus, and Sonnet makes the changes after the plan is approved.
Build the router in Services/PrintRouter.cs and the report in Services/RoutingReport.cs.
Call both from Program.cs after the printer check.
Program.cs passes PrintRouter the list from one Printer.GetPrinterNames call.
Each PDF in inbox/ is named <documentType>__<id>.pdf.
For each file, PrintRouter does this:
1. Finds the route for its document type in config/printer-routes.json.
2. Checks the route's printer against that list.
3. Builds PrintSettings from the route's paper size, orientation, DPI, copies and grayscale.
4. Sends the job with Printer.PrintAsync, but only under --send.
Each file gets the first of these decisions that fits:
- send, when the printer name matches an installed name exactly.
- name-mismatch, when it matches one after ignoring case and outer spaces.
- not-installed, when nothing matches.
- no-route, when the config has no route for the type.
Only send files reach the print call.
Put that call behind an interface so tests can use a fake.
Write out/routing-report.csv with a header row and one row per file, sorted by file name.
Its first columns are File, DocumentType, ConfiguredPrinter, Decision and InstalledName.
Then come PaperSize, Orientation, Dpi, Copies and Grayscale from the route.
InstalledName holds the installed printer for send and the near match for name-mismatch.
It stays empty for the other two decisions.
A no-route row leaves the five route columns empty too.
Write Grayscale as true or false.
Print a count of files per decision.
Run the program without --send and show me the CSV.
Then have the printer-name-auditor subagent review Services/.
Two parts of the prompt protect the routing.
- The send flag: without
--send, the program sends no job, so the agent can run and test the router on a machine whose printers open a dialog. - The auditor subagent: a custom subagent loads CLAUDE.md, so the printer name rules apply to its review as well. Of the built-in agents, Explore and Plan start without CLAUDE.md.
Can IronPrint routing code be tested without a license key?
Yes, provided the tests replace the print call. Reading the routing file, listing the installed printers, and building PrintSettings all run without a key, so every routing decision can be tested on a build server that holds no key.
Add a LabPrintRouter.Tests xUnit project that targets net9.0-windows.
It tests PrintRouter through a fake print sink.
The tests read the copies of inbox/ and config/ in their output folder.
Give the router a fixed printer list of "Adobe PDF", "Microsoft Print to PDF" and "Fax".
Assert these results:
- The decision for each of the 7 files.
- The fake receives the 2 result reports and nothing else.
- RPT-4471 goes out with A4, Portrait, 150 DPI, 1 copy and grayscale on.
- Printer.GetPrinterNames runs without throwing while no key is applied.
Set the project up this way:
- It lives in its own folder, and the repository root gets no solution file.
- LabPrintRouter.csproj must not compile anything in that folder.
- The test output folder gets copies of config/ and inbox/.
Run dotnet test one time, in a child process that has no IRONPRINT_LICENSE_KEY.
Tell me the pass count from that run.
What should you verify before trusting generated IronPrint code?
Review generated IronPrint code with these checks.
- The build: a member that IronPrint does not have stops
dotnet buildwith a compile error. The shipped IronPrint.xml also documents a method of the internalPdfiumUtilsclass, and code that calls it fails with CS0122. - The routing report: compare each decision with the routing file and the installed printer list before the first real job.
- One test page per printer: before release, confirm at each device that a test page printed where the route says.
The AI coding assistants guide gives one complete IronPrint task, with fixed pass criteria, as a repeatable test.
What did the prompts produce in a real run?
The four prompts ran in a single Claude Code session, in the order shown. Before prompt 3, the rules block was pasted into CLAUDE.md, and no one edited the code by hand.
- The Quickstart output named all four installed printers and marked one route installed and three missing.
- The routing report held one row for each of the 7 files.
- 2 files were marked send, 2 name-mismatch, 2 not-installed, and 1 no-route.
- Every decision matched our independent check against the installed printers.
- The near match was Adobe PDF for the courier manifest and Microsoft Print to PDF for the ward summary.
- A clean Release run in a fresh copy produced the same report.
- Without the key, the program reported IsLicensed as False and made the same 7 decisions.
- 10 of 10 tests passed in a process with no IRONPRINT_LICENSE_KEY variable.
- The subagent found no IronPrint member missing from IronPrint.xml.
- The subagent found one printer name assignment, placed after the exact check.
- The print queues were empty after the run, and no command in the transcripts used --send.
- The capture of the two send jobs held 3 A4 portrait pages for the Adobe PDF queue, each a 1239 by 1754 pixel image.
- Each prompt left a Release build with no warnings or errors.
- A scan of the transcripts, the session files, and the repository found no copy of the license key.
- Some steps ran differently from the page. A typed reply approved the prompt 3 plan, because the session ran headless. No job was printed, because every printer on the test machine opens a Save dialog, so the two send jobs were captured to XPS.
The first send row in the routing report was result-report__RPT-4471.pdf, a two-page result report from the inbox.
Output
The capture tool passed this row's settings from the routing report to IronPrint. The PDF below holds both captured pages without changes.
Result report RPT-4471 as IronPrint built the send job from the run's routing report. Both pages are A4 portrait at 150 DPI for the Adobe PDF queue, captured to a file in place of a printer.
Cursor has a Plan Mode as well, and the Cursor guide sends its prompt 3 in it. Codex reads its project rules from AGENTS.md, the counterpart of CLAUDE.md, as the ChatGPT Codex guide shows. This run's Claude Code version, models, and total duration appear in the run record beside the test machine.
Troubleshooting
- Print() Method Hangs in WPF and WinForms when
Printblocks the UI thread - web.config License Setup when a key set in Web.config is not applied
- ShowPrintDialog Behavior on Unsupported Target Frameworks when
ShowPrintDialogthrows on a target framework without UI support
Questions?
If you have any questions, reach out to support@ironsoftware.com
Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.