Editors

netCDf and VB 6.0

Find here code for vb.net

For scientific use please cite: Wieczorrek, C.; J. Planar Chromatogr. 18 (2005) 181-187 


Revised on 2008-08-04, thanks to Lowell Bahner, NOAA Office of Habitat Conservation


The following experience was made with netcdf.dll 3.6 on a Windows XP professionel system.


1. Error "Bad DLL Calling Convention"
All calls of netcdf.dll libraries from VB 6 causes an error "Bad DLL Calling Convetion". Do not care, just use an "On Error Resume Next" before every DLL call.


2. Returning strings from netcdf.dll functions
Returning strings from the netcdf.dll in VB6 is not an easy process!

a) the function nc_strerror
C-Definition

const char * nc_strerror(int ncerr);

Corresponding VB6 Definition:
Private Declare Function nc_strerror Lib "netcdf.dll" (ByVal ncerr as long) As Long
Surrender! No chance, this function is not talking to me :-(

b) the function nc_inq_libvers
C-Definition

const char * nc_inq_libvers(void);

Corresponding VB6 Definition:
Private Declare Function nc_inq_libvers Lib "netcdf.dll" () As Long
The returned value is a pointer to the string, so you need two API functions to get the string from the pointer,

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length as Long)
Private Declare Function IsBadStringPtr Lib "kernel32" Alias "IsBadStringPtrA" (ByVal lpsz As Long, ByVal ucchMax as Long) as Long

and a function that constructs the string:

Public Function StringFromPointer(StringPointer As Long, MaxLength As Long) As String
Dim sRet As String
Dim lret As Long
If lpString = 0 Then
StringFromPointer = ""
Exit Function
End If
If IsBadStringPtr(lpString, MaxLength) Then
StringFromPointer = ""
Exit Function
End If
sRet = Space$(MaxLength)
CopyMemory ByVal sRet, ByVal StringPointer, ByVal Len(sRet)
If Err.LastDllError = 0 Then
If InStr(sRet, Chr$(0)) > 0 Then
sRet = Left$(sRet, InStr(sRet, Chr$(0)) - 1)
End If
Else
sRet = ""
End If
StringFromPointer = sRet
End Function

To get the netcdf version write
Example:
Dim Version1 As Long
Dim Version2 As String
Version1 = nc_inq_libvers()
Version2 = StringFromPointer(Version1, 1024)


c) C-functions returning a string as "ByRef" parameter, e.g. nc_inq_dim, nc_inq_attname
C-Definitions:
int nc_inq_dim (int ncid, int dimid, char *name, size_t *lengthp);
int nc_inq_attname (int ncid, int varid, int attnum, char *name)


Corresponding VB6 Definitions do not work! The trick is to handle the ByRef-String as byte-array:
Private Declare Function nc_inq_dim Lib "netcdf.dll" (ByVal ncid As Long, ByVal dimid As Long, ByRef dimname As Byte, ByRef length As Long) As Long
Private Declare Function nc_inq_attname Lib "netcdf.dll" (ByVal ncid As Long, ByVal varid As Long, ByVal attnum As Long, ByRef attname As Byte) As Long


Define a bytearray to hold your string-characters
Dim bytename(1023) as Byte
Dim i As Integer

Initialize the array
For i = 0 to 1023
bytename(i) = 0
Next

Define a return-value for the dll functions
Dim errvalue As Long
nc_inq_dim returns also the length of the string
Dim stringlength As Long
Look for the requested dimension, handle as name the first element of your byte array
Example:
errvalue = nc_inq_dim(your_file_id, your_dimension_id, bytename(0), stringlength)
Dim dimensionname As String
For i = 0 to stringlength
dimensionname = dimensionname & Chr(bytename(i))
Next

errvalue = nc_inq_attname(your_file_id, your_variable_id, your_attribut_number, bytename(0))
Dim attributename As String
For i = 0 to stringlength
If bytename(i) = 0 Then Exit For
attributename = attributename & Chr(bytename(i))
Next



