/
NET 8 Plugin Example - All Products

NET 8 Plugin Example - All Products

Overview


With the release of ETABS v22.0.0 , SAFE v22.0.0 , SAP2000 v26.0.0 , and CSiBridge v26.0.0 , it is now possible to create plugins for CSI products using .NET 8 . To learn more about the .NET 8 platform , please see What's new in .NET 8 . This example will guide a developer through creating a simple .NET 8 plugin using the C# programming language. By making use of the cross-product CSI API library, this example is compatible with ETABS v22.0.0 , SAFE v22.0.0 , SAP2000 v26.0.0 , and CSiBridge v26.0.0 (or a higher version of any of these). 


Walkthrough

Prerequisites

This walkthrough uses Microsoft Visual Studio 2022 and the .NET 8 SDK. One of the following CSI products must be installed on the development computer: ETABS v22.0.0 , SAFE v22.0.0 , SAP2000 v26.0.0 , or CSiBridge v26.0.0 (higher versions of any product will also work)

Creating the plugin assembly

  1. Selecting C# as the language, create a new project of type Class Library. Ensure that the selected project type targets .NET or .NET Standard, not .NET Framework.


2. For convenience, set your project name to the desired name of your plugin. For this example, we will use the name CSiNET8PluginExample1


3. Select .NET 8.0 as the desired Framework


4. Once your project is created, add a reference to the CSI API library. This will be located in the installed program directory, e.g. C:\Program Files\Computers and Structures\ETABS 22\ .  


To create a plugin compatible with multiple CSI products, use the cross-product API library, CSiAPIv1.DLL . This library contains interfaces for all methods supported by all CSI products. Please note, however, that no CSI product implements every interface. Attempting to call an unsupported API method will return an error code. Developers should refer to the API documentation in the installed program directory to determine which methods are supported.

This example will use CSiAPIv1.DLL. If you are making a plugin only for one CSI product, you can use the program-specific API library instead of the cross-product library. The program-specific API libraries are:

ProgramAPI Library
ETABSETABSv1.DLL
SAFESAFEv1.DLL
SAP2000SAP2000v1.DLL
CSiBridgeCSiBridge1.DLL


5. Almost all plugins will depend on other, non-CSI libraries to execute complicated functionality. To demonstrate the correct management of dependent libraries, this plugin makes trivial use of the popular Newtonsoft.Json serialization/deserialization library. It is freely available on GitHub and can be added to your project using the NuGet package manager within Visual Studio.


5. Some manual edits must be made to the project file in order for the plugin to run successfully. Open the project file (in our example, CSiNET8PluginExample1.csproj ) in a text editor, and make the following edits:

    • Change the <TargetFramework> from net8.0 to net8.0-windows . This is needed to allow the plugin to instantiate Windows forms (Note: this plugin has only been tested to work on the Windows platform)
    • Add the <UseWindowsForms>true</UseWindowsForms> child element to the <PropertyGroup> parent element. This will allow you to add a Windows Form to your class library.
    • Add the <EnableDynamicLoading>true</EnableDynamicLoading> child element to the <PropertyGroup> parent element. This will allow the CSI product to load your project as a plugin. 
    • Locate the CSiAPIv1 library reference element. It should look like <Reference Include="CSiAPIv1"> and be inside an <ItemGroup> parent element. Add the following two child elements within the CSiAPIv1 reference element:
      • <Private>false</Private>
      • <ExcludeAssets>runtime</ExcludeAssets>

The importance of these two elements will be explained later in this tutorial.


After the above changes, your project file should look approximately as shown below on the right. It's ok if it doesn't match exactly.

Before:

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>net8.0</TargetFramework>	
		<ImplicitUsings>enable</ImplicitUsings>
		<Nullable>enable</Nullable>
		<Platforms>AnyCPU;x64</Platforms>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
	</ItemGroup>

	<ItemGroup>
	  <Reference Include="CSiAPIv1">
	    <HintPath>..\Program Files\Computers and Structures\ETABS 22\CSiAPIv1.dll</HintPath>
	  </Reference>
	</ItemGroup>

</Project>

After:

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>net8.0-windows</TargetFramework>	
		<ImplicitUsings>enable</ImplicitUsings>
		<Nullable>enable</Nullable>
		<Platforms>AnyCPU;x64</Platforms>
	 	<UseWindowsForms>true</UseWindowsForms>
		<EnableDynamicLoading>true</EnableDynamicLoading>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
	</ItemGroup>

	<ItemGroup>
	  <Reference Include="CSiAPIv1">
	    <HintPath>..\Program Files\Computers and Structures\ETABS 22\CSiAPIv1.dll</HintPath>
		<Private>false</Private>
		<ExcludeAssets>runtime</ExcludeAssets>
	  </Reference>
	</ItemGroup>

