Skip navigation

# PI Developers Club

5 Posts authored by: mhamel

# Install your PI System Server Components on a Windows Server 2012 Core machine

Posted by mhamel Jan 9, 2013

Hi everyone,

I thought it would be interesting to cover a security topic: use Windows Server Core with the PI System. Maybe these questions are already bubbling: why and what do I have to gain?

If you attended Jim Davidson lab on Improving the security of your PI Infrastructure: Whitelisting, Firewalls & Windows Core at vCampus Live! 2012, you have learned that top 4 strategies can block 85% of targeted cyberattacks. These strategies were:

• Application whitelisting
• Patching application
• Patching operating system
• Minimizing users with domain or local administrative privileges

One good way to facilitate patching is to minimize it. Windows Server Core has a small footprint and no UI related applications; the number of patches to install is then kept to the minimum. Another argument is the Microsoft Hyper-V server is free and includes all the core features of Windows Server 2012 and Hyper-V. It owns the same virtualization capabilities as the Data Center edition. I invite you to take a look at what Microsoft says about it. Also, OSIsoft recommends running the PI System on Windows Core Servers for security and to reduce patch cycles that could impact a PI System installation.

I have tested for you the different steps to install from scratch your PI System 2012 on Microsoft Hyper-V Server 2012. Do not hesitate to share your findings and issues. Enjoy!

Steps

1. Install the Microsoft Hyper-V Server 2012 (read Windows Server 2012 core).

2. Use the sconfig.cmd script to customize your installation. You can easily configure machine's name, domain belonging, windows update, network settings, etc. Make sure you activate the Windows Remote Management (WinRM) to allow for controlling your machine from the outside)

3. Set the firewall rules to allow PI System connectivity with ports 5450, 5457, 5458, 5459. You can perform this using a MMC Snap-in console (older way) or with the PowerShell module NetSecurity. I recommend some reading at Microsoft on the subject (here and here). If you use a MMC Snap-in console, you will need to point it to another computer to "control" the rules.

4. If you need to deactivate the local firewall because you use another firewall solution, you can do this with the netsh advfirewall command.

netsh advfirewall set allprofiles state off


4. Install the Microsoft SQL Server 2012 (any edition) from the command line. You cannot make use of the graphical interface to install the database engine.

setup.exe /q /ACTION=Install /FEATURES=SQLEngine,FullText,Conn /INSTANCENAME=MYINSTANCE /IACCEPTSQLSERVERLICENSETERMS /SECURITYMODE=SQL /SAPWD=mypassword /SQLSVCACCOUNT="NT AUTHORITY\NETWORK SERVICE" /AGTSVCACCOUNT="NT AUTHORITY\NETWORK SERVICE" /TCPENABLED=1 /NPENABLED=1 /SQLSYSADMINACCOUNTS="MYMACHINE\administrator"


Other features are available such as REPLICATION, IS and AS. You can consult this KB article to get all the possibilities.

5. Test the connectivity with another SQL Server Management Studio tool (SSMS). Don't forget to specify your instance name if you have installed SQL Server with on.

6. By default, the Browser service is disabled. If it is disabled on an instance of SQL Server running on Server Core, run the following command from the command prompt to enable it. The extra space after the equal sign is required.

sc config sqlbrowser start=  auto


7. After it is enabled, run the following command from the command prompt to start the service:

net start SQLBROWSER


or from a PowerShell prompt

Start-Service SQLBROWSER


8. Install the PI AF Server 2012 (PIAFServerWithEventFrames_2012_.exe) from the command line directly. This will deflate the package and launch the setup.exe command.

9. Install the PI AF Client 2012. You will need to ignore the warning regarding the lack of Internet Explorer. Also, during the PI SDK 2010 R2 (32 bit) installation, you will need to ignore the failure about the registration of richtxt32.ocx file. You can start the PI System Explorer (PSE) directly from the command line too.

10. Install the PI Notifications package. Unfortunately I found a bug when you install the client part, you won't be able to use the AFExplorer.exe from the command line. A support call has been created to address this problem. For now, only install the server part.

11. Install the PI Server 2012 following the general OSIsoft guidelines found in the PI-Server-2012-Installation-and-Upgrade-Guide_EN document. This document can be found in the Library.

12. Start the PI Server using the pisrvstart.bat script located under the c:\program files\pi\adm folder.

