Marcos Vainer Loeff

Getting started developing apps using managed C++ with PI AF SDK

Blog Post created by Marcos Vainer Loeff Employee on Nov 11, 2016

In this blog post, I will teach you how to get started developing a managed C++ Win32 Console Application using PI AF SDK. Although I know that the majority of our community would you PI AF SDK with C# or VB.NET, I am sure this article will be useful to some users.


Before we start, the source code of the project is available on this GitHub repository for you to download.


Let's get started creating a Win32 Console Application in Visual Studio as shown on the screenshot below.



Select a name for your project and click in "Ok". The Win32 Application Wizard will open. Click on "Next".



You will see the Application Settings. Finally, click on the "Finish" button.



You will see your solution under the Solution Explorer. On top menu, click on "Project" --> "SampleAFSDKCpp Properties...".


Under Configuration Properties --> General, change the Common Language Runtime Support option to Common Language Runtime Support (/clr)



Under Configuration Properties --> C/C++, change the Additional #using Directives option to %pihome%\AF\PublicAssemblies\4.0;%(AdditionalUsingDirectories).


Under Configuration Properties --> C/C++ --> Precompiled Headers, change the Precompile Header option to Not Using Precompiled Headers.




Finally, change the content of the SampleAFSDKCpp.cpp to:


#using <mscorlib.dll> // to use Console::WriteLine
#using <OSIsoft.AFSDK.dll>
#include <stdio.h>
// to printf()
using namespace System;
using namespace OSIsoft::AF;
using namespace OSIsoft::AF::Asset;
using namespace OSIsoft::AF::Time;
// Mark unmanaged code
#pragma unmanaged
void print(char *msg)
  printf("%s\n", msg);

// Switch back to managed code
#pragma managed

int main()
  // Write to the console through managed call
  Console::WriteLine("Hello world from managed method");
  PISystems ^piSystems = gcnew PISystems();
  //AF::PISystem  ^piSystem = piSystems->DefaultPISystem;
  PISystem  ^piSystem = piSystems["MARC-PI2016"];
  AFDatabase ^myDb = piSystem->Databases["AFSDKTest"];
  AFElement ^rootElement = myDb->Elements["Tables"];
  AFElement ^subRootElement = rootElement->Elements["InterpolatedTest"];
  AFAttribute ^myAttribute;
  for each (AFAttribute ^attribute  in subRootElement->Attributes)
  if (attribute->Name == "Attribute2")
  myAttribute = attribute;
  AFTime startTime = AFTime("*-1d");
  AFTime endTime = AFTime("*");
  AFTimeRange time = AFTimeRange(startTime, endTime);
  //time->StartTime = startTime;
  UnitsOfMeasure::UOM ^uom;
  AFValues^ values = myAttribute->Data->RecordedValues(time, OSIsoft::AF::Data::AFBoundaryType::Inside, uom, System::String::Empty, false, 0);

  for each (AFValue^ value in values)
  AFTime^ time = value->Timestamp;
  Console::WriteLine("Value: " + value->Value->ToString() + ", Timestamp: " + time->LocalTime);

  // use stdio to write to console
  print("hello world from unmanaged method");
  return 0;


When I press CONTROL + F5, I see the compressed values from the attribute whose path is \\MARC-Pi2016\AFSDKTest\Tables\InterpolatedTest|Attribute2.



Before we finish, there are two questions I would like to answer:


1)What is the gcnew?


The gcnew is an operator, just like the new operator, except that you don't have to delete anything created with it. It's garbage collected. You use gcnew for creating .Net managed types, and new for creating unmanaged types.


2)Why gcnew operator is not used to instantiate the AFTime?


Because actually AFTime is a struct and not a class. The gcnew operator should be used with classes only.



That is all! Hopefully using this simple example is enough for you to get started. If you have any suggestion or question to ask, please post a comment below!