Using QuickOPC from Visual C++ 6.0

From OPC Labs Knowledge Base
Revision as of 18:15, 22 February 2017 by User (talk | contribs)
Jump to navigation Jump to search


Visual C++ 6.0 (also contained in Visual Studio 6.0) is still in use in some programming shops. Although it is not a development environment that we officially target, it is perfectly possible to use QuickOPC from Visual C++ 6.0. QuickOPC exposes its API via COM, and that can be consumed from Visual C++ 6.0 well.

There are, however, some differences from the current Visual C++, and the examples we ship with QuickOPC do not work "out of the box" with Visual C++ 6.0.

This article explains the main differences, and how to overcome them.

The #import directive does not support the 'libid:' keyword

There are no project-scope directory settings

There is no CComSafeArray class

Default character width may be 8 bits

Example code

Below is a Visual C++ 6.0 example (ReadMultipleItems) modified from the one that we ship for Visual Studio 2012 or later. We have tested with a beta version of QuickOPC 2017.1, but it should work without changes with QuickOPC 2016.2 and many earlier versions as well.

The main file below (ReadMultipleItems.cpp) #include-s (through stdafx.h) a QuickOPC.h file, which contains common QuickOPC imports and definitions. Both these files are also listed, further below.

ReadMultipleItems.cpp

// $Header: $
// Copyright (c) CODE Consulting and Development, s.r.o., Plzen. All rights reserved.
// ReadMultipleItems.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"	// Includes "QuickOpc.h", and other commonly used files
#include <atlconv.h>

//

int _tmain(int argc, _TCHAR* argv[])
{
    USES_CONVERSION;

    // Initialize the COM library. Note: If you choose STA, you will be responsible for pumping messages.
    CoInitializeEx(NULL, COINIT_MULTITHREADED);

    // Instatiate the EasyOPC-DA client object
    _EasyDAClientPtr ClientPtr(__uuidof(EasyDAClient));

    _DAReadItemArgumentsPtr ReadItemArguments1Ptr(_uuidof(DAReadItemArguments));
    ReadItemArguments1Ptr->ServerDescriptor->ServerClass = L"OPCLabs.KitServer.2";
    ReadItemArguments1Ptr->ItemDescriptor->ItemId = L"Simulation.Random";

    _DAReadItemArgumentsPtr ReadItemArguments2Ptr(_uuidof(DAReadItemArguments));
    ReadItemArguments2Ptr->ServerDescriptor->ServerClass = L"OPCLabs.KitServer.2";
    ReadItemArguments2Ptr->ItemDescriptor->ItemId = L"Trends.Ramp (1 min)";

    _DAReadItemArgumentsPtr ReadItemArguments3Ptr(_uuidof(DAReadItemArguments));
    ReadItemArguments3Ptr->ServerDescriptor->ServerClass = L"OPCLabs.KitServer.2";
    ReadItemArguments3Ptr->ItemDescriptor->ItemId = L"Trends.Sine (1 min)";

    _DAReadItemArgumentsPtr ReadItemArguments4Ptr(_uuidof(DAReadItemArguments));
    ReadItemArguments4Ptr->ServerDescriptor->ServerClass = L"OPCLabs.KitServer.2";
    ReadItemArguments4Ptr->ItemDescriptor->ItemId = L"Simulation.Register_I4";

    COleSafeArray ArgumentsArray;
    ArgumentsArray.CreateOneDim(VT_VARIANT, 4);
    long i;
    i = 0; ArgumentsArray.PutElement(&i, &_variant_t((IDispatch*)ReadItemArguments1Ptr));
    i = 1; ArgumentsArray.PutElement(&i, &_variant_t((IDispatch*)ReadItemArguments2Ptr));
    i = 2; ArgumentsArray.PutElement(&i, &_variant_t((IDispatch*)ReadItemArguments3Ptr));
    i = 3; ArgumentsArray.PutElement(&i, &_variant_t((IDispatch*)ReadItemArguments4Ptr));

    //LPSAFEARRAY pArgumentsArray = ArgumentsArray.Detach();
    COleSafeArray ResultArray(ClientPtr->ReadMultipleItems(&ArgumentsArray.parray), VT_VARIANT);
    //ArgumentsArray.Attach(pArgumentsArray);

    for (i = 0; i < ResultArray.GetOneDimSize(); i++)
    {
	_variant_t vDAVtqResult;
	ResultArray.GetElement(&i, &vDAVtqResult);
	_DAVtqResultPtr DAVtqResultPtr = vDAVtqResult;

	_ExceptionPtr ExceptionPtr(DAVtqResultPtr->Exception);
	if (ExceptionPtr != NULL)
	{
            _variant_t exceptionAsString(ExceptionPtr->ToString);
	    _tprintf(_T("results(%d).Exception.ToString(): %s\n"), i, OLE2T(exceptionAsString.bstrVal));
	    continue;
	}

        _DAVtqPtr DAVtqPtr(DAVtqResultPtr->Vtq);
        _variant_t vtqAsString(DAVtqPtr->ToString);
        _tprintf(_T("results(%d).Vtq.ToString(): %s\n"), i, OLE2T(vtqAsString.bstrVal));
    }

    // Release all interface pointers BEFORE calling CoUninitialize()
    ArgumentsArray.Clear();
	ResultArray.Clear();
    ClientPtr = NULL;

    CoUninitialize();

    TCHAR line[80];
    _putts(_T("Press Enter to continue..."));
    _fgetts(line, sizeof(line), stdin);
    return 0;
}

