March8
This is the last installment of my posts about the .Net wrapper around CA Gen’s C Api. On this post I will show details on how to use the wrapper, taking as an example a real world application that I created to help us here at QAT on a client assignment.
What we wanted to identify was empty Action Blocks, i.e. Action Blocks with no statements. This was because the client had created models from subsets, and ended up with those stubs. First thing I did was to create a basic GUI to show the results. This GUI looked like that:

Note that there are other tabs with different functions, but for this post I will focus on the Statements per AB tab. Also, this project was created using Visual Studio 2005 and the C# language.
At the top of the window, there are two entry fields: one for the path where the dat files for the model are, and the other for a path where potential error messages would be saved (in a file called error.log). I added a file browser control (triggered by the “…” button) to allow for easier navigation. When the path is informed and the user clicks the “Connect” button, the following code is executed:
private void btnConnect_Click(object sender, EventArgs e)
{
if (api == null)
{
api = new QATGenCAPI(EncyType.Local);
}
try
{
api.OpenModel(fldModel1Path.Text, fldErrorPath.Text);
MessageBox.Show(“Connected to Model”);
ModelInfoDTO info = api.FetchModelInfo(0);
fldModelName.Text = info.modelName;
fldModelType.Text = info.modelType.ToString();
fldModelStatus.Text = info.modelStatus.ToString();
fldModelRelease.Text = info.modelRelease.ToString();
fldUserName.Text = info.modelUserName;
fldCodePage.Text = info.modelCodePage.ToString();
fldLangCode.Text = info.modelLangCode.ToString();
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
First, I check if any api object is already defined. If not, I create a new one, passing a type of EncyType.Local. This tells the wrapper I am connecting to a local model, not a CSE or HE. After I have the api object, I need to open the target model. This is done by calling the OpenModel method of the wrapper, passing the two paths I mentioned before (model and error). Then I call the FetchModelInfo method just to retrieve some basic info about the model to display to the user. All the code is surrounded by a try/catch, in the case something goes wrong. Remember, the wrapper always throws an exception if the Gen Api fails.
After the connection to the model is established, it is time to do the real work. When the user clicks the “Execute” button inside the tab, this event runs:
private void btnListStmt_Click(object sender, EventArgs e)
{
// Here I have some code to set up the listbox. I removed that for clarity
QATGenMetaModelObjects actonCollection = null;
QATGenMetaModelObjects abCollection = api.FetchAllModelTypeObjs(0,GenObject.ACBLKBAA);
foreach (QATGenMetaModelObject ab in abCollection)
{
// retrieve the ACTON collection for that AB
actonCollection = ab.FetchCardManyAsc(GenRelationship.DEFNDBY);
if ((chkOnlyNoStatements.Checked && actonCollection.count == 0) || !chkOnlyNoStatements.Checked)
{
// create the items to add to the list
item = listViewStmt.Items.Add(ab.Name.Trim());
item.SubItems.Add(“BAA”);
item.SubItems.Add(actonCollection.count.ToString());
}
}
abCollection = api.FetchAllModelTypeObjs(0,GenObject.ACBLKBSD);
foreach (QATGenMetaModelObject ab in abCollection)
{
// retrieve the ACTON collection for that AB
actonCollection = ab.FetchCardManyAsc(GenRelationship.DEFNDBY);
if ((chkOnlyNoStatements.Checked && actonCollection.count == 0) || !chkOnlyNoStatements.Checked)
{
item = listViewStmt.Items.Add(ab.Name.Trim());
item.SubItems.Add(“BSD”);
item.SubItems.Add(actonCollection.count.ToString());
}
}
listViewStmt.Sorting = SortOrder.Ascending;
listViewStmt.Sort();
}
The first thing I do is to define and initialize a QATGenMetaModelObjects object to hold my collection of statements in an Action Block. Then I fetch all Action Blocks BAA for that model. Note that I am using the enumeration that I defined for Gen Objects (GenObject.ACBLKBAA). This is much better than using a number (ACBLKBAA is in fact number 23. You can check that on your odrpta.chm file in the Gen directory). Another detail is the first parameter on the FetchAllModelTypeObjs method. This is the model id, and it will always be zero for local models. If we were dealing with CSE or HE, I would have to pass the corresponding id for the model.
Next, for each Action Block in the collection, I retrieve its collection of statements. Statements in Gen are represented by the metamodel object ACTON, and the relationship between an action block and statements is defined by the DEFNDBY relationship. I am using the FetchCardManyAsc method because we usually have many statements per Action Block. In my GUI I have a checkbox to indicate if only Action Blocks with no statements should be listed, and I am checking for that before I include the Action Block name and the number of statements in the list box. Notice the use of the count property of the actonCollection object to indicate the number of statements. This is another thing that was added to the wrapper when I introduced the collection concept.
Since Action Blocks BAA and BSD are two different objects in Gen, the next section in the code just repeats the same processing, but for BSD Action Blocks. The last two statements are used to sort the names in the listbox. I added other things to the GUI itself, like the ability to save the contents of the list as HTML, but this is unrelated to the Api.
I hope I could demonstrate with this example how easy it is to use QAT’s .Net wrapper around the C Api. In few lines of code, I was able to access information inside a Gen model, all this without having to rely on memory allocation and release, pointers and metamodel numbers. Uses for that wrapper are only limited by a developer’s imagination. We’ve used it here at QAT to do a whole range of things, from comparing identifiers between entities in different models to generating full code for Action Blocks. Reporting and cleaning of models are two other areas where the Api can sure help Gen customers. Again, all that functionality has been available for years. My contribution I believe was to make those features available in a more object-oriented way and at the same time shield the developer from having to deal with the oddities of the C language.
My next posts will focus on something that has been around for some time now: user-defined functions in Gen. Those functions can extend Gen’s native functionality and are indeed a suitable and more robust replacement for external action blocks.