13. Install PI SMT 2012. Yes, it works now under a pure .NET world, so you can launch it on the command line.

14. You can install the latest PI SDK 2012 (32 and 64 bits). During the PI SDK 2010 R2 (32 bit) installation, you will need to ignore the failure about the registration of richtxt32.ocx file.

15. Install PI ACE 2010 R2 SP1.

16. Install the PowerShell tools for the PI System.

I hope this would have shed some light on how easy it is to install a PI System on top of a Windows Core OS.

# Advanced test script for PI Web Services

Posted by mhamel Apr 20, 2012

Hi everyone,

I have decided follow up on my previous post on test PI Web Services using a Powershell script but by using this time the proxy library generated using the metadata returned by the Web Service itself.

If you want to test PI Web Services using other bindings or with more complex parameters, you generally need to write a small application using Visual Studio. As many components are involved during the installation such as configuring the virtual application on the Web Server (IIS), ASP.NET, application pool, web.config, PI SDK, AF SDK, security, etc. or also, other elements can come into the game: questions related to Active Directory forest, domain(s) and workgroup, and the network routes and filters, many things can go wrong. Your testing application cannot be tailored for a "one size fit all" one. It means you will need to re-configure the class(es) needed for communicating with the Web Service and modify the app.config file, and then you will rebuild your application. These steps can be tedious on the long run. I want to propose you an alternative to this by writing your Powershell script which is quick to edit.

You will need Powershell version 2.0 and later to achieve this as we are communicating with Web Services implementing the Windows Communication Foundation (WCF) which was introduced with the .NET Framework 3.5 that is supported with version 2.0. You will also need the C# compiler (csc.exe) installed under your C:\Windows\Microsoft.NET\Framework\v3.5 or C:\Windows\Microsoft.NET\Framework64\v3.5 and the SVCUtil.exe tool which is installed with Visual Studio. If you dont have Visual Studio installed on the machine you planned to work on, you can easily copy the files under any folder you want. You need to copy the SVCUtil.exe and SVCUtil.exe.config files. Dont forget to change the path where these files are copied if there are not at their default location. If you use the SVCUtil.exe from the .NET Framework 4.0, you will need to create or modify the powershell.exe.config file under the C:\Windows\System32\WindowsPowerShell\v1.0 folder with the following settings:

By default Powershell is configured to only use .NET Framework 2.0 assemblies, so you need to enable the use of 4.0 ones.

Lets examine the script in more details... This script contains three (3) functions to be used:

• PIWS-Https-TestGetPIArchiveData
• PIWS-NetTCP-TestGetPIArchiveData
• PIWS-RawXMLSOAP-TestGetPIArchiveData

The first two: PIWS-Https-TestGetPIArchiveData and PIWS-NetTCP-TestGetPIArchiveData make use of a proxy DLL which is dynamically compiled and added to the script to send request to PI Web Services, the last one is the function I introduced in my last blog entry regarding manually creating a SOAP/XML message to invoke PI Web Services methods.

Let start by explaining the logic of this script for functions: PIWS-Https-TestGetPIArchiveData and PIWS-NetTCP-TestGetPIArchiveData. First, you need to generate a proxy DLL representing the classes used by the Web Service, followed by loading it into Powershell namespace using the principle of reflection. You will need to create an instance of a class representing one of the services offered by PI Web Services (either PITimeSeries or PISearch); thereafter this instance is called your proxy object. It is important to configure the impersonation level used by the Powershell script.

#Allow delegation to occur.
$_Proxy.ClientCredentials.Windows.AllowedImpersonationLevel = [System.Security.Principal.TokenImpersonationLevel]::Delegation  In many cases, you will use a delegation level to enable your credentials to be passed between two machines. Finally, you will manipulate your proxy object and some classes to launch request against the different Web methods Generating a Proxy This step consists of calling the SVCUtil.exe command with the endpoint to generate two (2) files which will contain class definitions and the default app.config. Both files will be copied in the same folder of your Powershell script. Secondly, the CSC.exe command is called with the classes definition file to compile into an assembly file (*.dll). The AnyCPU platform is selected for compilation. If you are interested to know what command lines are invoked, I suggest you take a look in the script file attached at the end of this blog entry. Loading the Generated Assembly This step consists of loading the generated assembly file (*.dll), and two (2) other ones in the Powershell session namespace by the use of Reflection.Assembly.LoadFrom method. The System.ServiceModel and System.Security.Principal namespaces are loaded for their usage in the script. If you want to make use of the .config file generated, you have to invoke the CurrentDomain.SetData method with the path of the file before calling the Reflection.AssemblyLoadFrom method, otherwise it wont be taken into account. #Add the configuration file into the domain before loading the assembly if using default... if ($Configuration -eq "default") { [System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", $WSLibPath + ".config") } #Add a reference to the PISDK assembly. [reflection.assembly]::LoadFrom($WSLibPath) | Out-Null
#Add a reference to the servicemodel.
[Reflection.Assembly]::LoadWithPartialName("System.ServiceModel") | Out-Null
#Add a reference to ...
[Reflection.Assembly]::LoadWithPartialName("System.Security.Principal") | Out-Null


