Professional OPC
Development Tools

logos

Online Forums

Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions. You do not have to be a licensed user of our product.

Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible. Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.

Do not use the Contact page for technical issues.

C++ without visual studio

More
01 Jun 2012 09:35 #876 by support
Hi Neal.
ad 1) You are right. After looking into the VOLE sources and examples for some time, I came to conclusion that is probably an intentional design decision in the library, which allows it to stay away from most of reference counting that is otherwise necessary to properly handle COM objects' lifetime. Essentialy, the library only allows copying into the same or inner scope, by constructing a new object.
In your case, you can initialize the member right in the constructor, like this:

cHal_opc::cHal_opc()
: mOpcClient(object::create("OPCLabs.EasyDAClient.5.1", CLSCTX_ALL, vole::coercion_level::valueCoercion))

I think that with rethinking the code a little it should be possible to achieve what your need. In "worst" case, one may obtain IDispatch* pointer using object.get(), and pass it around freely (but then observe the refcounting rules).
ad 2) I think this is natural with pointers to any objects that get destroyed.
ad 3) This depends. Inside Windows functions and VARIANTs, which are used to pass arguments, wide strings are used. But vole::variant also has a constructor that takes normal string as input, so you can use that if you like; also, in your own code you can use whatever you like, but be aware that there is a reason why wide strings are used, and in general case the strings can contain wide characters, so restricting the code to char strings may not work in non-English locales etc.
ad 4) Unfortunately yes, you have to use SAFEARRAYs. It might be possible to find some compiler-independent easy awrapper around it.
ad 5) Whether you work with VTQs or just values, and whether you are connected locally or remotelly, are two unrelated things. The OPC Server always returns a Value-Timestamp-Quality combination anyway. The methods that return only Value are coded on top of that, inside QuickOPC, for applications that just need a Value of "good" quality but do not need timestamps. Note that unlike the methods that return VTQ, these methods wait until the quality becomes "good" (or a timeout elapses), so that a valid Value can actually be returned.
ad 6) There is a separate Reference installed with the product, there should be a shortcut to it under the Start menu group. It is also available online: www.opclabs.com/onlinedocs/Qui...
Best regards

Please Log in or Create an account to join the conversation.

More
29 May 2012 23:10 #875 by neal
Replied by neal on topic Re: C++ without visual studio
Hi there, thanks again for the quick (and helpful) response. I'm getting ever closer, but still have a few more questions. Here is where I'm at: (I have attached a visual studio project that demonstrates most of this)
1) I can copy the vole::object to another local variable, but not to a member variable in my class
2) I can access the vole::object through a pointer, but only until the original object goes out of scope and gets destroyed. I then throw an exception.
3) Do I have to use wide character arrays for all of the arguments to vole::object::invoke()? How should I handle arguments that I want to pass around internally as normal strings?
4) Do I have to use SAFEARRAY to interface with QuickOPC? It seems somewhat cumbersome for simply reading and writing a bunch of tag values. Is there an option of using stl::vectors or even just plain C arrays?
5) If I am using OPC locally on a single computer, do I likely need to use the whole VTQ interface? Can I get away with "ReadMultipleItemValues" and "WriteMultipleItemValues"?
6) What is the best reference document for getting the proper syntax for QuickOPC function calls? I have been looking at "Concepts-QuickOPC-Classic" (mostly pages 38-45) which has verbal descriptions, but not the kinds of full function prototypes I am used to seeing for library documentation.
7) I have temporarily given up on using a non-microsoft compiler because I don't have time to fight that battle right now. From what I have read online, vole/stlsoft should work just fine with gcc, but I couldn't get it working in the time I had. It is still very important for me to use as much standard C++ as possible so that I can migrate later.
Thanks again,
Neal


Please Log in or Create an account to join the conversation.

More
29 May 2012 11:26 #871 by support
ad 1) Creating EasyDAClient object does not directly correspond to making an OPC connection. The EasyDAClient is a relatively thin wrapper over an "engine" that lives internally in the component, and manages the OPC connections on a "per need" basis. It is therefore OK to create separate, short-lived EasyDAClient objects (this is intentional part of the design, as it is the usage mode likely to be used in Web applications).
This said, I do not see why it should not be possible to create one EasyDAClient object, and copy it around (the comstl::object is esentially just an interface pointer). I can add "object easyDAClient2 = easyDAClient;" behind creation of the EasyDAClient, and it compiles well for me. Maybe you can send me a piece of code that does not compile, for a review.
ad 2) Yes, it takes some effort to write a code with STLSoft and VOLE libraries. It also looks like that there is no full wrapper in them for Windows SAFEARRAY, which is commonly used, so this part has to be done "by hand". I have re-worked the whole initial example (for reading multiple items), and the code is here:

