Transport: Process¶
In process transport mode, HydroSym launches your plugin as a child process and communicates with it over stdin/stdout using a JSON-RPC 2.0 inspired protocol. This is an alternative to DLL transport for situations where NativeAOT is not feasible.
When to use process transport¶
- Your plugin uses a library that does not support NativeAOT (e.g.,
Newtonsoft.Jsonwith heavy reflection). - Your plugin is written in a language other than C# (Python, Java, Node.js, etc.).
- You need process isolation — a crash in the plugin process does not crash HydroSym.
For most C# plugins, prefer DLL transport.
Configuration¶
[Plugin]
Type=Process
Path=C:\ProgramData\HydroSym\plugins\AcmeErpPlugin\AcmeErpPlugin.exe
ConfigPath=C:\ProgramData\HydroSym\plugins\AcmeErpPlugin\acme.ini
Message format¶
Each message is a single line of JSON terminated by \n (LF). No Content-Length headers. No partial writes.
Request (HydroSym → plugin, via stdin)¶
{"id": 2, "method": "GetComponentParameters", "params": {"articleCode": "HV-301", "projectId": "PRJ-2025-0042"}}
Response (plugin → HydroSym, via stdout)¶
{"id": 1, "result": {"name": "Acme ERP Connector", "apiVersion": 1, "capabilities": ["getComponentParameters"]}}
Error response¶
NOT_SUPPORTED response¶
Protocol rules¶
- HydroSym sends one request at a time and waits for the response before sending the next.
- The
idfield is an integer that the plugin must echo back in its response. - Each message is exactly one line (no embedded newlines in the JSON).
- The plugin reads from stdin, writes to stdout.
- stderr is for logging only — write diagnostic messages there. HydroSym captures stderr and writes it to the HydroSym log file.
- The plugin must not write anything to stdout other than response messages.
Process lifecycle¶
HydroSym Plugin Process
│ │
│── launch (CreateProcess) ──> │ (process starts)
│<── ready (stdout: first msg) ─│ ← optional, or wait for GetInfo
│── {"id":1,"method":"GetInfo"} ─>│
│<── {"id":1,"result":{...}} ────│
│── {"id":2,"method":"Initialize"} →│
│<── {"id":2,"result":{}} ───────│
│ (normal operation) │
│── {"id":N,"method":"Finalize"} →│
│<── {"id":N,"result":{}} ───────│
│── close stdin ─────────────> │ (process should exit)
│ │ (process exits)
HydroSym waits up to 5 seconds for the process to exit after sending Finalize. If it does not exit, HydroSym kills it.
Implementing in C¶
With the SDK, the process transport is handled transparently. Use the ProcessPluginHost entry point:
The SDK reads requests from stdin, dispatches them to your plugin class, and writes responses to stdout. Your plugin class is identical to a DLL plugin — same HydroSymPluginBase overrides.
Implementing in another language¶
For non-.NET plugins, implement the JSON line protocol directly.
Python example:
import sys
import json
def handle_get_info(params):
return {
"name": "Python ERP Connector",
"vendor": "Acme",
"version": "1.0.0",
"apiVersion": 1,
"capabilities": ["getComponentParameters"],
}
def handle_get_component_parameters(params):
code = params.get("articleCode", "")
if code == "HV-301":
return {"articleCode": code, "status": "released", "price": 1250.0, "currency": "EUR"}
return None # will return notSupported
HANDLERS = {
"GetInfo": handle_get_info,
"Initialize": lambda p: {},
"Finalize": lambda p: {},
"GetComponentParameters": handle_get_component_parameters,
}
for line in sys.stdin:
req = json.loads(line.strip())
method = req.get("method")
req_id = req.get("id")
handler = HANDLERS.get(method)
if handler is None:
resp = {"id": req_id, "notSupported": True}
else:
try:
result = handler(req.get("params", {}))
if result is None:
resp = {"id": req_id, "notSupported": True}
else:
resp = {"id": req_id, "result": result}
except Exception as e:
resp = {"id": req_id, "error": {"code": "INTERNAL_ERROR", "message": str(e)}}
sys.stdout.write(json.dumps(resp) + "\n")
sys.stdout.flush()
Example session transcript¶
→ {"id":1,"method":"GetInfo","params":{}}
← {"id":1,"result":{"name":"Acme ERP","apiVersion":1,"capabilities":["getComponentParameters"]}}
→ {"id":2,"method":"Initialize","params":{"hydrosymVersion":"2025.55.0.0","configPath":"C:\\...\\acme.ini","windowsUser":"wouter"}}
← {"id":2,"result":{}}
→ {"id":3,"method":"GetComponentParameters","params":{"articleCode":"HV-301"}}
← {"id":3,"result":{"articleCode":"HV-301","status":"released","price":1250.0,"currency":"EUR"}}
→ {"id":4,"method":"CheckOut","params":{"filePath":"C:\\Projects\\valve.hsc"}}
← {"id":4,"notSupported":true}
→ {"id":5,"method":"Finalize","params":{}}
← {"id":5,"result":{}}
Testing from the command line¶
You can test your plugin process by piping JSON manually:
Or interactively on Windows:
$proc = Start-Process AcmeErpPlugin.exe -RedirectStandardInput -RedirectStandardOutput -PassThru
$proc.StandardInput.WriteLine('{"id":1,"method":"GetInfo","params":{}}')
$proc.StandardOutput.ReadLine()
Stderr for logging¶
Write diagnostic output to stderr. HydroSym captures it and appends it to the HydroSym log:
Do not write progress or debug output to stdout — it will be interpreted as a malformed JSON response.