C#

Agilent VISA.NET

C#

The viPrintf and viScanf VISA functions take variable argument lists in C.  Some of viScanf's variable arguments are references to primitive types (int, float, etc), meaning that the values themselves can be changed by the function.  There is no equivalent in either C# or VB .NET for variable argument lists with reference arguments, so no direct translation is available.  There are undocumented C# keywords (__arglist and __makeref, primarily) for calling printf- and scanf-style variable argument lists, but there is no guarantee of future support and these keywords do not work in VB .NET.

Agilent has chosen to implement a set of basic one-argument method overloads for viScanf, viPrintf, viSScanf, and viSPrintf.  This allows you to pass one argument to the functions, and the proper overload will be chosen based on the type of the argument.  Overloads for viScanf/viSScanf for reading arrays with the argument length parameter are also provided.  There are a total of 58 overloads in each header file to support this.  Adding a second parameter for all the functions and providing all the overloads would result in 484 overloads in each file, which is infeasible.  If you find yourself needing more choices than are in the Agilent-provided header files, you can make your own declarations based on your needs.

Let's look at one of the overloads provided in the C# header file:

[DllImportAttribute("VISA32.DLL", EntryPoint="#269", ExactSpelling=true, CharSet=CharSet.Ansi, SetLastError=true, CallingConvention=CallingConvention.Cdecl)] public static extern int viPrintf(int vi, string writeFmt, int[] arr);

This declaration allows viPrintf to write out an array of integers.  If, for example, you wanted to upload a waveform to an arbitrary waveform generator, you would typically send a SCPI header followed by the array data.  viPrintf doesn't understand .NET arrays, so it isn't aware of the size of the array; it only has a pointer to data.  The format string has two ways to inform viPrintf of the size of the array: a number can be in the format string itself, or the "*" symbol can be used to indicate that a parameter indicating the array size is passed as the first argument.  

The provided overload allows you to hard-code the format string with that SCPI header and the size of the array.  You could create a format string on the fly with the proper array size, but you might choose to avoid the overhead of string manipulation to create the format string.  Another solution that reduces your complexity and amount of code is to declare another override of viPrintf that allows for that extra parameter.  If this is something you want to do, you might declare your own version of viScanf like this:

C#

[DllImportAttribute("VISA32.DLL", EntryPoint="#269", 
		ExactSpelling=true, CharSet=CharSet.Ansi, 
		SetLastError=true, CallingConvention=CallingConvention.Cdecl)]
public static extern int viPrintf(int vi, string writeFmt, 
				  int pointCount, int[] waveform);

VB .NET

<DllImportAttribute("VISA32.DLL", EntryPoint:="#269", ExactSpelling:=True, _    CharSet:=CharSet.Ansi, SetLastError:=True, _    CallingConvention:=CallingConvention.Cdecl)> _ Public Function viPrintf(ByVal vi As Integer, ByVal writeFmt As String, _ ByVal pointCount As Integer, _ ByVal waveform() As Short) As Integer End Function

You might use this version in a function like this:

VB .NET

Private Sub SendWaveform(ByVal session As Integer, ByVal pointData() As Short)    Dim statusCode As Integer    statusCode = viPrintf(session, "DATA:DAC VOLATILE, %*hb" & vbLf, _        pointData.Length(), pointData)    If (statusCode < visa32.VI_SUCCESS) Then        Dim err As System.Text.StringBuilder = New System.Text.StringBuilder(256)        visa32.viStatusDesc(session, statusCode, err)        Throw New ApplicationException(err.ToString())    End If End Sub

The "%,*hb" format string means, in order, a parameter (%), that is an array with the length of the array data passed as another parameter (*), that is passed on the stack as a reference to an array of 16-bit integers (h), to be written to the instrument as an IEEE 488.2 binary block (b).  You can create these new function declarations because .NET simply sends the declared parameters to the underlying VISA C functions, and C variable argument list functions know how to parse the format string so that they can use the stack to get at the right data.  

Safety

As in the C/C++ world, you have to be very careful when using these methods.  Even though .NET protects its data internally, when it calls out to C DLL's it is up to the DLL to behave properly.  If the type that you pass to viPrintf/viScanf is different from the type the format string promises, you will have undefined behavior and possible program crashes.  For example, if you call viScanf with an 10-long array of integers, but the format string is "%dl,100" (promising space to write 100 integers), you can cause an illegal write to the program heap. This may result in the overwriting of program data and/or an illegal memory access exception.