// This example shows how to read 4 items at once, and display their values, timestamps and qualities.
// It is designed to only use C++ language features and libraries that are portable across compilers.

// Uses: STLSoft C and C++ Libraries, sourceforge.net/projects/stlsoft
// Uses: VOLE - A Neat C++ COM/Automation Driver, sourceforge.net/projects/vole
// You need to properly set the include directories to STLSoft and VOLE libraries.

// Tested with libraries: STLSoft 1.9.112, VOLE 0.7.3
// Tested with compiler: Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 (Visual Studio 2010)
// Tested with compiler: Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for x64 (Visual Studio 2010)
// (build only)

#include
#include
#include
#include
#include

int main()
{
using vole::object;
using vole::collection;

// Initialize OLE libraries
comstl::ole_initialiser oleinit;

// Instatiate the EasyOPC-DA client object
object easyDAClient = object::create("OPCLabs.EasyDAClient.5.1", CLSCTX_ALL, vole::coercion_level::valueCoercion);

// Create safearray of strings
comstl::variant itemIdArray;
itemIdArray.vt = VT_ARRAY | VT_BSTR;
SAFEARRAYBOUND sab;
sab.lLbound = 0; sab.cElements = 4;
itemIdArray.parray = SafeArrayCreate(VT_BSTR, 1, &sab);

// Fill safearray with Item IDs
comstl::bstr itemId;
LONG index;
index = 0; itemId = L"Simulation.Random"; SafeArrayPutElement(itemIdArray.parray, &index, itemId.get());
index = 1; itemId = L"Trends.Ramp (1 min)"; SafeArrayPutElement(itemIdArray.parray, &index, itemId.get());
index = 2; itemId = L"Trends.Sine (1 min)"; SafeArrayPutElement(itemIdArray.parray, &index, itemId.get());
index = 3; itemId = L"Simulation.Register_I4"; SafeArrayPutElement(itemIdArray.parray, &index, itemId.get());

comstl::variant vMissing;
vMissing.vt = VT_ERROR;

comstl::variant results = easyDAClient.invoke_method(L"ReadMultipleItems",
/*varMachineName:*/ L"",
/*varServerClass:*/ L"OPCLabs.KitServer.2",
/*varItemIds:*/ itemIdArray,
/*varStates:*/ vMissing,
/*varDataTypes:*/ vMissing,
/*varAccessPaths:*/ vMissing);

SAFEARRAY* resultArray = results.parray;
for (LONG i = 0; i <= 3; i++)
{
// Each element of the result array is a DAVtqResult object; obtain it
comstl::variant element;
SafeArrayGetElement(resultArray, &i, &element);
object daVtqResult(element);

// Obtain a DAVtq object (value/timestamp/quality) from the result
object daVtq = daVtqResult.get_property(L"Vtq");

// Now we can obtain the individual properties, or convert it to string
comstl::variant string = daVtq.invoke_method(L"ToString");
wprintf(L"results(%d).Vtq.ToString(): %s\n", (int)i, string.bstrVal);
}

char line[80];
puts("Press Enter to continue...");
fgets(line, sizeof(line), stdin);

return EXIT_SUCCESS;
}


I have no problems taking it further and add pieces that you will need (such as extracting individual properties out of the DAVtq object, proper error handling, etc.). Just let me know when you run into problems.
Have you made a build with a compiler other than from Microsoft?
Best regards,

Please Log in or Create an account to join the conversation.

