LIRA‑FEM allows converting files of certain formats, such as .DXF or .IFC, into design models, as well as modifying design models using LIRA‑FEM API methods.
LIRA‑FEM extensions, being part of the LIRA‑FEM API, allow external programs to connect their commands to the LIRA‑FEM ribbon, their calculations to any stage of LIRA‑FEM calculations, their reports to the LIRA‑FEM documentation system, include their data in the Lir‑file, and transfer it to external applications with editing support.
This article explains how to create LIRA‑FEM extensions and use the capabilities listed above. The article is structured so that simpler techniques are described first, and more complex ones follow. The simplest extension can be created using only the information from the «Quick Start» subsection. At the end of the article there is a reference guide for connecting extension modules written in different programming languages.
- Quick Start
- Supported Programming Languages
- Extension Identifier
- Registering Extension Functions for Launching from LIRA-FEM
- Managing the Ribbon Button Appearance
- Calling Context Help
- Visualizing Extension Data with Mosaics
- Deeper Integration
- LIRA-FEM API Objects and Interfaces for Extensions
- Implementing Programming Interfaces in a LIRA-FEM Extension
- Creating an Extension Object Instance and Connecting It to LIRA-FEM
- Main Extension Programming Interface ILiraExtension
- Embedding Extension Data into LIR Files Using the ILiraExtensionTableHelper Interface
- Result File Naming Requirements
- Documenting Result Tables and Input Data Tables
- Creating Updatable Screenshots for the Report Book
- Other LIRA-FEM API Object Methods for Extensions
- Standalone Extension Operation
- Reference for Windows Registry Keys and Parameters Related to LIRA-FEM Extensions
- Technical Specifications of LIRA-FEM Extension Technology
The simplest LIRA-FEM extension consists of an executable file and a registry entry. Let us create an extension that displays a traditional greeting like «Hello, World!» via a button that appears on the LIRA-FEM ribbon. To do this, create a text file with the HelloLira function in VBScript, as in the listing below, and save it at the path C:\Users\Public\Documents\Hello.vbs.
Sub HelloLira
MsgBox "Well, hello there, LIRA!"
End Sub
Now we need to create a button on the LIRA-FEM ribbon that will call this function. To do this, we need to add several keys and values to the Windows registry. The easiest way is to list the required keys with their values in a file with the *.reg extension and then run it. Create a text file C:\Users\Public\Documents\Hello.reg with the content shown in the listing below.
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\hli] @="Script:VBS" [HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\hli\HelloLira] @="C:\\Users\\Public\\Documents\\Hello.vbs"
Now we need to run it. To do this, open the Run dialog («Run») using the Win+R shortcut, type the path to the registry file C:\Users\Public\Documents\Hello.reg in the window that appears, and click OK.
The operating system will prompt for confirmation — allow it. After that, the Registry Editor will again prompt for permission to add the required keys and values — allow this too by clicking «Yes».
Finally, the Registry Editor should show a confirmation window. This window can simply be closed.
Now let us verify that the button was added to the LIRA-FEM ribbon. Launch LIRA-FEM. If it was already running during the steps above, restart it. After launching, you can see that a new «Tools» tab has appeared on the LIRA-FEM ribbon, with a button that has no icon but is labeled «HelloLira». A «Tools hli» menu with a «HelloLira» item has also appeared.
Let us click the «HelloLira» button to verify that it actually calls our HelloLira function. After clicking it, a window appears requesting permission to load into memory an executable module that is unknown to LIRA-FEM and has no trusted digital signature. Since we just wrote this module ourselves, there is nothing to worry about — simply allow it by selecting the option «Allow this LIRA-FEM extension to load and run, and do not ask again» and clicking OK.
After that, our function executes, showing the message box «Well, hello there, LIRA!».
Click OK in this message box and run the command again. This time the permission window does not appear and the «Well, hello there, LIRA!» box is invoked immediately.
A button without an icon on an empty «Tools» tab may seem overly austere at first glance, but we spent minimal effort creating it. The capabilities of LIRA-FEM extensions for creating ribbon buttons are in no way inferior to those of LIRA-FEM itself, as evidenced by the «Timber» tab, which is built entirely by the extension of the same name.
New tabs, panels, and command icons — all of this can be added to the LIRA-FEM ribbon using API functions or Windows registry parameters. A detailed list is provided at the end of this help section.
In the «Quick Start» subsection we learned a quick way to create LIRA-FEM extensions, but it is not always convenient to use extensions only through ribbon buttons. The following subsections describe important details and useful LIRA-FEM API capabilities for editing the design model.
The simplest extension we created in the «Quick Start» subsection was written in the interpreted language VBScript. To tell LIRA-FEM which language interpreter to load, we added the parameter @="Script:VBS" to the registry. In addition to VBScript, LIRA-FEM supports the following interpreted programming languages for creating extensions: JScript and V8Script. While VBScript and JScript are well-known languages long built into the Microsoft Windows operating system, V8Script, or V8 JavaScript, is a relatively young variant of JavaScript developed by Google. If we had written our extension in JScript or V8, we would have needed to specify @="Script:JS" or @="Script:V8" respectively instead of @="Script:VBS" in the registry file.
Besides the three interpreted languages, LIRA-FEM supports extensions written in compiled programming languages that support the COM (Component Object Model) or .NET technology, which includes practically all modern programming languages. Extensions written in compiled languages can be of two types: an executable module *.EXE or a dynamic link library *.DLL. For applications, dynamic libraries are preferable, as they allow calling LIRA-FEM API methods without additional parameter marshaling. While a single LIRA-FEM API method call from an *.EXE module may take milliseconds, calling the same method from a *.DLL takes nanoseconds — millions of times faster. For registering extensions in executable modules *.EXE, the registry parameter @="Exe:App" is provided. Dynamic libraries *.DLL can be compiled to .NET CLR code (e.g., C# or VB.NET) or to native code (e.g., C, C++). Registry parameters for registering *.DLL extensions in LIRA-FEM depend on the chosen language. For a class library written in .NET CLR C# or VB.NET, use @="Dll:Net4"; for a native code library written in C/C++, the registration string is either @="Dll:stdcall" or @="Dll:cdecl" depending on the calling convention. The stdcall convention is most common, but in C/C++ requires the __stdcall keyword on function declarations. If __stdcall is absent, use @="Dll:cdecl".
In the «Quick Start» subsection, to identify our extension we added the parameter hli to the Windows registry key HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS. As you can see, this parameter consists of three Latin letters. This restriction is related to file extensions and will be explained in more detail in the «Result File Naming Requirements» subsection below.
The extension identifier is a three-character parameter that may consist only of Latin letters and digits.
To register a LIRA-FEM extension, create a new subkey in the Windows registry key HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS with the name — the extension identifier.
As the default value of the registry key — the extension identifier — specify the loading instruction for the extension, one of the following string values:
*.DLL compiled to CLR code (.NET Framework 4).*.DLL (C/C++), functions use the stdcall calling convention.*.DLL (C/C++), functions use the cdecl calling convention..EXE.In the «Quick Start» subsection we created a custom extension consisting of a single function launched when the corresponding ribbon button is pressed. To tell the program which ribbon button to create and which function to call when it is pressed, we created the HelloLira subkey in the registry key with the name — the extension identifier HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\hli.
Importantly, the subkey name HelloLira matched the function name in the file C:\Users\Public\Documents\Hello.vbs, and we specified the path to this file as the default value of the subkey:
[HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\hli\HelloLira] @="C:\\Users\\Public\\Documents\\Hello.vbs"
This told LIRA-FEM in which file and under which name to look for the function. Since we did not specify when to call the HelloLira function, the program used the default behavior — placing a button with that name on the default «Tools» tab.
Calling an extension API function by pressing a ribbon button or selecting a menu item is just one way of connecting extension functions to LIRA-FEM. To specify the method and timing of the extension function call, create a string parameter named «CallAfter» in the Windows registry key with the function name; its value can be one of the following:
If none of the above parameters is specified, the default value Button is used.
It may happen that one extension function must be called after another's function at the same stage. In that case, it is important to specify not only the stage but also the execution order. To do this, after any value from AppStartup to AnalysisCSV, a launch moment can be specified as a double number after the «+» sign. For example: one extension can specify AnalysisFEA + 10.0 and another AnalysisFEA + 20.0. The larger the double number, the later the extension function starts.
Extension function registration can be performed not only using registry keys but also using LIRA-FEM API methods, which provide more capabilities. However, at least one extension function must be registered in the Windows registry to pass control to it and enable the extension to call LIRA-FEM API methods. This will typically be the function specified with "CallAfter"="AppStartup".
In the example in the «Quick Start» subsection, we created a button that received a default name, was placed on the default LIRA-FEM ribbon tab, and had no icon at all. All of this is easy to configure using the appropriate parameters of the Windows registry key with the name — the extension identifier and the name of the called extension function; in our example these are HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\hli and HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\hli\HelloLira.
The registry key with the name — the extension identifier — allows the following string parameters:
The registry key with the name — the extension function name — allows the following string parameters:
The values of RibbonTabImageListSmall, RibbonTabImageListLarge, RibbonImageSmall, RibbonImageLarge may contain a path to a .PNG, .BMP, or .ICO image for one or more buttons. You can specify either a full path to an image file on disk, or a full path to an image stored in the resources of a native application or library .EXE or .DLL. In the latter case, the path to the .EXE or .DLL file must be followed by a slash and the resource image identifier. For example: C:\path\to\dll\file.dll/#1001 — resource with numeric identifier «1001». Another example: C:\path\to\dll\file.dll/PNG1 — resource with string identifier «PNG1». A resource type hint can be added: C:\path\to\dll\file.dll/PNG/#1001 — a .PNG resource with numeric identifier «1001». Or C:\path\to\dll\file.dll/PNG/PNG1 — a .PNG resource with string identifier «PNG1».
Instead of /PNG/ you can specify /BMP/ or /2/ for .BMP images, or /ICO/ or /14/ for .ICO images. If no hint is given, .PNG is loaded; if not found — .BMP; if not found — .ICO.
RibbonTabImageListSmall, RibbonTabImageListLarge must contain a horizontal sprite of images sized 16x16 and 32x32 respectively. If «Large ribbon icons» mode is enabled, the sprite must consist of 24x24 and 48x48 images. Images of other sizes will be scaled to the required dimensions.
RibbonImageSmall, RibbonImageLarge may contain not only a path to the corresponding button image, but also the index of the image in the sprite of RibbonTabImageListSmall and RibbonTabImageListLarge respectively, starting from 0. If all buttons have RibbonImageSmall and RibbonImageLarge specified as image paths, there is no need to create button sprites and specify them in RibbonTabImageListSmall, RibbonTabImageListLarge. Conversely, if an image already exists in the sprite, there is no need to create a separate image for the button — simply specify the image index in the appropriate sprite parameter.
If you are creating an extension that will be used by people other than yourself, you will most likely want to add a help command to your extension's commands. A single help command is usually sufficient for a small extension. But if you have many commands, each with its own help section, it makes more sense to use so-called context help — pressing F1 on the keyboard opens the help section corresponding to the button currently under the mouse cursor. Another way to call context help for a ribbon command is to press Shift+F1 and, once the cursor changes to an arrow with a question mark, click the desired ribbon button. LIRA-FEM has built-in context help that works this way, and LIRA-FEM extensions can also connect to this help system.
To enable context help for your ribbon button, add the «HelpCommand» parameter to the Windows registry key of the extension command. For example, the following value attaches context help as a PDF file: "HelpCommand"="c:\Users\Public\Documents\ReadMe.pdf". Here is an example of calling help from the Internet: "HelpCommand"="https://sites.google.com/view/help/home". And here is an example of calling a section from a .CHM file — the same format used by LIRA-FEM for its own help system: "HelpCommand"="HH.EXE C:\Program Files (x86)\LIRALAND\LIRA-FEM 2026\bin\x64\help\ru\LiraFEM.chm::/Explain/_LiraSaprApi.htm"
Suppose your extension has read something from the input data and something from LIRA-FEM calculation results, performed its own calculation, and written a results file. Now a user of your extension faces the question of visually evaluating your results. For this, one can use the capabilities provided by LIRA-FEM input tables for building custom mosaics. This is essentially a method for building a mosaic that displays arbitrary custom values on the nodes or elements of the design model.
For example, the following function creates a mosaic in the window of a previously opened design model, coloring its 1st and 2nd elements.
Sub CreateMosaic() ' Connect to the LiraApplication object and get the active document with the design model Set LiraApp = CreateObject("LiraFEM.Application") Set Doc = LiraApp.ActiveDocument ' Create a mosaic table (46=kLiraTable_UserMosaicData) Params1 = Array("Element text mosaic" , 11 , 1) Set Mosaic1 = Doc.AllTables.CreateNewItem ( 46 , (Params1) ) ' Mosaic data (2D array) Dim Data1(1,4) Data1(0,1) = 1 : Data1(0,3) = "1.1 Red color" : Data1(0,4) = "#ff4444" Data1(1,1) = 2 : Data1(1,3) = "2.2 Green color" : Data1(1,4) = "#008800" ' Fill the table Mosaic1.SetContents(Data1) ' Apply changes Mosaic1.Apply(strErrs) ' Clean up — delete the just created table Doc.AllTables.Delete(Doc.AllTables.ItemCount-1) End Sub
In the example above, each element is assigned its own text and color. To create this type of mosaic, an array of parameters is passed to the CreateNewItem function. The first parameter is the mosaic name, «Element text mosaic». The second parameter is the table content type: the number 11 means the table will be filled with a number and an accompanying comment for each element. The third parameter 1 enables the color column in the table. The following table content types are supported for building mosaics:
It should be noted that instead of a mosaic on bar design sections, a diagram by design sections (type 3) can be built. And instead of a mosaic on plates, isofields (type 4) can be built.
All the information and examples provided above in this section could be reproduced in any programming language, including interpreted scripting languages such as VBScript or JScript. In the subsections below we will touch on capabilities that are not available from interpreted languages.
First, we will describe how to register extension functions and manage the appearance of buttons not through Windows registry parameters, as already described in the subsections above, but through LIRA-FEM API functions. Then we will cover more advanced capabilities such as responding to changes in the current load case number or to the launch of any command from the LIRA-FEM menu or toolbar, storing extension input data inside the design model file, and documenting the extension's input data and calculation results in the «Report Book» system. To continue reading this section, you will need additional programming knowledge that cannot be covered in this article: specifically, how to create dynamic class libraries (DLLs) and how to implement predefined programming interfaces.
In the diagram below, objects of the LIRA-FEM API intended for working with extensions are shown in yellow. Interfaces implemented by objects on the extension side are shown in white.
LIRA-FEM LIRA-FEM Extension Application object LiraApplication ¦ ¦ Ribbon ¦--LiraRibbon ¦ ¦ Extensions ¦--LiraExtensions ¦ ¦ Extension object ¦ '--------------------------------------------<<--ILiraExtension ¦ ¦ Document, or design model ¦--LiraDocument ¦ ¦ ¦ ¦ All input table groups and tables ¦ '--LiraTables ¦ ILiraExtensionTables ¦ ¦ ¦ ¦ Document input table group ¦ ¦--LiraTableGroup ¦ ¦ ILiraExtensionTableGroup ¦ ¦ ¦ ¦ Object for managing ¦ ¦ ¦ ¦ extension input table group ¦ ¦ ¦ '------------------------------<<--ILiraExtensionTableGroupHelper ¦ ¦ ¦ ¦ ¦ ¦ Document input table ¦ ¦----'--LiraTable ¦ ¦ ¦ ¦ ¦ ¦ Extension input table ¦ ¦----'--LiraExtensionTable ¦ ¦ ¦ Object for managing ¦ ¦ ¦ extension input table ¦ ¦ '------------------------------<<--ILiraExtensionTableHelper ¦ ¦ ¦ ¦ Measurement unit settings '------------LiraMeasurementUnits '--ILiraExtensionMeasurementUnits
The objects and programming interfaces for working with extensions reproduce, though with greater flexibility, the same use cases as the Windows registry key parameters described above, namely:
Through programming interfaces, additional extension use cases become available:
However, there is one use case that cannot be implemented using programming interface methods and can only be achieved by configuring Windows registry key parameters. This is the initial loading of the extension into the LIRA-FEM application address space. For the extension to load, its loading must be «registered» as described above in the «Registering Extension Functions for Launching from LIRA-FEM» subsection, at least loading the extension at application startup with CallAfter = AppStartup. Once the extension's function has been called — that is, once it has received control — the extension can configure the ribbon, register its other functions and much more, but the only way to «make» the application pass control to the extension is through the registry.
On the LIRA-FEM API side, the objects LiraRibbon, LiraExtensions, LiraTables, LiraTableGroup, LiraExtensionTable, and LiraExtensionMeasurementUnits are intended for extension work. Let us examine the methods of these objects.
| Object and its methods | Description, parameters |
|---|---|
| LiraRibbon | Object for managing the ribbon |
| AddExtensionTab() | Adds a new tab named TabName. TabKey is its abbreviation of Latin letters or digits. PathImagesSmall and PathImagesLarge are paths to image files. Returns the index of the added tab, starting from 0 |
| AddExtensionTabImage() | Adds a new image to the tab named TabName. ToLargeImages specifies where to add the image: for small or large tab images. ImagePath is the path to the image file. Returns the index of the added image from 0, or -1 on failure |
| GetExtensionTabCount() | Returns the number of tabs added to the ribbon using the AddExtensionTab() method |
| GetExtensionTabData() | Returns data for the i‑th tab: its name, shortcut key, number of 16x16 and 32x32 images |
| RemoveExtensionTab() | Removes the tab named TabName |
| AddPanel() | Adds a new panel named PanelName to the tab named TabName. PanelKey is its abbreviation of Latin letters or digits. ImageSmall is the tab image index. Returns the index of the added panel, starting from 0 |
| GetPanelCount() | Returns the number of panels on the tab named TabName |
| GetPanelData() | Returns data for the i‑th panel on the tab named TabName: its name, shortcut key, flags |
| RemovePanel() | Removes the panel named PanelName from the tab named TabName |
| AddButton() | Adds a new button ButtonName to tab TabName and panel PanelName. Clicking the button calls the function with ExtensionID. If ParentButton is not -1, the new button is part of its popup menu. Returns the new button index or -1 on failure |
| AddButtonSeparator() | Adds a new separator to tab TabName and panel PanelName. Returns its index or -1 on failure |
| GetButtonCount() | Returns the number of buttons or separators on tab TabName and panel PanelName. If ParentButton is not -1, returns data for its popup menu |
| GetButtonData() | Returns data for the i‑th button or separator on tab TabName and panel PanelName: its name, extension ID, function, parent button, tooltip, description, shortcut key, image, and flags |
| SetButtonData() | Sets data for the i‑th button on tab TabName and panel PanelName: its name, extension ID, function, parent button, tooltip, description, shortcut key, image, and flags. pImageSmall and pImageLarge — number of tab images |
| RemoveButton() | Removes the i‑th button from tab TabName and panel PanelName |
| Update() | Updates the ribbon |
As can be seen from the table above, all methods of the LiraRibbon object create, modify, or remove ribbon elements: tabs, panels, buttons, or separators between buttons. It should be noted that extensions cannot modify tabs that do not belong to extensions. The advantage of creating ribbon buttons through the LiraRibbon object compared to creating the same buttons from the registry is, first, dynamism — the extension can change buttons at runtime — and second, language dependency, since the ribbon element text can be configured based on the current interface language. Additionally, the AddButton function can create buttons in drop-down lists.
The next object is intended exclusively for registering extensions and their methods. Using its methods, for example, one LIRA-FEM extension can load another extension and register the extension's commands.
| Object and its methods | Description, parameters |
|---|---|
| LiraExtensions | Object for managing extensions and their functions |
| AddExtension() | Adds an extension. ExtensionID must consist of 3 Latin letters. The name is visible to the user. If Register is not 0, registers the extension in the system registry. Returns the index of the added extension, starting from 0 |
| GetExtensionCount() | Returns the number of added extensions |
| GetExtensionData() | Returns data for the i‑th extension. i is the extension index in the list, starting from zero |
| RemoveExtension() | Removes the extension with ExtensionID. If UnRegister is not 0, unregisters the extension and all its functions from the system registry |
| AddExtensionFunction() | Adds a function to call from a file at the path with extension ExtensionID. If the function is a method, it will look like 'MyNamespace.MyClass.MyMethod'. The name and description are visible to the user |
| GetExtensionFunctionCount() | Returns the number of added extension functions |
| GetExtensionFunctionData() | Returns data for function number i. See AddExtensionFunction() for a description of the returned parameters |
| RemoveExtensionFunction() | Removes the extension function with ExtensionID. Its registration in the Windows registry can also be cancelled if UnRegister is not 0 |
The next objects intended for working with extensions are LiraTables, LiraTableGroup, and LiraExtensionTable. Note that in the LIRA-FEM object diagram above, the objects LiraTables and LiraTableGroup, in addition to their default interface, support a specialized interface for working with extensions: ILiraExtensionTables and ILiraExtensionTableGroup respectively. These input table interfaces, together with the LiraExtensionTable object, allow extensions to create their own input table groups and input tables — and thus integrate their data into the LIRA-FEM design model document data. Since most methods of ILiraExtensionTables, ILiraExtensionTableGroup, and LiraExtensionTable repeat those of LiraTables, LiraTableGroup, and LiraTable, we will only review the properties and methods intended specifically for working with extensions.
| Object and its methods | Description, parameters |
|---|---|
| ILiraExtensionTables, ILiraExtensionTableGroup | All input tables, including tables managed by extensions An input table group, including a group managed by an extension |
| AddTable() | Adds a new table implemented using a LIRA-FEM extension helper object that supports the ILiraExtensionTableHelper interface (described in the subsection below). The ExtensionID parameter must contain the LIRA-FEM extension identifier (3 Latin letters). Returns the new table. |
| AddTableGroup() | Adds a new table group implemented using a LIRA-FEM extension helper object that supports the ILiraExtensionTableGroupHelper interface (described in the subsection below). The ExtensionID parameter must contain the LIRA-FEM extension identifier (3 Latin letters). Returns the new table group. |
| FindTable() | Returns the table previously added by the AddTable method and managed by the extension helper object with ID=HelperID. The ExtensionID parameter must contain the LIRA-FEM extension identifier (3 Latin letters). Returns null if the table was not added. |
| FindTableGroup() | Returns the table group previously added by the AddTableGroup method and managed by the extension helper object with ID=HelperID. The ExtensionID parameter must contain the LIRA-FEM extension identifier (3 Latin letters). Returns null if the group was not added. |
| GetGroupHelper() | Returns the extension helper object that actually implements this table. |
| ILiraExtensionTable | An input table managed by an extension. |
| GetTableHelper() | Returns the helper object that actually implements this table. |
An extension is not required to implement all interfaces shown with a transparent background in the diagram in the previous subsection. The table below shows in which cases each interface should be implemented on the extension side.
| Interface | Should be implemented when needed to |
|---|---|
| ILiraExtension | Manage the extension's own ribbon buttons, including the availability of commands belonging to the extension Receive notifications about changes in the current load case, design option, etc. Receive notifications about menu / ribbon command invocations Update screenshots for documentation using the «Report Book» Create instances of objects implementing the ILiraExtensionTableGroupHelper and ILiraExtensionTableHelper interfaces |
| ILiraExtensionTableGroupHelper | Configure parameters of the input table group belonging to the extension |
| ILiraExtensionTableHelper | Edit extension data as an input table Store extension data together with document data in the LIR‑file Create result tables for documentation using the «Report Book» |
Next we will review the methods of the interfaces mentioned in the table above, focusing on the most important ones.
So, you have concluded that your extension needs tighter integration with the application's user interface. In that case, your steps should be as follows:
How and when is an instance of an object implementing this programming interface created?
As already noted, for an extension to be loaded into the LIRA-FEM application address space, it must be registered in the registry. It is recommended to do this using the Windows registry parameter "CallAfter"="AppStartup", as shown in the «Registering Extension Functions for Launching from LIRA-FEM» subsection. After the necessary keys and values have been added to the Windows registry, the function specified in the registry key will be called during LIRA-FEM application loading. This function must create an instance of the extension object and add it to the corresponding LIRA-FEM object container called LiraExtensions.
For example, the Timber extension might load into the LIRA-FEM address space as follows. During installation it registers the Wood.dll dynamic library and its Startup method in the Windows registry:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\Wud] @="Dll:Net4" [HKEY_LOCAL_MACHINE\SOFTWARE\LIRA EXTENSIONS\Wud\Wood.WoodExtension.Startup] @="c:\\Users\\Public\\Documents\\WoodExtension\\Wood.dll" CallAfter="AppStartup"
And implements the Startup method in a dynamic library on the .NET Framework 4 platform in C#:
using LiraFEM;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Wood
{
public class WoodExtension : ILiraExtension
{
// Constructor must be public and have no parameters
public WoodExtension() { }
// Full name of this method, including namespace and class name, is registered in the registry
public void Startup()
{
// Our main task is to connect the extension object. Let's do it
string strThisDllPath = System.Reflection.Assembly.GetExecutingAssembly().Location; // Path to the extension DLL file
LiraApplication LiraApp = new LiraApplication(); // Get reference to the application
LiraExtensions LiraExts = LiraApp.Extensions; // Get reference to the extension container
LiraExts.AddExtension( // Add extension to the container
"Wud", // Extension ID consisting of 3 Latin letters
LiraExtensionLangEnum.kLiraExtensionLang_Dll_Net4, // Technology the extension is written in
"Timber structures design and analysis", // Extension name (optional)
LiraCompatibleVersionEnum.kLiraCompatibleVersion_Any, // Minimum application version compatible with the extension
LiraCompatibleVersionEnum.kLiraCompatibleVersion_Any, // Maximum application version compatible with the extension
this); // Reference to the extension
}
}
}
This is how an extension should load itself into the extension container. Creating ribbon buttons whose commands are handled by the extension can be done through the Windows registry. However, adding buttons can also be done programmatically, as shown in the example below. In this example we will add a function that emulates an analysis, and a ribbon button that calls this analysis function.
// Continuation of Startup() method double fFunctionsOrder = 0; // Formal parameter LiraExts.AddExtensionFunction( // Extension reference "Wud", // Extension ID consisting of 3 Latin letters "Wood.WoodExtension.DoAnalysis", // Full method name including namespace and class name strThisDllPath, // Full path to Wood.dll LiraCallAfterEnum.kLiraCallAfter_Button, // Method is called by clicking a ribbon button ref fFunctionsOrder, // Method call order (doesn't matter for ribbon buttons) "Timber analysis", // Method name (optional) "Performs timber analysis", // Function description (optional) "wud"); // Result file extension // Now let's programmatically add a tab and panel to the ribbon for the button that will call this function. LiraRibbon Ribbon = LiraApp.Ribbon; // Get reference to the ribbon Ribbon.AddExtensionTab("Wood"); // Add a new tab named Wood to the ribbon Ribbon.AddPanel("Wood", "Analysis"); // Add a new panel named Analysis to the new tab int iImgSmall = Ribbon.AddExtensionTabImage( // Add an icon for the button "Wood", // Which tab the icon is being added to false, // Is this an icon for a large button? strThisDllPath + ".ICO"); // Path to the icon. It is next to Wood.dll in the file Wood.dll.ico int iImgLarge = Ribbon.AddExtensionTabImage("Wood",true,strThisDllPath + ".ICO"); // same, for the large button // Add the button itself to the ribbon LiraCmdIdEnum eCmd = Ribbon.AddButton( "Wood", // Name of the new ribbon tab "Analysis", // Name of the new ribbon panel -1, // Parent button index. -1 for regular buttons "Wud", // Extension ID consisting of 3 Latin letters "Wood.WoodExtension.DoAnalysis", // Full method name including namespace and class name "Timber analysis", // Button caption "Performs Timber Analysis", // Button tooltip "More description comes here", // Button description "", // Shortcut keys iImgSmall, // Image index for small button iImgLarge); // Image index for large button m_CmdId2Func[eCmd] = "Wood.WoodExtension.DoAnalysis"; } Dictionary<LiraCmdIdEnum, string> m_CmdId2Func = new Dictionary<LiraCmdIdEnum, string>(); public void DoAnalysis() { System.Windows.Forms.MessageBox.Show("Timber analysis performed!"); }
For completeness, let us also add to the WoodExtension class the methods implementing the ILiraExtension interface, albeit without their implementation. These methods will be described in the next subsection.
string ILiraExtension.GetExtensionId() {return "Wud";}
string ILiraExtension.GetExtensionName() {return "Timber design and analysis";}
ILiraExtensionTableGroupItemHelper ILiraExtension.CreateTableHelper(ILiraExtensionTables Tables, int ExtensionHelperID) { return null;}
void ILiraExtension.SetApplication(LiraApplication App){}
void ILiraExtension.StateChanged(LiraDocument Doc, LiraCmdIdEnum Cmd, LiraViewCtlEnum Ctl){}
int ILiraExtension.GetExtensionCmdState(LiraCmdIdEnum Cmd, out bool pEnabled){pEnabled = true;return 0;}
bool ILiraExtension.CanExtensionDoAnalysis(){return false;}
bool ILiraExtension.UpdateScreenshotScreen(string Src, string Dat, LiraTable DstTable){return false;}
void ILiraExtension.DocumentClosing(LiraDocument Doc){}
void ILiraExtension.HelpContext(string TabName, string PanelName, string Function, int HtmlHelpCommand, long hParentWnd){}
void ILiraExtension.ApplicationClosing(){}
}
}The methods of the ILiraExtension interface are described in the table below.
| Interface and its methods | Description |
|---|---|
| ILiraExtension | Main extension interface for working in the LIRA-FEM environment |
| CreateTableHelper() | On request from the LIRA-FEM API, creates and returns an object managing an extension input table ILiraExtensionTable or an extension input table group ILiraExtensionTableGroup |
| GetExtensionId() | Returns the three-letter extension identifier |
| GetExtensionName() | Returns the extension name |
| SetApplication() | Passes a reference to the LiraApplication object to the extension |
| StateChanged() | Passes information to the extension about any ribbon button press or menu item selection, as well as changes to the current load case number, design option, etc. |
| GetExtensionCmdState() | On request from the LIRA-FEM API, returns to the application information about the availability of the ribbon button belonging to the extension |
| CanExtensionDoAnalysis() | On request from the LIRA-FEM API, returns to the application information about whether the extension can perform an analysis. This is required, for example, during a through-analysis or when configuring analysis parameters |
| UpdateScreenshotScreen() | Called when updating a screenshot created with an image of extension data. |
| DocumentClosing() | Called when a LIRA-FEM document is closing. The implementation of this method must release all references to document objects that may be held by the extension object |
| HelpContext() | Called when the user opens help for an extension command. The implementation must open the corresponding help section of the extension. |
| ApplicationClosing() | Called when the LIRA-FEM application is closing. The implementation must release all references to application objects that may be held by the extension object |
Most of the 11 methods of the ILiraExtension interface shown in the table have trivial implementations — in most cases these methods can do nothing. This applies, for example, to the notification methods SetApplication(), StateChanged(), DocumentClosing(), ApplicationClosing(). If the extension does not track ribbon button presses not created by the extension itself, and if changes to the current load case or design option are of no interest, these methods can truly be left empty. Another 3 methods — GetExtensionId(), GetExtensionName(), HelpContext() — also have extremely simple implementations. GetExtensionId() and GetExtensionName() just return two predefined strings, while HelpContext() simply opens the help section corresponding to the passed command name. Somewhat more complex is the implementation of CanExtensionDoAnalysis(). This method must check whether the extension has enough data to perform an analysis. For example, the Timber extension implements this method by verifying that its input data tables are defined.
The implementation of this method in Timber might look like this:
bool ILiraExtension.CanExtensionDoAnalysis() // Can the extension perform analysis { // with identifier "Wud"? LiraApplication App = new LiraApplication(); // Get reference to the LIRA-FEM application LiraDocument Doc = App.ActiveDocument; // Get reference to the active document LiraTables Ts = Doc.AllTables; // Get reference to input tables ILiraExtensionTables ETs = Ts as ILiraExtensionTables; // Cast it to the interface for working with extensions ILiraExtensionTable ET0 = ETs.FindTable(1, "Wud"); // Search for table with identifier 1 of extension "Wud" ILiraExtensionTable ET1 = ETs.FindTable(2, "Wud"); // Search for table with identifier 2 of extension "Wud" return ET0 != null && ET1 != null; // If both tables are found, analysis can be performed }
In the first three lines of this function, the extension gets a reference to the container of all input tables. This container also holds tables containing input data of extension input tables (a separate subsection below is dedicated to how extension input data is stored within LIR‑files). The reference to the container is then cast to the ILiraExtensionTables interface. We could have used the default LiraTables object methods, but the ILiraExtensionTables interface contains the convenient FindTable() method which we will use here. We cast the reference to ILiraExtensionTables and call its FindTable() method to search for input tables belonging to the extension with identifier «Wud». If tables with identifiers 1 and 2 of this extension are found, the function considers that sufficient data is available for analysis.
The extension method GetExtensionCmdState() must be implemented similarly — it answers the application's query about whether the ribbon button belonging to the extension is available. Suppose there is a button on the ribbon that should show a mosaic of the extension's input data, coloring the design model elements in different colors. Then the implementation of GetExtensionCmdState() must check whether this input data has been defined yet, and if not — make the button unavailable. In the Timber extension such an implementation might look like this:
int ILiraExtension.GetExtensionCmdState(LiraCmdIdEnum eCmd, out bool pEnabled)
{
pEnabled = false;
string strFunctionName;
if(m_CmdId2Func.TryGetValue(eCmd, out strFunctionName))
{
if(strFunctionName == "WudTblElsMosaic")
{
LiraApplication App = new LiraApplication(); // Get reference to the LIRA-FEM application
LiraDocument Doc = App.ActiveDocument; // Get reference to the active document
ILiraTables Ts = Doc.AllTables; // Get reference to input tables
ILiraExtensionTables ETs = Ts as ILiraExtensionTables; // Cast it to the interface for working with extensions
ILiraExtensionTable ET0 = ETs.FindTable(0, "Wud"); // Search for table with identifier 0 of extension "Wud"
pEnabled = (ET0 != null); // If the table is found, the button can be activated
}
else //if (strFunctionName == "...")
{
// other checks
}
}
return 0;
}
The code of GetExtensionCmdState() above is in many ways similar to the CanExtensionDoAnalysis() implementation, with the difference that the function name associated with the command is first retrieved by the command identifier eCmd, and then the corresponding input table is searched for that name. For now, let us set aside the value returned by this function and return to it in the «Creating Updatable Screenshots» subsection. Let us also set aside the implementations of UpdateScreenshotScreen() and CreateTableHelper() — we will return to them in the following subsections. Since two interface methods have been left without explanation, the summary of the ILiraExtension interface will be only partial. From the partial description already given, one can conclude that this interface is designed to receive notifications about events occurring in the LIRA-FEM application — such as document closing, application closing, menu or ribbon command invocation, and screenshot updates for the Report Book. It can also respond to the application's query about the availability of ribbon buttons belonging to the extension.
The undescribed method ILiraExtension.CreateTableHelper() is precisely intended to create, on the extension side, an object implementing the ILiraExtensionTableHelper interface — capable of storing extension data together with other design model data inside a file with the .LIR extension. Is such an object always necessary? Of course not. First, the extension may have no data of its own and may use only LIRA-FEM document data. If there is no data, no interface for storing it is needed. Second, the extension may store its data in a separate file, leaving the user to copy it alongside the LIR‑file when transferring a task to another computer. But if neither of these applies, you will most likely want to implement the ILiraExtensionTableHelper interface to store extension data inside the LIR‑file.
A key feature of data stored in a LIR‑file through the ILiraExtensionTableHelper interface is its transparency. All data of an extension connected to a LIRA-FEM design model document through the ILiraExtensionTableHelper interface is either input tables or their parameters, and therefore any LIRA-FEM user can freely view it. The methods of the ILiraExtensionTableHelper interface and its parent interface ILiraExtensionTableGroupItemHelper are shown in the table below.
| Interface and its methods | Description |
|---|---|
| ILiraExtensionTableGroupItemHelper | Intended for displaying and editing parameters of the input table group belonging to the extension. |
| ILiraExtensionTableHelper | Intended for displaying and editing the input table belonging to the extension and its parameters. Methods common to both interfaces: |
| GetName() | Returns the name of the input table or input table group for display as a tree item in the «Input Tables» window. |
| GetExtensionHelperID() | Returns the unique identifier of the input table or input table group belonging to the extension. This must be a number from 0 to 255. |
| GetParameterCount() | Returns the number of parameters. Parameters can be viewed in the «Parameters» window of input tables. |
| GetParameter() | Returns data for the i‑th parameter. Parameters can be viewed in the «Parameters» window of input tables. |
| SetParameter() | Sets the value of the i‑th parameter of the input table or input table group. Parameters can be viewed in the «Parameters» window of input tables. |
| EditParameter() | Offers the extension to open a popup window for editing the i‑th parameter. Returns true if the extension confirmed the edit, or false otherwise. |
| GetParameterAllowedValues() | Returns an array of variants, each of which is a valid value of the i‑th parameter. |
| ILiraExtensionTableHelper | The methods below exist only in the ILiraExtensionTableHelper interface: |
| GetColumnCount() | Returns the number of columns in the controlled table. |
| GetColumn() | Returns data for the i‑th column: its name, tooltip, width as a percentage of the table width, data type, units of measurement, and decimal places for floating-point and double-precision numbers. pData is a combination of LiraTableColumnEnum flags. |
| EditCell() | Offers the extension to edit the cell contents of the controlled table in a popup window. Col and Row are the column and row numbers, starting from zero. The pCell parameter contains the cell contents, and pRow contains the row contents as a string array. The extension can modify pCell or pRow. |
| GetCellAllowedValues() | Returns options for the contents of cell (Row, Col) of the controlled table as an array of text strings or variants. pDivider is the character separating the language-independent part of the cell text from the language-dependent part. |
| IsDataFromOutside | This property is true if the data of the controlled table is stored neither in the table itself nor in the LIRA-FEM document. In this case, RefillFromOutsideData is called to fill it, and ApplyToOutsideData is called when applying data. |
| IsResult | Whether the controlled input table is a result table. The Apply() method cannot be called for a result table. |
| RefillFromOutsideData() | Called only when the IsDataFromOutside property is true. Cancels all changes made to the table and fills its contents with data from the external data source defined by the Lira extension. |
| CanApply() | Called only when the IsDataFromOutside property is false. Checks for errors in the controlled table. Returns a non-zero value if no errors are found, or 0 otherwise. pErrs receives error information if any. The error format is the same as in the LiraTable.Apply method. |
| ApplyToOutsideData() | Called only when the IsDataFromOutside property is true. Validates and applies the table contents. Resets the IsModified flag. Returns a non-zero value on success, or 0 otherwise. The pErrs format is the same as in the LiraTable.Apply method. |
| FindInModel() | Returns pElementNumbers or pSelectedNodes (both starting from 1) corresponding to table rows (zero-indexed). Each parameter except Table is a variant array, each variant containing a long value. |
| FindInTable() | Returns pRowNumbers (starting from 0) of table elements corresponding to ElementNumbers or NodeNumbers (both starting from 1). Each parameter except Table is a variant array, each variant containing a long value. |
| CopyToAdaptCellValue() | Called by the CopyTo method when 1 FE in the target document (TD) corresponds to 2 or more FEs in the source document (SD). The SrcCells parameter is a variant array, one per FE in the SD. Return the FE value in the TD. All values are in column Col. |
| DocumentClosing() | Called before the document containing the controlled table is closed. |
The methods of ILiraExtensionTableGroupItemHelper and ILiraExtensionTableHelper allow full control over the corresponding input table group or input table. To create a custom input table group or input table, the extension must use the appropriate method of the ILiraExtensionTableGroup interface. For example, in response to a ribbon button press «Create input data table», the extension can execute the following code block:
LiraApplication LiraApp = new LiraApplication(); // Get reference to the LIRA-FEM application LiraDocument LiraDoc = LiraApp.ActiveDocument; // Get reference to the active document LiraTables Tables = LiraDoc.AllTables; // Get reference to the container of all tables ILiraExtensionTableGroup ExtTables = Tables as ILiraExtensionTableGroup; // Cast the reference // Create an object managing the input table group from our Wood extension ILiraExtensionTableGroupItemHelper GrpHelper = new WoodLiraExtensionTableGroupHelper(); // Create an input table group controlled by the GrpHelper object ILiraExtensionTableGroup WoodTableGroup = ExtTables.AddTableGroup(GrpHelper, "Wud"); // Create an object managing the input data table of our Wood extension ILiraExtensionTableHelper TblHelper = new WoodLiraExtensionTableHelper(); // Create an input table controlled by TblHelper in the controlled group ILiraExtensionTable WoodTable = WoodTableGroup.AddTable(TblHelper, "Wud");
Once an input table group or input table has been created, it becomes visible in the «Input Tables» window and starts being saved together with the design model in the LIR‑file. When configuring parameters of the input table group or input table controlled by the extension, the methods of ILiraExtensionTableGroupItemHelper are called, and when editing the extension's controlled input table, the methods of ILiraExtensionTableHelper are called. Thus, from the user's perspective, editing extension data is indistinguishable from editing design model data through input tables.
Extension data saved inside the LIR‑file together with the extension-controlled input table or input table group must be linked back to the extension upon loading. For this purpose, the CreateTableHelper() method is provided in the ILiraExtension interface. The method ILiraExtension.CreateTableHelper() must return a reference to the helper object for the table or table group controlled by the extension, identified by its identifier.
ILiraExtensionTableGroupItemHelper ILiraExtension.CreateTableHelper(ILiraExtensionTables ExtTables, long ExtensionHelperID)
The returned reference is bound to the table or input table group, thus making this table or group controlled by the extension again after loading the LIR‑file.
Of course, an extension can create files with any names. However, if the result file name is completely arbitrary, the user will need to remember which file to bring along when transferring a task with results to another computer. On the other hand, LIRA-FEM has a built-in capability to create a ZIP‑archive with the task's input data and results. This subsection contains very simple requirements for the folder and name under which to save the extension's result file so that it is included in the ZIP‑archive along with other design model results.
File extensions can have more than three characters, but windows like «Open File» do not distinguish between file extensions with the same first three characters, so limiting the file extension length to three characters is a sensible decision. The extension identifier that generated such a file within LIRA-FEM is unique, so using it to differentiate result files is also sensible.
If the result of an extension's calculation is a rectangular table, it can be included in the LIRA-FEM documentation system called the Report Book. The Report Book allows accumulating screenshots, tables, and text documents, updating them after recalculations, and typesetting a unified report in .DOCX format. To include your result table in the Report Book, you must first make it an input table controlled by the extension. In other words, you need to provide a separate object implementing the ILiraExtensionTableHelper interface for the result table, and add this table to the other input tables after the analysis.
LiraApplication LiraApp = new LiraApplication(); LiraDocument LiraDoc = LiraApp.ActiveDocument; LiraTables Tables = LiraDoc.AllTables; ILiraExtensionTableGroup ExtTables = Tables as ILiraExtensionTableGroup; // Find the previously created input table group belonging to our extension (let its ID = 0) ILiraExtensionTableGroup WoodTableGroup = ExtTables.FindTableGroup(0, "Wud"); // Create an object managing the result input table ILiraExtensionTableHelper ResultTblHelper = new WoodLiraExtensionResultTableHelper(); // Create an input table controlled by ResultTblHelper in the controlled group ILiraExtensionTable WoodResultTable = WoodTableGroup.AddTable(ResultTblHelper, "Wud");
A result input table is not really an input table, since results cannot be changed. The «Apply» button in such a table does nothing. The ILiraExtensionTableHelper.IsDataFromOutside property must return true. All that can be done with such a table is to refill it using ILiraExtensionTableHelper.RefillFromOutsideData() to update its data. Any input table, including one containing results, can be added to the Report Book. This can be done programmatically using the LiraTable.AddToReportBook() method. Thus, both the input data tables and the result tables controlled by the extension can easily be included in the Report Book and typeset into a unified DOCX‑file together with the rest of the results.
In the «Visualizing Extension Data with Mosaics» subsection we showed how to programmatically build an arbitrary mosaic, coloring the design model elements in different colors. The resulting mosaic, isofield, or diagrams can be added to the Report Book to include them in the overall task report. Neither building mosaics nor including them in the Report Book required developing any objects implementing predefined interfaces. However, mosaics added to the Report Book have a problem — they are not updatable. If after a recalculation the results of your extension's analysis change, the corresponding mosaics in the Report Book will have to be rebuilt manually.
However, if you implement the ILiraExtension interface in your object, you can also implement its ILiraExtension.UpdateScreenshotScreen() method. This method is precisely intended for regenerating screenshots already added to the Report Book. To do this, you need to know one secret: you must sign your mosaic so that you can later «recognize» your signature and update the mosaic according to new data.
So, let us sign our mosaic. For variety, let us do this not in VBScript as was done in the «Visualizing Extension Data with Mosaics» section, but in C#.
void CreateMosaic(string strTableContentPostfix)
{
// Connect to the LiraApplication object and get the active document with the design model
LiraApplication LiraApp = new LiraApplication();
LiraDocument LiraDoc = LiraApp.ActiveDocument;
// Create a mosaic table
object Params1 = new object[] { "Element text mosaic", "11" + strTableContentPostfix, 1 };
LiraTable Mosaic1 = LiraDoc.AllTables.CreateNewItem(LiraTableEnum.kLiraTable_UserMosaicData, Params1) as LiraTable;
// Mosaic data (2D array)
object Data1 = new object[2,5]{{ "", 1, "", "1.1 Red color", "#ff4444" },
{ "", 2, "", "2.2 Green color", "#008800" }};
// Fill the table
Mosaic1.SetContents(Data1);
// Apply changes
string strErrs = "";
Mosaic1.Apply(out strErrs);
// Clean up — delete the just created table
// LiraDoc.AllTables.Delete(LiraDoc.AllTables.ItemCount - 1);
}
Comparing this C# function with its VBScript predecessor, you can see that the new function has a strTableContentPostfix parameter, which is added as a postfix to the second parameter in the array passed to AllTables.CreateNewItem().
Looking more closely, that same parameter in the array changed its type from int to string, which is normal — otherwise we could not append a suffix. Finally, the last line that deleted the just-created input table with mosaic data has been commented out. Incidentally, leaving the mosaic data table among the extension's input tables is not such a bad idea — it allows the user to later open this input table and configure its display on the design model. This is exactly what the Timber extension does — it leaves the mosaic input table so the user can adjust it if desired.
Let us call this function in the already registered analysis function, passing the «correct» postfix strTableContentSuffix.
public void DoAnalysis()
{
System.Windows.Forms.MessageBox.Show("Timber analysis performed!");
CreateMosaic(". Src=Wud:MyMosaicSign");
}
Next, click the «Timber Analysis» button, open the Input Tables window with the newly created table, and look at its parameters. The application windows will look approximately like the figure below.
As can be seen, our «correct» postfix "Src=Wud:MyMosaicSign" has made it into the second parameter of the input table.
The description of this parameter states that the postfix must contain the signature of the extension that created it. The postfix format must be as follows:
. Src=Name:Parameters
where
. Src= is an identifier showing that extension data follows,
Name: is the three-letter extension identifier followed by a colon — the separator between the extension identifier and the mosaic parameters,
Parameters is the actual signature that the function ILiraExtension.UpdateScreenshotScreen() must recognize.
During screenshot update, the LIRA-FEM application recognizes the screenshot taken from the input table containing custom mosaic data, searches for its «signature», and calls the ILiraExtension.UpdateScreenshotScreen() method in the extension with the corresponding identifier, passing the signature — that is, the data after the «. Src=» identifier — to the method. In our case, the parameter «Wud:MyMosaicSign» will be passed to ILiraExtension.UpdateScreenshotScreen(). In response, the extension must recognize its signature, and if recognized — rebuild the same mosaic with its current data in the design model window, using the same input table for building the custom mosaic. Let us implement such a function, replacing the red color with blue to verify that the update actually occurred.
bool ILiraExtension.UpdateScreenshotScreen(string strSrc, string strDat, LiraTable DstTable)
{
if(strSrc != "Wud:MyMosaicSign")
{
return false; // This signature is unknown to us
}
if(DstTable == null) // If the input table for this mosaic was deleted —
{ // recreate it
LiraApplication LiraApp = new LiraApplication(); // Connect to the LiraApplication object
LiraDocument LiraDoc = LiraApp.ActiveDocument; // Get the active document with the design model
object Params1 = new object[] { "Element text mosaic", "11. Src=" + strSrc, 1 }; // Table creation parameters
DstTable = LiraDoc.AllTables.CreateNewItem(LiraTableEnum.kLiraTable_UserMosaicData, Params1) as LiraTable;
}
object Data1 = new object[2, 5] {{ "", 1, "", "1.1 Blue color", "#0000ff" }, // Mosaic data (2D array)
{ "", 2, "", "2.2 Green color", "#008800" }};
DstTable.SetContents(Data1); // Fill the table
DstTable.Apply(out string strErrs); // Apply changes
// We won't delete the table so the user can later adjust its appearance in the design model window
return true; // update completed successfully
}
To verify how this function worked, add to the Report Book a screenshot showing the mosaic built by the DoAnalysis() function above. Then right-click on the screenshot and select «Update selected items». The ILiraExtension.UpdateScreenshotScreen() method will execute, and the red color in the mosaic and in the Report Book screenshot will change to blue:
From the diagram in the «LIRA-FEM API Objects and Interfaces for Extensions» subsection, it was clear that the LiraExtensions and LiraRibbon objects appeared for working with extensions, providing extension connection, while other objects received additional programming interfaces. But new functions intended for working with extensions also appeared in the core objects. In this subsection we will comment on the most important of them.
| Object and its methods | Description |
|---|---|
| LiraDocument | Document containing the design model |
| CurrentLoadCase | Load case number in input data mode. Sets or returns a number, starting from 1, for the current input data load case (or mounting stage, if applicable) |
| CurrentDesignOption | Design option number. Sets or returns a number, starting from 1, indicating the current design option |
| LiraApplication | Application |
| Extensions | Returns a reference to the LiraExtensions object (see the «LIRA-FEM API Objects and Interfaces for Extensions» section) |
| Ribbon | Returns a reference to the LiraRibbon object (see the «LIRA-FEM API Objects and Interfaces for Extensions» section) |
| GetFilePath() | Allows obtaining paths to folders configured in the LIRA-FEM «Directories» window |
| GetInterfaceLanguage() | Returns the LIRA-FEM graphical user interface language |
| GetCmdState() | Returns the state of the menu/toolbar/ribbon element (button) corresponding to the command specified in the Cmd parameter. The pEnabled parameter returns a value indicating whether the element (button) is enabled. The pSel parameter returns the element state: 0 — not selected (not checked, not pressed), 1 — selected (checked, pressed). |
| SetCmdState() | Launches the command with the identifier specified in the Cmd parameter. Unlike the LiraApplication.StartCmd method, this method does not return control until the command finishes executing. |
| GetViewState() | Intended for determining the current load case, design option, and other current settings displayed in the LIRA-FEM window status bar. Returns the state of the dropdown list passed in the Ctrl parameter. The pEnabled parameter returns a value indicating whether the list is enabled. The pSel parameter returns the index of the selected item in the list (from 0; -1 means nothing is selected). The optional pItems parameter returns the text of all list items as a string array. |
| SetViewState() | Intended for changing the current load case, design option, and other current settings displayed in the LIRA-FEM window status bar. Sets the new selected item Sel of the dropdown list passed in the Ctrl parameter. |
| GetViewStateTimePoints() | Gets the array of integration points for viewing time-history analysis results. Returns an array of long values (integration steps, starting from 1). The TimeHistory parameter specifies the time-history number starting from 1, pStep returns the step in seconds, and pPointCount — the total number of available time points. |
| SetViewStateTimePoints() | Sets the time points (integration steps) for viewing time-history analysis results. The lTimeHistory parameter specifies the time-history number starting from 1, and TimePoints is an array containing the integration steps (starting from 1) needed for viewing. |
Among the methods listed above, a few deserve special mention as the most useful.
First, LiraApplication.SetCmdState() allows launching commands or switching application modes; this new method waits for the command to finish, unlike LiraApplication.StartCmd(), after which you had to wait an indefinite time for execution to complete.
Second, passing Ctrl=kLiraViewCtl_MainWindow (kLiraViewCtl_MainWindow=2000) to LiraApplication.GetViewState() lets you obtain the HANDLE of that window — it is returned in the pItems parameter. Also, passing Ctrl=kLiraViewCtl_Plot (kLiraViewCtl_Plot=1000) allows you to find out whether some mosaic or diagrams are currently active. If a mosaic built using a custom mosaic input table is currently active, its signature can be obtained in the pItems parameter of this method. The mosaic display can also be disabled by calling LiraApplication.SetViewState() with Ctrl=kLiraViewCtl_Plot and Sel=0.
With this knowledge, we can finally complete the ILiraExtension.GetExtensionCmdState() method, in which the return value was always zero. Now we can detect that our mosaic is active and return a non-zero value so that its button appears «pressed» on the ribbon.
public void Startup()
{
...
//Let's add another function and button for building the mosaic
LiraExts.AddExtensionFunction("Wud", "Wood.WoodExtension.DrawPlot", strThisDllPath, LiraCallAfterEnum.kLiraCallAfter_Button, ref fFunctionsOrder);
eCmd = Ribbon.AddButton("Wood","Analysis",-1,"Wud","Wood.WoodExtension.DrawPlot","Mosaic");
m_CmdId2Func[eCmd] = "Wood.WoodExtension.DrawPlot";
}
static Dictionary<LiraCmdIdEnum, string> m_CmdId2Func = new Dictionary<LiraCmdIdEnum, string>();
public void DoAnalysis() { System.Windows.Forms.MessageBox.Show("Timber analysis performed!"); }
public void DrawPlot() { CreateMosaic(". Src=Wud:MyMosaicSign"); }
//Updated implementation of ILiraExtension.GetExtensionCmdState() method
int ILiraExtension.GetExtensionCmdState(LiraCmdIdEnum eCmd, out bool pEnabled)
{
pEnabled = false;
string strFunctionName;
if(m_CmdId2Func.TryGetValue(eCmd, out strFunctionName))
{
LiraApplication App = new LiraApplication(); // Get reference to the LIRA-FEM application
LiraDocument Doc = App.ActiveDocument; // Get reference to the active document
if(strFunctionName == "Wood.WoodExtension.DoAnalysis")
{
ILiraTables Ts = Doc.AllTables; // Get reference to input tables
ILiraExtensionTables ETs = Ts as ILiraExtensionTables; // Cast it to the interface for working with extensions
ILiraExtensionTable ET0 = ETs.FindTable(0, "Wud"); // Search for table with identifier 0 of extension "Wud"
pEnabled = (ET0 != null); // If the table is found, analysis can be performed
}
else if(strFunctionName == "Wood.WoodExtension.DrawPlot")
{
pEnabled = true; // Let the mosaic button always be available
bool bPlotEnabled = false;
object pItems = null;
if(App.GetViewState(LiraViewCtlEnum.kLiraViewCtl_Plot, out bPlotEnabled, ref pItems) != 0 && bPlotEnabled)
{ // Some mosaic is active. Is it ours?
object[] Items = pItems as object[];
if(Items != null && Items.Length > 0)
{
string strPlot = Items[0] as string;
if(strPlot != null && strPlot.Contains(". Src=Wud:MyMosaicSign"))
{// Yes, this is our mosaic. Press the button:
return 1;
}
}
}
}
else //if (strFunctionName == "...")
{
// other checks
}
}
return 0;
}
For various reasons, it is sometimes convenient to focus the user's attention on one or several elements of the design model. For this purpose, LIRA-FEM has built-in so-called local modes for reinforced concrete structure design (LARM) and steel structure design (STC). In the STC local mode, for example, calculation tracing is available — expanding the numbers of the final result into formulas down to the original forces and geometric characteristics of steel profiles — whereas this tracing is not available in the LIRA-FEM general steel calculation. The local mode allows moving some functions out of LIRA-FEM, for example those requiring significant additional computation time.
LIRA-FEM API gives extension developers the ability to create similar operating modes for their extension, and automatically transfers almost all capabilities for editing input data and creating results from the LIRA-FEM application to the local mode. Only the ability to build mosaics on the design model is not transferred, since the full design model is not available in local mode. An example of a local mode is the Wood.exe application — the local mode of the Timber extension.
Opening Wood.exe, we see an interface very similar to the Input Tables window.
It contains the same Timber extension input tables as in LIRA-FEM, and clicking the ellipsis in a cell opens the same windows for editing cell contents. All these capabilities are automatically provided by LIRA-FEM API through the specialized control LiraExtensionTablesControl class. A user of this control will find in it an implementation of the already familiar LIRA-FEM API interfaces. The interaction diagram of objects and interfaces in the extension's local mode is shown below.
LIRA-FEM API LIRA-FEM Extension
Control element
LiraExtensionTablesControl
¦
¦ All input table groups and tables
'--LiraExtensionTables
ILiraExtensionTables
¦ ¦ Object for managing
¦ ¦ extension input tables
¦ '------------------------------<<--ILiraExtensionTableGroupHelper
¦
¦ Document input table group
¦--LiraExtensionTableGroup
¦ ILiraExtensionTableGroup
¦ ¦ ¦ Object for managing
¦ ¦ ¦ extension input table group
¦ ¦ '------------------------------<<--ILiraExtensionTableGroupHelper
¦ ¦
¦ ¦ Document input table
¦----'--LiraExtensionTable
¦ ¦
¦ ¦ Extension input table
¦----'--LiraExtensionTable
¦ ¦ Object for managing
¦ ¦ extension input table
¦ '------------------------------<<--ILiraExtensionTableHelper
¦
¦ Measurement unit settings
'--LiraExtensionMeasurementUnits
ILiraExtensionMeasurementUnits
Let us analyze the differences between the local operating mode and the LIRA-FEM operating mode. In local mode, the LiraApplication and LiraDocument objects are absent; the latter's place is taken by the LiraExtensionTablesControl class control. It contains a parallel implementation of input tables, including the container of all input tables ILiraExtensionTables. Input tables and their groups can be created within this container. However, input table and group objects in this implementation do not support the [I]LiraTable and [I]LiraTableGroup interfaces, but the extension can use their versions ILiraExtensionTable and ILiraExtensionTableGroup. These interfaces are also supported in LIRA-FEM mode, so in most cases the extension can use the *LiraExtensionTable* interface versions instead of *LiraTable*. Only when interacting with the design model can the extension request the *LiraTable* interfaces and through them the LiraDocument or LiraApplication objects.
In other cases, the capabilities of the *LiraExtensionTable* interfaces will be fully sufficient. Measurement units are available only as the ILiraExtensionMeasurementUnits interface, but again this is not a problem since it supports all methods of the [I]LiraMeasurementUnits interface. Therefore the extension can use ILiraExtensionMeasurementUnits always — both in LIRA-FEM mode and in local mode.
| Interface and its methods | Description |
|---|---|
| LiraExtensionTablesControl | Main extension interface for working in the LIRA-FEM environment |
| AllowEditTree | Allows the user to delete or move items in the tree view of this control. |
| Tables | All input tables of one local-mode extension document |
The fundamental difference between the extension's local operating mode and the LIRA-FEM operating mode is the absence of the ILiraExtension object in local mode. This object is never created; instead, the application must support an object implementing the ILiraExtensionTablesHelper interface. Its sole purpose is essentially to create ILiraExtensionTableGroupHelper and ILiraExtensionTableHelper objects when a local-mode document is loaded. The ILiraExtensionTablesHelper interface is small:
| Interface and its methods | Description |
|---|---|
| ILiraExtensionTablesHelper | A service interface, a replacement for ILiraExtension, which allows loading input tables from files stored on the hard drive |
| CreateTableHelper() | On request from the LIRA-FEM API, creates and returns an object managing an extension input table ILiraExtensionTable or an extension input table group ILiraExtensionTableGroup |
| GetExtensionId() | Returns the three-letter extension identifier |
| GetExtensionName() | Returns the extension name |
| ExtensionDocumentClosing() | Called when a standalone document in the standalone extension application is closing. Requires releasing references to objects in the standalone document. |
To create a LIRA-FEM extension local mode, you need to create an .EXE application and place the LiraExtensionTablesControl class control on its form or dialog panel. In Microsoft Visual Studio, right-click in the Toolbox window, select the Choose Items context menu item, then enable the LiraExtensionTablesControl class control on the COM tab and click OK.
After placing the control on the form it will look like this:
If you were unable to place this control on the form on the first attempt, perform the following steps:
As can be seen after placing the control on the form, it encapsulates input table editing. Your application simply needs to instantiate the required tables, just as the ILiraExtension object did. The control's language matches the LIRA-FEM language.
Data saving and loading is performed using ILiraExtensionTables.SaveToFile() and ILiraExtensionTables.LoadFromFile(). Because ILiraExtensionTables.LoadFromFile() can read data written by LiraTables.ExportToFile(), transferring data from LIRA-FEM mode to the extension's local mode is very straightforward. The Timber extension provides a special command «Local element analysis mode» for this. When this command is invoked, several temporary tables are created in which the extension collects data exclusively for the selected design model elements, then exports them using LiraTables.ExportToFile() and then opens this file with the Wood.exe application.
By default in the extension's local mode, input table operations such as renaming, moving, or copying tables in the tree by drag-and-drop or clipboard are disabled. They can be enabled in the AllowEditTree property of the LiraExtensionTablesControl class control placed on the form. All tables exported from LIRA-FEM mode to local mode that do not have their own ILiraExtensionTableHelper implementation will be displayed in local mode simply as text with no ability to edit cells in the corresponding dialog panels. In local mode, building mosaics on the design model and Report Book documentation are not available.
The LiraExtensionTablesControl class control is installed and removed together with LIRA-FEM, but does not require a LIRA-FEM license to operate.
| Key | Parameter | Allowed values | Comments | Corresponding API method |
|---|---|---|---|---|
HKEY_LOCAL_MACHINE\Software\LIRA EXTENSIONS\ | Key for registering all extensions | |||
| Abc | Latin letters and digits only | Three-letter extension identifier | LiraExtension.AddExtension() | |
| (Default) | Script:VBS | Extension written in VBScript | same | |
| Script:JS | Extension written in JScript | |||
| Script:V8 | Extension written in V8 JavaScript | |||
| Dll:Net4 | Extension is a DLL on .NET Framework 4 (C# etc.) | |||
| Dll:stdcall | Extension is a native DLL with _stdcall calling convention | |||
| Dll:cdecl | Extension is a native DLL with _cdecl calling convention | |||
| Exe:App | Extension is a standalone EXE application | |||
| Version | 20AB - 20CD | LIRA-FEM versions the extension is compatible with, e.g. "2026 - 2027". If not specified, the application is compatible with all LIRA-FEM versions starting from 2026. | same | |
| Name | Any text | Extension name | same | |
| RibbonTab | Any text | Name of the ribbon tab where extension buttons will be placed | LiraRibbon.AddExtensionTab() | |
| RibbonTabImageListSmall | Limited by OS file system | Path to a horizontal sprite with 16x16 or 24x24 images (depending on the «Large ribbon icons» setting) for extension buttons on the ribbon | same | |
| RibbonTabImageListLarge | Limited by OS file system | Path to a horizontal sprite with 32x32 or 48x48 images (depending on the «Large ribbon icons» setting) for extension buttons on the ribbon | same | |
| Function | Limited by the programming language | Name of the extension function or method including the namespace and class name, if applicable | LiraExtension.AddExtensionFunction() | |
| (Default) | Limited by OS file system | Path to the VBS/JS or DLL/EXE file containing the function | same | |
| CallAfter | Button | Function must be called when a ribbon button is pressed | same | |
| AppStartup | Function must be called immediately after the application loads* | |||
| Analysis000 | Function must be called before the finite element (FE) analysis* | |||
| AnalysisFEA | Function must be called immediately after the FE analysis* | |||
| AnalysisCLA | Same, after the Combinations of Loads and Actions (CLA) calculation* | |||
| AnalysisDCF | Same, after the Design Forces Combinations (DFC) calculation* | |||
| AnalysisUCF | Same, after the Unified Forces Combinations (UFC) calculation* | |||
| AnalysisDCL | Same, after the Design Load Combinations (DLC) calculation* | |||
| AnalysisLoF | Same, after the Loads of Fragment calculation* | |||
| AnalysisPES | Same, after the Principal and Equivalent Stresses calculation* | |||
| AnalysisCRK | Same, after crack width calculation for nonlinear problems* | |||
| AnalysisDOs | Same, after design option calculations (RC, steel, masonry)* | |||
| AnalysisCSV | Same, after CSV result files are created (if requested)* | |||
| Name | Any text | Ribbon button caption** | LiraRibbon.AddButton() | |
| ToolTip | Any text | Ribbon button tooltip** | same | |
| Description | Any text | Ribbon button description** | same | |
| RibbonImageSmall | Limited by OS file system | Index from 0 in the sprite, or path to a 16x16 or 24x24 image (depending on the «Large ribbon icons» setting)** | same | |
| RibbonImageLarge | Limited by OS file system | Index from 0 in the sprite, or path to a 32x32 or 48x48 image (depending on the «Large ribbon icons» setting)** | same | |
| RibbonPanel | Any text | Name of the panel on the ribbon tab where the button is placed | LiraRibbon.AddPanel() | |
| RibbonAlwaysLarge | 1 or 0 | Button is always large** | LiraRibbon.AddButton() | |
| HelpCommand | Limited by OS file system | Help call for this extension function**, e.g.: C:\Program Files (x86)\LIRALAND\LIRA-FEM 2026\bin\x64\help\ru\LiraFEM.chm | ILiraExtension.HelpContext() | |
| * A call order can be specified as a double number after the plus sign «+», e.g., Analysis000 + 123.456 | ||||
| ** Only for functions with "CallAfter"="Button" | ||||
This subsection lists the programming languages in which LIRA-FEM extensions can be written and debugged.
Supported in-proc programming languages:
Supported out-proc programming languages:
In-proc debugging:
Out-proc debugging:
If you find a mistake and want to inform us about it, select the mistake, then hold down the CTRL key and click ENTER.
Comments