C++ and PI - the PI SDK

Blog Post created by andreas on Nov 23, 2011

Recently we got increasing interest in providing sample code in C++.


While C++ is a very powerful language you should be aware that .NET provides you with many little things that make your life as a developer much easier.


In the following post I will create a simple command line application that gets the snapshot of tags. The usage is:


PISDK_GetSnapshot <SERVERNAME> <Tagname1> <Tagname2> <…>


So as you may guess, we start with an empty C++ command line project:




Now let’s go to the code. First some includes:


#include "stdafx.h"
"ATLComTime.h" // for COleDateTime

Then we import the PI SDK libraries.


//need to import sdk
"E:\PIPC\PISDK\PISDKCommon.dll"    no_namespace
"E:\PIPC\LIBRARY\PITimeServer.dll" no_namespace
#import "E:\PIPC\PISDK\PISDK.dll"          rename("Connected", "PISDKConnected") no_namespace

One of the things that constantly drives me crazy when switching between C++ and C#.NET is handling the PIValue, especially the variant PIValue->Value – so the very first thing I’d like to do is introducing a simple class that does this for me:


class MyPIValue
_PIValuePtr spPIValue;

MyPIValue (_PIValuePtr);
double  dblValue;
int     intValue;
_bstr_t bstrValue;
_bstr_t bstrTimeStamp;
COleDateTime codtTimeStamp;

The purpose of my simple class is to get variables that handle much better than the variant (at least in my simple sample code snippets).


Now let’s go to the constructor. Actually we need to figure where to find the data – and that is derived by the variant type. The following code gets me first a COleDateTime and a string representation of that – this is the timestamp, and later the value part as integer, double or string representation:


MyPIValue::MyPIValue (_PIValuePtr pv) {
codtTimeStamp = pv->TimeStamp->LocalDate;
bstrTimeStamp = (_bstr_t)codtTimeStamp.Format(_T("%d-%b-%Y %H:%M:%S"));
DigitalStatePtr tmpDigitalState = NULL;
IDispatchPtr    tmpDispatch = NULL;
_PITimePtr      tmpPITime = NULL;
COleDateTime    tmpTS;
HRESULT         hr = E_FAIL;

_variant_t vT = pv->Value;
vt = vT.vt;

switch (vT.vt) {
case VT_I4:
// Int32
intValue = vT.lVal;
dblValue = intValue;
bstrValue = (_bstr_t)intValue;
case VT_I2:
// Int16
intValue = vT.iVal;
dblValue = intValue;
bstrValue = (_bstr_t)intValue;
case VT_R8:
// Float64
dblValue = vT.dblVal;
intValue = (int)dblValue;
bstrValue = (_bstr_t)dblValue;
case VT_R4:
// Float16/Float32
dblValue = vT.fltVal;
intValue = (int)dblValue;
bstrValue = (_bstr_t)dblValue;
case VT_BSTR:
// String
bstrValue = vT.bstrVal;
dblValue = 0;
intValue = 0;
// Digital?
tmpDispatch = vT.pdispVal;
hr =  tmpDispatch.QueryInterface(__uuidof(DigitalState),&tmpDigitalState);
if (hr == S_OK) {
bstrValue = tmpDigitalState->Name;
intValue = tmpDigitalState->Code;
dblValue = intValue;
// Timestamp?
hr =  tmpDispatch.QueryInterface(__uuidof(_PITime),&tmpPITime);
if (hr == S_OK) {
tmpTS = tmpPITime->LocalDate;
bstrValue = (_bstr_t)tmpTS.Format(_T("%d %B %Y %H:%M:%S"));
intValue = 0;
dblValue = 0;
default :
dblValue = 0.0;
intValue = 0;
bstrValue = "n/a";


Preparation doneJ!


As mentioned in the beginning, I want to get the snapshot – so what do I need? The PISDK, the Server, the Point, the Value and just for the fun the PISDK Version J:


IPISDKPtr       spPISDK = NULL;            /* The PISDK */
PISDKVersionPtr spSDKVersion = NULL;       /* PI SDK Version */

ServerPtr       spServer = NULL;           /* The Server */
PIPointPtr      spPIPoint = NULL;          /* The PI Point */
_PIValuePtr     spPIValue = NULL;          /* The PI value */

Now the code – we initialize COM, check for the command line parameters and finally create the PISDK. After this has been done, we print out the PI SDK version, connect to PI and print out the snapshot of all tags provided as command line arguments:


int _tmain(int argc, _TCHAR* argv[])
// Initialize COM
// Check the command line switches
if (argc < 3) {
std::cout << "Command Line:" << std::endl
  << (_bstr_t)argv[0] << " SERVERNAME TAGNAME(s)";
return (1);
// Create an instance of the PI SDK
// Print out the PI SDK version
spSDKVersion = spPISDK->PISDKVersion;
std::cout << std::endl << "PI-SDK Version "
  << spSDKVersion->Version << " Build "
  << spSDKVersion->BuildID << std::endl;
// get the PI Server
spServer = spPISDK->GetServers()->GetItem((_bstr_t)argv[1]);
// You can use more than just one tagname
for (int ii = 2; ii< argc; ii++) {
// Tagname
std::cout << (_bstr_t)argv[ii] << std::endl;
spPIPoint = spServer->PIPoints->GetItem((_bstr_t)argv[ii]);
// Snapshot
spPIValue = spPIPoint->Data->Snapshot;
MyPIValue mPV(spPIValue);
std::cout << mPV.bstrTimeStamp << " ";
std::cout << mPV.bstrValue << std::endl;
catch( _com_error Err )
std::cout << "Error: "
                        << Err.Description()
                        << " : "
                        << Err.Error()
                        << std::endl;
return (1);
return 0;


And here is the result:




This was the first post - stay tuned for more examples using C++!