</Project>


6. With the modifications to the project file, you should be able to add a new Windows Form to your project. Name the form Form1 .



7. Edit the form using the Windows Forms Designer. The exact layout of the form is not important, but for the copy and paste code in the steps below to operate correctly, the form must be named Form1, and must contain a Button control named button1 and RichTextBox control named richTextBox1 .


8. Open the Form1.cs file and paste in the following code. Don't worry about compilation errors, you'll add the other required classes in the following steps.

using CSiAPIv1;
using Newtonsoft.Json;

namespace CSiNET8PluginExample1
{
    public partial class Form1 : Form
    {

        private cSapModel _sapModel;
        private cPluginCallback _pluginCallback;
        private int errorCode = 0; // default return code is no error

        public Form1()
        {
            InitializeComponent();
            FormClosing += Form1_FormClosing;
        }

        public void SetSapModel(ref cSapModel inSapModel, ref cPluginCallback inPluginCallback)
        {
            _sapModel = inSapModel;
            _pluginCallback = inPluginCallback;

            richTextBox1.Text = "Hello CSI Friends";
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // It is very important to call _pluginCallback.Finish(errorCode) when the form closes, !!!
            // otherwise, the CSI program will wait and be hung !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            _pluginCallback.Finish(errorCode);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                // Exercise some simple Newtonsoft.Json functionality
                string jsonText = JsonConvert.SerializeObject(richTextBox1.Text);
                string deserializedText = JsonConvert.DeserializeObject<string>(jsonText);
                
                CreateStructure(deserializedText);
            }
            catch (Exception ex)
            {
                errorCode = 2; // will be visible to plugin end-user for debugging purposes
                MessageBox.Show("The following error occurred:" + Environment.NewLine + ex.Message);
            }
            finally
            {
                this.Close();
            }
        }

        public void CreateStructure(string text)
        {
            int CallResult;
            int i;
            string FrameName = "FrameName";

            var aVectorFont = new cVectorFont();
            double CharHeight = 200.0;
            var HAlignment = cVectorFont.TextAlignment.kTA_HLeft;
            var VAlignment = cVectorFont.TextAlignment.kTA_VTop;
            double[] textX = new double[1], textY = new double[1];
            int NumPts;

            // test for exception handling
            if (string.Equals(text, "crash", StringComparison.InvariantCultureIgnoreCase))
            {
                textX[99] = 0; // out of bounds
            }

            aVectorFont.FillTextVertices(text.ToUpper(), CharHeight, HAlignment, VAlignment, ref textX, ref textY);
            NumPts = textX.Length;

            CallResult = _sapModel.InitializeNewModel();
            CallResult = _sapModel.File.NewBlank();
            for (i = 0; i < NumPts - 2; i++)
            {
                FrameName = "FrameName" + (i / 2).ToString();
                CallResult = _sapModel.FrameObj.AddByCoord(textX[i], textY[i], 0, textX[i + 1], textY[i + 1], 0, ref FrameName);
                i++;
            }

            _sapModel.View.RefreshView(0, false);
        }
    }
}
    


9. In your project, locate the Class1.cs file and rename it to cPlugin.cs . Then open it and paste in the following code. Please pay attention to the comments in the code, they contain important information about correct plugin operation.

using CSiAPIv1;

namespace CSiNET8PluginExample1
{
    // Implementing the cPluginContract interface is not required, however
    // it is recommended to ensure that the required cPlugin methods are created correctly.
    // Do not implement the Info or Main methods explicitly,
    // i.e. their method signatures are correct as is
    public class cPlugin : cPluginContract
    {
        private static string _modality = "Non-Modal";
        private static string _versionString = "2.0";
        private int errorCode = 0; // default return code is no error

        public int Info(ref string Text)
        {
            try
            {
                Text = "This plugin is supplied by Computers and Structures, Inc., " +
                       "as a simple example for developers of new plugins for CSI products. " +
                       "It starts a new blank model, then converts a line of text into " +
                       "frame objects and adds them to the model. It trivially uses the popular " +
                       "Newtonsoft.Json library to demonstrate proper dependency management. " +
                       "If you enter the " +
                       "text \"crash\", an error will be generated for testing purposes. " +
                       "Version " + _versionString;
            }
            catch (Exception)
            {
            }

            return 0;
        }