More
28 May 2012 04:06 #869 by neal
Replied by neal on topic Re: C++ without visual studio
OK, I think I am finally back to working on my opc interface again. Based upon your vole/stlsoft sample code, I am able to read data values from my PLC into a standard c++ program. (Which is a great step forward, thank you!) However, I have run into a few additional questions. Hopefully, these are easy ones....
1) I am trying to encapsulate the opc interface (mainly to hide plc tag names) in a simple c++ class. My starting assumption is that I should only create the connection once (say, in the constructor) and then subsequent calls to the class should be able to jump right to readItemValue calls. (Is this correct?) However, the object::create static functional call returns an object (rather than a pointer) and I am having trouble keeping it in scope unless I call object::create in the same function as readItemValue. I tried creating a member variable within my class of type vole::object and assigning it with object:create in my constructor, but the compiler complains about accessing a private = operator. I can create a pointer (vole::object*) and point it to easyDAClient, but that only seems to work until the original easyDAClient goes out of scope and gets destroyed. Any suggestions? How attrocious is it to call object::create before every read or write?
2) While vole/stlsoft seem to be working, I cannot claim to actually understand them yet and their documentation is not very helpful. How do I translate things like multi-item read and write operations from the atl-based example at the beginning of this thread to the vole/stlsoft syntax?
Thank you so much for all of your help. In the end, my use of opc should be relatively simple (just read and write data from a local PLC) so I hope that I am getting quite close.
Neal

Please Log in or Create an account to join the conversation.

More
29 Apr 2012 18:14 #831 by support
Your (non-buildable) project is set to be a .NET project written in C++ (with CLR support). Though it should be possible to make it work this way as well, I am quite confident that this is *not* what you want anyway, because your primary goal is to create a code portable between compilers, and the .NET support is Microsoft specific. You have probably selected "CLR Console Application" or something similar when creating a project from template.
I suggest you start over again, with selecting some of the projects that are not .NET-based, e.g. "Win32 Console Application".
Best regards

Please Log in or Create an account to join the conversation.

More
26 Apr 2012 20:39 #830 by neal
Replied by neal on topic Re: C++ without visual studio
Thank you! I was able to load, compile, and run your MSVS project in VS 2010 express! I have not yet tried to do anything useful with it (like communicate with our real opc server) but now that the compiling problems are out of the way I can actually try once I have some time (which unfortunately might be a while...)
I may try to compare your (working) VS project file with my (not working) VS project file to try and understand what I was doing wrong in the setup. My hope (possibly overly optimistic) is that understanding this might also shed some light on the troubles I was having using gcc. Any ideas or suggestions anyone has on this topic would of course be greatly appreciated. I have attached my super-simple (and not working) VS project in case that is helpful.
Thanks again for all of your help.

Please Log in or Create an account to join the conversation.

More
26 Apr 2012 19:13 #829 by support
Hello,
I am attaching a project in VS2010 that builds fine. My guess is that the project needs to link to a proper library of Win32 exports, or include a header files that instructs the compiler to link automatically. The unresolved functions are standard Win32 entry points, they are not any Visual C++ -specific functions, which is a good sign.
I have not tried this yet with VS2005 or VS2010 Express, as they are not on any of our systems currrently. And you will probably not be able to load my project into them either. But I try those that tomorrow or over the weekend, and let you know then.
Best regards

Please Log in or Create an account to join the conversation.

