Architecture¶
Overview¶
HydroSym loads one plugin per installation. The plugin can be:
- A native DLL loaded in-process (
Type=DLLin the INI). - A separate executable launched as a child process (
Type=Process).
Both transports use the same method names and JSON schemas. Your plugin code is identical either way; only the transport layer differs.
graph LR
HS[HydroSym] -->|"Invoke(Method, JSON)"| DLL[Plugin DLL\nin-process]
DLL -->|"Callback(JSON)"| HS
HS2[HydroSym] -->|"JSON-RPC over stdin"| PROC[Plugin Process\nout-of-process]
PROC -->|"JSON-RPC over stdout"| HS2
The single entry point¶
For DLL plugins, HydroSym calls one exported function:
void HydroSymPlugin_Invoke(
const char* method, // null-terminated UTF-8 method name
const char* requestJson, // null-terminated UTF-8 JSON payload
ResponseCallback callback, // function pointer to call with the result
void* callbackContext // opaque value, pass back unchanged
);
The callback signature:
typedef void (*ResponseCallback)(
int resultCode, // 0=OK, 1=ERROR, 2=NOT_SUPPORTED
const char* responseJson, // null-terminated UTF-8 JSON
void* callbackContext // the value passed to Invoke
);
The plugin must call the callback exactly once per Invoke call, before returning. The response JSON buffer is only valid until Invoke returns.
The SDK handles all of this. You override methods on HydroSymPluginBase and never interact with the entry point directly.
JSON as the data format¶
Every request and response is a JSON object. Advantages:
- Language agnostic — any language that can export a C-callable function can implement a plugin.
- Extensible — new optional fields can be added without breaking existing plugins.
- Inspectable — you can log and replay exact calls for debugging.
String encoding on the wire is UTF-8 for DLL transport and UTF-8 JSON-RPC for process transport.
The callback pattern¶
The callback design lets plugins perform asynchronous work internally (e.g., an await call to a database) while keeping the C entry point synchronous. The SDK marshals the callback thread-safely.
Thread safety
HydroSym calls Invoke from its main UI thread. Do not block the callback for long operations — use async/await within the SDK's task scheduler, which resolves before the callback is fired. Long-blocking calls will freeze HydroSym's UI.
Memory ownership¶
- HydroSym owns the
methodandrequestJsonbuffers. They are valid only for the duration of theInvokecall. - The plugin owns the
responseJsonbuffer. It must remain valid until the callback returns. - The SDK manages all buffer lifetimes automatically when you use
HydroSymPluginBase.
See Transport: DLL for low-level details.
The capabilities array¶
Your plugin declares which methods it supports in GetInfo.capabilities. HydroSym uses this to decide what to call:
- Methods not listed are never called; HydroSym treats them as
NOT_SUPPORTEDwithout invoking the plugin. - Methods listed will be called at the appropriate times — HydroSym assumes you handle them.
This means you only implement what you need. A simple read-only ERP connector might only implement getComponentParameters and getProjectVariables.
Transport selection¶
| DLL | Process | |
|---|---|---|
| Performance | Fast (in-process, no serialization overhead) | Slower (IPC, process startup) |
| Isolation | Crash in plugin = crash in HydroSym | Plugin crash is isolated |
| NativeAOT required | Yes | No |
| Debugging | Attach to HydroSym process | Run plugin standalone, attach separately |
| Best for | Production use | Plugins that need full .NET reflection, Java, Python, etc. |
Most plugins should use DLL transport. Use process transport only if NativeAOT is not an option.