        public void Main(ref cSapModel sapModel, ref cPluginCallback pluginCallback)
        {
            var aForm = new Form1();

            try
            {
                aForm.SetSapModel(ref sapModel, ref pluginCallback);

                if (string.Compare(_modality, "Non-Modal", true) == 0)
                {
                    // Non-modal form, allows graphics refresh operations in CSI program, 
                    // but Main will return to CSI program before the form is closed.
                    aForm.Show();
                }
                else
                {
                    // Modal form, will not return to CSI program until form is closed,
                    // but may cause errors when refreshing the view.
                    aForm.ShowDialog();
                }

                // It is very important to call pluginCallback.Finish(errorCode) when the form closes, !!!
                // otherwise, the CSI program will wait and be hung !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // This must be done inside the closing event for the form itself, not here !!!!!!!!!!!!!!

                // If you have only algorithmic code here without any forms, 
                // then call pluginCallback.Finish(errorCode) here before returning to the CSI program

                // errorCode = 0 indicates that the plugin completed successfully
                // ie pluginCallback.Finish(0)
                // errorCode = (Any non-zero integer) indicates that the plugin closed with an error
                // ie pluginCallback.Finish(1)
                // If an error occurs, the errorCode value will be displayed
                // to the plugin end-user in a message box, for debugging purposes.

                // If your code will run for more than a few seconds, you should exercise
                // the Windows messaging loop to keep the program responsive. You may 
                // also want to provide an opportunity for the user to cancel operations.

            }
            catch (Exception ex)
            {
                errorCode = 1;
                MessageBox.Show("The following error terminated the plugin:" + Environment.NewLine + ex.Message);

                // call Finish to inform the CSI program that the plugin has terminated
                try
                {
                    pluginCallback.Finish(errorCode); // error code 1 will be visible to plugin end-user for debugging purposes
                }
                catch (Exception)
                {
                }
            }
        }
    }
}

10. In your project, create a new class file and name it cVectorFont.cs . Open the file and paste in the following code. This code converts alphanumeric characters to coordinates that can be used to create frame objects. Since the functionality is specific to this example, don't worry about studying this class closely.

namespace CSiNET8PluginExample1
{
    using System;

    public class cVectorFont
    {
        public enum TextAlignment
        {
            kTA_HLeft = 0,
            kTA_HCenter,
            kTA_HRight,
            kTA_VBottom,
            kTA_VCenter,
            kTA_VTop
        }

        protected const int VF_CHARACTERS = 96;
        protected const int VF_MOVES_PER_CHARACTER = 20;
        protected const double VF_ASPECT_RATIO = 0.5;

        struct VFData
        {
            public double XI;
            public double YI;
            public int pen;
        }

        VFData[,] vf;

        public cVectorFont()
        {
            vf = new VFData[VF_CHARACTERS + 1, VF_CHARACTERS + 1];
            Initialize();
        }