More
25 Apr 2012 17:07 #826 by neal
Replied by neal on topic Re: C++ without visual studio
After moving to a new building and getting sucked into a few other projects, I am finally back to trying to get QuickOPC to work via StlSoft/Vole. Unforunately, I don't seem to be getting very far and I suspect that it is because I am missing something very basic.
My goal had been to use the mingw/gcc compiler since I am stuck in the windows world for now. From what little documentation I can find on StlSoft, it claims to support gcc natively, but since I was having so many problems I decided to try and get it working in Visual Studio first since that is what you tested with.
In both Visual C++ 2010 Express (free) and an old version of Visual Studio (2005) I created a bare minimum CLR Console app from the startup wizard and added the comstl and vole includes as below:
// VoleTest.cpp : main project file.
#include "stdafx.h"
#include
#include
using namespace System;
int main(array ^args)
{
Console::WriteLine(L"Hello World");
return 0;
}
The comstl include doesn't create a problem right away, but as soon as I uncomment the vole.hpp include I get a bunch of linker errors
Build started: Project: VoleTest, Configuration: Debug Win32
Linking...
VoleTest.obj : error LNK2028: unresolved token (0A0005FF) "extern "C" long __stdcall VariantCopy(struct tagVARIANT *,struct tagVARIANT *)" (?VariantCopy@@$$J18YGJPAUtagVARIANT@@0@Z) referenced in function "public: __thiscall stlsoft::comstl_project::variant::variant(class stlsoft::comstl_project::variant const &)" (??0variant@comstl_project@stlsoft@@$$FQAE@ABV012@@Z)
VoleTest.obj : error LNK2028: unresolved token (0A000602) "extern "C" long __stdcall VariantClear(struct tagVARIANT *)" (?VariantClear@@$$J14YGJPAUtagVARIANT@@@Z) referenced in function "public: static void __cdecl stlsoft::comstl_project::VARIANT_policy::clear(struct tagVARIANT *)" (?clear@VARIANT_policy@comstl_project@stlsoft@@$$FSAXPAUtagVARIANT@@@Z)
VoleTest.obj : error LNK2028: unresolved token (0A000604) "extern "C" void __stdcall VariantInit(struct tagVARIANT *)" (?VariantInit@@$$J14YGXPAUtagVARIANT@@@Z) referenced in function "public: static void __cdecl stlsoft::comstl_project::VARIANT_policy::init(struct tagVARIANT *)" (?init@VARIANT_policy@comstl_project@stlsoft@@$$FSAXPAUtagVARIANT@@@Z)
VoleTest.obj : error LNK2028: unresolved token (0A000692) "extern "C" unsigned int __stdcall SysStringLen(wchar_t *)" (?SysStringLen@@$$J14YGIPA_W@Z) referenced in function "public: unsigned int __thiscall stlsoft::comstl_project::bstr::length(void)const " (?length@bstr@comstl_project@stlsoft@@$$FQBEIXZ)
VoleTest.obj : error LNK2028: unresolved token (0A000696) "extern "C" void __stdcall SysFreeString(wchar_t *)" (?SysFreeString@@$$J14YGXPA_W@Z) referenced in function "public: __thiscall stlsoft::comstl_project::c_str_VARIANT_proxy_w::~c_str_VARIANT_proxy_w(void)" (??1c_str_VARIANT_proxy_w@comstl_project@stlsoft@@$$FQAE@XZ)
VoleTest.obj : error LNK2028: unresolved token (0A000697) "extern "C" wchar_t * __stdcall SysAllocString(wchar_t const *)" (?SysAllocString@@$$J14YGPA_WPB_W@Z) referenced in function "public: __thiscall stlsoft::comstl_project::errorinfo_desc::errorinfo_desc(class stlsoft::comstl_project::errorinfo_desc const &)" (??0errorinfo_desc@comstl_project@stlsoft@@$$FQAE@ABV012@@Z)
VoleTest.obj : error LNK2019: unresolved external symbol "extern "C" wchar_t * __stdcall SysAllocString(wchar_t const *)" (?SysAllocString@@$$J14YGPA_WPB_W@Z) referenced in function "public: __thiscall stlsoft::comstl_project::errorinfo_desc::errorinfo_desc(class stlsoft::comstl_project::errorinfo_desc const &)" (??0errorinfo_desc@comstl_project@stlsoft@@$$FQAE@ABV012@@Z)
VoleTest.obj : error LNK2019: unresolved external symbol "extern "C" void __stdcall SysFreeString(wchar_t *)" (?SysFreeString@@$$J14YGXPA_W@Z) referenced in function "public: __thiscall stlsoft::comstl_project::c_str_VARIANT_proxy_w::~c_str_VARIANT_proxy_w(void)" (??1c_str_VARIANT_proxy_w@comstl_project@stlsoft@@$$FQAE@XZ)
VoleTest.obj : error LNK2019: unresolved external symbol "extern "C" unsigned int __stdcall SysStringLen(wchar_t *)" (?SysStringLen@@$$J14YGIPA_W@Z) referenced in function "public: unsigned int __thiscall stlsoft::comstl_project::bstr::length(void)const " (?length@bstr@comstl_project@stlsoft@@$$FQBEIXZ)
VoleTest.obj : error LNK2019: unresolved external symbol "extern "C" void __stdcall VariantInit(struct tagVARIANT *)" (?VariantInit@@$$J14YGXPAUtagVARIANT@@@Z) referenced in function "public: static void __cdecl stlsoft::comstl_project::VARIANT_policy::init(struct tagVARIANT *)" (?init@VARIANT_policy@comstl_project@stlsoft@@$$FSAXPAUtagVARIANT@@@Z)
VoleTest.obj : error LNK2019: unresolved external symbol "extern "C" long __stdcall VariantClear(struct tagVARIANT *)" (?VariantClear@@$$J14YGJPAUtagVARIANT@@@Z) referenced in function "public: static void __cdecl stlsoft::comstl_project::VARIANT_policy::clear(struct tagVARIANT *)" (?clear@VARIANT_policy@comstl_project@stlsoft@@$$FSAXPAUtagVARIANT@@@Z)
VoleTest.obj : error LNK2019: unresolved external symbol "extern "C" long __stdcall VariantCopy(struct tagVARIANT *,struct tagVARIANT *)" (?VariantCopy@@$$J18YGJPAUtagVARIANT@@0@Z) referenced in function "public: __thiscall stlsoft::comstl_project::variant::variant(class stlsoft::comstl_project::variant const &)" (??0variant@comstl_project@stlsoft@@$$FQAE@ABV012@@Z)
C:\Users\tanner\Documents\Visual Studio 2005\Projects\VoleTest\Debug\VoleTest.exe : fatal error LNK1120: 12 unresolved externals
Build log was saved at "file://c:\Users\tanner\Documents\Visual Studio 2005\Projects\VoleTest\VoleTest\Debug\BuildLog.htm"
VoleTest - 13 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
I have downloaded the stlsoft and vole headers and added their paths as include directories (as evidenced by the fact that the #include did not fail) but I am clearly doing something wrong in setting up the project. As is probably obvious, I am a complete neofite with Visual Studio (part of why I wanted to use gcc) so please forgive my ignornace. Does anyone have any suggestions of what I am missing? Can you send me the complete visual studio project/solution?
Thanks again for all of your help. I still desperately want to get an opc client running because the alternative looks even worse.... :)
Neal



