Skip to content

Migration from the Legacy TFMS_ API

If you have an existing DLL integration built against the old HydroSym TFMS_ function pointer API, this guide walks you through migrating to the new Plugin API.

Method mapping

Old function New method Notes
TFMS_MeridianGetPropertiesFunc GetProjectVariables Returns a map instead of writing to a PChar buffer
TFMS_MeridianSetPropertiesFunc SetProjectVariables Receives a map instead of reading PChar pairs
TFMS_MeridianUploadFileFunc UploadDrawing or SaveFile Use UploadDrawing for PDF/DXF uploads; SaveFile for vault copy
TFMS_SQLGetDataFunc GetComponentParameters Returns structured JSON instead of writing to result buffers
TFMS_TeamcenterGetSystemPartFunc GetComponentParameters Same — one method handles all ERP backends
TFMS_TeamcenterBeginBomFunc + TFMS_NewBomItemFunc + TFMS_TeamcenterEndBomFunc UploadBom Single call with a complete list of BomItem objects
TFMS_PDMSystemGetFileStatusFunc GetFileStatus Returns a structured status object
TFMS_PDMSystemSaveFileFunc SaveFile Cleaner request/response; no buffer management
TFMS_PDMSystemCheckOutFileFunc CheckOut
TFMS_PDMSystemCheckInFileFunc CheckIn

There is no direct equivalent of the old DLL initialization (DllMain / global setup). Use Initialize instead.

Key differences

1. JSON instead of PChar buffer management

Old API:

// Caller allocates a buffer, DLL writes to it
procedure TFMS_MeridianGetProperties(
    AProjectPath: PChar;
    ABuffer: PChar; ABufferSize: Integer);

New API — return a JSON object, no buffer management:

{
  "variables": {
    "CustomerName": "Bosch Rexroth",
    "Revision": "C"
  }
}

Your C# code simply returns a Dictionary<string, string>. The SDK handles serialization.

2. Single entry point

Old API: many exported functions, each registered as a function pointer.

New API: one exported function HydroSymPlugin_Invoke(method, requestJson, callback, ctx). The SDK handles dispatch to your overridden methods.

You do not need to manage DLL exports manually.

3. Structured error responses

Old API: boolean return value (true = OK, false = error), with error text in a shared buffer.

New API: PluginException with an error code and message:

throw new PluginException("NOT_FOUND", "Article HV-999 not found.");

4. Menu items declared in GetInfo

Old API: menu items were hardcoded in the DLL and registered via function pointers at startup.

New API: declare all menu items in GetInfo.menus. HydroSym reads them at load time.

Old code:

TFMS_RegisterMenuItem('acme-search', 'Search Acme ERP...', 'plugin', ...);

New code:

Menus = [ new MenuItem { Id = "acme-search", Label = "Search Acme ERP...", Location = "plugin" } ]

Step-by-step migration

Step 1: Create the new project

dotnet new hydrosym-plugin -n AcmeErpPlugin

Step 2: Map your capabilities

Look at your old DLL and note which TFMS_ functions you implemented. Translate to capabilities:

Capabilities =
[
    "getComponentParameters",  // was: TFMS_SQLGetDataFunc
    "getProjectVariables",     // was: TFMS_MeridianGetPropertiesFunc
    "setProjectVariables",     // was: TFMS_MeridianSetPropertiesFunc
    "uploadBom",               // was: TFMS_Teamcenter*BomFunc
]

Step 3: Port Initialize

Your old DLL probably had global variables initialized in DllMain or a setup function. Move this to Initialize:

public override void Initialize(InitializeRequest request)
{
    // was: global DllMain setup
    _connectionString = ReadConfig(request.ConfigPath);
    _db = new SqlConnection(_connectionString);
    _db.Open();
}

Step 4: Port GetProjectVariables

// Old: TFMS_MeridianGetProperties(projectPath, buffer, bufferSize)
// New:
public override GetProjectVariablesResponse GetProjectVariables(
    GetProjectVariablesRequest request)
{
    var props = QueryErpProperties(request.ProjectPath);
    return new GetProjectVariablesResponse { Variables = props };
}

Step 5: Port GetComponentParameters

// Old: TFMS_SQLGetData(articleCode, resultBuffer, bufferSize)
// New:
public override GetComponentParametersResponse GetComponentParameters(
    GetComponentParametersRequest request)
{
    var row = QueryErpArticle(request.ArticleCode);
    return new GetComponentParametersResponse
    {
        ArticleCode = request.ArticleCode,
        Status      = row.Status,
        Price       = row.Price,
        // ...
    };
}

Step 6: Port UploadBom

The old API used three separate calls (BeginBom / NewBomItem × N / EndBom). The new API receives the complete list in one call:

// Old: three-phase call
// New:
public override UploadBomResponse UploadBom(UploadBomRequest request)
{
    foreach (var item in request.Items ?? [])
    {
        InsertBomLine(item.ArticleCode, item.Quantity, item.Position);
    }
    return new UploadBomResponse { BomId = CommitBom() };
}

Step 7: Build and test

dotnet publish -c Release
HydroSymPluginTester.exe --dll AcmeErpPlugin.dll --call GetInfo

Step 8: Configure HydroSym

Replace the old [Plugin] INI settings with the new format:

; Old
PluginDLL=C:\...\OldPlugin.dll
PluginType=TFMS

; New
[Plugin]
Type=DLL
Path=C:\ProgramData\HydroSym\plugins\AcmeErpPlugin\AcmeErpPlugin.dll

Backward compatibility

HydroSym detects which API a DLL implements. If the DLL exports HydroSymPlugin_Invoke, it is treated as a new-style plugin. If it exports TFMS_RegisterPlugin, it is treated as a legacy plugin. Both can coexist within the same HydroSym installation for a transition period.

Once you have migrated and verified the new plugin, remove the old DLL from the INI.