# ArchestrA MXAccess Toolkit User's Guide

> Invensys Systems, Inc. © 2002–2010, 2014. Application Server 2014.

# Welcome

This guide describes how to use the MXAccess Toolkit. ArchestrA Galaxy data access is exposed to .NET and COM clients through a programmable object model called MXAccess. You can use the MXAccess object model to write programs that automate reading and writing data values in the attributes of ArchestrA objects.

You can view this document online or you can print it, in part or whole, by using the print feature in Adobe Reader.

This guide assumes you know how to use Microsoft Windows, including navigating menus, moving from application to application, and moving objects on the screen. If you need help with these tasks, see the Microsoft Help.

# Documentation Conventions

This documentation uses the following conventions:

<table>
  <thead>
    <tr>
      <th>Convention</th>
      <th>Used for</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Initial Capitals</td>
      <td>Paths and file names.</td>
    </tr>
    <tr>
      <td>Bold</td>
      <td>Menus, commands, dialog box names, and dialog box options.</td>
    </tr>
    <tr>
      <td>Monospace</td>
      <td>Code samples and display text.</td>
    </tr>
  </tbody>
</table>

# Technical Support

Wonderware Technical Support offers a variety of support options to answer any questions on Wonderware products and their implementation.

Before you contact Technical Support, refer to the relevant section(s) in this documentation for a possible solution to the problem. If you need to contact technical support for help, have the following information ready:

*   The type and version of the operating system you are using.
*   Details of how to recreate the problem.
*   The exact wording of the error messages you saw.
*   Any relevant output listing from the Log Viewer or any other diagnostic applications.
*   Details of what you did to try to solve the problem(s) and your results.
*   If known, the Wonderware Technical Support case number assigned to your problem, if this is an ongoing problem.

# Chapter 1: Getting Started

Use the MXAccess Toolkit to interface a program to ArchestrA Message Exchange. The Toolkit supports a simple, lightweight interface that permits:

*   Connecting to an ArchestrA Galaxy.
*   Subscribing to one or more attributes and receiving data updates from these attributes.
*   Writing new values to these attributes.
*   Authenticating one or more users so that the program can write to attributes that require secured writes or verified writes.

At minimum, a deployed ArchestrA Platform must exist on the computer running the client application.

## Components of the MXAccess Toolkit

The MXAccess Toolkit contains:

*   The MXAccess32.tlb type library .
*   The MxAccess.tlb type library.
*   The COM/.NET support files:
    *   The .NET interop DLL ArchestrA.MxAccess.dll
    *   The ArchestrA.MxAccess.dll .NET policy file.
    *   Code samples for Visual Basic, .NET, Visual C++, and C#.
*   Documentation.

The MXAccess Toolkit installs these files to the following locations.

<table>
<thead>
<tr>
<th>Files</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td>Type libraries</td>
<td>32-bit operation systems:<br>\Program Files\ArchestrA\Framework\bin<br><br>64-bit operating systems:<br>\Program Files (x86)\ArchestrA\Framework\bin</td>
</tr>
<tr>
<td>Interop file</td>
<td>32-bit operation systems:<br>\Program Files\ArchestrA\Framework\bin<br><br>64-bit operating systems:<br>\Program Files (x86)\ArchestrA\Framework\bin</td>
</tr>
<tr>
<td>Code samples</td>
<td>32-bit operating systems:<br>\Program Files\ArchestrA\Toolkits\Samples<br><br>64-bit operating systems:<br>\Program Files (x86)\ArchestrA\Toolkits\Samples</td>
</tr>
</tbody>
</table>

# Registering the Interface Files

The installation registers the type library and interop file for you. You can use the following procedure to copy these files to a computer and register them yourself. A common example requiring manual registration is running the code samples on an earlier version of Application Server. This is because earlier versions of Application Server did not contain the needed files.

## To manually register the interface files

1 Copy the files MxAccess.tlb and ArchestrA.MxAccess.dll as follows:
* On 32-bit operating systems, from the \Program Files\ArchestrA\Framework\Bin folder on the MXAccess Toolkit CD to the folder \Program Files\ArchestrA\Framework\bin on your hard drive.
* On 64-bit operating systems, from the \Program Files (x86)\ArchestrA\Framework\Bin folder on the MXAccess Toolkit CD to the folder \Program Files (x86)\ArchestrA\Framework\bin on your hard drive

Registering the Interface Files 2 Register the type library. Open a command prompt window and type the regtlib command followed by the complete path to the type library.

* For example, on a 32-bit operating system:

```
regtlib "C:\Program
Files\ArchestrA\Framework\bin\MxAccess32.tlb"
```

* For example, on a 64-bit operating system:

```
regtlib "C:\Program Files
(x86)\ArchestrA\Framework\bin\MxAccess32.tlb"
```

**Note:** You must enclose the folder path within quotation marks because there is a space in the Program Files folder name.

3 Register the interop file.
a Open two Explorer windows. Browse to the \WINDOWS\assembly folder in one window. In the other window:
* On a 32-bit operating system, browse to the \Program Files\ArchestrA\Framework\bin folder.
* On a 64-bit operating system, browse to the \Program Files (x86)\ArchesrA\Framework\bin folder.
b If an ArchestrA.MxAccess folder exists in the \WINDOWS\assembly folder, you must uninstall it. Do this by right-clicking ArchestrA.MxAccess in the \WINDOWS\assembly folder and then clicking **Uninstall**. Drag the new ArchestrA.MxAccess.dll from the ArchestrA\Framework\bin folder to the WINDOWS\assembly folder.

# Chapter 2: Using the Interface Files in Programs

After you install and register the type library and interop, you can use them in your programs.

To develop new software to use the MXAccess interface, also known as LMXProxy, you must include a reference to the interface in your project. How to do this differs according to which language you are using. This documentation describes using the MXAccess Toolkit with three languages supported by Microsoft Visual Studio: Visual Basic.NET, C#, and Visual C++.

For the program to connect to the Galaxy and access ArchestrA data, a deployed ArchestrA Platform must exist on the computer running the client application. However, you do not need ArchestrA or a MXAccess runtime license installed on your computer to compile and link the program.

# Adding a Toolkit Reference Using Visual Basic.NET

You can add a toolkit reference in Visual Basic.NET.

## To add a reference

1. On the **Project** menu, click **Add Reference**. The **Add Reference** dialog box appears.
2. Click the **Browse** tab.
3. Navigate to:
    * (32-bit operating system) \Program Files\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.
    * (64-bit operating system) \Program Files (x86)\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.

## To confirm that the MXAccess reference has been added to your project

1. On the **Project** menu, click **<AppName> Properties**, where AppName is the actual name of your project. You can also use Solution Explorer and double-click the My Project icon.
2. Click the **References** tab. The **Reference Names** list includes ArchestrA.MxAccess.
3. Show the path to the DLL file.
4. On the **View** menu, click **Object Browser**. The **Object Browser** appears.
5. Expand the icon for ArchestrA.MxAccess to see all the interfaces and other members provided by the MXAccess interface.

Adding a Toolkit Reference Using C# # Adding a Toolkit Reference Using C#

You can add a toolkit reference in C#.

## To add a reference

1.  On the **Project** menu, click **Add Reference**. The **Add Reference** dialog box appears.
2.  Click the **Browse** tab.
3.  Navigate to:
    *   (32-bit operating system) \Program Files\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.
    *   (64-bit operating system) \Program Files(x86)\ArchestrA\Framework\bin and select the ArchestrA.MxAccess.dll interop file.

## To confirm that the MXAccess reference has been added to your project

1.  Open Solution Explorer and expand the **References** icon.
2.  On the **View** menu, click **Object Browser**. The **Object Browser** pane appears.
3.  You can expand the icon for ArchestrA.MxAccess to see all the interfaces and other members provided by the MXAccess interface.

# Adding a Toolkit Reference Using C++

You can add a toolkit reference in C++.

**Note:** Visual C++ supports smart pointers. When used with COM interface pointers, smart pointers can be a powerful way to ensure that COM objects get properly instantiated at startup and cleaned up at shutdown. The code examples in this toolkit include one C++ project that uses smart pointers (the ActiveX dialog box) and another that makes it clear where the COM objects are being created and destroyed. For more information on using smart pointer templates in your C++ program, see the Visual Studio documentation.

## To add a reference

1. In the file stdafx.h, add a line such as the following example to import the type library:

```c++
#import "C:\Program Files\ArchestrA\Framework\bin\MxAccess32.tlb" no_namespace raw_interfaces_only
```

When you compile, Visual Studio creates a file named MxAccess.tlh.

2. Open this file to see the interfaces and other members provided by the MXAccess interface.

# Handling Exceptions in Your API Code

Include exception handling when any of the API calls are invoked.

*   **Visual Basic.NET**
    Add an OnError statement and handling to your functions and subroutines.
*   **C#**
    Enclose your code in a try block and provide error handling and display in a catch block.
*   **Visual C++**
    Enclose your code in a try block and provide error handling and display in a catch block. You should also inspect the HRESULT returned from the API functions and add code to handle failures.

Declaring and Instantiating the LMXProxyServer Object The code examples show you the basic operations of using the MXAccess API function calls. Although they include rudimentary exception handling, they do not handle all possible errors and exceptions. Your program should include exception and error processing to provide robust operation and problem diagnostics appropriate for your specific application.

# Declaring and Instantiating the LMXProxyServer Object

To use the MXAccess Toolkit, your program must declare the LMXProxyServer object. Use the following examples, depending on which language you use.

## Examples

[Visual Basic.NET]
```vb
Dim WithEvents LMX_Server As ArcestrA.MxAccess.LMXProxyServer
```