Please Log in or Create an account to join the conversation.

More
04 Apr 2012 14:00 #818 by neal
Replied by neal on topic Re: C++ without visual studio
Thanks for the quick response and the sample code. I tried it very briefly yesterday and it compiled right away but I got a bunch of link errors from the stlsoft lib (which is odd since it is a header-only library). There are tons of cases online of people using this library with the compilers I am trying to use, so it is likely that I simply do not have my environment set up right. We're in the middle of moving buildings at the moment, so it may be a few days before I have time to look into the linker issues. I'll make sure to reply to this thread once I figure it out.

Please Log in or Create an account to join the conversation.

More
31 Mar 2012 16:49 #816 by support
Hello.
I have created a simple example that - I believe - only uses portable C++ and libraries. Obviously it is still dependent on Windows API and its headers, but it should not be dependent on specifics of Microsoft compiler or stuff like ATL, MFC. Here it is:

// This example shows how to read value of a single OPC item, and display it.
// It is designed to only use C++ language features and libraries that are portable across compilers.

// Uses: STLSoft C and C++ Libraries, sourceforge.net/projects/stlsoft
// Uses: VOLE - A Neat C++ COM/Automation Driver, sourceforge.net/projects/vole
// You need to properly set the include directories to STLSoft and VOLE libraries.

// Tested with libraries: STLSoft 1.9.112, VOLE 0.7.3
// Tested with compiler: Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 (Visual Studio 2010)
// Tested with compiler: Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for x64 (Visual Studio 2010)

#include
#include
#include
#include
#include

int main()
{
using vole::object;
using vole::collection;

// Initialize OLE libraries
comstl::ole_initialiser oleinit;

object easyDAClient = object::create("OPCLabs.EasyDAClient.5.1", CLSCTX_ALL, vole::coercion_level::valueCoercion);

comstl::variant vMissing;
vMissing.vt = VT_ERROR;

double value = easyDAClient.invoke_method<double>(L"ReadItemValue",
/*varMachineName:*/ L"",
/*varServerClass:*/ L"OPCLabs.KitServer.2",
/*varItemId:*/ L"Simulation.Random",
/*varDataType:*/ vMissing,
/*varAccessPath:*/ vMissing);

printf("value: %g\n", value);

char line[80];
puts("Press Enter to continue...");
fgets(line, sizeof(line), stdin);

return EXIT_SUCCESS;
}


Can you try to build and run it with compilers that you have experience with, and let me know about the results?
Best regards,
Zbynek Zahradnik

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.096 seconds