Instantiating
This step consists of creating a proxy client object representing your connection with the Web Service. You can invoke one of its constructors that takes the name of the endpoint as argument. The other allows defining a custom configuration that you can control programmatically.

Build your Request
This step looks like what you would do if you were using Visual Studio. You create your requests and send them to the GetPIArchiveData method which will return an array of TimeSeries object that you can manipulate.

To utilize your script, you will need to open a command prompt and call powershell or call powershell directly from the Run command. As a very important step, you will need to unlock the policy for using this type of script by invoking this command: Set-ExecutionPolicy RemoteSigned at the Powershell prompt. Afterward, you can invoke your script wherever you saved it by simply typing its full path and name. If you receive errors directly inside the Powershell session, it means something is not configured properly somewhere. Otherwise in case of success you will receive a screen that looks like the following.

If you are using PI Web Services 2010 R2 or R3 installed on IIS and using the Net TCP binding, the default port used is 808. You will need to specify the port in the URI of the Web Service.

If you are interested you can also test other bindings such as the name pipes. Although, it works only within the boundary of a unique machine. Try different paths involving PI Data Archive, PI AF or PI Performance Equation.

Please find a copy of the script here. Share some thoughts or comments with me by replying to my blog.

# Easy test script for PI Web Services

Posted by mhamel Jan 24, 2012

One of the common things I have noticed with Web Services is that after you installed them you need a quick way to test them to say they work or not. As PI Web Services responds to web queries using the Simple Object Access Protocol (SOAP) related data from the PI Server and/or the PI AF Server, you need to test the pathways. Many components are involved during the installation: configure the virtual application on the Web Server (IIS), ASP.NET, application pool, web.config, PI SDK, AF SDK, security, etc. Also, other elements can come into the game: questions related to Active Directory forest, domain(s) and workgroup, and the network routes and filters. Looking at all these details, I know many things can go wrong. So, my focus is starting from a basic scenario to test the main components to assess a minimal set of things. I learned with Web Services that the more security layer you add the more difficult it becomes. Anyway, your first idea should be testing that each components can communicate and close the loop. The higher level of security will come up at a later time.

The first test to do is to try to open a connection on your Web Server, then try to reach the folder of your virtual application (here it is the PI Web Services). Try to retrieve the service descriptors (WSDL) which will be returned when you query the service handler at this URI: http://<server>/PIWebServices/PITimeSeries.svc?wsdl. If you got success until that part you need to test a specific method provided by this service but you will need to either code your application or use a Web Service functional tool. Both of these solutions require time or an installation process that would leave a footprint on the machine. What you want it's an easy solution that could say my installation is working from A to Z. This takes more sense if you are performing the installation at a customer's site on production assets. There are a lot of chances they would refuse you the rights to install other pieces of software that might affect the server.

An easy solution is possible for you and it is a Powershell script. For those of you which would not be familiar with Powershell, I propose you a nice and short description coming from Wikipedia which is: "Windows PowerShell is Microsoft's task automation framework, consisting of a command-line shell and associated scripting language built on top of, and integrated with the .NET Framework. PowerShell provides full access to COM and WMI, enabling administrators to perform administrative tasks on both local and remote Windows systems."

The use of scripts give you strong advantages, as it does not install a codebase on the machine and it can be easily modified to suit your needs. You will need Powershell version 2.0 and later to achieve this. The version 1.0 of Powershell was based on .NET Framework 2.0 only but the later version was ported to use the .NET Framework 3.5. As we are communicating with Web Services implementing the Windows Communication Foundation (WCF) which was introduced with the .NET Framework 3.5 we need Powershell version 2.0. For an easy introduction, I will cover only the basics to connect using basic bindings (meaning with no security), other bindings would be part of a later post.