        protected void Initialize()
        {
            string[] VectorChar = new string[97];
            string tmpString;
            int i, j, k;

            VectorChar[1] = "923 000";
            VectorChar[2] = "323 422 343 442 462 572 482 382 272 362 342 923 000";
            VectorChar[3] = "383 272 262 162 172 272 683 572 562 462 472 572 923 000";
            VectorChar[4] = "133 172 573 532 643 042 063 662 923 000";
            VectorChar[5] = "133 532 642 552 152 062 172 572 383 322 923 000";
            VectorChar[6] = "163 262 372 282 182 072 162 573 132 423 522 632 542 442 332 422 923 000";
            VectorChar[7] = "623 172 282 482 472 052 032 122 422 532 632 923 000";
            VectorChar[8] = "483 372 362 262 272 372 923 000";
            VectorChar[9] = "683 472 252 232 412 602 923 000";
            VectorChar[10] = "283 472 652 632 412 203 923 000";
            VectorChar[11] = "333 372 563 142 053 652 543 162 923 000";
            VectorChar[12] = "333 372 053 652 923 000";
            VectorChar[13] = "213 322 332 232 222 322 923 000";
            VectorChar[14] = "053 652 923 000";
            VectorChar[15] = "223 322 332 232 222 923 000";
            VectorChar[16] = "682 923 000";
            VectorChar[17] = "123 522 632 672 582 182 072 032 122 133 572 923 000";
            VectorChar[18] = "323 522 423 482 372 923 000";
            VectorChar[19] = "173 282 582 672 662 032 022 622 923 000";
            VectorChar[20] = "033 122 522 632 642 552 352 553 662 672 582 282 172 923 000";
            VectorChar[21] = "423 622 523 582 042 642 923 000";
            VectorChar[22] = "033 122 522 632 642 552 152 182 582 923 000";
            VectorChar[23] = "583 262 152 042 032 122 522 632 642 552 152 923 000";
            VectorChar[24] = "023 682 182 072 923 000";
            VectorChar[25] = "123 522 632 642 452 252 162 172 282 482 572 562 452 253 042 032 122 923 000";
            VectorChar[26] = "023 442 662 672 582 182 072 062 152 552 923 000";
            VectorChar[27] = "223 322 332 232 222 253 352 362 262 252 923 000";
            VectorChar[28] = "213 322 332 232 222 322 253 352 362 262 252 923 000";
            VectorChar[29] = "533 152 572 923 000";
            VectorChar[30] = "143 542 563 162 923 000";
            VectorChar[31] = "043 452 062 352 042 923 000";
            VectorChar[32] = "223 322 243 342 352 562 572 482 182 072 923 000";
            VectorChar[33] = "553 542 342 352 552 662 572 272 062 032 222 522 632 923 000";
            VectorChar[34] = "052 382 652 622 043 642 923 000";
            VectorChar[35] = "082 482 572 562 452 552 642 632 522 022 053 452 923 000";
            VectorChar[36] = "573 482 182 072 032 122 522 632 923 000";
            VectorChar[37] = "082 582 672 632 522 022 923 000";
            VectorChar[38] = "082 582 353 052 023 622 923 000";
            VectorChar[39] = "082 682 453 052 923 000";
            VectorChar[40] = "353 552 642 632 522 122 032 072 182 482 572 923 000";
            VectorChar[41] = "082 053 652 683 622 923 000";
            VectorChar[42] = "123 522 323 382 183 582 923 000";
            VectorChar[43] = "033 122 422 532 582 383 682 923 000";
            VectorChar[44] = "082 053 352 683 352 622 923 000";
            VectorChar[45] = "083 022 622 923 000";
            VectorChar[46] = "082 352 682 622 923 000";
            VectorChar[47] = "082 622 682 582 923 000";
            VectorChar[48] = "033 072 182 582 672 632 522 122 032 923 000";
            VectorChar[49] = "082 582 672 662 552 052 923 000";
            VectorChar[50] = "033 072 182 582 672 632 522 122 032 343 432 622 923 000";
            VectorChar[51] = "082 582 672 662 552 052 453 442 622 923 000";
            VectorChar[52] = "033 122 522 632 642 552 152 062 072 182 482 572 923 000";
            VectorChar[53] = "323 382 083 682 923 000";
            VectorChar[54] = "083 032 122 522 632 682 923 000";
            VectorChar[55] = "083 052 322 652 682 923 000";
            VectorChar[56] = "083 022 352 622 682 923 000";
            VectorChar[57] = "682 083 622 923 000";
            VectorChar[58] = "323 352 082 683 352 923 000";
            VectorChar[59] = "083 682 022 622 253 452 923 000";
            VectorChar[60] = "423 222 282 482 923 000";
            VectorChar[61] = "083 622 923 000";
            VectorChar[62] = "223 422 482 282 923 000";
            VectorChar[63] = "153 372 552 923 000";
            VectorChar[64] = "013 612 923 000";
            VectorChar[65] = "453 362 372 272 262 362 923 000";
            VectorChar[66] = "623 532 422 122 032 042 152 452 542 652 543 532 923 000";
            VectorChar[67] = "072 043 252 552 642 632 522 122 032 923 000";
            VectorChar[68] = "553 152 042 032 122 522 632 923 000";
            VectorChar[69] = "623 672 643 452 152 042 032 122 522 632 923 000";
            VectorChar[70] = "623 122 032 042 262 362 353 352 923 000";
            VectorChar[71] = "623 532 522 122 032 252 662 923 000";
            VectorChar[72] = "153 672 643 452 152 042 032 122 522 632 552 923 000";
            VectorChar[73] = "623 532 042 032 352 923 000";
            VectorChar[74] = "623 532 642 552 153 043 032 122 923 000";
            VectorChar[75] = "623 532 042 032 172 462 642 662 923 000";
            VectorChar[76] = "623 032 042 353 923 000";
            VectorChar[77] = "623 042 032 332 442 362 572 482 562 662 652 552 923 000";
            VectorChar[78] = "623 042 032 332 442 362 572 482 562 662 923 000";
            VectorChar[79] = "123 522 632 672 643 452 152 042 032 122 923 000";
            VectorChar[80] = "623 122 032 042 152 452 543 562 552 923 000";
            VectorChar[81] = "123 522 632 672 643 452 152 042 032 122 253 152 162 662 923 000";
            VectorChar[82] = "623 122 032 042 152 452 543 532 572 482 562 652 923 000";
            VectorChar[83] = "143 672 643 452 152 042 032 122 522 632 552 352 923 000";
            VectorChar[84] = "623 032 042 232 352 923 000";
            VectorChar[85] = "623 432 442 353 252 552 643 662 923 000";
            VectorChar[86] = "623 352 342 142 152 372 552 923 000";
            VectorChar[87] = "623 352 342 142 152 372 562 652 572 482 923 000";
            VectorChar[88] = "623 342 352 372 582 662 642 562 342 923 000";
            VectorChar[89] = "623 342 352 372 582 662 552 342 352 562 652 923 000";
            VectorChar[90] = "623 032 042 352 623 923 000";
            VectorChar[91] = "272 662 282 372 482 572 923 000";
            VectorChar[92] = "223 622 232 332 442 542 923 000";
            VectorChar[93] = "473 523 532 633 923 000";
            VectorChar[94] = "243 632 293 243 142 082 093 193 293 433 532 622 652 923 000";
            VectorChar[95] = "253 172 282 582 672 632 522 122 032 923 000";
            VectorChar[96] = "233 242 443 442 442 623 342 923 000";

            for (i = 1; i <= VF_CHARACTERS; i++)
            {
                for (j = 1; j <= VF_MOVES_PER_CHARACTER; j++)
                {
                    k = (4 * j) - 4; // k = (4 * j) - 3;
                    tmpString = VectorChar[i].Substring(k + 2, 1);
                    vf[i, j].pen = int.Parse(tmpString);
                    if (vf[i, j].pen == 0)
                        break;
                    tmpString = VectorChar[i].Substring(k, 1);
                    vf[i, j].XI = double.Parse(tmpString);
                    vf[i, j].XI *= VF_ASPECT_RATIO;
                    tmpString = VectorChar[i].Substring(k + 1, 1);
                    vf[i, j].YI = double.Parse(tmpString);
                }
            }
        }

