Wangcaisoft.DotNet.BaanWindowsLib
0.1.0
dotnet add package Wangcaisoft.DotNet.BaanWindowsLib --version 0.1.0
NuGet\Install-Package Wangcaisoft.DotNet.BaanWindowsLib -Version 0.1.0
<PackageReference Include="Wangcaisoft.DotNet.BaanWindowsLib" Version="0.1.0" />
<PackageVersion Include="Wangcaisoft.DotNet.BaanWindowsLib" Version="0.1.0" />
<PackageReference Include="Wangcaisoft.DotNet.BaanWindowsLib" />
paket add Wangcaisoft.DotNet.BaanWindowsLib --version 0.1.0
#r "nuget: Wangcaisoft.DotNet.BaanWindowsLib, 0.1.0"
#:package Wangcaisoft.DotNet.BaanWindowsLib@0.1.0
#addin nuget:?package=Wangcaisoft.DotNet.BaanWindowsLib&version=0.1.0
#tool nuget:?package=Wangcaisoft.DotNet.BaanWindowsLib&version=0.1.0
Wangcaisoft.DotNet.BaanWindowsLib
An enhanced and extended .NET port of mfussenegger/baanlib
β a wrapper around the Baan/Infor LN OLE automation object (Baan.Application.*) that adds a more
natural .NET API plus several productivity enhancements.
Instead of having to build the call string by hand:
// The "raw" OLE automation equivalent
dynamic baan = Activator.CreateInstance(Type.GetTypeFromProgID("Baan.Application.erpln"));
baan.Timeout = 3600;
baan.ParseExecFunction("odll_name", "some.function.name(\"with\", \"a\", \"few\", \"arguments\")");
baan.Quit();
β¦Wangcaisoft.DotNet.BaanWindowsLib lets you write it the natural way, with the DLL/object name, function path and
arguments expressed as chained member access:
using Wangcaisoft.DotNet.BaanWindowsLib;
using (var b = new Baan("Baan.Application.erpln"))
{
b.odll_name.some.function.name("with", "a", "few", "arguments");
int var = 1;
string foo = "test";
b.odll_name.some.function.name(var, foo);
}
About this enhanced .NET port
Wangcaisoft.DotNet.BaanWindowsLib is not a line-by-line translation of
mfussenegger/baanlib. It is an enhanced and extended
.NET port: it keeps the original's simple OLE-automation model and API semantics, while adding
.NET-idiomatic productivity features on top:
- Multi-targeting β runs on .NET Framework 4.6.2+, and .NET 6 / 8 / 10 (Windows).
- Structured return values β
BaanResultdecodes"code|message"status strings. - Fail-fast option β
ThrowOnErrorResultthrowsBaanResultExceptionon a non-zero code. - Single-parameter pattern β pack action + data into one string, decompose on the ERP side.
- Bilingual documentation and GitHub Actions CI.
The original Baan/LN OLE behavior (ParseExecFunction, Error codes, etc.) is preserved exactly.
Use Cases
This library is designed for integrating a .NET application (Web API, Windows Service, desktop client, scheduled job, etc.) with Infor LN (formerly Baan) ERP. In Infor LN / Baan you develop your business logic as a 4GL DLL (object) and expose its functions through the OLE automation object. From .NET you can then call those DLL functions directly β no manual OLE call-string building, no COM plumbing.
Typical Scenarios
- Trigger Infor LN business actions (create order, post, status changeβ¦) from an external system.
- Batch data integration / interface sync, driven from the .NET side.
- Orchestrate multiple DLL-function calls from .NET into a cross-system workflow.
Quick Start
dotnet add package Wangcaisoft.DotNet.BaanWindowsLib
using Wangcaisoft.DotNet.BaanWindowsLib;
// Connect to the Infor LN / Baan OLE automation object
using (var b = new Baan("Baan.Application.erpln"))
{
// DLL/object name, function path and arguments are expressed as chained member access
b.odll_name.some.function.name("with", "a", "few", "arguments");
int var = 1;
string foo = "test";
b.odll_name.some.function.name(var, foo);
}
To further reduce typing, the API can also be used like this:
using (var b = new Baan("Baan.Application.erpln"))
{
dynamic f = b.ottstpapihand;
var put = f.stpapi.put.field;
put("sessioncode", "fieldname1", "value1");
put("sessioncode", "fieldname2", "value2");
f.end.session("sessioncode");
}
Single-Parameter Simplification Pattern
When a 4GL DLL function has many or frequently-changing parameters, pack the action + data into a single string argument and decompose it inside the DLL function to run the relevant logic. This keeps the .NET call site stable while the ERP-side logic can evolve without changing the interface signature.
using Wangcaisoft.DotNet.BaanWindowsLib;
using (var b = new Baan("Baan.Application.erpln"))
{
// Single parameter: action and data packed with a delimiter
var result = b.odll_name.process("CREATE|ORDER|1001|2026-07-11");
}
Corresponding Infor LN / Baan 4GL DLL side (illustrative):
function extern long process(string packed)
{
string action(10), entity(10), key(20), dt(20)
long ret
action = substr$(packed, 1, pos(packed, "|") - 1)
packed = substr$(packed, pos(packed, "|") + 1)
entity = substr$(packed, 1, pos(packed, "|") - 1)
| ... keep decomposing and execute the matching logic
if (action = "CREATE") {
ret = dal.create(entity, key, dt)
}
return(ret)
}
Return Value Convention ("code|message")
If your 4GL DLL returns a status string in the form "<code>|<message>" β for example
"0|Success" or "1|Error" β decode it into a structured result with BaanResult instead of
splitting the string by hand:
using Wangcaisoft.DotNet.BaanWindowsLib;
using (var b = new Baan("Baan.Application.erpln"))
{
var raw = b.odll_name.process("200|CREATE|ORDER|1001"); // returns "0|Success"
var r = BaanResult.Parse(raw);
if (!r.IsSuccess)
Console.WriteLine($"Error {r.Code}: {r.Message}");
}
BaanResult decodes: "0|Success" β Code = 0, Message = "Success"; "1|Error" β
Code = 1, Message = "Error"; a bare number is treated as the code; a string without a delimiter
is treated as a success message. The delimiter is configurable: BaanResult.Parse(raw, ';').
Fail-fast with ThrowOnErrorResult
To make a non-zero code throw automatically instead of returning the raw value, enable
ThrowOnErrorResult (off by default, so existing behavior is unchanged):
using (var b = new Baan("Baan.Application.erpln"))
{
b.ThrowOnErrorResult = true;
b.odll_name.process("200|CREATE|ORDER|1001"); // throws BaanResultException on a "1|..." result
}
Company Configuration (BSE_COMPNR)
This library only forwards ParseExecFunction(dllName, method) β it does not pass a company
number itself. The company is bound to the OLE connection / environment that the ProgID points to,
i.e. it is decided on the Baan Worktop (BW) client side, not in .NET code.
The official way to set the company is via the BW environment's set command using the
environment variable:
-set BSE_COMPNR=<compnr>
- Infor ERP Baan 5.0c and later (including Infor LN): write
-set BSE_COMPNR=200 - Infor ERP Baan IVc4: the first occurrence must be written as
"-- -set BSE_COMPNR=200"(note the leading--)
Place it in the Environment (Configuration) command / arguments of the BW client. In
BECS (BW Environment and Configuration Selector) you register an OLE automation name per
environment, and that environment's company is fixed by BSE_COMPNR.
Official reference β Infor LN 10.4 BW install, set command
Related set variables you can use in the same place:
-set PACKAGE_COMB=<packcomb>β overrides the package combination (overrides user data)set USER=<username>β changes the user who starts the bshell
Approach A β one company = one OLE automation name (recommended)
Register a separate OLE automation name per company in BECS, each environment configured with its
own -set BSE_COMPNR=.... The .NET side then just picks the ProgID:
string progId = companyNo == 100
? "Baan.Application.erpln.c100"
: "Baan.Application.erpln.c200";
using (var b = new Baan(progId))
{
b.odll_name.some.function.name("arg1", "arg2");
}
Approach B β switch company at runtime (single-parameter pattern)
If you do not want one environment per company, run under a single environment and pass the company as the first parameter to your own 4GL DLL, which switches company internally before running the logic:
using (var b = new Baan("Baan.Application.erpln"))
{
// first parameter = company number, the rest is business data
var result = b.odll_name.process("200|CREATE|ORDER|1001|2026-07-11");
}
Note:
BSE_COMPNRcan only be set when the bshell starts, so it must be configured on the BW side (Approach A) or handled inside the 4GL DLL (Approach B). It cannot be set from .NET after theBaanobject is created.
Requirements
- Windows only. The library talks to the Baan/Infor LN OLE automation object via COM.
- One of the following runtimes:
- .NET Framework 4.6.2 or later
- .NET 6 (Windows), .NET 8 (Windows) or .NET 10 (Windows)
- The Baan/Infor LN client must be installed and its
Baan.Application.*OLE class registered (only required at runtime, not for building or running the unit tests).
Limitations
- Windows only. The library talks to the Baan/Infor LN OLE automation object
(
Baan.Application.*) through COM, which is only available on Windows. - On-premise / local deployment only β Infor LN Cloud is NOT supported. The library depends on
the Baan Worktop (BW) client, which provides the
Baan.Application.*OLE automation object. Infor LN Cloud (the multi-tenant cloud deployment) does not ship or support the BW client, so theBaan.Application.*OLE class is unavailable there. For LN Cloud integration, use Infor ION or the LN SOAP / REST web services instead.
Installation
dotnet add package Wangcaisoft.DotNet.BaanWindowsLib
How it works
Baan derives from System.Dynamic.DynamicObject. Any attribute access that is not one of the
well-known COM properties (Timeout, ReturnValue, FunctionCall, ReturnCall, Binary)
starts a chain. When the chain is finally invoked, Wangcaisoft.DotNet.BaanWindowsLib builds the Baan function string
(numbers are emitted verbatim, everything else is wrapped in double quotes) and calls
ParseExecFunction(dllName, methodName).
After the call it reads the OLE Error property:
Error |
Throws |
|---|---|
-1 |
UnknownDllException |
-2 |
UnknownMethodException |
| other | returns ReturnValue |
This mirrors the upstream Python implementation exactly.
API
public class Baan : DynamicObject, IDisposable
{
public Baan(string progId); // creates the OLE object, Timeout = 3600
public Baan(string progId, object baan); // wraps an existing object (testing/advanced)
public void Close(); // calls Quit()
public void Dispose(); // -> Close()
public object Timeout { get; set; } // OLE Timeout (seconds)
public object ReturnValue { get; } // last ReturnValue
public object FunctionCall { get; } // last function call string
public object ReturnCall { get; } // last return call string
public object Binary { get; set; } // binary mode flag
public bool ThrowOnErrorResult { get; set; } // throw BaanResultException on non-zero code
}
public class BaanWrapper : DynamicObject
{
public string DllName; // first segment of the chained path
public string MethodName; // remaining segments, joined by '.'
public string GetCallingMethod(params object[] args); // builds the call string
}
public readonly struct BaanResult
{
public int Code { get; } // 0 = success
public string Message { get; }
public string Raw { get; } // original return value
public bool IsSuccess { get; } // Code == 0
public static BaanResult Parse(object returnValue, char delimiter = '|');
}
public class UnknownDllException : Exception { }
public class UnknownMethodException : Exception { }
public class BaanResultException : Exception { public int Code { get; } }
Building and testing
You can build/test a single project or the whole solution (DotNet.BaanWindowsLib.sln):
# Restore, build and test all target frameworks (via the solution)
dotnet build DotNet.BaanWindowsLib.sln -c Release
dotnet test DotNet.BaanWindowsLib.sln -c Release
# Or target a single project / framework
dotnet build src/DotNet.BaanWindowsLib/DotNet.BaanWindowsLib.csproj -c Release -f net8.0-windows
# Produce a NuGet package
dotnet pack src/DotNet.BaanWindowsLib/DotNet.BaanWindowsLib.csproj -c Release
The unit tests use an in-memory fake of the OLE object, so they run without a real Baan/Infor LN installation (and on CI).
License
MIT. This is an independent .NET port of mfussenegger/baanlib (also MIT).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0-windows7.0 is compatible. net7.0-windows was computed. net8.0-windows was computed. net8.0-windows7.0 is compatible. net9.0-windows was computed. net10.0-windows was computed. net10.0-windows7.0 is compatible. |
| .NET Framework | net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 4.6.2
- Microsoft.CSharp (>= 4.7.0)
-
net10.0-windows7.0
- No dependencies.
-
net6.0-windows7.0
- No dependencies.
-
net8.0-windows7.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 133 | 7/13/2026 |