The script contains two cmdlets: PIWS-TestGetPIArchiveDataWithSOAP and Format-XML, the first one is to send a SOAP message to PI Web Services via a WebClient object and receive the answer, the second is process for processing the XML returned string. Starting from cmdlet PIWS-TestGetPIArchiveDataWithSOAP, we need to create a Webclient object to communicate with the PI Web Services, a WebHeaderCollection object to define the content of the message and the SOAP action, and finally an XML message that will contain all the necessary information for the Web Services. Using the [string]::Format method, you can easily manipulate an XML template with the different arguments to replace within to form your message. The final stage is to post your message to PI Web Services and wait for an answer. This is done as a synchronous process. The returned XML is formatted with the second cmdlet and output to the console. The PIWS-TestGetPIArchiveDataWithSOAP cmdlet is called with the last instruction which is part of the main of your script. You need to pass the endpoint, the URI of PI Web Services, the item which is a valid path, the start and end time, and the number of values to return for interpolated values. Save your file with the name PIWS-TestGetPIArchiveDataWithSOAP.ps1.

To utilize your script, you will need to open a command prompt and call powershell or call powershell directly from the Run command. As a very important step, you will need to unlock the policy for using this type of script by invoking the cmdlet: Set-ExecutionPolicy RemoteSigned at the Powershell prompt. Afterward, you can invoke your script wherever you saved it by simply typing its full path and name. If you receive errors directly inside the Powershell session, it means something is not configured properly somewhere. Otherwise in case of success you will receive a screen that looks like the following.

The same error message will be sent thorough Powershell then if it was your Visual Studio environment. Try different paths involving PI AF to test this part of the configuration. After successful tests from the machine, you can copy your script to a client machine and see if it still works from there. This completes the testing workflow for basic bindings.

Get a copy of this script from this link (as publishing Powershell scripts is not nice on the Web). Stay tune for later blog posts and a white paper to test all other Web methods offered by PI Web Services. Did you use scripts of your own before, share some thoughts with me by replying to my blog.

I have added a link to a new version of the script here. It fixes the PIArcManner object parameters to handled properly.

# Surprising Discovery about PI ACE 2.x

Posted by mhamel Jan 18, 2012

Recently I built for myself a brand new PI System environment to test and develop around the new PI System for my upcoming role in vCampus (Yes I joined the team this week).  I was looking for installing a virtual machine that would host the server components of the PI System: PI Server 2010 SP1 and PI AF 2010 R3. I decided I’ll test the use case of an environment with no graphical user interface such as Windows Server core.

For those of you that would not be familiar with what Windows Server Core is, it is a significantly scaled-back installation where no Windows Explorer shell is installed. All configuration and maintenance is done entirely through command line interface windows, or by connecting to the machine remotely using Microsoft Management Console. You can learn more about it using this Microsoft link and is offered free by Microsoft up to now and since its initial release.

My next step was to verify the OS specifications for the different components required such as Microsoft SQL Server, .NET Framework 4, PI Server 2010 SP1, PI AF 2010 R3, PI SDK 1.4, etc. and all of them could work on a Windows Server core environment (offered by Windows Server 2008 R2 SP1). So, I downloaded it from one of the Microsoft Download sites, installed and configured it with an unprecedented ease with a very nice tool called Core Configurator (available here at CodePlex). I’ll blog later on the advantages given by this tool.

The installation process didn’t go as a straight line for some components but I found my way out. How I succeeded would be part of further blog posts. As a final step, I launched the pisrvstart.bat script from the command prompt to start the PI Server and PI AF Server, and watched the messages returned by the different PI Subsystems at the look of any eventual errors but none of them came. I utilized PI System Management Tools and PI System Explorer from a client machine to validate my new installation and everything was up and running smoothly. Congratulations! The PI Server and PI AF components were installed and functioning at 100%.

I started wondering if all the pieces installed with the PI Server and PI AF, such as the PISDKUtility.exe could run directly from this core environment. I invoked the pisdkutility.exe command, the tool that replaced the former AboutPI-SDK.exe and I got the PI SDK Utility window in my screen. I tested all the options and all of them were working great.

