Hints and Tips for Using the Keysight Technologies VISA COM Libraries
Using the VISA COM I/O API in a .NET Environment
The VISA COM I/O API is a programming interface standardized by the IVI Foundation for communicating with instruments over GPIB, LAN, USB, RS-232, or other hardware interfaces. Keysight Technologies has an implementation of the VISA COM I/O standard that works with Keysight I/O hardware as well as the computer standard I/O interfaces. VISA COM I/O is an update of the older VISA C API to work in and with Microsoft’s COM technology.
Keysight provides VISA.NET as a VISA API that works seamlessly and naturally in Microsoft .NET. The only reason to use VISA COM in .NET is if you need to use existing VISA COM code in new .NET programs, or have other backward compatibility requirements that dictate the use of VISA COM.
That said, Microsoft has integrated robust support for COM components in the .NET environment. The COM interfaces tend to translate well into .NET equivalents via automated wrapper-generator tools that Microsoft provides. Due to this COM support in .NET, VISA COM can work well in a .NET environment.
An in-depth explanation and tutorials with example programs are beyond the scope of this help file. However, a discussion of Keysight’s VISA COM I/O implementation with example C# and Visual Basic (VB) programs is available in the application note Using VISA COM I/O API in .Net which is available at:
http://literature.cdn.keysight.com/litweb/pdf/5989-6338EN.pdf
or
http://www.keysight.com/find/iosuite. Select the Document Library tab and scroll down to find the file.
The #import Directive
The #import directive is a C++ preprocessor directive that causes a type library to be parsed into special header files with the extensions .tli and .tlh that provide a C++-friendly wrapper around COM interfaces. Among its features are that failing methods throw C++ Exceptions of type _com_error instead of returning bad HRESULT values, and COM types such as VARIANT and BSTR are replaced with classes such as _variant_t and _bstr_t. Including the #import directive in your code is equivalent to including the comdef.h header file and the .tlh and .tli files, which are found in the output directory of your project.
Because exceptions can be thrown, you should wrap all your code in exception handlers. This is the preferred way to use the VISA COM libraries from Visual C++, and because the object syntax is similar to how COM is used in Visual Basic, the transition from one to the other is simpler. View the Microsoft Developer Network Web site or your MSDN subscription to learn more about this directive.
Why Don't the Optional Parameters Work in C++?
The VISA COM I/O help file's API section shows many methods, such as IResourceManager::Open, that have default parameters in Microsoft Visual Basic (and other development environments) but not in C++. There are a couple of complementary reasons for this. One reason is that not all COM methods that have optional parameters are supported by the C++ language. Methods that have return values, like IResourceManager::Open, have the return value parameter as a pointer to the value at the last parameter in the parameter list. Since in C++ parameters with defaults must come before parameters without defaults, C++ does not support default parameters for methods with return value parameters. The other reason is that the encouraged way of doing C++ COM development (illustrated in the examples) is through the use of the #import directive. While the midl.exe compiler will leave in default parameters where possible, the #import directive strips out the default parameters (probably because of the inconsistency problem mentioned above).
Understanding COM Interface Programming
This is a topic too complex for a complete overview here, but there are a couple of points to keep in mind when using VISA COM I/O.
- Assignment isn't the same as copying.
COM objects almost always have multiple interfaces, or views into the objects. Each interface exposes a set of methods you can call on the object. Setting an interface reference variable, such asDim x as IMessage
in Visual Basic orIMessagePtr x;
in C++, to another interface reference variable does not copy the object, but creates two variables pointing to the same object. You can intersperse calls to the various reference variables for an object without confusing the object (unless the object has some specific method invocation protocol, of course.) See the topic on VISA COM Interfaces vs. VISA COM Objects for a more detailed description of the distinction between interface references and objects.
- Objects don't go away until all the variables referencing them go away (and even that isn't always enough).
COM objects use reference counting to decide when to delete themselves. They know how many interface reference variables exist; when the count goes to zero, they delete themselves. This happens automatically when you use Visual Basic or C++ with the #import and comdef.h smart pointers. Additionally, in garbage-collected environments such as VB.NET, C#.NET, and Java, the removal of the last reference still isn't enough to guarantee immediate deletion of the object; the garbage collector may take its time deleting the object. For these reasons, you should call the Close method on VISA COM I/O objects as soon as possible. This method does not delete the object itself, but it causes the object to close all the external resources it holds and to attempt to reduce its affect on the operation of your program to the bare minimum (a small memory footprint usually).
- Be aware of how COM strings work in Visual C++.
COM strings in C++ are of type BSTR. BSTR is just a typedef of wchar_t*, but you cannot create a BSTR by allocating an array of wchar_t's. wchar_t is a wide character (16 bits) that is meant to hold Unicode values. BSTR's are arrays of wchar_t's, but with the string length prepended in the first four bytes that the BSTR points to. Use SysAllocString and its related functions, or use the _bstr_t type (defined in comdef.h), to allocate and convert BSTR values. If you choose not to use _bstr_t, you may also need to convert wide character strings to ASCII strings and ASCII strings to wide character strings. In that case, you will need to familiarize yourself with the MultiByteToWideChar and related functions.
Viewing the VISA COM API
Because COM APIs look different in each language, no one API document is applicable to all COM programming environments. Different development environments have different ways of viewing this library. To open the Object Browser In Visual Studio, press Ctrl+Alt+J, or click View > Object Browser.
Figure 1:The Visual Basic Object Browser
In C++, the #import preprocessor directive generates .tlh and .tli header files that contain a C++ version of the API as it appears in the type library.
Figure 2: The Visual C++ TLH File
ProgIDs vs. ClassIDs
The two main methods of locating a COM object to be
created are Programmatic IDs (version-independent strings) and Class IDs (globally unique identifiers referring to a specific component version). The
examples in this help file use Class IDs because they are easier to use from
Visual Basic. Visual Basic examples often use the syntax Dim X as new Y
which implicitly uses the Class ID to create the object. The other choice is
to use VB’s CreateObject function, which receives a ProgID parameter to
locate a component and create it.