3. nc_create, nc_open
C-Definitions
int nc_create (const char* path, int mode, int *ncid);
int nc_open (const char* path, int mode, int *ncid);

Corresponding VB6 Definitions
Private Declare Function nc_create Lib "netcdf.dll" (ByVal path as String, ByVal mode as Long, ByRef ncid as Long) As Long
Private Declare Function nc_open Lib "netcdf.dll" (ByVal path as String, ByVal mode as Long, ByRef ncid as Long) As Long

No problems


4. nc_redef, nc_enddef, nc_close
C-Definitions
int nc_redef (int ncid);
int nc_enddef (int ncid);
int nc_close (int ncid);

Corresponding VB6 Definitions
Private Declare Function nc_redef Lib "netcdf.dll" (ByVal ncid) As Long
Private Declare Function nc_enddef Lib "netcdf.dll" (ByVal ncid) As Long
Private Declare Function nc_close Lib "netcdf.dll" (ByVal ncid) As Long

No problems


5. nc_def_dim
C-Definition
int nc_def_dim (int ncid, const char *name, size_t length, int *dimid);

Corresponding VB6 Definition
Private Declare Function nc_def_dim Lib "netcdf.dll" (ByVal ncid As Long, ByVal name As String, ByVal length As Long, ByRef dimid As Long) As Long

No problems


6. nc_put_att_text
C-Definition
int nc_put_att_text (int ncid, int varid, const char *name, size_t length, const char *attvalue);

Corresponding VB6 Definition
Private Declare Function nc_put_att_text Lib "netcdf.dll" (ByVal ncid As Long, ByVal varid As Long, ByVal name As String, ByVal length As Long, ByVal attvalue As String) As Long
(remember to handle strings ByVal and not ByRef)

No problems


7. nc_put_var_float
C-Definition
int nc_put_var_float (int ncid, int varid, const float *varvalue);

Corresponding VB6 Definition
Private Declare Function nc_put_var_float Lib "netcdf.dll" (ByVal ncid As Long, ByVal varid As Long, ByRef varvalue as Single)

Note:
For a netcdf variable without dimension write

Example:
Dim MyValue as Single
Call nc_put_var_float (ByVal File_ID, ByVal Variable_ID, MyValue)

For an netcdf variable with at least one dimensions and, let's say, 500 elements write

Example:
Dim MyValue(1 To 500) as Single
Call nc_put_var_float (ByVal File_ID, ByValVariable_ID, MyValue(1))

Just handle the first element of the VB6 array. This example set all 500 values into the netcdf variable

No problems


All functions from point 3. to 7. work without change in VB 6.


Now it becomes a little difficult.


8. nc_put_var_text
C-Definition
int nc_put_var_text (int ncid, int varid, const char *stringvalue)

Corresponding VB6 Definition
Private Declare Function nc_put_var_text Lib "netcdf.dll" (ByVal ncid As Long, ByVal varid As Long, ByVal stringvalue as String)

This works only if the variable has no dimensions!
If it has one dimension and this dimension has for example 50 elements, then the string must not exceed 50 bytes. If it has less than 50 bytes its must be filled up with Chr(0) to 50 bytes. This is because the string is interpreted as 50-element array, each element is one byte.

Example:
In my application chromatographic data is stored as CDF file. You can give every peak of the chromatogram a name. The CDF file contains the dimensions

string_32 = 32
peak_number = 7

and a variable

peak_name(peak_number, string_32)

In this example the chromatogram contains 7 peaks and you can attach up to 32 characters as name to every peak. The software automatically generates three-digit-numbers like 001, 002 as names.
To handle these seven 3-character peak names as one string the string has to be constructed as follows:

Dim PeakNames As String
For Counter = 1 To 7 ' one name for every peak
PeakNames = PeakNames & Format(Counter, "000") & String(29, Chr(0)) ' 32 bytes for every name
Next
Call nc_put_var_text (ByVal File_ID, ByVal Variable_ID, ByVal PeakNames)