        public void FillTextVertices(string inStr, double CharHeight, TextAlignment HAlignment, TextAlignment VAlignment, ref double[] tX, ref double[] tY)
        {
            int i, j, NumChars, NumPts, pos;
            const double CharWidth = VF_ASPECT_RATIO * 9; // 9 is initial height

            NumPts = 0;
            NumChars = inStr.Length;

            for (pos = 0; pos <= NumChars - 1; pos++)
            {
                if (Convert.ToByte(inStr.Substring(pos, 1).FirstOrDefault()) == 13)
                {
                    //do nothing
                }
                else if (Convert.ToByte(inStr.Substring(pos, 1).FirstOrDefault()) == 10)
                {
                    //do nothing
                }
                else
                {
                    i = Convert.ToByte(inStr.Substring(pos, 1).FirstOrDefault()) - 31;
                    for (j = 1; j <= VF_MOVES_PER_CHARACTER; j++)
                    {
                        if (vf[i, j].pen == 2)
                            NumPts += 2;
                    }
                }
            }

            tX = new double[NumPts + 1];
            tY = new double[NumPts + 1];
            int LineStart = 0;
            int LineEnd = 1;
            double YOffset = 0.0;
            double CharStartX = 0.0;
            double XCurrent = 0.0;
            double YCurrent = 0.0;
            double XStart = 0.0;
            double YStart = 0.0;

            for (pos = 0; pos <= NumChars - 1; pos++)
            {
                if (Convert.ToByte(inStr.Substring(pos, 1).FirstOrDefault()) == 13)
                {
                    YOffset -= 9.0 + 2.0; // 9.0 is initial char height, 2.0 is spacing
                    CharStartX = 0.0;
                }
                else if (Convert.ToByte(inStr.Substring(pos, 1).FirstOrDefault()) == 10)
                {
                }
                else
                {
                    i = Convert.ToByte(inStr.Substring(pos, 1).FirstOrDefault()) - 31; // ASCII 32 is VectorFont(1)   

                    if (vf[i, 1].pen == 2)
                    {
                        XStart = CharStartX;
                        YStart = 2.0 + YOffset;
                    }

                    for (j = 1; j <= VF_MOVES_PER_CHARACTER; j++)
                    {
                        if (vf[i, j].pen == 0)
                            break;

                        if (vf[i, j].pen == 2)
                        {
                            XCurrent = vf[i, j].XI + CharStartX;
                            YCurrent = vf[i, j].YI + YOffset;

                            tX[LineStart] = XStart; tX[LineEnd] = XCurrent;
                            tY[LineStart] = YStart; tY[LineEnd] = YCurrent;

                            LineStart += 2; LineEnd += 2;

                            XStart = XCurrent;
                            YStart = YCurrent;
                        }
                        else if (vf[i, j].pen == 3)
                        {
                            XStart = vf[i, j].XI + CharStartX;
                            YStart = vf[i, j].YI + YOffset;
                        }
                    }
                    CharStartX += CharWidth;
                }
            }

            double ScaleFactor = CharHeight / 9.0;
            double OffsetX = 0.0;
            var OffsetY = 0.0;

            switch (HAlignment)
            {
                case TextAlignment.kTA_HCenter:
                    {
                        OffsetX = -NumChars * CharWidth / 2.0;
                        break;
                    }

                case TextAlignment.kTA_HRight:
                    {
                        OffsetX = -NumChars * CharWidth;
                        break;
                    }
            }

            switch (VAlignment)
            {
                case TextAlignment.kTA_VCenter:
                    {
                        OffsetY = -9 / 2.0;
                        break;
                    }

                case TextAlignment.kTA_VTop:
                    {
                        OffsetY = 0.0;
                        break;
                    }

                case TextAlignment.kTA_VBottom:
                    {
                        OffsetY = -9.0;
                        break;
                    }
            }

            for (i = 0; i <= NumPts - 1; i++)
            {
                tX[i] += OffsetX; tY[i] += OffsetY;
                tX[i] *= ScaleFactor; tY[i] *= ScaleFactor;
            }
        }
    }
}