An idea started to take form in my head. What about PI ACE! Wouldn’t be that great if it could work within such an environment? I looked at the documentation and nothing was said that it works or not. Thus I copied the setup kits, launch the installation and respond to standard installation questions. I was brought up to the point of choosing the components to be installed, from which I only selected the PI ACE Scheduler 2.x item and click on the OK button. The installation had completed perfectly and left me at the command prompt. Knowing that PI ACE Scheduler service is not started by default, I started it by using the sc start piacenetscheduler command and verified its status with sc query piacenetscheduler command a few seconds later.

A few glimpses at the PI Message Log using the PI SDK Utility returned me that the PI ACE (PI Advanced Computing Engine) 2010 R2 (version 2.1.50.2) was running on a Windows Server core and awaiting my first calculation module.  A few minutes later, I had created a typical basic calculation module firing naturally and made of the multiplication by two of the CDT158 point into an output point which was holding the results. The results seen were more than expected. I just started imagining all the new horizons and use cases that could be addressed with a core environment (This will be part of a further blog post).

Have you attempted some other tests with PI System components and the Windows Server Core environment? Please share your attempts, your ideas or your questions.

# PISDK or PISDKClass?

Posted by mhamel Jan 16, 2012

Have you ever use the reflection to examine your code produced after a successful compilation of your PI System application using the PISDK? For those of you that are not familiar with the reflection principle, it is the process by which a computer program can observe (do type introspection) and modify its own structure and behavior at runtime. I did recently for one of my project where I had to write the same project but using C# instead of Visual Basic. Something caught my eyes when reviewed the code. Some of the declarations I made with the PISDK objects were replaced with PISDKClass instead as shown below.

'Class Member Objects.

Private _MyPISDK As PISDK

'...

Private Sub OpenPIServerConnection()

_MyPISDK = New PISDK.PISDK

_PIServer = _MyPISDK.Servers(_TargetPIServer)

If Not _PIServer.Connected Then

_PIServer.Open()

End If

End Sub

Excerpt of my project written in VB.NET

'Class Member Objects.

Private _MyPISDK As PISDK

'...

Private Sub OpenPIServerConnection()

Me._MyPISDK = New PISDKClass

Me._PIServer = Me._MyPISDK.Servers.Item(Me._TargetPIServer)

If Not Me._PIServer.Connected Then

Me._PIServer.Open("")

End If

End Sub

Excerpt of the code rendered via reflection

Why the compiler replaced the instantiation of the _MyPISDK object with the PISDKClass instead of the PISDK one? Several examples of codes in these forums show connectivity with the PI Server using the PISDKClass class instead of the PISDK one as described in the PISDK help file. I dug the subject a bit and here what I found.

If you look carefully at the PI SDK object model you can see the PISDK object is declared as a COM class, meaning this object is creatable by using its constructor; if you compare the COM Interop library OSIsoft.PISDK.dll, the same object is declared as an interface. Let refresh our mind on the basic definition of a class and an interface enumerated below.

Class Definition

A class is a construct that is used as a blueprint to create instances of itself (referred most of the time as instance objects). A class defines constituent members which enable these class instances to have state and behavior.

Interface Definition

An interface contains only the signatures of methods, delegates or events. The implementation of the methods is done in the class that implements the interface. An interface is not creatable by definition.

Declare a New Object from an Interface?

Per the documentation we should be able to create a new PISDK top-level object with the New keyword either in the COM or the .NET world. How will it be possible within the .NET world as the New keyword cannot be used with an interface?

The answer lies into how a COM Interop library is formed. A class is COM's language-independent way of defining a class in the object-oriented sense. A class can be a group of similar objects or a class is simply a representation of a type of object; it should be thought of as a blueprint that describes the object. A coclass supplies concrete implementation(s) of one or more interfaces (a default interface and sometimes secondary interfaces). In COM, such concrete implementations can be written in any programming language that supports COM component development, e.g. Delphi, C++, Visual Basic, etc.

When this COM coclass is converted into the .NET world i.e. a .NET interop, a public interface with the same name as the COM coclass is created; it is known as a coclass interface and has no members itself. This .NET interface derives from the converted COM coclass’ default interface. Both created interfaces are marked with the same IID. To complete the conversion of this COM coclass, another piece is created to bridge the COM and .NET worlds. This piece is a Runtime Callable Wrapper (RCW) class that will be named with the COM class name and the "Class" suffix. The RCW class calls the native CoCreateInstance COM function for creating the COM object that it wraps. The RCW class will convert each call to the COM calling convention.