stdafx.h

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#if !defined(AFX_STDAFX_H__FEF9DF0F_01E8_4734_B0FC_90EB0159E0BF__INCLUDED_)
#define AFX_STDAFX_H__FEF9DF0F_01E8_4734_B0FC_90EB0159E0BF__INCLUDED_

#define _WIN32_DCOM			

#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers

#include <afx.h>
#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxdtctl.h>		// MFC support for Internet Explorer 4 Common Controls
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>			// MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

#include <iostream>

#include "QuickOpc.h"
// TODO: reference additional headers your program requires here

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__FEF9DF0F_01E8_4734_B0FC_90EB0159E0BF__INCLUDED_)

QuickOPC.h

// $Header: $
// Copyright (c) CODE Consulting and Development, s.r.o., Plzen. All rights reserved.
// This files imports all QuickOPC libraries (and system libraries they require), creating Compiler COM Support wrappers.
// It also contains additional definitions for creating event sinks.
#pragma once



// #import system libraries

// mscorlib
#pragma warning(push)
#pragma warning(disable:4278)	// 'ReportEvent': identifier in type library 'BED7F4EA-1A96-11D2-8F08-00A0C9A6186D' is already a macro; use the 'rename' qualifier
#if _MSC_VER >= 1300
#define IMPORT_MSCORLIB "libid:BED7F4EA-1A96-11D2-8F08-00A0C9A6186D"
#else
#define IMPORT_MSCORLIB "mscorlib.tlb"
#endif
#import IMPORT_MSCORLIB
#pragma warning(pop)
using namespace mscorlib;

// System.Drawing
#if _MSC_VER >= 1300
#define IMPORT_SYSTEM_DRAWING "libid:D37E2A3E-8545-3A39-9F4F-31827C9124AB"
#else
#define IMPORT_SYSTEM_DRAWING "System.Drawing.tlb"
#endif
#import IMPORT_SYSTEM_DRAWING
using namespace System_Drawing;

// System.Windows.Forms
#pragma warning(push)
#pragma warning(disable:4192)	// automatically excluding 'IDataObject' while importing type library 'System.Windows.Forms.tlb'
#if _MSC_VER >= 1300
#define IMPORT_SYSTEM_WINDOWS_FORMS "libid:215D64D2-031C-33C7-96E3-61794CD1EE61"
#else
#define IMPORT_SYSTEM_WINDOWS_FORMS "System.Windows.Forms.tlb"
#endif
#import IMPORT_SYSTEM_WINDOWS_FORMS
#pragma warning(pop)
using namespace System_Windows_Forms;