9. nc_def_var
C-Definition
int nc_def_var (int ncid, const char *name, nc_type xtype, int ndims, const int dimids[], int *varid);

Corresponding VB6 Definition
Private Declare Function nc_def_var Lib "netcdf.dll" (ByVal ncid As Long, ByVal name As String, ByVal xtype As Long, ByVal ndims As Long, ByRef dimids() As Long, ByRef varid As Long) As Long

THIS DOES NOT WORK!

netcdf.dll requires dimids as C-array (4 bytes for every element). The VB-array dimids() contains some header bytes for every dimension and they are not suitable for the dll. I found two solutions:


Way A (suitable for variables with 0 or 1 dimension)
Handle the one-element-array as long.

Private Declare Function nc_def_var Lib "netcdf.dll" (ByVal ncid As Long, ByVal name As String, ByVal xtype As Long, ByVal ndims As Long, ByRef dimids As Long, ByRef varid As Long) As Long

Example:
Dim Dimension_ID As Long
Dim Dimensions As Long
Dim Variable_ID As Long
Dimension_ID = 2
Dimensions = 1 ' one dimensional variable
Call nc_def_var (ByVal File_ID, ByVal Variable_Name, ByVal Variable_Type, ByVal Dimensions, Dimension_ID, Variable_ID)


Way B (suitable for 0, 1 and multi-dimensional variables)
Handle the multi-element-array as string!

If you want to use both methods A nd B in your project you have to "recycle" the above declaration:

Private Declare Function nc_def_var_str Alias "nc_def_var" Lib "netcdf.dll" (ByVal ncid As Long, ByVal name As String, ByVal xtype As Long, ByVal ndims As Long, ByVal dimids As String, ByRef varid As Long) As Long

(remember to handle strings ByVal and not ByRef)

If you prefer to use only method B you can declare the function as follows:

Private Declare Function nc_def_var Lib "netcdf.dll" (ByVal ncid As Long, ByVal name As String, ByVal xtype As Long, ByVal ndims As Long, ByVal dimids As String, ByRef varid As Long) As Long

(remember to handle strings ByVal and not ByRef)

A C-long variable contains four bytes. So your string needs 4 bytes for every array-element. For example, your variable is two-dimensional with the varids Variable_1_ID = 3 and Variable_2_ID = 5. The bits in a long variable with value = 3 are equal to

11000000 | 00000000 | 00000000 | 00000000

and that is equal to a 4 byte string with character(3) & character(0) & character(0) & character(0)

So you can construct the string as follows:


Example:
Dim Variable_1_ID As Long
Dim Variable_2_ID As Long
Dim Dimension_ID As String * 8 ' four bytes for every dimension
Dim Dimensions As Long
Dim Variable_ID As Long
Dimensions = 2 ' two dimensional variable
Variable_1_ID = 3
Variable_2_ID = 5
' now construct the string
Dimension_ID = chr(Variable_1_ID) & chr(0) & chr(0) & chr(0) & chr(Variable_2_ID) & chr(0) & Chr(0) & Chr(0)
Call nc_def_var_str (ByVal File_ID, ByVal Variable_Name, ByVal Variable_Type, ByVal Dimensions, ByVal Dimension_ID, Variable_ID)


In this simple example the variable-id's must not be greater than 255. If higher values are needed the value must be devided into the 8 low-bits and 8 hight-bits:

Example:
Dimension_ID = chr(Integer from low-bits ID_1) & chr(Integer from high-bits ID_1) & chr(0) & chr(0) & chr(Integer from low-bits ID_2) & chr(Integer from high-bits ID_2) & Chr(0) & Chr(0)



The functions mentioned above are all functions used in our project. They all work fine with VB 6. All other netcdf functions should work as described or in a very similar way.


Good Luck.

 

Copyright 2018 by MACHEREY-NAGEL GmbH & Co. KG   Imprint / Impressum Privacy StatementTerms Of Use