Creating a new object derived from a coclass interface will result into creating the desired COM object via the RCW class. The coming example will shed some light on how this done. The RunTime Callable Wrapper is explained in the following section.

Runtime Callable Wrapper

COM differs from the .NET Framework object model in several important ways:

·          Clients of COM objects must manage the lifetime of those objects; the Common Language Runtime (CLR) manages the lifetime of objects in its environment.

·          Clients of COM objects discover whether a service is available by requesting an interface that provides that service and getting back an interface pointer, or not. Clients of .NET objects can obtain a description of an object's functionality using reflection (analysis of the libraries/assemblies themselves).

·          .NET objects reside in memory and are managed by the .NET Framework execution environment. The execution environment can move objects around in memory for performance reasons, in which case it will update all references to the objects it moved. Unmanaged clients, having obtained a pointer to an object, rely on the object to remain at the same location. These clients have no mechanism for dealing with an object whose location is not fixed.

To overcome these differences, the runtime provides wrapper classes to make both managed and unmanaged clients think they are calling objects within their respective environment. Whenever your managed client calls a method on a COM object, the runtime creates a runtime callable wrapper (RCW). RCWs abstract the differences between managed and unmanaged reference mechanisms, among other things. The runtime also creates a COM callable wrapper (CCW) to reverse the process, enabling a COM client to seamlessly call a method on a .NET object. As the following illustration shows, the perspective of the calling code determines which wrapper class the runtime creates.

Example

Creating a top-level PISDK object like presented below is correct from the .NET world because the PISDK interface is in fact a coclass interface defaulting to the RCW class called PISDKClass.

Dim _PISDK as New PISDK.PISDK

The .NET Visual Basic or .NET C# linker or interpreter will change your code to respect the official syntax to create a variable to the PISDK interface and instantiate it with the PISDKClass class:

Dim _PISDK as PISDK.PISDK

_PISDK = New PISDK.PISDKClass

You can then interchangeably use these:

'Example 1

Dim _PISDK as New PISDK.PISDK

Or

'Example 2

Dim _PISDK as New PISDK.PISDKClass

Mainly the difference resides on the members exposed by both implementations, the PISDK coclass interface inherits only from the IPISDK interface and the PISDKClass class inherits from the IPISDK, PISDK, IPIAppIdentity and the IPIGlobalRestorer. Creating an object instance from the PISDKClass will give you natively access to members of all implemented interfaces without having to cast the object into another interface. This is a faster and more straight-forward way to use the PISDK top-level objects, as shown in the following examples.

' Use the PISDK coclass interface and derived into a different

' interface to access more members.

' Variables / Ojects.

Dim MyPISDK As New PISDK.PISDK

Dim AppIdentity as PISDK.IPIAppIdentity

Dim PIserver As PISDK.Server

'Derive this object to the IPIAppIdentity interface.

AppIdentity = DirectCast(MyPISDK, IPIAppIdentity)

' Set a unique identifier for your application.

AppIdentity.Identifier = "00000000-0000-0000-0000-000000000000"

' Set a reference to the PI Server.

PIserver = MyPISDK.Servers.DefaultServer

' Open the connection.

PIserver.Open(ConnectionString)

' Access directly all members by using the PISDKClass RCW class.

' Variables / Objects.

Dim MyPISDK As New PISDK.PISDKClass

Dim PIserver As PISDK.Server

' Set a unique identifier for your application.

MyPISDK.Identifier = "00000000-0000-0000-0000-000000000000"

' Set a reference to the PI Server.

PIserver = MyPISDK.Servers.DefaultServer

' Open the connection.

PIserver.Open(ConnectionString)

All COM coclasses from the PI SDK can be instanciated using the RCW classes the same way. The RCW classes in the COM Interop library are adding the "Class" suffix at the end such as for PIValueClass and PIValuesClass for handling the PIValue and PIValues class definitions from the COM world.

I hope this shed some light on the topic. Either you choose the standard PISDK and let the compiler replaces it or you use directly the PISDKClass definition in your project, it will perform the same.

By date: By tag: