Using LINKNLOAD to Call External Programs
The LINKNLOAD function provides simplified access to external routines in Dynamic Link Libraries (DLLs). LINKNLOAD calls a function in a DLL and returns a scalar value. Parameters are passed through PV-WAVE to the specified external function by reference, thus allowing the external function to alter values of PV-WAVE variables. It is the simplest method for attaching your own C code to PV-WAVE.
Usage
result = LINKNLOAD(object, symbol[, param1, ..., paramn])
Parameters
object—A string specifying the filename, optionally including file path, of the DLL to be linked and loaded.
symbol—A string specifying the function name (symbol entry point) to be invoked in the DLL file.
parami—The data to be passed as a parameter to the function.
For more information on the LINKNLOAD parameters and optional keywords see the discussion of LINKNLOAD in the PV‑WAVE Reference.
Discussion
LINKNLOAD lets you call a C function (or a FORTRAN function) from PV‑WAVE almost as if you were calling a PV‑WAVE function. The called function can obtain information from PV‑WAVE through passed parameters or by accessing PV‑WAVE’s variables directly (see "Accessing Data in PV‑WAVE Variables").
Any simple PV‑WAVE data type can be passed as a parameter to a C or FORTRAN routine. By default, parameters are passed by reference (not by value), and thus it is up to the programmer whether or not the C or FORTRAN function alters the parameter’s value. Use the Value keyword to pass parameters by value. Parameters are passed in the traditional C fashion of argc and argv. The C function must know the type to expect for each parameter and must cast it to a C variable of the correct type.
For FORTRAN, since the parameters are passed as pointers, functions are provided to access their values. These functions are:
*WLNL_GETBYTE
*WLNL_GETSHORT
*WLNL_GETINT32
*WLNL_GETLONG
*WLNL_GETFLOAT
*WLNL_GETDOUBLE
*WLNL_GETCOMPLEX
*WLNL_GETSTRING
*WLNL_GETBYTE_ARRAY
*WLNL_GETSHORT_ARRAY
*WLNL_GETINT32_ARRAY
*WLNL_GETLONG_ARRAY
*WLNL_GETFLOAT_ARRAY
*WLNL_GETDOUBLE_ARRAY
*WLNL_GETCOMPLEX_ARRAY
*WLNL_GETSTRING_ARRAY
For detailed information on these functions, see the file:
(WIN) %WAVE_DIR%\util\variables\README
 
note
Make sure the number, type, and dimension of the parameters passed to the external function match what the external function expects (this can most easily be done from within PV‑WAVE before calling LINKNLOAD). Furthermore, the length of string parameters must not be altered and multi-dimensional arrays are flattened to one-dimensional arrays.
Because PV-WAVE LONG variables use 8 bytes on 64-bit platforms, passing them between PV-WAVE, C, and FORTRAN applications requires the use of the OPI_long and INTEGER*8 data types, respectively.
Note that with LINKNLOAD, pointers are used to get the value of a variable. For example:
B = WLNL_GETBYTE(VAL(ARGP(1)))
Here, the value of ARGP represents a pointer, which is the same size as a long integer. Thus, ARGP needs to be declared as an INTEGER*8 variable.
Accessing the Data in PV‑WAVE Variables
The wavevars function can be used to access the results generated by PV-WAVE in a user-written application called with LINKNLOAD. wavevars is a C function that can be invoked from code that is dynamically linked to PV-WAVE to obtain data from PV-WAVE’s data variable space.
The wavevars calling sequence is:
result = wavevars(&argc, &argv, &argp);
For detailed information on wavevars, see "Accessing Data in PV‑WAVE Variables".
 
note
See also the section "Using the Option Programming Interface". The Option Programming Interface (OPI) functions allow user-written C code to access PV‑WAVE variables and use other PV‑WAVE functionality. OPI provides greater flexibility and control than wavevars.
Example 1: Calling a C Program
The C code referred to in this example can be found online. You can print the program or view it using a text editor. The example is in the file:
%WAVE_DIR%\demo\interapp\linknloadc\linknloadc.c
In this example, parameters are passed using the conventional argc, argv strategy. argc indicates the number of data pointers which are passed from PV-WAVE within the array of pointers called argv. The pointers in argv can be cast to the desired type as the example program demonstrates.
Accessing the External Function with LINKNLOAD
The following PV-WAVE code demonstrates how the C function defined in this example could be invoked.
ln = LINKNLOAD('linknloadc.dll', 'WaveParams', BYTE(1), 2, 23i, $
LONG(3), FLOAT(4), DOUBLE(5), COMPLEX(6,7), 'eight')
The resulting output is:
1 2 23 3 4.000000 5.000000 <6.000000,7.000000i> 'eight'
Using the INFO command, you can see that LINKNLOAD returns the scalar value 1.
INFO, ln LN     LONG     =     1
The example program works with both scalars and arrays since the actual C program above only looks at the first element in the array and since PV‑WAVE collapses multi-dimensional arrays to one-dimensional arrays:
ln = LINKNLOAD('linknloadc.dll', 'WaveParams', [BYTE(1)], $
[[2,3],[4,5]], [[22i,33],[44,55]], [LONG(3)], [FLOAT(4)], $
[DOUBLE(5)], [COMPLEX(6,7)], ['eight'])
The resulting output is:
 1 2 22 3 4.000000 5.000000 <6.000000,7.000000i> 'eight'
Example 2: Calling a FORTRAN Program
In this example, parameters are passed from PV‑WAVE to the FORTRAN external function. Variables from PV‑WAVE are passed as pointers to the FORTRAN function. In the FORTRAN function, the WLNL_GET* functions are used to retrieve the values of the variables. See "Discussion" for more information on these functions.
 