11. With all the code in place, compile your project. You can use AnyCPU or x64 as the platform, but not x86. After successful compilation, the output directory of your project should look similar to this:

All of the project's dependencies should be present in this directory, except for one: the CSiAPIv1.DLL should NOT be included in this folder. The purpose of the two child elements you added to the <Reference Include="CSiAPIv1"> parent element in the project file was to exclude this assembly from being copied to the project output directory. If the CSiAPIv1.DLL is present in the same directory as your plugin assembly, type errors will occur at runtime.

The file with extension deps.json will manage your plugin assembly's dependencies. Your plugin's dependencies will not interfere with those of CSI products. For instance, all CSI products make use of the Newtonsoft.Json package, but your plugin will only use the Newtonsoft.Json.dll present in the same folder as your plugin.

Using the plugin in a CSI product

Your project output directory should now contain your built plugin assembly ( CSiNET8PluginExample1.DLL ) and all of its dependencies, except CSiAPIv1.DLL . The steps below demonstrate how to call this plugin from within a CSI product. The screenshots below are of ETABS, but the steps are the same for every CSI product.


  1. From the Tools menu, select Add/Show Plugins


2. In the External Plugin Data form, click the Browse button.

 


3. In the file select dialog, navigate to your project output directory and select your plugin assembly ( CSiNET8PluginExample1.DLL )


4. Your plugin information is now displayed in the form.

    • Plugin Name: This is the name of your plugin assembly and cannot be modified.
    • Menu Text: A default menu text is assigned to your plugin. You can change this to any name you want, but it must not conflict with any other plugin.
    • Plugin Path: The location of your plugin


Click the Add button to load your plugin.


5. You should now see your plugin in the list, with the status OK. If desired, you can click the Info button to see information about your plugin in a pop-up window.


6. Your plugin should now be available in the Tools menu. Clicking on it for the first time will bring up an info box before displaying the plugin form.


7. In the plugin form, you can enter any text you like, then click the button.


8. The CSI program will create a new model that displays your message with frame objects.


9. If you enter the text crash in the plugin textbox, a message box will appear with your chosen error code.




For download

Download the Visual Studio solution of this example, with an already built, ready-to-run plugin assembly: CSiNET8PluginExample1.zip

For developers who need to use .NET Framework 4.7.1 - 4.8.1 , an example .NET Framework 4.8 project is provided here: CSiFramework48PluginExample2.zip

References

https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support#simple-plugin-with-no-dependencies

Related content