[C#]
```csharp
ArcestrA.MxAccess.LMXProxyServer LMX_Server;
```

[Visual C++]
```cpp
ILMXProxyServer* pLMX_Server;
```
or, if using a smart pointer
```cpp
CComPtr<ILMXProxyServer> pLMX_Server;
```
Create an instance of the LMXProxyServer. Use the following examples, depending on which language you use.

## Examples

[Visual Basic.NET]
```vb
LMX_Server = New ArcestrA.MxAccess.LMXProxyServer
```

[C#]
```csharp
LMX_Server = new ArcestrA.MxAccess.LMXProxyServer;
```

[Visual C++]

```c++
HRESULT hr = CoCreateInstance (__uuidof(LMXProxyServer),
    NULL,
    CLSCTX_INPROC_SERVER,
    __uuidof(ILMXProxyServer),
    (void **)&pLMX_Server);
```

or, if using a smart pointer

```c++
HRESULT hr =
    pLMX_Server.CoCreateInstance (__uuidof( LMXProxyServer ));
```

In Visual C++ your program must initialize COM before it can perform any operations with COM. Most likely, you will create the LMXProxy in the main program thread. For example:

```c++
HRESULT hr =
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
```

initializes COM for the apartment threaded model. Be sure to call CoUninitialize() when your program exits.

Use the following examples at the end of your program to discard the LMXProxyServer.

[Visual Basic.NET]

```vb
LMX_Server = Nothing
```

(or simply let it go out of scope)

[C#]

```csharp
LMX_Server = null;
```

(or simply let it go out of scope)

[Visual C++]

```c++
HRESULT hr = pLMX_Server->Release();
pLMX_Server = NULL;
```

Or, if using a smart pointer, you can simply let it go out of scope.

Connecting Handlers for Events Reported from the LMXProxyServer # Connecting Handlers for Events Reported from the LMXProxyServer

The MXAccess Toolkit supports receiving notifications from the LMXProxyServer. In COM, these notifications are handled as connection points. In .NET, these are handled as delegates. Your program must implement a handler for each type of event you expect to receive and connect it to the events using the mechanisms appropriate for the programming language you are using.

Visual Basic and C# make this implementation fairly easy. Visual C++ requires a little more setup to create a COM object that exposes the necessary interface, including support for the IDispatch interface.

The events that must be handled are:

*   **OnDataChange()**
    Reports the value and status of an ArcestrA Attribute to which the program has subscribed.
*   **OnWriteComplete()**
    Reports the status after a write operation completes, indicating such information as whether the write operation succeeded or failed.
*   **OperationComplete()**
    Reports the status when an Advanced Communication Management operation has been completed, indicating such information as whether it succeeded or failed.
*   **OnBufferedDataChange()**
    Reports the VTQ buffer and the status of an ArcestrA Attribute to which the program has subscribed.

The specifics of what your event handlers do depend on the purpose of your program. This section focuses primarily on how to connect the handlers in your program to the event callback mechanism. For more information about what should be inside the code for the event handlers, see "Handling the OnDataChange Callback" on page 62, see "Handling the OnWriteComplete Callback" on page 72, see "Handling the OperationComplete Callback" on page 41, and see "Handling the OnBufferedDataChange Callback" on page 63.

Handling events involves implementing required methods, exposing them as event handlers, and connecting the event handlers to the LMXProxyServer's Connection Points.

[Visual Basic.NET]

Declare the methods as:

```vbnet
Private Sub LMX_OnDataChange(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByVal pvItemValue As Object,
    ByVal pwItemQuality As Integer,
    ByVal pftItemTimeStamp As Object,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
    Handles LMX_Server.OnDataChange
```

and

```vbnet
Private Sub LMX_OnWriteComplete(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
    Handles LMX_Server.OnWriteComplete
```

and

```vbnet
Private Sub LMX_OperationComplete(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
    Handles LMX_Server.OperationComplete
```

and

```vbnet
Private Sub LMX_OnBufferedDataChange(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByVal dtDataType As ArchestrA.MxAccess.MxDataType,
    ByVal pvItemValue As Object,
    ByVal pwItemQuality As Object,
    ByVal pftItemTimeStamp As Object,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
    Handles LMX_Server.OnBufferedDataChange
```

Connecting Handlers for Events Reported from the LMXProxyServer At run time, Visual Basic automatically connects the event handlers when you instantiate the LMX_Server object.

**Note:** The names do not have to be LMX_OnDataChange, LMX_OnWriteComplete, and LMX_OperationComplete, but the parameter types in the function syntax must match declarations in the LMXProxy interface.

[C#]

Declare the methods as:

```csharp
private void LMX_OnDataChange(
    int hLMXServerHandle,
    int phItemHandle,
    object pvItemValue,
    int pwItemQuality,
    object pftItemTimeStamp,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
```
and

```csharp
private void LMX_OnWriteComplete(
    int hLMXServerHandle,
    int phItemHandle,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
```
and

```csharp
private void LMX_OperationComplete(
    int hLMXServerHandle,
    int phItemHandle,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
```
and

```csharp
private void LMX_OnBufferedDataChange(
    int hLMXServerHandle,
    int phItemHandle,
    ArchestrA.MxAccess.MxDataType dtDataType,
    object pvItemValue, object pvItemQuality,
    object pvItemTimeStamp,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
```

At run time, C# does not automatically connect these event handlers for you. You must do this in your code. After you have instantiated the LMX_Server object, the following three lines will connect the methods as delegates for the object:

```csharp
LMX_Server.OnDataChange += new
    _ILMProxyServerEvents_OnDataChangeEventHandler(LMX_OnDataChange);
LMX_Server.OnWriteComplete += new
    _ILMProxyServerEvents_OnWriteCompleteEventHandler(LMX_OnWriteComplete);
LMX_Server.OperationComplete += new
    _ILMProxyServerEvents_OperationCompleteEventHandler(LMX_OperationComplete);
LMX_Server.OnBufferedDataChange += new
    _ILMProxyServerEvents2_OnBufferedDataChangeEventHandler(LMX_OnBufferedDataChange);
```

**Note:** The names do not have to be LMX_OnDataChange, LMX_OnWriteComplete, and LMX_OperationComplete, but the parameter types in the function syntax must match declarations in the LMXProxy interface.

[Visual C++]

Declare the methods as:

```cpp
HRESULT CLMX_Events_Shim::OnDataChange (
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    VARIANT pvItemValue,
    long pwItemQuality,
    VARIANT pftItemTimeStamp,
    SAFEARRAY *pSAItemStatus
);
```
and

```cpp
HRESULT CLMX_Events_Shim::OnWriteComplete (
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    SAFEARRAY *pSAWriteItemStatus
);
```
and

```cpp
HRESULT CLMX_Events_Shim::OperationComplete (
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    SAFEARRAY *pSAWriteItemStatus);
```

Connecting Handlers for Events Reported from the LMXProxyServer and

```c
HRESULT CLMX_Events_Shim::OnBufferDataChange(
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    ArchestrA.MxAccess.MxDataType dtDataType,
    VARIANT pvItemValue,
    VARIANT pwItemQuality,
    VARIANT pftItemTimeStamp,
    SAFEARRAY *pSAItemStatus);
```

where CLMX_Events_Shim is a class derived from the base class LMXProxyServerEvents.

The sample code includes a header file and implementation of CLMXProxyServerEvents that is derived from IDispatch and handles passing IDispatch methods to the OnDataChange(), OnWriteComplete(), and OperationComplete() methods.

At run time, C++ does not automatically connect these event handlers for you. You must do this in your code. After you have instantiated the LMX_Server object, you must instantiate the CLMX_Events_Shim object. It helps to have two methods in CLMX_Events_Shim that encapsulate the Connect() and Disconnect() operations:

```c
void CLMX_Events_Shim::Connect()
{
    // check whether we are already connected
    if (!m_bConnected)
    {
        // attempt to call Advise on the
        // IConnectionPoint
        DWORD cookie = 0;
        IConnectionPointContainer* pcPC = 0;
        HRESULT hr = pLMX_Server->QueryInterface(&pcPC);
        if (SUCCEEDED(hr))
        {
            hr = pcPC->FindConnectionPoint(
                __uuidof(_ILMXProxyServerEvents), &m_pCP);
            if (SUCCEEDED(hr))
            {
                hr = m_pCP->Advise(this, &cookie);
                m_LmxInterfaceCallBacksCookie = cookie;
                m_bConnected = true;
            }
            pcPC->Release();
        }
    }
}
```

and

```c++
void CLMX_Events_Shim::Disconnect()
{
    // If we are connected then call Unadvise on
    // the IConnectionPoint
    if (m_bConnected)
    {
        m_bConnected = false;
        IConnectionPoint* pConnectionPoint = m_pCP;
        m_pCP->Unadvise(m_LmxInterfaceCallBacksCookie);
        pConnectionPoint->Release();
    }
}
```

After you instantiate the LMXProxyServer object, make sure that you also instantiate an object of type CLMX_Events_Shim and then call its Connect() method. When you are preparing to delete (or even Unregister) the LMXProxyServer object, you should call the Disconnect() method to stop servicing the event callbacks.

**Note:** The class names do not have to be CLMX_Events_Shim and CLMXProxyServerEvents but the overall organization of these classes should be similar to the code samples. Because the methods are exposed directly as a COM interface, the names DO have to be OnDataChange, OnWriteComplete, and OperationComplete and the parameter types in the function syntax must match declarations in the LMXProxy interface.

# Initializing the LMXProxyServer Connection

The LMXProxyServer must be initialized prior to making other method calls on the COM object's interface. Use the Register() method, which connects your component to LMX.

## Register() Method

Connects your component to LMX.

### Syntax

**[Visual Basic.NET]**
```vb
Function Register (ByVal pClientName As String) As Integer
```
**[C#]**
```csharp
int Register(string pClientName);
```
**[Visual C++]**
```cpp
HRESULT __stdcall Register(BSTR pClientName, long *phLMXServerHandle);
```

Shutting Down the LMXProxyServer Connection **Parameters**

pClientName
Name of your component.

**Returns**

Handle of the connection. The handle that is returned represents the LMX connection. Your program should preserve this handle for the life time of the connection because it is used on all subsequent calls to other methods on the interface.

**Examples**

[Visual Basic.NET]
```vbnet
hLMX = LMX_Server.Register("TestApp")
```
[C#]
```csharp
hLMX = LMX_Server.Register("TestApp");
```
[Visual C++]
```cpp
BSTR bstrTemp = ::SysAllocString(L"TestApp");
hr = pLMX_Server->Register(bstrTemp, &hLMX);
::SysFreeString(bstrTemp);
```

# Shutting Down the LMXProxyServer Connection

The LMXProxyServer connection must be released when your application shuts down or when it no longer needs to be connected to the Galaxy. Use the Unregister() method, which disconnects your component from LMX.

Before calling the Unregister() method, you must unadvise all active items and remove them from the internal tables using the UnAdvise() and RemoveItem() methods.

## Unregister() Method

Disconnects your component from LMX.

**Syntax**

[Visual Basic.NET]
```vbnet
Sub Unregister (ByVal hLMXServerHandle As Integer)
```
[C#]
```csharp
void Unregister(int hLMXServerHandle);
```
[Visual C++]
```cpp
HRESULT __stdcall Unregister(long hLMXServerHandle);
```

**Parameters**

*   **hLMXServerHandle**
    Handle of the connection.

**Returns**

Nothing.

**Remarks**

After calling the Unregister() method, you can discard the LMXProxyServer object. If your program is one that connects to LMX for a time, disconnects, and connects again, you may want to keep the LMXProxyServer object, rather than releasing it and creating a new one each time.

**Examples**

[Visual Basic.NET]
```vbnet
Call LMX_Server.Unregister (hLMX)
```
[C#]
```csharp
LMX_Server.Unregister (hLMX);
```
[Visual C++]
```cpp
hr = pLMX_Server->Unregister (hLMX);
```

# Adding Item References to the Internal Tables

To access an ArchestrA Attribute, you must first add it to the internal table of attribute references that LMX maintains for your connection. Use the AddItem() or the AddItem2() method.

## AddItem() Method

Adds an attribute to the internal table of attributes.

**Syntax**

[Visual Basic.NET]
```vbnet
Function AddItem (
    ByVal hLMXServerHandle As Integer,
    ByVal strItemDef As String
)
As Integer
```

Adding Item References to the Internal Tables [C#]

```csharp
int AddItem(
    int hLMXServerHandle, string strItemDef
);
```

[Visual C++]

```cpp
HRESULT __stdcall AddItem(
    long hLMXServerHandle,
    BSTR strItemDef,
    long *phItem
);
```

**Parameters**

**hLMXServerHandle**
Handle of the connection.

**strItemDef**
Reference string for the attribute.

**Returns**

Handle of the item. The handle that is returned represents the item in the LMX connection's internal table. Your program should retain this handle because it is used on all subsequent calls to other methods involved in reading and writing data for that attribute.

**Remarks**

You can add several attributes to the table by calling the AddItem() method for each one.

Before you can write to an attribute or receive data update notifications for it, you must put the item on advise. For more information, see "Advise() Method" on page 33.

When shutting a program down, you should unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.

**Examples**

[Visual Basic.NET]

```vbnet
hItem = LMX_Server.AddItem(hLMX, strItemDef)
```

[C#]

```csharp
hItem = LMX_Server.AddItem(hLMX, strItemDef);
```

[Visual C++]

```c++
BSTR bstrTemp = ::SysAllocString(szItemDef);
    hr = pLMX_Server->AddItem(hLMX, bstrTemp, &hItem);
::SysFreeString (bstrTemp);
```

If the attribute represents a single data value, reference the item as follows:

[Single data value]
ud1.x

[Single element of an array:]
ud1.ar[2]

To reference all elements of an attribute that is an array, follow the reference with brackets:

[Entire array:]
ud1.ar[ ]

# AddItem2() Method

Adds an attribute to the internal table of attributes, using a relative reference and an Object name. This makes it easier to resolve a reference for an attribute relative to an indicated Object.

The reference string must use one of the five relative reference names:

*   MyPlatform
*   MyEngine
*   MyArea
*   MyContainer
*   Me

The context string identifies the Object to which the reference corresponds.

## Syntax

[Visual Basic.NET]

```vb
Function AddItem2 (
    ByVal hLMXServerHandle As Integer,
    ByVal strItemDef As String
    ByVal strItemCtxt As String
)
As Integer
```

Adding Item References to the Internal Tables [C#]

```csharp
int AddItem2(
    int hLMXServerHandle,
    string strItemDef
    string strItemCtxt
);
```

[Visual C++]

```cpp
HRESULT __stdcall AddItem2 (
    long hLMXServerHandle,
    BSTR strItemDef,
    BSTR strItemCtxt,
    long *phItem
);
```

**Parameters**

**hLMXServerHandle**
Handle of the connection.

**strItemDef**
Reference string for the attribute.

**strItemCtxt**
Context string for the related object.

**Returns**

Handle of the item. The handle that is returned represents the item in the LMX connection's internal table. Your program should retain this handle because it is used on all subsequent calls to other methods involved in reading and writing data for that attribute.

**Remarks**

You can add several attributes to the table by calling the AddItem() method for each one.

Before you can write to an attribute or receive data update notifications for it, you must put the item on advise. For more information, see "Advise() Method" on page 33.

When shutting the program down, you should unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.

The AddItem2() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.

## Examples

[Visual Basic.NET]
```vb
hItem = LMX_Server.AddItem2(hLMX, strItemDef, strItemCtxt)
```

[C#]
```csharp
hItem = LMX_Server.AddItem2(hLMX, strItemDef, strItemCtxt);
```

[Visual C++]
```cpp
BSTR bstrTemp = ::SysAllocString(szItemDef);
BSTR bstrCtxt = ::SysAllocString(szItemCtxt);
hr = pLMX_Server->AddItem2(hLMX, bstrTemp, bstrCtxt,
&hItem);
::SysFreeString(bstrTemp);
::SysFreeString(bstrCtxt);
```

If the Attribute belongs to the Platform on which an Object is running, you do not need to know the actual name of the Platform. You can add an item using MyPlatform in the reference string, and specify the Object as the context for the reference, using its global name. Other relative references can be specified in the same way:

*   CPULoad on the Platform on which AnalogDevice_001 is running:
    *   hItem = LMX_Server.AddItem2(hLMX, "MyPlatform.CPULoad", "AnalogDevice_001");
*   Scan period of the Engine on which ud1 is hosted:
    *   hItem = LMX_Server.AddItem2(hLMX, "MyEngine.Scheduler.ScanPeriod", "ud1");

## AddBufferedItem() Method

You can configure your client program to subscribe to buffered attributes. Buffered attributes support retrieving and processing of multiple data items which are received from field devices during AppEngine's Scan Period. The multiple data items are received by buffered attributes once per scan period in the form of buffer, in order to eliminate data folding.

### Syntax

[Visual Basic.NET]
```vb
Function AddBufferedItem(
    hLMXServerHandle As Integer,
    strItemDef As String,
    strItemCtxt As String
)
    As Integer

Adding Item References to the Internal Tables [C#]

```csharp
int AddBufferedItem(
    int hLMXServerHandle,
    string strItemDef,
    string strItemCtxt)
```

[Visual C++]

```cpp
HRESULT __stdcall AddBufferedItem (
    long hLMXServerHandle,
    BSTR strItemDef,
    BSTR strItemCtxt,
    long * phItem )
```

**Parameters**

**hLMXServerHandle**
Handle of the connection.

**strItemDef**
Reference string for the attribute.

**strItemCtxt**
Context string for the related object.

**Returns**

Handle of the item. The handle that is returned represents the item in the LMX connection's internal table. Your program should retain this handle because it is used on all subsequent calls to other methods involved in reading and writing data for the buffered attribute.

**Remarks**

You can add several attributes to the table by calling the AddBufferedItem() method for each one.

Before you can write to an attribute or receive data update notifications for it, you must put the item on advise. For more information, see "Advise() Method" on page 33.

When shutting a program down, you should unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.

**Examples**

[Visual Basic.NET]

```vbnet
hItem = LMX_Server.AddBufferedItem(hLMX, strItemDef, strItemCtxt)
```

[C#]
```csharp
hItem = LMX_Server.AddBufferedItem(hLMX, strItemDef,
    strItemCtxt);
```

[Visual C++]
```cpp
LMX_Server.AddBufferedItem(hLMXServerHandle, strItemDef, strItemCtxt, phItem);
```

# Removing Item References from Internal Tables

When you no longer need access to a particular attribute, you can remove the reference to it from the internal table. Use the RemoveItem() method.

## RemoveItem() Method

Removes an attribute from the internal table of attributes.

### Syntax

[Visual Basic.NET]
```vbnet
Sub RemoveItem (
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer
)
```

[C#]
```csharp
void RemoveItem(
    int hLMXServerHandle,
    int hItem
);
```

[Visual C++]
```cpp
HRESULT __stdcall RemoveItem(
    long hLMXServerHandle,
    long hItem
);
```

### Parameters

**hLMXServerHandle**
    Handle of the connection.

**hItem**
    Handle of the item.

Putting Items on Advise **Returns**
Nothing.

**Remarks**
If the item is active—that is, if it has been placed on advise—unadvise the item before removing it. For more information, see "UnAdvise() Method" on page 36.
When shutting down, unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.

**Examples**
[Visual Basic.NET]
Call LMX_Server.RemoveItem(hLMX, hItem)
[C#]
LMX_Server.RemoveItem(hLMX, hItem);
[Visual C++]
hr = pLMX_Server->RemoveItem(hLMX, hItem);

# Putting Items on Advise

To receive data updates regarding an attribute, you can put it on advise, also known as subscribing to an item. Use the Advise() method or the AdviseSupervisory() method.

## Advise() Method

Adds advise to an attribute.

### Syntax
[Visual Basic.NET]
```vbnet
Sub Advise(
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer
)
```
[C#]
```csharp
void Advise(
    int hLMXServerHandle,
    int hItem
);
```

[Visual C++]

```c++
HRESULT __stdcall Advise(
    long hLMXServerHandle,
    long hItem
);
```

**Parameters**

**hLMXServerHandle**
Handle of the connection.

**hItem**
Handle of the item.

**Returns**

Nothing.

**Remarks**

After you have put an item on advise, it is considered active. If the data of that attribute changes, your program receives a notification of the update through the OnDataChange() event.

You must put an item on advise before you can write to it.

You must call the AddItem() method on an item before you can put it on advise. When shutting down the program, you should unadvise all active items and remove all items from the internal table before calling Unregister(). For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.

**Examples**

[Visual Basic.NET]

```vb
Call LMX_Server.Advise(hLMX, hItem)
```

[C#]

```csharp
LMX_Server.Advise(hLMX, hItem);
```

[Visual C++]

```c++
hr = pLMX_Server->Advise(hLMX, hItem);
```

Putting Items on Advise # AdviseSupervisory() Method

Adds advise to an attribute.

## Syntax

**[Visual Basic.NET]**

```vbnet
Sub AdviseSupervisory(
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer
)
```

**[C#]**

```csharp
void AdviseSupervisory(
    int hLMXServerHandle,
    int hItem
);
```

**[Visual C++]**

```cpp
HRESULT __stdcall AdviseSupervisory(
    long hLMXServerHandle,
    long hItem
);
```

## Parameters

**hLMXServerHandle**
Handle of the connection.

**hItem**
Handle of the item.

## Returns

Nothing.

## Remarks

The Advise() method establishes a User Connection to ArchestrA data, while the AdviseSupervisory() method establishes a Supervisory Connection. Both of these connections provide updates via the OnDataChange() callback and status information via the OnWriteComplete() and OperationComplete() callbacks. When you attempt to invoke a Write operation on the item, the Supervisory Connection does a Supervisory Set on the Attribute; it does not require a login by a user.

The AdviseSupervisory() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.

The Supervisory Connection is appropriate if your application runs in the background, such as a Windows Service, which normally does not interact with users via a GUI. Using AdviseSupervisory() for the items enables the program to write to items that require Secured Writes or Verified Writes, without requiring a user login to permit the write.

You must put an item on advise before you can write to it.

You must call the AddItem() method on an item before you can put it on advise. When shutting down the program, you should unadvise all active items and remove all items from the internal table before calling Unregister(). For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.

**Examples**

[Visual Basic.NET]
```vb
Call LMX_Server.AdviseSupervisory(hLMX, hItem)
```
[C#]
```csharp
LMX_Server.AdviseSupervisory(hLMX, hItem);
```
[Visual C++]
```cpp
hr = pLMX_Server->AdviseSupervisory(hLMX, hItem);
```

## Unadvising Items

When your program no longer needs to receive data updates and no longer needs to write to an attribute, it can unadvise the item—that is, it can take it off advise. Use the UnAdvise() method.

## UnAdvise() Method

Stops the advise from the attribute.

### Syntax

[Visual Basic.NET]
```vb
Sub UnAdvise(
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer
)
```
[C#]
```csharp
void UnAdvise (
    int hLMXServerHandle,
    int hItem
);
```

Unadvising Items [Visual C++]

```c++
HRESULT __stdcall UnAdvise (
    long hLMXServerHandle,
    long hItem
);
```

**Parameters**

**hLMXServerHandle**
Handle of the connection.

**hItem**
Handle of the item.

**Returns**

Nothing.

**Remarks**

When shutting down, unadvise all active items and remove all items from the internal table before calling the Unregister() method. For more information, see "UnAdvise() Method" on page 36 and "RemoveItem() Method" on page 32.

**Examples**

**[Visual Basic.NET]**

```vb
Call LMX_Server.UnAdvise(hLMX, hItem)
```

**[C#]**

```csharp
LMX_Server.UnAdvise(hLMX, hItem);
```

**[Visual C++]**

```cpp
hr = pLMX_Server->UnAdvise(hLMX, hItem);
```

# Advanced Communication Management

After an attribute has been put on advise, the application can call Suspend() to temporarily stop receiving updates without tearing down the subscription to the attribute. Updates can be resumed by calling Activate(). The callback OperationComplete() returns the status information about processing these function calls.

The Suspend() and Activate() methods are on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.

When an item is suspended or activated, that item should not be taken off advise or removed from the internal tables until the OperationComplete() event is received, indicating whether the update to the Advanced Communication Management status is successful or an error has occurred.

If the event indicates that an OperationComplete error has been detected, check the category and error code for further information.

To suspend updates, call the Suspend() method.

## Suspend() Method

Temporarily stops receiving updates without removing the subscription to the attribute.

### Syntax

**[Visual Basic.NET]**

```vbnet
Sub Suspend(ByVal hLMXServerHandle As Integer,
            ByVal hItem As Integer,
            ByRef pMxStatus As ArcestrA.MxAccess.MxStatus)
```

**[C#]**

```csharp
void Suspend(int hLMXServerHandle,
             int hItem,
             out ArcestrA.MxAccess.MxStatus pMxStatus);
```

**[Visual C++]**

```cpp
HRESULT Suspend(long hLMXServerHandle,
                long hItem,
                MxStatus *pMxStatus);
```

### Parameters

**hLMXServerHandle**
Handle of the connection

**hItem**
Handle of the item

Advanced Communication Management **Returns**

MxStatus indicating success, and whether operation is pending.

**Remarks**

If the returned MxStatus indicates an error, it may be because the handle to the LMXServer or the handle to the item is not registered. If it is successful, check the category of the returned MxStatus, as the operation may still be pending. Upon completion, an OperationComplete event will be received, indicating whether the operation has completed successfully or there is a problem.

To resume updates, call the Activate() method.

**Examples**

[Visual Basic.NET]

Call LMX_Server.Suspend(hLMX, hItem, hItemStatus)

[C#]

LMX_Server.Suspend(hLMX, hItem, out ItemStatus);

[Visual C++]

hr = pLMX_Server->Suspend(hLMX, hItem, &ItemStatus);

## Activate() Method

Starts receiving updates without removing the subscription to the attribute.

**Syntax**

[Visual Basic.NET]

```vbnet
Sub Activate(ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer,
    ByRef pMxStatus As ArcestrA.MxAccess.MxStatus)
```

[C#]

```csharp
void Activate(int hLMXServerHandle,
    int hItem,
    ArcestrA.MxAccess.MxStatus pMxStatus);
```

[Visual C++]

```cpp
HRESULT Activate(long hLMXServerHandle,
    long hItem,
    MxStatus *pMxStatus);
```

**Parameters**

*   **hLMXServerHandle**
    Handle of the connection
*   **hItem**
    Handle of the item

**Returns**

MxStatus indicating success, and whether operation is pending.

**Remarks**

If the returned MxStatus indicates an error, it may be because the handle to the LMXServer or the handle to the item is not registered. If it is successful, check the category of the returned MxStatus, as the operation may still be pending. Upon completion, an OperationComplete event will be received, indicating whether the operation has completed successfully or there is a problem.

When either Suspend() or Activate() is called, the completion of the status change may take some time to process. When the processing is finished, an OperationComplete event is received, indicating which item has been suspended or activated, and whether the operation was successful.

**Examples**

[Visual Basic.NET]
```vbnet
Call LMX_Server.Activate(hLMX, hItem, ItemStatus)
```
[C#]
```csharp
LMX_Server.Activate(hLMX, hItem, out ItemStatus);
```
[Visual C++]
```cpp
hr = pLMX_Server->Activate(hLMX, hItem, &ItemStatus);
```

Handling the OperationComplete Callback # Handling the OperationComplete Callback

The LMXProxyInterface emits an event for OperationComplete when your program invokes a Suspend() or Activate() function. The information your program receives identifies whether the change was successful and any error code information that may be appropriate.

## OperationComplete Event

Reports the status of an operation on an Attribute subscription, such as Suspend or Activate.

### Syntax

**[Visual Basic.NET]**

```vbnet
Private Sub LMX_OperationComplete(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByVal pvItemValue As Object,
    ByVal pwItemQuality As Integer,
    ByVal pftItemTimeStamp As Object,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OperationComplete
```

**[C#]**

```csharp
private void LMX_OperationComplete(
    int hLMXServerHandle,
    int phItemHandle,
    object pvItemValue,
    int pwItemQuality,
    object pftItemTimeStamp,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[] ItemStatus
);
```

**[Visual C++]**

```cpp
HRESULT CLMX_Events_Shim::OperationComplete(
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    VARIANT pvItemValue,
    long pwItemQuality,
    VARIANT pftItemTimeStamp,
    SAFEARRAY *pSAItemStatus
);
```

**Parameters**

*   **hLMXServerHandle**
    Handle of the connection.
*   **phItemHandle**
    Handle of the item.
*   **ItemStatus**
    Array of Message Exchange statuses.

**Returns**

Nothing.

**Remarks**

For more information, including the function prototypes for the different language, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.

Interpreting the OperationComplete status is very much like interpreting the data change status and involves examining the contents of the MxStatus structure. If the success member of the status is true, the Suspend() or Activate() operation has been accepted and completed. If the success member is false, check the other structure members for additional information about why the operation failed.

**Examples**

**[Visual Basic.NET]**

```vbnet
If ItemStatus(0).success Then
    CommunicationManagement_Status.Text = "Operation Complete - status OK"
Else
    CommunicationManagement_Status.Text = "Operation Complete w/error - cat: " & ItemStatus(0).category & " Src: " & ItemStatus(0).detectedBy & " detail: " & ItemStatus(0).detail
End If
```

**[C#]**

```csharp
if (ItemStatus[0].success != 0) {
    CommunicationManagement_Status.Text = "Operation Complete - status OK";
} else {
    CommunicationManagement_Status.Text = "Operation Complete w/error - cat: " + ItemStatus[0].category + " Src: " + ItemStatus[0].detectedBy + " detail: " + ItemStatus[0].detail;
}
```

Writing Data Values [Visual C++]

```c
if (ItemStatus[0].success != 0) {
    SetDlgItemText(hMainDlg, IDC_CommunicationManagement_Status,
        L"Operation Complete - status OK");
} else {
    TCHAR error_text[256];
    swprintf_s(error_text,
        sizeof(error_text)/sizeof(TCHAR), L"Operation Complete
        w/error - cat: %d Src: %d detail: %d",
        ItemStatus[0].category, ItemStatus[0].detectedBy,
        ItemStatus[0].detail);
    SetDlgItemText(hMainDlg,
        IDC_CommunicationManagement_Status, error_text);
}
```

# Writing Data Values

To write data to an attribute, call the Write() method or the WriteSecured() method.

To write data to an attribute, call the Write() method or the WriteSecured() method.

To write data and a specific timestamp to an attribute, call the Write2() method or the WriteSecured2() method.

To write to an item, the data must be in an object (or in C++ a VARIANT) of the proper internal type—integer, double, string, or other supported data types.

String data can be used to write to an attribute of any data type, so long as the string can be converted to the appropriate data type. Otherwise, the MxStatus returned by the OnWriteComplete() event indicates an error. For example, writing "1/27/2014 03:43:02 PM" to an integer value generates an error.

# Write() Method

Writes data to an attribute.

## Syntax

[Visual Basic.NET]

```vb
Sub Write(
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer,
    ByVal pItemValue As Object,
    ByVal UserID As Integer
)
```

[C#]

```csharp
void Write(
    int hLMXServerHandle,
    int hItem,
    object pItemValue,
    int UserID
);
```

[Visual C++]

```cpp
HRESULT __stdcall Write(
    long hLMXServerHandle,
    long hItem,
    VARIANT pItemValue,
    long UserID
);
```

**Parameters**

**hLMXServerHandle**
The handle of the connection.

**hItem**
The handle of the item.

**pItemValue**
The new data value.

**UserID**
The ID "cookie" for the user. If ArchestrA security is not enabled, this value is -1 or 0.

**Returns**

None.

**Remarks**

Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.

Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.

When an item is written, that item should not be taken off advise or removed from the internal tables until the OnWriteComplete() event is received, indicating whether the write is successful or an error has occurred.

Writing Data Values If the event indicates that an OnWriteComplete error has been detected, check the error code.

* A code of 1008 indicates that the user does not have the proper security to write to this item.
* A code of 1012 indicates that a secured write is required.
* A code of 1013 indicates that a verified write is required.

For more information, see "WriteSecured() Method" on page 47 and "AuthenticateUser() Method" on page 54. Also check the documentation on LMX details for other possible error codes.

If a secured write or verified write is required, call the WriteSecured() method with the same item handle as was used for the Write() method.

**Examples**

[Visual Basic.NET]
```vbnet
Call LMX_Server.Write(hLMX, hItem, pItemValue, uindex1)
```
[C#]
```csharp
LMX_Server.Write(hLMX, hItem, pItemValue, uindex1);
```
[Visual C++]
```cpp
hr = pLMX_Server->Write(hLMX, hItem, pItemValue, uindex1);
```

## Write2() Method

Writes data and a timestamp to an attribute.

### Syntax

[Visual Basic.NET]
```vbnet
Sub Write2(
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer,
    ByVal pItemValue As Object,
    ByVal pItemTime As Object,
    ByVal UserID As Integer
)
```
[C#]
```csharp
void Write2(
    int hLMXServerHandle,
    int hItem,
    object pItemValue,
    object pItemTime,
    int UserID
);
```

[Visual C++]

```c
HRESULT __stdcall Write2(
    long hLMXServerHandle,
    long hItem,
    VARIANT pItemValue,
    VARIANT pItemTime,
    long UserID
);
```

**Parameters**

**hLMXServerHandle**
The handle of the connection.

**hItem**
The handle of the item.

**pItemValue**
The new data value.

**pItemTime**
The new timestamp.

**UserID**
The ID "cookie" for the user. If ArcestrA security is not enabled, this value is -1 or 0.

**Returns**

None.

**Remarks**

Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.

Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.

The Write2() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.

If a secured write or verified write with a timestamp is required, call the WriteSecured2() method with the same item handle as was used for the Write2() method.

**Examples**

[Visual Basic.NET]

```vb
Call LMX_Server.Write2(hLMX, hItem, pItemValue, pItemTime, uindex1)
```

[C#]

```csharp
LMX_Server.Write2(hLMX, hItem, pItemValue, pItemTime, uindex1);
```

Writing Data Values [Visual C++]
```c++
hr = pLMX_Server->Write2(hLMX, hItem, pItemValue, pItemTime,
    uindex1);
```

# WriteSecured() Method

Writes data to an attribute that requires authentication, either as a secured write or as a verified write.

## Syntax

[Visual Basic.NET]
```vb
Sub WriteSecured(
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer,
    ByVal CurrentUserID As Integer,
    ByVal VerifiedUserID As Integer,
    ByVal pItemValue As Object
)
```
[C#]
```csharp
void WriteSecured(
    int hLMXServerHandle,
    int hItem,
    int CurrentUserID,
    int VerifiedUserID,
    object pItemValue
);
```
[Visual C++]
```c++
HRESULT __stdcall WriteSecured(
    long hLMXServerHandle,
    long hItem,
    long CurrentUserID,
    long VerifiedUserID,
    VARIANT pItemValue
);
```

## Parameters

**hLMXServerHandle**
*   Handle of the connection.

**hItem**
*   Handle of the item.

**CurrentUserID**
*   ID "cookie" for the current user.

**VerifiedUserID**
ID "cookie" for the verified user, or -1 or 0 if only a secured write is being done.

**pItemValue**
The new data.

**Returns**
Nothing.

**Remarks**
If the attribute requires a secured write or verified write, you write to it with the WriteSecured() method. A secured write is analogous to submitting a change request that requires one signature, while a verified write corresponds to a change request that requires two signatures.

Each user must be authenticated by name and password, thereby obtaining the ID "cookie." For more information, see "AuthenticateUser() Method" on page 54.

Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.

Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.

**Examples**

[Visual Basic.NET]
```vb
Call LMX_Server.WriteSecured(hLMX, hItem, uindex1, uindex2,
    pItemValue)
```

[C#]
```csharp
LMX_Server.WriteSecured(hLMX, hItem, uindex1, uindex2,
    pItemValue);
```

[Visual C++]
```cpp
hr = pLMX_Server->WriteSecured(hLMX, hItem, uindex1, uindex2,
    pItemValue);

Writing Data Values # WriteSecured2() Method

Writes data and timestamp to an attribute that requires authentication, either as a secured write or as a verified write.

## Syntax

**[Visual Basic.NET]**

```vbnet
Sub WriteSecured2 (
    ByVal hLMXServerHandle As Integer,
    ByVal hItem As Integer,
    ByVal CurrentUserID As Integer,
    ByVal VerifiedUserID As Integer,
    ByVal pItemValue As Object,
    ByVal pItemTime As Object
)
```

**[C#]**

```csharp
void WriteSecured2 (
    int hLMXServerHandle,
    int hItem,
    int CurrentUserID,
    int VerifiedUserID,
    object pItemValue,
    object pItemTime
);
```

**[Visual C++]**

```cpp
HRESULT __stdcall WriteSecured2 (
    long hLMXServerHandle,
    long hItem,
    long CurrentUserID,
    long VerifiedUserID,
    VARIANT pItemValue,
    VARIANT pItemTime
);
```

## Parameters

**hLMXServerHandle**
Handle of the connection.

**hItem**
Handle of the item.

**CurrentUserID**
ID "cookie" for the current user.

**VerifiedUserID**
ID "cookie" for the verified user, or -1 or 0 if only a secured write is being done.

pItemValue
The new data.

pItemTime
The new timestamp.

**Returns**
Nothing.

**Remarks**
If the attribute requires a secured write or verified write, you write to it with the WriteSecured2() method. A secured write is analogous to submitting a change request that requires one signature, while a verified write corresponds to a change request that requires two signatures.

Each user must be authenticated by name and password, thereby obtaining the ID "cookie." For more information, see "AuthenticateUser() Method" on page 54.

Before you can write to an attribute, you must add the item to the internal table and put it on advise. For more information, see "AddItem() Method" on page 26 and "Advise() Method" on page 33.

Upon completion of the write, your program receives notification of the success/failure status through the OnWriteComplete() event.

The WriteSecured2() method is on the ILMXProxyServer4 interface, which extends the ILMXProxyServer interface.

**Examples**

[Visual Basic.NET]
```vbnet
Call LMX_Server.WriteSecured2 (hLMX, hItem, uindex1,
uindex2, pItemValue, pItemTime)
```

[C#]
```csharp
LMX_Server.WriteSecured2 (hLMX, hItem, uindex1,
uindex2, pItemValue, pItemTime);
```

[Visual C++]
```cpp
hr = pLMX_Server->WriteSecured2 (hLMX, hItem,
uindex1, uindex2, pItemValue, pItemTime);
```

Writing Data Values # Writing to a Single Element

When writing to a single ArchestrA element—that is, when you are not writing to several members of an array—it is possible to pass a string object (or VARIANT) to the Write() or WriteSecured() method:

[Visual Basic.NET]
```vb
Dim vValue as String
Call LMX_Server.Write(hLMX, hItem, vValue, uindex1)
```

[C#]
```csharp
string vValue;
LMX_Server.Write(hLMX, hItem, vValue, uindex1);
```

[Visual C++]
```cpp
TCHAR sValue;
    ... // set value of sValue to string
    // representing the new value
VARIANT varItem;
    if  (ItemAsVariant (&varItem, sValue) {
        hr = pLMX_Server->Write(hLMX, hItem, varItem, uindex1);
    }
```

# Writing to an Array

When writing to an array of ArchestrA elements, you must build an object (or in C++ a VARIANT) that contains the data as an array. It can be an array of strings or it can be an array of the actual data type.

For more information on implementing the handling in the ItemArrayAsVariant() and ReleaseItemArrayAsVariant() methods, see "Encapsulating Data Values in Visual C++" on page 52.

[Visual Basic.NET]
```vb
Dim vArray(2) as String
vArray(0) = vValue1.Text
vArray(1) = vValue2.Text
vArray(2) = vValue3.Text
Call LMX_Server.Write(hLMX, hItem, vArray, uindex1)
```

[C#]
```csharp
string[] vArray = { vValue1.Text, vValue2.Text, vValue3.Text };
LMX_Server.Write(hLMX, hItem, vArray, uindex1);
```

[Visual C++]

```c++
#define INTARRAY_LEN 256
typedef TCHAR INTARRAY_ELEM [INTARRAY_LEN]
INTARRAY_ELEM intarray[3];

... // set values of intarray[0], intarray[1], //intarray[2] to strings representing new values

VARIANT varArray;

// build SAFEARRAY and put it into varArray
// (see example of implementation below)

if (ItemArrayAsVariant (&varArray, VT_BSTR, intarray, 3))
{
    hr = pLMX_Server->Write(hLMX, hItem, varItem, uindex1);
    ReleaseItemArrayAsVariant (&varItem, 3);
}
```

# Encapsulating Data Values in Visual C++

To encapsulate the new data values as a SAFEARRAY, the simplest approach is to let the VARIANT mechanism handle the data conversion to the appropriate data type. Note that there is a slight difference on how the data should be handled if strings (BSTRs) are being stored in the SAFEARRAY. You must copy the string pointer into the entry, instead of the actual value.

The following example shows how to convert the new entries and put them into a SAFEARRAY. This same function is also included in the sample code for C++.

```c++
bool ItemArrayAsVariant (VARIANT* pvarItems,
    VARTYPE datatype,
    INTARRAY_ELEM* intarray, long NumEntries)

{
    bool bRetVal = false;
    long i;
    HRESULT hr = S_OK;
    VariantInit (pvarItems);
    // set up a safe array to hold the data
    SAFEARRAYBOUND rgsabound[1];
    rgsabound[0].lBound = 0;
    rgsabound[0].cElements = NumEntries;
    SAFEARRAY *psa = SafeArrayCreate (datatype, 1, rgsabound);
    if (psa)
    {
        // create entries
        VARIANT varEntry;
```

Encapsulating Data Values in Visual C++ ```c++
for (i = 0; (i < NumEntries) && (SUCCEEDED(hr)); i++) {
    VariantInit(&varEntry);
    varEntry.vt = VT_BSTR;
    varEntry.bstrVal = ::SysAllocString(intarray[i]);
    switch (datatype) {
        case VT_BSTR : {
            SafeArrayPutElement(psa, &i, (void *)varEntry.bstrVal);
        }
        break;
        default : {
            hr = VariantChangeType(&varEntry,
                                   &varEntry, 0, datatype);
            // since VARIANT is a union, just
            // point to where value is stored
            SafeArrayPutElement(psa, &i, (void *)&varEntry.lVal);
        }
        break;
    } // switch //
    // free the temporary string
    if (varEntry.vt == VT_BSTR) {
        ::SysFreeString(varEntry.bstrVal);
    }
}
// modify the flags in fFeatures
if (datatype == VT_BSTR) {
    psa->fFeatures |= (unsigned short) FADF_BSTR;
} else {
    psa->fFeatures = 0;
}
if (SUCCEEDED(hr)) {
    // attach the array to the variant
    pvarItems->parray = psa;
    pvarItems->vt = (unsigned short) (datatype | VT_ARRAY);
    // indicate successfully created
    bRetVal = true;
}
return (bRetVal);
}
```

```c
void ReleaseItemArrayAsVariant (VARIANT* pvarItems, long NumEntries)
{
    UNREFERENCED_PARAMETER(NumEntries);
    SAFEARRAY *psa = pvarItems->parray;
    if (psa) {
        SafeArrayDestroy(psa);
    }
}
```

# Authenticating Users

Some ArchestraA data is secured. The entire Galaxy can have security enabled and individual attributes can be secured in such a way that they can be updated only if the user has certain login credentials. Some attributes require a verified write, which means that two different users must confirm that the update should take place. This involves calling the AuthenticateUser() method, which asks the Galaxy to check a user's credentials and return an ID "cookie" that can be used in the Write() and WriteSecured() method calls.

# AuthenticateUser() Method

## Syntax

**[Visual Basic.NET]**

```vb
Function AuthenticateUser(
    ByVal hLMXServerHandle As Integer,
    ByVal VerifyUser As String,
    ByVal VerifyUserPsw As String
)
    As Integer
```

**[C#]**

```csharp
int AuthenticateUser(
    intLMXServerHandle,
    string VerifyUser,
    string VerifyUserPsw
);
```

**[Visual C++]**

```cpp
HRESULT __stdcall AuthenticateUser(
    long hLMXServerHandle,
    BSTR VerifyUser,
    BSTR VerifyUserPsw,
    long *UserId
);
```

Authenticating Users **Parameters**

*   **hLMXServerHandle**
    Handle of the connection.
*   **VerifyUser**
    User account name.
*   **VerifyUserPsw**
    User password.

**Returns**

ID "cookie" for the user, or zero if the user's credentials are rejected.

**Remarks**

The AuthenticateUser() method gives a name and password to the Galaxy for authorization. If the user name and password are valid, a GUID is generated for the log-in. However, instead of returning this GUID to the caller, the LMXProxyServer stores the GUID in its internal tables and assigns it an integer index—an ID "cookie"—that can be used in subsequent calls to other methods. If the user's credentials are invalid (wrong name, wrong password or both), the underlying COM function returns HRESULT 0x80070057 indicating that the parameters are invalid. In C#, this will throw a System.ArgumentException; and in VB it will throw an error which is identified as Err.Number 5. Your program should be prepared to check the HRESULT or handle the exception, and indicate that the user's credentials are invalid.

**Examples**

[Visual Basic.NET]
```vbnet
uindex1 = LMX_Server.AuthenticateUser(hLMX, uidText, pwdText)
```
[C#]
```csharp
uindex1 = LMX_Server.AuthenticateUser(hLMX, uidText, pwdText);
```
[Visual C++]
```cpp
BSTR bstrUser = ::SysAllocString(szUserId);
BSTR bstrPwd = ::SysAllocString(szUserPwd);
hr = pLMX_Server->AuthenticateUser(hLMX, bstrUser, bstrPwd,
    &uindex1);
::SysFreeString(bstrPwd);
::SysFreeString(bstrUser);
```

# Using an Established User Authentication

When your program calls the AuthenticateUser() method, LMX authenticates the user name and password and gets a GUID from the ArchestrA Galaxy that is valid for the current session. To simplify use in MXAccess, the LMXProxyServer then places this GUID in an internal table and associates it with an integer ID "cookie" so that the interfaces do not need to pass a GUID as a parameter every time an operation such as the Write() method or WriteSecured() is invoked. In short, the GUID is obtained, but your program never sees it.

If your program is a component hosted in InTouch—for example, an ActiveX object hosted in an InTouch application window—the policy is for the InTouch HMI to handle the authentication process, using its own log-in dialog boxes. In particular, WindowViewer presents a log-in dialog box when:

*   An operator logs into the InTouch application.
*   A secured write is to be performed.
*   A verified write is to be performed.

WindowViewer keeps track of the GUIDs for these users. For your program to use these GUIDs to perform secured writes and verified writes, you must add them to the internal tables for the LMXProxyServer. Do this by calling the ArchestrAUserId() method, which passes the GUID to the LMXProxyServer, checks it for validity, and returns the corresponding ID cookie. You can then use this cookie with the other interface methods, such as the Write() and WriteSecured() method.

Note that this method is on the ILMXProxyServer2 interface, which extends the ILMXProxyServer interface.

## ArchestrAUserId() Method

Adds a GUID to the internal table and returns a user ID.

### Syntax

[Visual Basic.NET]

```vbnet
Function ArchestrAUserId(
    ByVal hLMXServerHandle As Integer,
    ByVal UserIdGuid As String
)
As Integer
```

Using an Established User Authentication [C#]

```csharp
int ArcestrAUserToId(
    int hLMXServerHandle,
    string UserIdGuid);
```

[Visual C++]

```cpp
HRESULT __stdcall ArcestrAUserToId(
    long hLMXServerHandle,
    BSTR UserIdGuid, long *UserId
);
```

**Parameters**

**hLMXServerHandle**
Handle of the connection.

**UserIdGuid**
String containing the GUID for the user ID.

**Returns**

User ID cookie, or zero if the user credentials are rejected.

**Remarks**

This method is used primarily in the context of a component hosted by the InTouch HMI. The InTouch HMI is written mostly in unmanaged code and therefore does not generally support hosting .NET visual components in an InTouch application window. However, an ActiveX component can be hosted in an InTouch window. Code samples in this Toolkit include an example in C++, implementing a simple ActiveX component that uses this interface.

Every time you call the ArcestrAUserToId() method with a valid GUID, the GUID is added to the internal table and a new ID is returned. If you add the same GUIDs over and over again, the table continues to grow and previous IDs returned for those same GUIDs can still be used to perform Write() and WriteSecured() operations.

To minimize duplicate entries, your program can maintain a map of GUIDs already submitted to the ArcestrAUserToId() method and their corresponding ID cookies and submit a GUID only if it has not already been used.

**Examples**

[Visual Basic.NET]

```vbnet
uindex1 = LMX_Server.ArcestrAUserToId(hLMX, uidGuid)
```

[C#]

```csharp
uindex1 = LMX_Server.ArcestrAUserToId(hLMX, uidGuid);
```

[Visual C++]

```c++
BSTR bstrGuid = ::SysAllocString(szUserIdGuid);
hr = pLMX_Server->ArchestrAUserToId(hLMX, bstrGuid, &uindex1);
::SysFreeString(bstrGuid);
```

# Obtaining User Authentication from InTouch

When a component hosted by the InTouch HMI requires a user log-in, as for a secured write or verified write, let the InTouch HMI handle the authentication using its own user log-in dialog boxes. There are two ways to get user GUIDs from the InTouch HMI:

*   Call the get_LoggedInUserGuid() method to retrieve the GUID for the user currently logged in, if any.
*   Call the LogInInTouchUser() method to show a confirmation log-in from the currently logged-in user (and, if appropriate, a verification user).

These are not methods of the MXAccess Toolkit, but are instead methods provided by the InTouch HMI itself, through the AppServerSecurity.dll.

To access the AppServerSecurity methods, you must import a reference to the AppServerSecurity.dll into your program.

[Visual C++]

*   In the file stdafx.h, add a line such as the following to import the dll:

```c++
#import "C:\Program Files\Wonderware\InTouch\AppServerSecurity.dll"
no_namespace, raw_interfaces_only
```
*   When you compile, Visual Studio creates a file AppServerSecurity.tlh. You can open this file to see the interface methods and other members provided by the AppServerSecurity interface.

In addition to the methods described above, the DLL includes the following enumerations for the LogInInTouchUser() method:

```c++
enum WriteType
{
    WriteType_Undefined = 0,
    WriteType_VerifiedWrite = 1,
    WriteType_SecuredWrite = 2
};
```

```c++
enum LoginDetailError {
    LoginDetailError_NoError = 0,
    LoginDetailError_SecuredLoginFailed = 1,
    LoginDetailError_VerifierLoginFailed = 2
};

To retrieve the GUID of the currently logged-in user, call the get_LoggedInUserGuid() method.

[Visual C++]

## Syntax

```c++
HRESULT get_LoggedInUserGuid (BSTR * loggedInUserId);
```

## Example

```c++
// get GUID from InTouch for user
// currently logged in, if any
CComPtr<IInTouchLogin> pInTouchLogin;
HRESULT hr = pInTouchLogin.CoCreateInstance(
    _uuidof( CurrentInTouchSecurity ));
if (SUCCEEDED(hr))
{
    CComBSTR bstrGUID;
    hr = pInTouchLogin->get_LoggedInUserGuid(&bstrGUID);
    if (SUCCEEDED(hr))
    {
        // add GUID to internal table
        if ((m_pPLMX_Server != NULL) && (m_hLMX != 0))
        {
            long tempIndex = 0;
            hr = pLMX_Server->ArchestrAUserToId(m_hLMX,
                bstrGUID, &tempIndex);
            if (SUCCEEDED(hr) && (tempIndex != 0))
            {
                // use tempIndex as new user index
                ...
            }
        }
    }
}
```

To invoke an InTouch log-in dialog and get GUIDs for the currently logged-in user (if any) and a verification user (if appropriate), call the LogInInTouchUser() method.

[Visual C++]

## Syntax

```c++
HRESULT LogInInTouchUser ( enum WriteType typeOfWrite,
    BSTR * userId,
    BSTR * verifierId,
    VARIANT_BOOL * success,
    enum LoginDetailError * detailCode);
```

## Example

```c++
// get GUID(s) from InTouch by invoking
// log-in dialog
CComPtr<IInTouchLogin> pInTouchLogin;
HRESULT hr = pInTouchLogin.CoCreateInstance(
    _uuidof( CurrentInTouchSecurity ));
if (SUCCEEDED(hr))
{
    WriteType typeOfWrite =
        WriteType_VerifiedWrite;
    CComBSTR bstrUserID;
    CComBSTR bstrVerifierID;
    VARIANT_BOOL varbSuccess = VARIANT_FALSE;
    LoginDetailError detailCode =
        LoginDetailError_NoError;
    // determine type of login required
    if (bNeedVerifiedWrite)
        typeOfWrite = WriteType_VerifiedWrite;
    else
        typeOfWrite = WriteType_SecuredWrite;
    // Ask InTouch to get user login(s) and
    // return GUIDs
    hr = pInTouchLogin->LogInInTouchUser(typeOfWrite,
        &bstrUserID,
        &bstrVerifierID, &varbSuccess, &detailCode);
    if (SUCCEEDED(hr))
    {
        // get status information
        bool bSuccess =
            (varbSuccess != VARIANT_FALSE);
        if (bSuccess)
        {
            // GUID(s) obtained
            // Add them to internal tables,
            // get corresponding ID(s)
            if ((pLMX_Server != NULL) && (hLMX != 0))
```

```c++
{
    long tempIndex1 = 0;
    long tempIndex2 = 0;
    hr =
        m_pLMX_Server->ArchestrAUserToId(m_hLMX,
            bstrUserID, &tempIndex1);
    if (SUCCEEDED(hr) &&
        (typeOfWrite == WriteType_VerifiedWrite))
    {
        hr =
            m_pLMX_Server->ArchestrAUserToId(m_hLMX,
                bstrVerifierID, &tempIndex2);
    }
    // now use tempIndex1,
    // tempIndex2 to update the IDs
    if (tempIndex1 != 0)
        // secured user
        uindex1 = tempIndex1;
    if (tempIndex2 != 0)
        // verifier user
        uindex2 = tempIndex2;
    }
}
else
{
    // handle error or indicate problem,
    // using returned detailCode for
    // further information
    ...
}
}
}

The new user ID "cookies" may now be used in the WriteSecured()
method.
```

# Handling the OnDataChange Callback

The LMXProxyInterface triggers an OnDataChange event when an update occurs to an ArcestrA Attribute that your program has on advise. The information your program receives contains the updated data values and status information. If appropriate, it may also provide error code information.

## OnDataChange Event

Reports the value and the status of an ArcestrA Attribute to which the program has subscribed.

### Syntax

**[Visual Basic.NET]**

```vbnet
Private Sub LMX_OnDataChange(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByVal pvItemValue As Object,
    ByVal pwItemQuality As Integer,
    ByVal pftItemTimeStamp As Object,
    ByRef ItemStatus() As ArcestrA.MxAccess.MXSTATUS_PROXY
)
```

```vbnet
Handles LMX_Server.OnDataChange
```

**[C#]**

```csharp
private void LMX_OnDataChange (
    int hLMXServerHandle,
    int phItemHandle,
    object pvItemValue,
    int pwItemQuality,
    object pftItemTimeStamp,
    ref ArcestrA.MxAccess.MXSTATUS_PROXY[] ItemStatus
);
```

**[Visual C++]**

```cpp
HRESULT CLMX_Events_Shim::OnDataChange (
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    VARIANT pvItemValue,
    long pwItemQuality,
    VARIANT pftItemTimeStamp,
    SAFEARRAY *pSAItemStatus
);
```

Handling the OnBufferedDataChange Callback **Parameters**

*   **hLMXServerHandle**
    *   Handle of the connection
*   **phItemHandle**
    *   Handle of the item
*   **pvItemValue**
    *   New data value
*   **pwItemQuality**
    *   New quality
*   **pftItemTimeStamp**
    *   New timestamp
*   **ItemStatus**
    *   Array of Message Exchange statuses

**Returns**

Nothing.

**Remarks**

For more information, including the function syntax for the different languages, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.

# Handling the OnBufferedDataChange Callback

You can configure your client program to subscribe to buffered attributes. Your client program can also be configured to receive data change events for those attributes.

When LMXProxy passes buffered data back to the client application, it builds three parallel SafeArray Variants, one each for:

*   Value (V)
*   Timestamp (T)
*   Quality (Q)

# OnBufferDataChange Event

Reports the VTQ buffer and the status of an ArchestrA Attribute to which the program has subscribed.

## Syntax

**[Visual Basic.NET]**

```vb
Sub LMX_OnBufferDataChange(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByVal dtDataType As ArchestrA.MxAccess.MxDataType,
    ByVal pvItemValue As Object,
    ByVal pwItemQuality As Object,
    ByVal pftItemTimeStamp As Object,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
    Handles LMX_Server.OnBufferDataChange
```

**[C#]**

```csharp
LMX_OnBufferDataChange(
    int hLMXServerHandle,
    int phItemHandle,
    ArchestrA.MxAccess.MxDataType dtDataType,
    object pvItemValue, object pvItemQuality,
    object pvItemTimeStamp,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[] ItemStatus)
```

**[Visual C++]**

```cpp
HRESULT OnBufferDataChange (
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    MxDataType dtDataType,
    VARIANT pvItemValue,
    VARIANT pwItemQuality,
    VARIANT pftItemTimeStamp,
    SAFEARRAY *pSAItemStatus)
```

## Parameters

**hLMXServerHandle**
Handle of the connection.

**phItemHandle**
Handle of the item.

**dtDataType**
Data type of the buffered data.

**pvItemValue**
New data value.

Determining Data Change Status *   *pwItemQuality*
    New quality.
*   *pftItemTimeStamp*
    New timestamp.
*   *ItemStatus*
    Array of Message Exchange statuses.

**Returns**

Nothing.

**Remarks**

You must note that when your client program attempts a buffered subscription to an attribute that does not have buffering enabled, a data change event occurs. This data change event message will have an MXStatus category of **MXCategoryConfigurationError** and will have a detail code of **MX_E_InvalidPropertyId**.

For more information, including the function syntax for the different languages, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.

# Determining Data Change Status

If the data change notification indicates success, then you should process the received data. Otherwise, you should notify the user or log the error, as is appropriate for your application.

[Visual Basic.NET]

```vbnet
Dim sErrorStatus As String

If ItemStatus(0).success Then
    ' process data, update displays, etc.
Else
    sErrorStatus = "Data Change Error - C:" & _
        ItemStatus(0).category & " S:" & ItemStatus(0).detectedBy & _
        " D:" & ItemStatus(0).detail
End If
```

[C#]

```csharp
if (ItemStatus[0].success != 0) {
    // process data, update displays, etc.
} else {
    string sErrorStatus;
    sErrorStatus = "Data Change Error - C:" + 
        ItemStatus[0].category + " S:" + ItemStatus[0].detectedBy + 
        " D:" + ItemStatus[0].detail;
}
```

[Visual C++]

MXSTATUS_PROXY *ItemStatus;

hr = SafeArrayAccessData(pSAItemStatus, (void HUGEP* FAR*)&ItemStatus);

if (ItemStatus[0].success != 0) {
    // process data, update displays, etc.
} else {
    TCHAR sErrorStatus [256];
    swprintf_s (sErrorStatus,
    sizeof(sErrorStatus)/sizeof(TCHAR), L"OnDataChange w/error - cat: %d Src: %d detail: %d", ItemStatus[0].category, ItemStatus[0].detectedBy, ItemStatus[0].detail);
}

hr = SafeArrayUnaccessData(pSAItemStatus);

# Displaying Data Quality and Time

The new data value has an associated Quality, which is an integer, and represents whether the data is good, bad, pending, and initializing. The new data value also has an associated timestamp, which represents when the data was updated. These properties can be easily displayed in .NET. For displaying the Time in Visual C++, refer to the section below on Retrieving Data Types in Visual C++.

## Syntax

[Visual Basic.NET]

```vbnet
Private Sub LMX_OnDataChange(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByVal pvItemValue As Object,
    ByVal pwItemQuality As Integer,
    ByVal pftItemTimeStamp As Object,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
Handles LMX_Server.OnDataChange
```

## Example

```vbnet
Quality.Text = pwItemQuality
Time.Text = pftItemTimeStamp
```

Displaying Data Quality and Time [C#]

```csharp
private void LMX_OnDataChange(
    int hLMXServerHandle,
    int phItemHandle,
    object pvItemValue,
    int pwItemQuality,
    object pftItemTimeStamp,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
```

**Example**

```csharp
Quality.Text = pwItemQuality.ToString();
Time.Text = pftItemTimeStamp.ToString();
```

[Visual C++]

```cpp
HRESULT CLMX_Events_Shim::OnDataChange (
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    VARIANT pvItemValue,
    long pwItemQuality,
    VARIANT pftItemTimeStamp,
    SAFEARRAY *pSAIItemStatus
);
```

**Example**

```cpp
swprintf_s (strDisplayedText, TotalLen, L"%d", pwItemQuality);
SetDlgItemText (hMainDlg, IDC_Quality, strDisplayedText);
Variant_to_String (&pftItemTimeStamp, strDisplayedText,
sizeof(strDisplayedText)/sizeof(TCHAR));
SetDlgItemText (hMainDlg, IDC_Time, strDisplayedText);
```

[ActiveX]

**Example**

```cpp
swprintf_s (strDisplayedText, TotalLen, L"%d", pwItemQuality);
m_pMxAccessAxDlg->SetDlgItemText(IDC_Quality,
strDisplayedText);
Variant_to_String (&pftItemTimeStamp, strDisplayedText,
sizeof(strDisplayedText)/sizeof(TCHAR));
m_pMxAccessAxDlg->SetDlgItemText(IDC_Time, strDisplayedText);
```

# Determining Data Type

Because the new data value is encapsulated as an object (or in Visual C++ as a VARIANT), it is necessary to cast the new data value to the appropriate data type to process it. If the data is an array of values, you must un-package the object and treat it as an array of the appropriate type. This is easily handled in .NET, but requires more effort in Visual C++.

[Visual Basic.NET]

```vbnet
Dim objectType As Type
Dim count As Integer

' check whether data is array or single item
objectType = pvItemValue.GetType()

If Not objectType.IsArray Then
    ' received data is a single entry, handle it
    ' do something with pvItemValue
    ...
Else
    ' received data is an array,
    ' handle all the values
    For count = 0 To UBound(pvItemValue, 1) Step 1
        ' do something with pvItemValue(count)
        ...
    Next count
End If
```

[C#]

```csharp
Type objectType;
int count;

objectType = pvItemValue.GetType();

if (!objectType.IsArray) {
    // received data is a single entry, handle it
    // do something with pvItemValue
    ...
}
```

Determining Data Type ```csharp
} else {
    // received data is an array, re-cast data type
    // and handle all the values
    Array pvArray = (Array) pvItemValue;
    for (count = pvArray.GetLowerBound(0);
        count < pvArray.GetUpperBound(0);
        count++) {
        // do something with pvArray[count]
        ...
    }
}
```

[Visual C++]

```c
VARTYPE Variant_Type;

long count;

Variant_Type = pvItemValue.vt;

if ((Variant_Type & VT_ARRAY) == 0) {
    // received data is a single entry, handle it
    // do something with pvItemValue
    ...
} else {
    // received data is an array,
    // re-cast data type and handle all the values
    Variant_Type = (VARTYPE) (Variant_Type ^ VT_ARRAY);
    SAFEARRAY *pvArray = pvItemValue.parray;
    SAFEARRAYBOUND* pSABound = pvArray->rgsabound;
    long LowerBound = pSABound->lbound;
    long UpperBound = pSABound->cElements + LowerBound;
    for (count = LowerBound; count < UpperBound; count++) {
        // do something with pvArray at index count
        // see example implementation below
        Process_SafeArrayElement (pvArray, Variant_Type, count,
            ...);
    }
}
```

For more information about implementing handling in the `Process_SafeArrayElement()` function, see "Retrieving Data Types in Visual C++" on page 70.

When you have the new data value in the appropriate form, you can use it and the array of Message Exchange statuses to update the values that are tracked by your program. What your program does with status information up to you—whether to update status displays, perform error handling, and so on.

# Retrieving Data Types in Visual C++

The most reliable way to retrieve data from a SAFEARRAY of values is to use the SafeArrayGetElement() method. However, this method call must be made with a data variable of the appropriate type, so it can be processed correctly. For an array of BSTR, it is slightly more efficient to access the entire array and reference the individual strings by index.

There are several ways to handle the DATE data type. The following are two simple approaches to coerce the value to a string:

* Use the VariantChangeType() function to convert the DATE to a BSTR.
* Use the VariantTimeToDosDateTime() function to convert the DATE to a DOS date and a DOS time, which can then be formatted into a string.

The following code example shows how to extract entries from the SAFEARRAY. This function is included in the sample code for C++.

```c++
void SafeArrayElement_to_String(SAFEARRAY *pvArray, VARTYPE datatype, long count, LPTSTR szValue, size_t BufLen)
{
    switch (datatype)
    {
    case VT_BOOL :
    {
        VARIANT varBoolItem;
        VariantInit(&varBoolItem);
        SafeArrayGetElement(pvArray, &count, &varBoolItem);
        swprintf_s(szValue, BufLen, L"%s",
            (varBoolItem.boolVal ? L"true" : L"false"));
    }
    break;
    case VT_I4 :
    {
        long iLongElement = 0;
        SafeArrayGetElement(pvArray, &count, &iLongElement);
        swprintf_s(szValue, BufLen, L"%ld", iLongElement);
    }
    break;
    case VT_R8 :
    {
        double iDoubleElement = 0.0;
        SafeArrayGetElement(pvArray, &count,
            &iDoubleElement);
        swprintf_s(szValue, BufLen, L"%f", iDoubleElement);
    }
    break;
    }
}
```

Retrieving Data Types in Visual C++ ```c++
case VT_R4 :
{
    float iFloatElement = 0.0;
    SafeArrayGetElement (pvArray, &count, &iFloatElement);
    swprintf_s (szValue, BufLen, L"%f", iFloatElement);
}
break;
case VT_DATE :
{
    DATE dtElement;
    SafeArrayGetElement (pvArray, &count, &dtElement);
    if (bDisplayDateUsingVariant)
    {
        // One way to display is to let
        // the VARIANT mechanism handle it
        VARIANT varDate;
        VariantInit (&varDate);
        varDate.vt = VT_DATE;
        varDate.date = dtElement;
        HRESULT hr = VariantChangeType (&varDate, &varDate,
            0, VT_BSTR);
        if (SUCCEEDED(hr))
        {
            wscpy_s (szValue, BufLen, varDate.bstrVal);
            ::SysFreeString(varDate.bstrVal);
        } else {
            wcscpy_s (szValue, BufLen, L"????");
        }
    }
    else
    {
        // Another way is to extract the
        // date/time fields explicitly
        // note that a DOS time is only
        // accurate to a resolution of
        // 2 seconds
        unsigned short wDosDate;
        unsigned short wDosTime;
        if (VariantTimeToDosDateTime (dtElement, &wDosDate,
            &wDosTime))
        {
            int da = wDosDate & 0x001F;
            int mo = (wDosDate & 0x01E0) >> 5;
            int yr = ((wDosDate & 0xFE00) >> 9) + 1980;
            int sc = (wDosTime & 0x001F) << 1;
            int mn = (wDosTime & 0x07E0) >> 5;
            int hr = (wDosTime & 0xF800) >> 11;
            swprintf_s (szValue, BufLen,
                L"%02d/%02d/%04d %02d:%02d",
                mo, da, yr, hr, mn, sc);
        }
    }
```

```c
else
{
    wcscpy_s (szValue, BufLen, L"????");
}
}
break;
case VT_BSTR :
{
    BSTR HUGE *pbstr;
    HRESULT hr = SafeArrayAccessData(pvArray, (void
HUGE**) &pbstr);
    swprintf_s (szValue, BufLen, L"%s", pbstr[count]);
    hr = SafeArrayUnaccessData(pvArray);
}
break;
default :
{
    wcscpy_s (szValue, BufLen, L"????");
}
break;
} // switch //
}
```

# Handling the OnWriteComplete Callback

The LMXProxyInterface triggers an event for OnWriteComplete when your program calls the Write() or WriteSecured() function. The information your program receives identifies whether the write was successful and any error code information that may be appropriate.

## OnWriteComplete Event

Reports the status when a write operation has been completed, indicating such information as whether it succeeded or failed.

### Syntax

[Visual Basic.NET]

```vb
Private Sub LMX_OnWriteComplete(
    ByVal hLMXServerHandle As Integer,
    ByVal phItemHandle As Integer,
    ByRef ItemStatus() As ArchestrA.MxAccess.MXSTATUS_PROXY
)
    Handles LMX_Server.OnWriteComplete
```

Handling the OnWriteComplete Callback [C#]

```csharp
private void LMX_OnWriteComplete(
    int hLMXServerHandle,
    int phItemHandle,
    ref ArchestrA.MxAccess.MXSTATUS_PROXY[ ] ItemStatus
);
```

[Visual C++]

```cpp
HRESULT CLMX_Events_Shim::OnWriteComplete(
    long hLMXServerHandle,
    ITEMHANDLE phItemHandle,
    SAFEARRAY *pSAWriteItemStatus
);
```

where CLMX_Events_Shim is a class derived from the base class LMXProxyServerEvents.

## Parameters

**hLMXServerHandle**
Handle of the connection.

**phItemHandle**
Handle of the item.

**ItemStatus**
Array of Message Exchange statuses.

## Returns

Nothing

## Remarks

For more information, including the function syntax for the different languages, see "Connecting Handlers for Events Reported from the LMXProxyServer" on page 19.

Interpreting the write status is very much like interpreting the data change status and involves examining the contents of the MxStatus structure.

*   If the success member of the status is true, the write operation was accepted, but it may not be complete. Check the category member of the status to determine whether the write is still pending.
*   If the success member is false, check the other structure members for additional information about why the write operation failed. It could be that the operation requires a secured or verified write, instead of an "ordinary" write.

You can check the Message Exchange statuses to determine whether the write was successful and to examine any error codes that have been returned. What your program does with the status information up to you—whether to update status displays, perform error handling, and so on.

Any attempt to write to a buffered item will result in a OnWriteComplete status event returning an MxStatus with an MxCategoryOperationalError category and an detail code of MX_E_NotWriteable.

## Examples

[Visual Basic.NET]

```vbnet
If ItemStatus(0).success Then
    If ItemStatus(0).category = ArchestrA.MxAccess.MxStatusCategory.MxCategoryPending Then
        WriteStatus.Text = "Write Pending..."
    Else
        WriteStatus.Text = "Write Complete - status OK"
    End If
Else
    If ItemStatus(0).detail = MX_E_SecuredWrite Then
        ' 1012, secured write
        ' either re-try as a secured write
        ' or generate an error message
        ...
    Else
        If ItemStatus(0).detail = MX_E_VerifiedWrite Then
            ' 1013, verified write
            ' either re-try as a verified write
            ' or show an error message
            ...
        Else
            ' some other kind of error,
            ' display information
            WriteStatus.Text = "Write Complete w/error - cat: " &
                ItemStatus(0).category & " Src: " &
                ItemStatus(0).detectedBy & " detail: " &
                ItemStatus(0).detail
        End If
    End If
End If
```

Handling the OnWriteComplete Callback [C#]

```csharp
if (ItemStatus[0].success != 0) {
    if (ItemStatus[0].category == ArchestrA.MxAccess.MxStatusCategory.MxCatgeoryPending) {
        WriteStatus.Text = "Write Pending...";
    } else {
        WriteStatus.Text = "Write Complete - status OK";
    }
} else {
    if (ItemStatus[0].detail == MX_E_SecuredWrite) {
        // 1012, secured write
        // either re-try as a secured write
        // or generate an error message
        ...
    } else {
        if (ItemStatus[0].detail == MX_E_VerifiedWrite_val) {
            // 1013, verified write
            // either re-try as a verified write
            // or show an error message
        } else {
            // some other kind of error,
            // display information
            PokeValue.Text =
                "Write Complete w/error - cat: " +
                ItemStatus[0].category + " Src: " +
                ItemStatus[0].detectedBy +
                " detail: " + ItemStatus[0].detail;
        }
    }
}
```

[Visual C++] 

```cpp
if (ItemStatus[0].success != 0) {
    if (ItemStatus[0].category == MxCatgeoryPending) {
        SetDlgItemText(hMainDlg, IDC_PokeValue,
            L"Write Pending...");
    } else {
        SetDlgItemText(hMainDlg, IDC_PokeValue,
            L"Write Complete - status OK");
    }
}

```c
} else {
    if (ItemStatus[0].detail == MX_E_SecuredWrite) {
        // 1012, secured write
        // either re-try as a secured write
        // or generate an error message
        ...
    } else {
        if (ItemStatus[0].detail == MX_E_VerifiedWrite) {
            // 1013, verified write
            // either re-try as a verified write
            // or show an error message
            ...
        } else {
            // some other kind of error,
            // display information
            TCHAR error_text[256];
            swprintf_s (error_text,
                sizeof(error_text)/sizeof(TCHAR),
                L"Write Complete w/error - cat: "
                L"%d Src: %d" "detail: %d",
                ItemStatus[0].category, ItemStatus[0].detectedBy,
                ItemStatus[0].detail);
            SetDlgItemText (hMainDlg, IDC_WriteStatus,
                error_text);
        }
    }
}
```

# Setting Update Intervals for Buffered Attributes

LMXProxy will also allow your client program to configure the rate at which buffered data change updates are received. If you do not set this rate, the buffered data update events are sent to the client program once per second.

## Syntax

**[Visual Basic.NET]**

```vb
Sub SetBufferedUpdateInterval (
    ByVal hLMXServerHandle As Integer,
    ByVal lUpdateInterval As Integer)
```

**[C#]**

```csharp
void SetBufferedUpdateInterval (
    int hLMXServerHandle,
    int lUpdateInterval)
```

Setting Update Intervals for Buffered Attributes [Visual C++]

```c++
HRESULT __stdcall SetBufferedUpdateInterval(
    long hLMXServerHandle,
    long lUpdateInterval)
```

**Parameters**

*   **hLMXServerHandle**
    *   Handle of the connection

*   **lUpdateInterval**
    *   Specifies the update interval in milliseconds

**Examples**

[Visual Basic.NET]

```vb
LMX_Server.SetBufferedUpdateInterval(hLMX, Interval)
```

[C#]

```csharp
LMX_Server.SetBufferedUpdateInterval(hLMX, Interval);
```

[Visual C++]

```c++
m_pServerCMInterface->SetBufferedUpdateInterval(hLMXServerHandle, 3000);
```

**Remarks**

If you set a **Negative** or **Zero (0)** value, it will return an error with value E-INVALIDARG HResult.

If you set a **Positive** value, the value will be rounded to the next modulo 100 value. For example, a value of 1-100 will be 100, and 101 will be set to 200 and so on.

# Chapter 3: Using Code Samples

Code samples support Microsoft Visual Studio 2012 or later. Code samples run on Application Server 2014 or later version nodes that have a Platform deployed. For information about running the samples on a node with an earlier version of Application Server (for example, 2.1 Patch 02), see "Registering the Interface Files" on page 10.

To build the sample projects VcppMxAccessActiveX and VcppMxSample on a 64-bit operating system, you must modify the import statement to include (x86) in the file path.

**To build the sample projects on a 64-bit operating system**

1. Modify the import statement in stdafx.h.as shown in the following example:

```c
#import "C:\Program Files (x86)\ArchestrA\Framework\Bin\MxAccess32.tlb" no_namespace, raw_interfaces_only
```

2. Modify the import path in stdafx.h to match the InTouch installation directory as shown in the following example:

```c
#import "C:\Program Files (x86)\Wonderware\InTouch\AppServerSecurity.dll" no_namespace, raw_interfaces_only
```

# To implement the basic program

The examples for a stand-alone program are in Visual Basic, C#, and Visual C++. All three examples implement the basic program.

<img>MxAccess C# Example window showing various buttons and text fields for application registration, item functions, item value, poke item, security, operation status, communication management functions, and buffered data interval management.</img>

The code examples show how to connect to Message Exchange, access ArchestrA data, and shut down. The text fields and buttons allow you to select an attribute, add a reference for it, put it on advise, receive data updates, and write to the attribute. It also supports logging on under one or two names, to demonstrate secured writes and verified writes.

For information about setting up a simple ArchestrA Galaxy configuration for running the sample application, see "Setting Up a Simple Galaxy Configuration for the Sample Applications" on page 86.

This is a very simple demonstration program and does not keep track of adding and advising multiple items, even though the LMXProxy is capable of doing so. If you enter a new item name and add or advise that item, the program unadvises and removes the present item, if any.

The sample program has a few safeguards in place to demonstrate the possibility of including such safeguards in your own code:

*   If you click **Add Item** before clicking **Register**, the program registers for you.
*   If you click **Advise** before clicking **Add Item**, the program adds the item and registers it, if necessary.
*   If you click **Remove Item**, the program checks whether there are any items on advise. If so, it unadvises them for you.
*   If you click **Unregister**, the program checks whether there are any items on advise. If so, it unadvises them for you. It then checks whether there are any items in the reference table. If so, it removes those items for you and then unregisters.

**To access data in an ArchestrA Galaxy**

1.  Start the program.
2.  In the **Application Level Functions** area, click **Register** to connect to LMX Message Exchange.
3.  In the **Item Functions** area, do the following:
    *   a Type the item name.
    *   b Click **AddItem** to add the item to the reference tables.
    *   c If you wish use AdviseSupervisory, check the box marked **Supervisory Connection**. If you wish to use Advise, clear the box.
    *   d Click **Advise** to put the item on advise. Updates for the item appear. If item is not on advise, you do not receive data updates.

4 In the **Poke into item** area, do the following:
a Type the value in the first text box.
b If you are poking to only a single-valued attribute, leave the remaining text boxes blank. If you are poking to an array, type values for the array in the two remaining text boxes.
c If you wish to write the timestamp as well as the value, type the desired timestamp in the **Time** text box. Otherwise, leave the **Time** text box blank.
d Click **Poke** to write the value.

Note: Success or failure is shown in the first text box. If the attribute requires a secured or verified write, the program attempts it automatically. It succeeds only if you have logged on under one or more account names.

5 In the **Security** area, do the following:
a Type the name and password for **User1** and **User2**. If you are logging on to one account, use the **User1** and **Pwd1** text boxes. If you are logging on to two accounts, use **User2** and **Pwd2** text boxes.
b Click **Register UserIds** to log on. After logging on, the program succeeds in secured writes and verified writes, if the accounts have the appropriate credentials for the selected item.

6 In the **Communication Management Functions** area, do the following:
a Click **Suspend** to pause data updates.
b Click **Activate** to resume updates.

7 In the **Items Function** area, do the following:
a Click **Unadvise** to take the item off advise. You no longer receive data updates.
b Click **RemoveItem** to remove the item from the reference tables.

8 In the **Applications Level Functions** area, click **Unregister** to disconnect from LMX Message Exchange.

9 Shut down the program.

Using an ActiveX Code Sample # Using an ActiveX Code Sample

This example of ActiveX code demonstrates the ArchestrAUserToId() method. Use this method in applications, such as an InTouch application, where the user log on is done outside your component. The program is similar to the other code samples, although there are some minor differences.

<img>MxAccess ActiveX Sample window with various functions and fields including Application Level Functions (Register, Unregister), Item Functions (Advise, UserDefined_001.Attr1, Unadvise), Item (Value 1000, Quality 192, Time 8/6/2008 4:28:59.696 PM, Status), Poke into item (Value 1000, Time 8/6/2008 4:28:59.696 PM, Status Write Complete - status OK), Communication Management Functions (Suspend, Activate, Status Operation Complete - status OK), GUID of Logged-in User (Get GUID, ID {84F0B924-B158-4777-BEFD-A4BE10CC774A}), Security (Secured Write, Verified Write, Log In), IDs (IDs 1, 2, 3), Status SUCCESS, Detail OK.</img>

*   The edit fields for PokeValue1 and PokeValue2 are omitted to leave more room for other buttons and display fields. Although the control shows array attributes, it does not support writing an array of new values. See the other code examples for an implementation of writing an array.
*   Buttons and edit fields have been added for retrieving and displaying the GUIDs and corresponding ID "cookies".

The ActiveX control must be registered on your computer and then registered with WindowMaker. Create a simple InTouch application with a single window and place an instance of the ActiveX control on the window. Configure the application for ArchestrA security. Configure WindowViewer so the application window is the start-up window for the application.

**To access data in an ArchestrA Galaxy using ActiveX**

1. Start WindowViewer.
2. In the **Application Level Functions** area, click **Register** to connect to LMX Message Exchange.
3. In the **Item Functions** area, do the following:
    a. Type the item name.
    b. Click **Advise** to put the item on advise. Updates for the item appear.
4. In the **Poke into item** area, do the following:
    a. Type the value in the **Value** text box.
    b. If you wish to write the timestamp as well as the value, type the desired timestamp in the **Time** text box. Otherwise, leave the **Time** text box blank.
    c. Click **Poke** to write the value. Results appear in the **Status** box.
    If the Galaxy is secured, and you have not obtained the ID for the logged on user, the write should fail. If the attribute requires a secured write or a verified write, and the program has not yet obtained the required IDs (of the logged on user and of a verified user, if needed), then the write fails.
5. In the **GUID of Logged in User** area, click **Get GUID**.
    If the user has not logged on, zeros appear for the GUID. The ID "cookie" should also be zero. In the InTouch application, click **Log-in** on the **Security** menu to log on, and then click **Get GUID**. The display shows the GUID of the logged on user and a non-zero ID "cookie."
6. In the **Poke into item** area, click **Poke**. Attributes that require only an ordinary write, or a secured write succeed.
7. In the **Item Functions** area, click **Unadvise** and edit the item name to select an attribute that requires a secured write or a verified write. Then, click **Advise** to put the new item on advise.

Using an ActiveX Code Sample **8** In the **Security** area, click **Log In** to show an InTouch log on dialog box.

*   If **Secured Write** is selected, a confirmation dialog box just for the logged on user is shown. If **Verified Write** is selected, a dialog box is shown that prompts the logged on user to confirm the password and asks the verifying user to enter a name and password.
*   The display should show the GUID of the user(s) and corresponding ID "cookies," along with the status and detail information about the log on.
*   If the user password is incorrect, the function indicates it failed to obtain a GUID.
*   If **Verified Write** is selected and the logged on user password is incorrect, the function indicates it failed to obtain the GUIDs, even if the verifying user logon is correct.
*   If **Verified Write** is selected and the verifying user logon is incorrect, the function indicates it failed to obtain the GUIDs, even if the logged-in user's password is correct.
*   If the proper GUIDs have been obtained, secured and verified writes should now work.

**9** In the **Communication Management Functions** area, do the following:
    a Click **Suspend** to pause data updates.
    b Click **Activate** to resume updates.

**10** In the **Items Function** area, click **Unadvise** to take the item off advise. You no longer receive data updates.

**11** In the **Applications Level Functions** area, click **Unregister** to disconnect from LMX Message Exchange.

This sample program has the same safeguards in place as the other examples to ensure that the Register operation is invoked before attempting any other operations and to ensure that a proper shut down takes place, even if the user does not click **Unregister** before terminating WindowViewer.

# Setting Up a Simple Galaxy Configuration for the Sample Applications

You must have an ArchestrA Galaxy and a MXAccess runtime license to run the sample applications. For most operations you can use a Galaxy with no security. You can register, add items, put them on advise, see the updates, and write to them.

If you already have a Galaxy set up, you may want to simply run the program and enter the name of an attribute of some object that has been created and deployed.

## Setting up Attributes

You can establish a simple set of attributes that can be referenced in the sample application dialog box and will update automatically. If no security is enabled, you can simply start up the sample application and even write to the selected attributes.

**Note:** To advise an entire array, the reference should be of the form "ud1.ar[ ]".

### To set up a simple set of attributes

1. Start the ArchestrA IDE.
2. In the **Deployment** window, do the following:
    a. Create an instance of a WinPlatform.
    b. Create an AppEngine and assign it to the WinPlatform.
    c. Create an Area and assign it to the AppEngine.
    d. Create an instance of $UserDefined, give it the name "ud1", and assign it to the Area.
    e. Double click ud1 to start the editor.
3. On the **UDAs** tab, add the following attributes and make them all user writable:
    * x, of data type Integer
    * ar, of data type Integer, an array of 3 elements
    * dt, of data type Time
    * ardt, of data type Time, an array of 3 elements

Setting Up a Simple Galaxy Configuration for the Sample Applications 4 On the **Scripts** tab, create the following script s1:
   Execution type: execute
   Expression: 1
   Trigger type: While true
   Trigger period and Deadband 0
   Statements
   * me.x = me.x + 1;
   * me.ar[1] = me.ar[1] + 1;
   * me.ar[2] = me.ar[2] + 2;
   * me.ar[3] = me.ar[3] + 3;

5 Save and close the editor.

6 Deploy the WinPlatform and all objects underneath.

# Setting up a Simple Security Configuration

To exercise secured writes and verified writes, you must configure the Galaxy for security and have at least two users that can be configured to log in.

## To set up a simple security configuration

1 Start ArchestrA IDE, and select **Galaxy**, and then **Configure** and **Security**. The **Configure Security** dialog box appears.

2 Click the **Authentication Mode** tab and review the settings.
If the current security mode is "none" you must enable security. For the purposes of this exercise, select Galaxy security. If the security mode changes, you must shut down the Galaxy, open the IDE again and log on. A recommended log on in this case is as Administrator. See the ArchestrA documentation for more information.

3 Click the **Roles** tab, and then do the following:
   a Select the **Default** role, and then clear all the permissions.
   b Create the **Writers** role. Check all the **General** permissions and all the **Operational** permissions.
   c Create the **NonWriters** role. Check all the **General** permissions and all the **Operational** permissions, except for the **Can modify Operate attributes**. Leave that box unchecked.

4 Click the **Users** tab, then do the following:
a Add user **Writer1** with Default and Writers roles.
b Add user **Writer2** with Default and Writers roles.
c Add user **NonWriter** with Default and NonWriters roles.

5 Create a unique password for each user.

With security in place, you can put an attribute on advise and see updates, without logging on. But to write to an attribute, even one with simple Operate security, you must log on as Writer1 or Writer2. If you are logged on as NonWriter, an attempt to write produces an error message with error code 1008.

To exercise the secured write and verified write operations, you can edit ud1 and add two more attributes.

**To add more attributes**

1 Click the **UDAs** tab.
    a Add y, of data type Integer, with security mode of Secured Write.
    b Add z, of data type Integer, with security mode of Verified Write.

2 Click the **Scripts** tab, and then add the following lines to the existing script s1:
    ```
    me.y = me.y + 1;
    me.z = me.z + 1;
    ```

3 Save and close the editor.

4 Undeploy and re-deploy ud1.

This adds ud1.y and ud1.z as attributes that you can advise and write, using the appropriate secured or verified write options.

The sample application does a Write() first to obtain a status using the OnWriteComplete() event and from that status determines whether the attribute requires a secured write or a verified write, then attempts the appropriate call to WriteSecured() - or reports an error status. If you are running the application in debug mode, you can set breakpoints inside the OnDataChange() event handler to trace through the code and see this process in action.

Setting Up a Simple Galaxy Configuration for the Sample Applications # Example Security for the Role of Writers

The following dialog box shows how you can configure security for the role of writers.

<img>Screenshot of the "Configure Security" dialog box showing roles and permissions configuration.</img>

# Example Security for the Role of NonWriters

The following dialog box shows how you can configure security for the role of non-writers.

<img>Screenshot of the "Configure Security" dialog box showing roles and permissions configuration.</img>

# Appendix A: Status and Error Codes

MXAccess status and error codes report on the health of the application.

The status and error codes are presented in one of four types:

*   MxStatusDetail Values
*   MxStatusCategory Values
*   MxStatusSource Values
*   ResolutionStatus Values

An MxStatus is actually a structure, which contains four data fields:

```c
typedef struct MxStatus {
    VARIANT_BOOL success;
    MxStatusCategory category;
    MxStatusSource detectedBy;
    short detail;
} MxStatus;
```

## MxStatusDetail Values

The MxStatusDetail values are shown in the following table:

<table>
  <tr>
    <th>Value</th>
    <th>Status</th>
  </tr>
  <tr>
    <td>0</td>
    <td>MX_S_Success</td>
  </tr>
  <tr>
    <td>1</td>
    <td>MX_E_RequestTimedOut</td>
  </tr>
  <tr>
    <td>2</td>
    <td>MX_E_PlatformCommunicationError</td>
  </tr>
  <tr>
    <td>3</td>
    <td>MX_E_InvalidPlatformId</td>
  </tr>
  <tr>
    <td>4</td>
    <td>MX_E_InvalidEngineId</td>
  </tr>
  <tr>
    <td>5</td>
    <td>MX_E_EngineCommunicationError</td>
  </tr>
  <tr>
    <td>6</td>
    <td>MX_E_InvalidReference</td>
  </tr>
  <tr>
    <td>7</td>
    <td>MX_E_NoGalaxyRepository</td>
  </tr>
  <tr>
    <td>8</td>
    <td>MX_E_InvalidObjectId</td>
  </tr>
  <tr>
    <td>9</td>
    <td>MX_E_ObjectSignatureMismatch</td>
  </tr>
  <tr>
    <td>10</td>
    <td>MX_E_AttributeSignatureMismatch</td>
  </tr>
  <tr>
    <td>11</td>
    <td>MX_E_ResolvingAttribute</td>
  </tr>
  <tr>
    <td>12</td>
    <td>MX_E_ResolvingObject</td>
  </tr>
  <tr>
    <td>13</td>
    <td>MX_E_WrongDataType</td>
  </tr>
  <tr>
    <td>14</td>
    <td>MX_E_WrongNumberOfDimensions</td>
  </tr>
  <tr>
    <td>15</td>
    <td>MX_E_InvalidIndex</td>
  </tr>
  <tr>
    <td>16</td>
    <td>MX_E_IndexOutOfOrder</td>
  </tr>
  <tr>
    <td>17</td>
    <td>MX_E_DimensionDoesNotExist</td>
  </tr>
  <tr>
    <td>18</td>
    <td>MX_E_ConversionNotSupported</td>
  </tr>
  <tr>
    <td>19</td>
    <td>MX_E_UnableToConvertString</td>
  </tr>
  <tr>
    <td>20</td>
    <td>MX_E_Overflow</td>
  </tr>
  <tr>
    <td>21</td>
    <td>MX_E_NmxVersionMismatch</td>
  </tr>
  <tr>
    <td>22</td>
    <td>MX_E_NmxInvalidCommand</td>
  </tr>
  <tr>
    <td>23</td>
    <td>MX_E_LmxVersionMismatch</td>
  </tr>
  <tr>
    <td>24</td>
    <td>MX_E_LmxInvalidCommand</td>
  </tr>
  <tr>
    <td>25</td>
    <td>MX_E_GalaxyRepositoryBusy</td>
  </tr>
  <tr>
    <td>26</td>
    <td>MX_E_EngineOverloaded</td>
  </tr>
  <tr>
    <td>1000</td>
    <td>MX_E_InvalidPrimitiveId</td>
  </tr>
  <tr>
    <td>1001</td>
    <td>MX_E_InvalidAttributeId</td>
  </tr>
  <tr>
    <td>1002</td>
    <td>MX_E_InvalidPropertyId</td>
  </tr>
  <tr>
    <td>1003</td>
    <td>MX_E_IndexOutOfRange</td>
  </tr>
  <tr>
    <td>1004</td>
    <td>MX_E_DataOutOfRange</td>
  </tr>
  <tr>
    <td>1005</td>
    <td>MX_E_IncorrectDataType</td>
  </tr>
</table>

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1006</td>
      <td>MX_E_NotReadable</td>
    </tr>
    <tr>
      <td>1007</td>
      <td>MX_E_NotWriteable</td>
    </tr>
    <tr>
      <td>1008</td>
      <td>MX_E_WriteAccessDenied</td>
    </tr>
    <tr>
      <td>1009</td>
      <td>MX_E_UnknownError</td>
    </tr>
    <tr>
      <td>1010</td>
      <td>MX_E_ObjectInitializing</td>
    </tr>
    <tr>
      <td>1011</td>
      <td>MX_E_EngineInitializing</td>
    </tr>
    <tr>
      <td>1012</td>
      <td>MX_E_SecuredWrite</td>
    </tr>
    <tr>
      <td>1013</td>
      <td>MX_E_VerifiedWrite</td>
    </tr>
    <tr>
      <td>1014</td>
      <td>MX_E_NoAlarmAckPrivilege</td>
    </tr>
    <tr>
      <td>8000</td>
      <td>MX_E_AutomationObjectSpecificError</td>
    </tr>
  </tbody>
</table>

## MxStatusCategory Values

The MxStatusCategory values are shown in the following table:

<table>
  <thead>
    <tr>
      <th>Value</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>-1</td>
      <td>MxStatusCategoryUnknown</td>
    </tr>
    <tr>
      <td>0</td>
      <td>MxCategoryOk</td>
    </tr>
    <tr>
      <td>1</td>
      <td>MxCategoryPending</td>
    </tr>
    <tr>
      <td>2</td>
      <td>MxCategoryWarning</td>
    </tr>
    <tr>
      <td>3</td>
      <td>MxCategoryCommunicationError</td>
    </tr>
    <tr>
      <td>4</td>
      <td>MxCategoryConfigurationError</td>
    </tr>
    <tr>
      <td>5</td>
      <td>MxCategoryOperationalError</td>
    </tr>
    <tr>
      <td>6</td>
      <td>MxCategorySecurityError</td>
    </tr>
    <tr>
      <td>7</td>
      <td>MxCategorySoftwareError</td>
    </tr>
    <tr>
      <td>8</td>
      <td>MxCategoryOtherError</td>
    </tr>
  </tbody>
</table>

## MxStatusSource Values

The MxStatusSource values are shown in the following table:

<table>
  <tr>
    <th>Value</th>
    <th>Status</th>
  </tr>
  <tr>
    <td>-1</td>
    <td>MxSourceUnknown</td>
  </tr>
  <tr>
    <td>0</td>
    <td>MxSourceRequestingLmx</td>
  </tr>
  <tr>
    <td>1</td>
    <td>MxSourceRespondingLmx</td>
  </tr>
  <tr>
    <td>2</td>
    <td>MxSourceRequestingNmx</td>
  </tr>
  <tr>
    <td>3</td>
    <td>MxSourceRespondingNmx</td>
  </tr>
  <tr>
    <td>4</td>
    <td>MxSourceRequestingAutomationObject</td>
  </tr>
  <tr>
    <td>5</td>
    <td>MxSourceRespondingAutomationObject</td>
  </tr>
</table>

## ResolutionStatus Values

The ResolutionStatus values are shown in the following table:

<table>
  <tr>
    <th>Value</th>
    <th>Status</th>
  </tr>
  <tr>
    <td>0</td>
    <td>unresolved</td>
  </tr>
  <tr>
    <td>1</td>
    <td>resolvingObject</td>
  </tr>
  <tr>
    <td>2</td>
    <td>resolvingAttribute</td>
  </tr>
  <tr>
    <td>3</td>
    <td>resolved</td>
  </tr>
  <tr>
    <td>4</td>
    <td>invalidReference</td>
  </tr>
  <tr>
    <td>5</td>
    <td>noGalaxyRepository</td>
  </tr>
  <tr>
    <td>6</td>
    <td>waitingToResolveAgainstDb</td>
  </tr>
  <tr>
    <td>7</td>
    <td>waitingToResolveAttribute</td>
  </tr>
  <tr>
    <td>8</td>
    <td>retrievingRedundancyStatus</td>
  </tr>
</table>