// #import QuickOPC libraries

// OpcLabs.BaseLib
#if _MSC_VER >= 1300
#define IMPORT_OPCLABS_BASELIB	"libid:ecf2e77d-3a90-4fb8-b0e2-f529f0cae9c9"
#else
#define IMPORT_OPCLABS_BASELIB	"OpcLabs.BaseLib.tlb"
#endif
#import IMPORT_OPCLABS_BASELIB \
	rename("value", "Value")
using namespace OpcLabs_BaseLib;

// OpcLabs.BaseLibForms
#if _MSC_VER >= 1300
#define IMPORT_OPCLABS_BASELIBFORMS	"libid:A0D7CA1E-7D8C-4D31-8ECB-84929E77E331"
#else
#define IMPORT_OPCLABS_BASELIBFORMS	"OpcLabs.BaseLibForms.tlb"
#endif
#import IMPORT_OPCLABS_BASELIBFORMS
using namespace OpcLabs_BaseLibForms;

// OpcLabs.EasyOpcClassic
#if _MSC_VER >= 1300
#define IMPORT_OPCLABS_EASYOPCCLASSIC	"libid:1F165598-2F77-41C8-A9F9-EAF00C943F9F"
#else
#define IMPORT_OPCLABS_EASYOPCCLASSIC	"OpcLabs.EasyOpcClassic.tlb"
#endif
#import IMPORT_OPCLABS_EASYOPCCLASSIC \
	rename("itemId", "ItemId") \
	rename("machineName", "MachineName") \
	rename("requestedUpdateRate", "RequestedUpdateRate") \
	rename("serverClass", "ServerClass")
using namespace OpcLabs_EasyOpcClassic;

// OpcLabs.EasyOpcUA
#if _MSC_VER >= 1300
#define IMPORT_OPCLABS_EASYOPCUA	"libid:E15CAAE0-617E-49C6-BB42-B521F9DF3983"
#else
#define IMPORT_OPCLABS_EASYOPCUA	"OpcLabs.EasyOpcUA.tlb"
#endif
#import IMPORT_OPCLABS_EASYOPCUA \
	rename("attributeData", "AttributeData") \
	rename("browsePath", "BrowsePath") \
	rename("endpointDescriptor", "EndpointDescriptor") \
	rename("expandedText", "ExpandedText") \
	rename("inputArguments", "InputArguments") \
	rename("inputTypeCodes", "InputTypeCodes") \
	rename("nodeDescriptor", "NodeDescriptor") \
	rename("nodeId", "NodeId") \
	rename("value", "Value")
using namespace OpcLabs_EasyOpcUA;

// OpcLabs.EasyOpcForms
#if _MSC_VER >= 1300
#define IMPORT_OPCLABS_EASYOPCFORMS	"libid:2C654FA0-6CD6-496D-A64E-CE2D2925F388"
#else
#define IMPORT_OPCLABS_EASYOPCFORMS	"OpcLabs.EasyOpcForms.tlb"
#endif
#import IMPORT_OPCLABS_EASYOPCFORMS
using namespace OpcLabs_EasyOpcForms;



// DISPID-s

#define DISPID_EASYAECLIENTCONFIGURATIONEVENTS_LOGENTRY	1

#define DISPID_EASYAECLIENTEVENTS_NOTIFICATION 2001

#define DISPID_EASYDACLIENTCONFIGURATIONEVENTS_LOGENTRY	1

#define DISPID_EASYDACLIENTEVENTS_ITEMCHANGED 1002

#define DISPID_EASYUACLIENTCONFIGURATIONEVENTS_LOGENTRY	1

#define DISPID_EASYUACLIENTEVENTS_DATACHANGENOTIFICATION  1002
#define DISPID_EASYUACLIENTEVENTS_EVENTNOTIFICATION  1003
#define DISPID_EASYUACLIENTEVENTS_SERVERCONDITIONCHANGED  1101