You can find the following listed file in:
%WAVE_DIR%\demo\interapp\linknloadfor\linknloadfor.f
 
INTEGER*4 FUNCTION WAVEPARAMS(ARGC, ARGP)
INTEGER*4 ARGC, ARGP(*)
 
BYTE                     B
INTEGER*2                S
INTEGER*4                I
INTEGER*4                L
REAL*4                   F
DOUBLE PRECISION         D
STRUCTURE /CMPLX/
REAL R,I
END STRUCTURE
RECORD /CMPLX/	 C
INTEGER*4                RET
CHARACTER*80             STR
INTEGER*4                NCHAR
 
INTEGER*4            WLNL_GETLONG
INTEGER*4            WLNL_GETINT32
INTEGER*4         WLNL_GETSTRING
INTEGER*4         WLNL_GETCOMPLEX
INTEGER*2            WLNL_GETSHORT
BYTE                 WLNL_GETBYTE
REAL                 WLNL_GETFLOAT
DOUBLE PRECISION     WLNL_GETDOUBLE
 
IF (LOC(ARGC) .NE. 8) THEN
PRINT *,'Wrong # of parameters',LOC(ARGC)
WAVEPARAMS = 0
RETURN
ENDIF
 
B   = WLNL_GETBYTE(%VAL(ARGP(1)))
S   = WLNL_GETSHORT(%VAL(ARGP(2)))
I   = WLNL_GETINT32(%VAL(ARGP(3)))
L   = WLNL_GETLONG(%VAL(ARGP(4)))
F   = WLNL_GETFLOAT(%VAL(ARGP(5)))
D   = WLNL_GETDOUBLE(%VAL(ARGP(6)))
RET = WLNL_GETCOMPLEX(%VAL(ARGP(7)),C.R,C.I)
NCHAR = WLNL_GETSTRING(%VAL(ARGP(8)),STR,%VAL(80))
 
     PRINT 100,B,S,I,L,F
100  FORMAT(' BYTE=',I3,',SHORT=',I6,',INT32=',I8,',LONG=',I8,
     +           ',REAL=',F10.5)
     PRINT 200,D,C.R,C.I
200  FORMAT(' DOUBLE=',D15.5,',COMPLEX=[',F10.5,',',F10.5,']')
     PRINT 300,STR,NCHAR
300    FORMAT(' STRING=',A80,',NCHAR=',I6)
 
WAVEPARAMS = 1
RETURN
END
Compiling the Example FORTRAN Routine
Instructions for building and running this example are in the file:
%WAVE_DIR%\demo\interapp\linknloadfor\README.md
Accessing the External Function with LINKNLOAD
The following PV‑WAVE code demonstrates how the FORTRAN function defined in this example could be invoked. This code is located in the file:
%WAVE_DIR%\demo\interapp\linknloadfor\linknloadfor.pro
 
ln = LINKNLOAD('linknloadfor.dll','waveparams', BYTE(1), 2, $
23i, LONG(3), FLOAT(4), DOUBLE(5), COMPLEX(6,7), 'eight')
The resulting output is:
BYTE=  1,SHORT=     2,INT32=      23,LONG=       3,REAL= 4.00000
 DOUBLE=    0.50000D+01,COMPLEX=[   6.00000,   7.00000]
 STRING=eight  ,NCHAR=     5
Using the INFO command, you can see that LINKNLOAD returns the scalar value 12345 as expected.
INFO, ln
; PV-WAVE prints: LN     LONG     =     12345
The example program works with both scalars and arrays since the actual FORTRAN program above only looks at the first element in the array and since PV‑WAVE collapses multi-dimensional arrays to one-dimensional arrays:
ln = LINKNLOAD('linknloadfor.dll','waveparams', [BYTE(1)], $
[[2,3],[4,5]], [[2i,3],[4,5]], [LONG(3)], [FLOAT(4)], $
[DOUBLE(5)], [COMPLEX(6,7)], ['eight'])
The resulting output is:
BYTE=  1,SHORT=     2,INT32=       2,LONG=       3,REAL= 4.00000
 DOUBLE=    0.50000D+01,COMPLEX=[   6.00000,   7.00000]
 STRING=eight  ,NCHAR=     5
Example 3
In this example, the PV‑WAVE program calls a C wrapper function, passing it PV‑WAVE variables as parameters. The C wrapper calls the FORTRAN function, passing along the same parameters. The FORTRAN function modifies the values of the PV‑WAVE variables it receives as parameters. The FORTRAN function then returns control to PV‑WAVE, passing to PV‑WAVE the result of the function. The new values, assigned to the PV‑WAVE variables by the FORTRAN program, are accessible within PV‑WAVE upon return from the FORTRAN program.
This example contains three programs:
*linknloadfor_wavevars.pro—A PV‑WAVE main program that uses LINKNLOAD to call a FORTRAN function. The PV‑WAVE program passes parameters to a C wrapper, which in turn calls the desired FORTRAN function to modify the PV‑WAVE parameters. When control is returned to PV‑WAVE, the values of the PV‑WAVE variables which were passed as parameters are changed.
*linknloadfor_wrapper.c—A C function that is called by PV‑WAVE, via LINKNLOAD. This routine is a C wrapper that allows a PV‑WAVE program to pass parameters to and invoke a FORTRAN function. The FORTRAN function can modify the values of the PV‑WAVE variables passed as parameters and the modified values will be accurately reflected upon return to PV‑WAVE.
*linknloadfor_wavevars.f—The FORTRAN function to be called by the C wrapper.
The C and PV-WAVE code used for this example can be found in the directory
%WAVE_DIR%\demo\interapp\linknloadfor_wavevars
Compiling and Linking the Programs
Instructions for building and running this example are in the file:
%WAVE_DIR%\demo\interapp\linknloadfor_wavevars\README.md