TN8373 Creating ActiveX for CitectSCADA using Visual CSharp 2010
TN8373 Creating ActiveX for CitectSCADA using Visual CSharp 2010
The document goes through creating an ActiveX object using Visual C# 2010, it shows
examples of implementing core functionality (Graphical User Interface, Properties, Methods
and Events) within the ActiveX object. And finally, step by step instructions on using each of
the implemented ActiveX functionality from within a SCADA Project.
Audience
The contents of this document are targeted towards SCADA engineers, systems integrators
and individuals with intermediate to advanced level knowledge of CitectSCADA/Vijeo Citect,
Cicode Programming and .NET Programming.
Page | 1
Creating a Visual Studio Project
Create a new Project in Visual Studio 2010 using the ‘Class Library’ template as shown
below. Give your project a name. In this example, we will name the project
‘MyFirstActiveX’.
Figure 1
Page | 2
Once the project is created, delete the automatically generated class file ‘Class1.cs’ from the
Solution Explorer.
Figure 2
Page | 3
Defining Properties and Methods
In order for the ActiveX to expose Properties and Methods, an interface needs to be defined
which will contain information related to the exposed Properties and Methods. The interface
will get registered into the system registry which then allows applications using the ActiveX
to determine the Properties and Methods exposed by the ActiveX.
Figure 3
Page | 4
Figure 4
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("2E907764-32F1-4F3B-823C-11515494316E")]
The 1st attribute flags the interface to be ‘Com’ visible. This is important since CitectSCADA’s
ActiveX container is a COM client i.e. only accepts COM ActiveX.
The 2nd attribute is required by COM clients, in order to accept event notifications.
The 3rd attribute is used by the system registry when registering the Interface.
Note: the Guid must be unique. Depending on the version of Visual Studio used, you can generate a
unique Guid via the following menu option ‘Tools|Create Guid|(Guid Format 5)’. Alternatively, there
are several websites that offer generating ‘Guid’ online.
Page | 5
In order to expose the Properties and Methods defined in the Interface, the access modifier
for the Interface needs to be set to ‘public’
Each Property/Method definition in the interface must have a unique [DispId(n)] attribute.
For this tutorial, we’ll define the following Properties and Methods:
Properties
o Name: StrProperty DataType: string
o Name: IntProperty DataType: int
o Name: FloatProperty DataType: float
Methods
o SampeMethod()
o SampleMethodWithArgs(string arg1)
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace MyFirstActiveX
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("2E907764-32F1-4F3B-823C-11515494316E")]
public interface IMyFirstActiveX
{
#region Properties
// Define Properties exposed by the ActiveX here...
[DispId(1)]
string StrProperty { get; set; }
[DispId(2)]
int IntProperty { get; set; }
[DispId(3)]
float FloatProperty { get; set; }
#endregion
#region Methods
// Define Methods exposed by the ActiveX here...
[DispId(4)]
void SampleMethod();
[DispId(5)]
void SampleMethodWithArgs(string arg1);
#endregion
}
}
Page | 6
Defining Events
ActiveX’s can also contain Events. To expose Events to Clients so that they can subscribe to
it, we need to add an Interface similar to the Interface for Properties and Methods. This time
however the name for the interface will be ‘IMyFirstActiveXEvents’
As mentioned previously,
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace MyFirstActiveX
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("6ec6f4f9-e2c4-4705-9d3c-a521c3b32ae0")]
public interface IMyFirstActiveXEvents
{
[DispId(1)]
void OnValueChanged(string propertyname);
[DispId(2)]
void OnSelectionChanged(string selectedvalue);
[DispId(3)]
void OnButtonClicked();
Page | 7
User Interface (UI)
Now that we’ve decided on the Properties, Methods and Events the ActiveX will expose, let’s
create a User Interface (UI) for the ActiveX.
We’ll use the UI in this tutorial to visualise and make changes to the Properties exposed by
the ActiveX, fire the events, as well as indicate when the exposed method gets called by the
client.
Add a new Item to the project, using the ‘User Control’ template. Name it : ‘SampleUI’. As
shown
Figure 5
Page | 8
The UI will contain the following visual elements:
Listbox
o Name: listBox1
o Items:
Item 1
Item 2
Item 3
Item 4
Item 5
Groupbox
o 3 x Labels
Name: label1 Text: Str Property:
Name: label2 Text: Int Property:
Name: label3 Text: Float Property:
o 3 x Textbox:
Name: txtStrProperty
Name: txtIntProperty
Name: txtFloatProperty
o 1 x Button
Name: btnUpdateProperties
Text: Update Properties
Button
o Name: btnClickEvent
o Text: Fire OnButtonClick Event
ListView
o Name: listView1
o Columns:
Name: TimeStamp Text: Time Stamp
Name: Details Text: Details
Page | 9
The final UI should look similar to this:
Figure 6
Once the UI elements are added, switch to the Code view (code behind)
The SampleUI Class will need to be decorated with the following attributes:
[ComVisible(true)]
[Guid("75C8832B-05CB-47D7-A6CB-0E706230BACB")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMyFirstActiveX))]
[ComSourceInterfaces(typeof(IMyFirstActiveXEvents))]
Next, the SampleUI class needs to implement the ‘IMyFirstActiveX’ interface. This is done by
adding the name of the interface to the class definition as shown:
Page | 10
Implementing the Properties and Methods
As for the Methods, whenever it is called, a new line with the method name and any given
arguments will be appended to the ListView.
StrProperty
IntProperty
Page | 11
FloatProperty
SampleMethod
SampleMethodWithArgs
Helper Methods
Page | 12
Implementing the Events
Events need to be implemented via a delegate. The delegate’s signature (i.e. the return type
and arguments) must match the events defined in the Interface (for this tutorial:
‘IMyFirstActiveXEvents’).
The Event names must match the ones specified in the interface as well (for this tutorial:
‘IMyFirstActiveXEvents’) .
The access modifier for both, the delegate and event must be set to ‘public’
Now that the Events and corresponding delegates have been defined and added. We can
start using them.
We need to fire these events based on actions performed on the ActiveX’s UI. Therefore we
need to subscribe to the UI elements events. To do this, add the following lines of code to
the SampleUI constructor (after the ‘InitializeComponent();’ method):
Code:
public SampleUI()
{
InitializeComponent();
Page | 13
Code:
int intValue;
if (int.TryParse(txtIntProperty.Text, out intValue))
this.IntProperty = intValue;
float floatValue;
if (float.TryParse(txtFloatProperty.Text, out floatValue))
this.FloatProperty = floatValue;
Page | 14
COM Control Registration
During the registration process, certain registry keys will need to be added to allow for this
ActiveX to appear in the ActiveX controls list. This is done via the code shown below.
Note: This is a .NET assembly and therefore cannot be registered using ‘regsvr32.exe’. To
register this assembly, we need to use ‘RegAsm.exe’.
[ComRegisterFunction()]
public static void RegisterClass(string key)
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");
// Create the 'Control' key - this allows it to show up in the ActiveX control container
RegistryKey ctrl = k.CreateSubKey("Control");
ctrl.Close();
[ComUnregisterFunction()]
public static void UnregisterClass(string key)
{
StringBuilder sb = new StringBuilder(key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");
Page | 15
Making the Assembly COM Visible
Figure 7
Figure 8
Page | 16
Automatically Registering Assembly
We can setup visual studio to automatically register the assembly as part of the build
process. To enable this option, follow the screenshot shown below:
Figure 9
Note: To be able to register the assembly as part of the build process, you’ll need to run
Visual Studio as an ‘Administrator’.
Page | 17
Using the ActiveX in CitectSCADA/Vijeo Citect
For this tutorial, we will use the Example project which ships with CitectSCADA/Vijeo Citect
(version 7.2).
Run Citect Explorer, select the following from the Project List: ‘Example|Graphics|Pages’
using the Contents of Pages, select the ‘Test’ page (double click) as shown:
Figure 10
Page | 18
Once the ‘Test’ page is open, using the ‘Tools’ menu bar, select the ‘ActiveX’ button .
From the ‘Insert ActiveX Control’ popup that appears, select ‘MyFirstActiveX.SampleUI’.
Figure 11
Upon inserting the ‘ActiveX’ control on the ‘Test’ page, the properties dialog box for the
ActiveX should appear.
Figure 12
Notice the ‘Properties’ and ‘Events’ exposed by the ActiveX. These can now be associated
to Tags defined in CitectSCADA/Vijeo Citect. Save the page for now.
Page | 19
Associating ActiveX Properties to CitectSCADA/Vijeo Citect Tags
Navigate to Citect Project Editor, then select the following options from the menu bar:
‘Tags|Local Variables’.
Using the ‘Local Variables’ form, add the following local variables:
Having defined the ‘Local Variables’ or ‘Tags’, we can go ahead and associate the activex
properties to these tags.
Navigate back to the ‘Test’ page in ‘Graphics Builder’. Open the Properties Dialog box for
the ‘MyFirstActiveX.SampleUI’ ActiveX control, and set the association for each of the 3
Properties to the appropriate tag as shown in the table below (an example for the Float
Property is shown in
Figure 13):
Figure 13
Page | 20
This implies that whenever the ‘OnValueChanged‘ fires, CitectSCADA/Vijeo Citect will
evaluate the ActiveX’s property value and update the associated Tag defined in
CitectSCADA/Vijeo Citect to the value of the Activex’s property.
Cicode can be used to interact with the ActiveX. In this section of the tutorial, we’ll
demonstrate how cicode is used to perform the following:
Before starting with cicode, we need to take note of the following information:
Object Name
Event Class
Figure 14
Object Name: is used in Cicode to get a reference to the ActiveX. Once a reference is
obtained, it can be used to ‘Get’ or ‘Set’ property values as well as invoke/call methods
exposed by the ActiveX.
Event Class: is used for Event subscription. Cicode uses the ‘Event Class’ as one of the
keywords to subscribe to an Event exposed by the ActiveX.
Page | 21
Create a new Cicode file via Citect Explorer as show below:
Figure 15
Page | 22
Calling a Method exposed by the ActiveX
SampleMethod()
SampleMethodWithArgs(string arg)
Code:
FUNCTION CallActiveXMethod()
OBJECT hObject = ObjectByName("AN503");
_OBJECTCallMethod(hobject, "SampleMethod");
END
The ‘ObjectByName ()’ function used in our custom cicode helps us obtain a reference to the
ActiveX object. This reference is then used by ‘_OBJECTCallMethod ()’ to call/execute any of
the methods exposed by the ActiveX object.
Note: For more information of the built-in cicode functions, please refer to the CitectSCADA/Vijeo
Citect Help.
Page | 23
Writing and Reading a Property value exposed by the ActiveX
Writing
FloatProperty
IntProperty
StringProperty
The 3 properties mentioned above support ‘GET’ as well as ‘SET’. This means that we can
Read as well as Write to these properties.
To keep things simple, we can define a single cicode function which will allow us to write to
either of these properties.
Code:
Reading
Reading the Property value exposed by the ActiveX is very similar to writing. The main
difference being, the custom cicode function returns a value, and the built in cicode function
used to read the property value is ‘_OBJECTGetProperty()’.
Code:
RETURN strValue;
END
Page | 24
Subscribing to Events exposed by the ActiveX
OnValueChanged
OnButtonClicked
OnSelectionChanged
OnValueChanged – this event has been subscribed to via the Tag Association (Figure 13)
As for the 2 remaining Events, we can subscribe to them via cicode as shown below.
Note: The key element for subscribing to an ActiveX event via Cicode, is the function name
and its arguments.
Figure 16
Page | 25
Navigate back to the Graphics Builder ‘Test’ page and add the following elements to the
page. This will assist in visualising values and executing the cicode defined in the previous
steps.
The top 3 elements will help visualise the Local Variable/Tag values, whilst the buttons will
help us execute the appropriate cicode function.
Figure 17
Figure 18
Page | 26
Figure 19
Figure 20
Page | 27
Figure 21
Figure 22
Page | 28
Figure 23
Figure 24
Page | 29
Figure 25
Figure 26
Page | 30
Figure 27
The ‘Test’ page should finally look similar to the one shown below:
Figure 28
Page | 31
Perform the following steps and then run the Example project to Test the ActiveX:
Additional Information
When working with ActiveX, take extreme precaution around exceptions. Any un-handled
exception thrown by the ActiveX, will result in the exception propagating to the ActiveX
container (In this case CitectSCADA/Vijeo Citect) and therefore resulting in the SCADA
system crashing.
Do not run long running blocking code on the main thread of the ActiveX, as this will result in
Citect’s main thread being blocked.
Page | 32
Disclaimer
By using the information contained within this document, you agree to the following:
Disclaimer of Liability
Page | 33
© 2012 Schneider Electric. All rights
reserved.
http://www.schneider-electric.com