Using GetModulePara in C#
A TcCOM object makes its parameters available through the ITcModuleInstance interface. Each parameter is addressed via a numeric **parameter ID (PID)**, which is derived from the module's TMC description. This article shows how to read parameter values using the Automation Interface in C# and correctly convert them to the appropriate .NET type.
The key issue is that GetModulePara returns the data as raw, untyped bytes. Without knowing the parameter type, these bytes cannot be interpreted meaningfully. The following three rules show how to obtain the correct value depending on the situation.
Requirements
This requires an open TwinCAT project containing a TcCOM object whose Tree Item already exists. In the samples, treeItem is the object obtained via LookupTreeItem, which is cast to ITcModuleInstance:
var mi = (ITcModuleInstance)treeItem;
The pid used (e.g., 0x050300B1) is the parameter ID from the module's TMC description.
What GetModulePara returns
GetModulePara returns a VARIANT that contains a byte array with VT_ARRAY | VT_UI1. These are raw bytes in little-endian format, without any type information. In C#, this VARIANT returned as a byte[].
Because the bytes do not contain any type information, the caller must know how many bytes the parameter occupies and how they should be interpreted. Without the module's TMC description – or a prior call to GetTypeOfModulePara – the bytes cannot be interpreted correctly.
Which function for which situation
The following table summarizes which of the three access functions is suitable for which parameter type:
|
Situation |
Recommendation |
|---|---|
|
The parameters are |
|
|
The parameter is |
|
|
Type is unknown |
Call |
GetModuleParaDWORD for scalar parameters
For all scalar parameters up to 4 bytes in size, GetModuleParaDWORD is the simplest choice. The function returns an uint directly, so byte arrays are not necessary. The return value is then cast to the parameter's actual type:
var mi = (ITcModuleInstance)treeItem;
uint pid = 0x050300B1;
uint rawValue = mi.GetModuleParaDWORD(pid);
// Cast according to type:
int asInt = (int)rawValue;
bool asBool = rawValue != 0;
GetModulePara + BitConverter for larger types
Parameters larger than 4 bytes – such as LREAL (8 bytes) or LINT (8 bytes) – as well as structs cannot be read using GetModuleParaDWORD. Here, you retrieve the raw byte array using GetModulePara and convert it to the target type using BitConverter. It is important to choose the BitConverter method that matches the parameter type, since the bytes are in little-endian format:
var mi = (ITcModuleInstance)treeItem;
uint pid = 0x050300B1;
byte[] bytes = (byte[])mi.GetModulePara(pid);
// LREAL (= double, 8 bytes):
double position = BitConverter.ToDouble(bytes, 0);
// LINT (= long, 8 bytes):
long lint = BitConverter.ToInt64(bytes, 0);
// DINT (= int, 4 bytes):
int dint = BitConverter.ToInt32(bytes, 0);
GetTypeOfModulePara + ITcTypeInstance.Name
If the type of a parameter is unknown at development time, it can be determined at runtime using GetTypeOfModulePara. The function returns a ITcTypeInstance object whose .Name property returns the type name directly as a readable string – there is no need to compare it against magic numbers. Using this type name, you can then select the appropriate conversion for the byte array returned by GetModulePara:
var mi = (ITcModuleInstance)treeItem;
uint pid = 0x050300B1;
mi.GetTypeOfModulePara(pid, out ITcTypeInstance typeInstance);
string typeName = typeInstance.Name; // e.g. "LREAL", "UDINT", "BOOL", "DINT"
byte[] bytes = (byte[])mi.GetModulePara(pid);
object value = typeName switch {
"LREAL" => BitConverter.ToDouble(bytes, 0),
"LINT" => BitConverter.ToInt64(bytes, 0),
"DINT" => BitConverter.ToInt32(bytes, 0),
"UDINT" or "DWORD" => BitConverter.ToUInt32(bytes, 0),
"INT" => BitConverter.ToInt16(bytes, 0),
"UINT" or "WORD" => BitConverter.ToUInt16(bytes, 0),
"BOOL" => bytes[0] != 0,
_ => bytes // Struct or unknown type — return raw bytes
};
The _ branch catches structs and unknown types and returns the raw byte array. For composite types, you can determine the exact set-up using ITcTypeInstance.IecDeclaration if necessary and parse the byte array accordingly.
![]() | This makes rule 3 the most robust approach when the parameter type is not known at development time. It works for all scalar types without requiring prior knowledge of the byte size. For known scalar parameters, rule 1 remains the simplest solution. For details on the returned type descriptor, see the article ITcTypeInstance. |
