My problem is about calling a fotran DLL from VB.Net. This dll involves a derived data type.
I never had problems with Visual Basic 6.0, but I really don't know how to make all this work with VB.NET.
First of all I create a very simple dll which should export a record with various data types (a fixed lenght string, a numeric value and an array along with the number of items stored in it.
!-------------------------------------------------------------
subroutine CVF_VBNET_DLL(myrecord)
!DEC$ ATTRIBUTES DLLEXPORT::CVF_VBNET_DLL
!DEC$ ATTRIBUTES ALIAS : "CVF_VBNET_DLL" :: CVF_VBNET_DLL
! Variables
integer max_npts, i
parameter(max_npts=100)
type t_type
sequence
character*(20) mystring
double precision mypar
double precision myarray(max_npts)
integer npts
end type t_type
type(t_type)::myrecord
! Body of CVF_VBNET_DLL
! I fill up myrecord with some values
! I want VB.NET to read the values of this record
myrecord%mystring="This is mystring"
myrecord%mypar=123.456
myrecord%npts=10
do i=1,myrecord%npts
myrecord%myarray(i)=i**2
enddo
end subroutine CVF_VBNET_DLL
!-------------------------------------------------------------
I can easily use this DLL in Visual Basic 6 with the following code in a module
'------------------------------------------------------------
Option Explicit
Public Declare Sub CVF_VBNET_DLL Lib "CVF_VBNET_DLL" (myrecord As t_type)
Public Type t_type
mystring As String * 20
myval As Double
myarray(1 To 100) As Double
npts As Long
End Type
'------------------------------------------------------------
and this code in the form
'------------------------------------------------------------
Option Explicit
Private Sub Command1_Click()
Dim myrecord As t_type
Dim temp As String
Dim i As Long
Call CVF_VBNET_DLL(myrecord)
With myrecord
For i = 1 To .npts
temp = temp & .myarray(i) & vbCrLf
Next i
Text1.Text = .mystring
Text2.Text = .myval
Text3.Text = .npts
Text4.Text = temp
End With
End Sub
'------------------------------------------------------------
How can I do the same thing using Visual Basic .NET instead of VB 6 ???
Comments
: I never had problems with Visual Basic 6.0, but I really don't know how to make all this work with VB.NET.
:
: First of all I create a very simple dll which should export a record with various data types (a fixed lenght string, a numeric value and an array along with the number of items stored in it.
:
: !-------------------------------------------------------------
: subroutine CVF_VBNET_DLL(myrecord)
:
: !DEC$ ATTRIBUTES DLLEXPORT::CVF_VBNET_DLL
: !DEC$ ATTRIBUTES ALIAS : "CVF_VBNET_DLL" :: CVF_VBNET_DLL
:
:
: ! Variables
: integer max_npts, i
: parameter(max_npts=100)
:
: type t_type
: sequence
: character*(20) mystring
: double precision mypar
: double precision myarray(max_npts)
: integer npts
: end type t_type
:
: type(t_type)::myrecord
:
: ! Body of CVF_VBNET_DLL
: ! I fill up myrecord with some values
: ! I want VB.NET to read the values of this record
:
: myrecord%mystring="This is mystring"
: myrecord%mypar=123.456
: myrecord%npts=10
: do i=1,myrecord%npts
: myrecord%myarray(i)=i**2
: enddo
:
: end subroutine CVF_VBNET_DLL
: !-------------------------------------------------------------
:
: I can easily use this DLL in Visual Basic 6 with the following code in a module
:
: '------------------------------------------------------------
: Option Explicit
:
: Public Declare Sub CVF_VBNET_DLL Lib "CVF_VBNET_DLL" (myrecord As t_type)
:
: Public Type t_type
: mystring As String * 20
: myval As Double
: myarray(1 To 100) As Double
: npts As Long
: End Type
: '------------------------------------------------------------
:
: and this code in the form
:
: '------------------------------------------------------------
: Option Explicit
:
: Private Sub Command1_Click()
: Dim myrecord As t_type
: Dim temp As String
: Dim i As Long
:
: Call CVF_VBNET_DLL(myrecord)
:
: With myrecord
: For i = 1 To .npts
: temp = temp & .myarray(i) & vbCrLf
: Next i
: Text1.Text = .mystring
: Text2.Text = .myval
: Text3.Text = .npts
: Text4.Text = temp
: End With
: End Sub
: '------------------------------------------------------------
:
: How can I do the same thing using Visual Basic .NET instead of VB 6 ???
:
For starters your going to run into the fact that .NET doesn't support fixed length strings natively and uses zero-based arrays....
Here is how u can re-declare your t_type in VB.NET....
[code]
Public Structure t_type
Public mystring As String
Public myval As Double
Public myarray As Array
Public npts As Integer
End Structure
[/code]
To simulate 1 based VB6 arrays your going to have to declare the structure then set the myarray value like so ...
[code]
Dim t As t_type
t.myarray = Array.CreateInstance( _
GetType(Double), _
New Integer() {100}, _
New Integer() {1})
[/code]
However, if possible convert the array to zero-base instead and reword the Structure like so ....
[code]
Public Structure t_type
Public mystring As String
Public myval As Double
Public myarray As Double(99)
Public npts As Integer
End Structure
[/code]
VBFixedString attribute object changes the default behavior of .NET strings to a fixed length string rather than the typical variable length string. In order to use this attribute, you must have the Microsoft.VisualBasic namespace imported. In fact to do anything u could originally do in VB6, I would completely investigate that namespace. Hopefully this gets u started.
[blue]
Private Sub Command1_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles Command1.Click
Dim myrecord As t_type
Dim temp As String
Dim i As Integer
Call CVF_VBNET_DLL(myrecord)
With myrecord
For i = 1 To .npts
temp = temp & .myarray(i) & vbCrLf
Next i
Text1.Text = .mystring
Text2.Text = CStr(.myval)
Text3.Text = CStr(.npts)
Text4.Text = temp
End With
End Sub
[/blue]
and this code in a module
[blue]
Module Module1
Public Declare Sub CVF_VBNET_DLL Lib "CVF_VBNET_DLL" (ByRef myrecord As t_type)
Public Structure t_type
Public mystring As String
Dim myval As Double
Dim myarray() As Double
Dim npts As Integer
Public Sub Initialize()
ReDim myarray(100)
End Sub
End Structure
End Module
[/blue]
but when compiling I get this error:
[red]
Can not marshal field myarray of type t_type: This type can not be marshaled as a structure field.
[/red]
I'm geting confused...
: : My problem is about calling a fotran DLL from VB.Net. This dll involves a derived data type.
: : I never had problems with Visual Basic 6.0, but I really don't know how to make all this work with VB.NET.
: :
: : First of all I create a very simple dll which should export a record with various data types (a fixed lenght string, a numeric value and an array along with the number of items stored in it.
: :
: : !-------------------------------------------------------------
: : subroutine CVF_VBNET_DLL(myrecord)
: :
: : !DEC$ ATTRIBUTES DLLEXPORT::CVF_VBNET_DLL
: : !DEC$ ATTRIBUTES ALIAS : "CVF_VBNET_DLL" :: CVF_VBNET_DLL
: :
: :
: : ! Variables
: : integer max_npts, i
: : parameter(max_npts=100)
: :
: : type t_type
: : sequence
: : character*(20) mystring
: : double precision mypar
: : double precision myarray(max_npts)
: : integer npts
: : end type t_type
: :
: : type(t_type)::myrecord
: :
: : ! Body of CVF_VBNET_DLL
: : ! I fill up myrecord with some values
: : ! I want VB.NET to read the values of this record
: :
: : myrecord%mystring="This is mystring"
: : myrecord%mypar=123.456
: : myrecord%npts=10
: : do i=1,myrecord%npts
: : myrecord%myarray(i)=i**2
: : enddo
: :
: : end subroutine CVF_VBNET_DLL
: : !-------------------------------------------------------------
: :
: : I can easily use this DLL in Visual Basic 6 with the following code in a module
: :
: : '------------------------------------------------------------
: : Option Explicit
: :
: : Public Declare Sub CVF_VBNET_DLL Lib "CVF_VBNET_DLL" (myrecord As t_type)
: :
: : Public Type t_type
: : mystring As String * 20
: : myval As Double
: : myarray(1 To 100) As Double
: : npts As Long
: : End Type
: : '------------------------------------------------------------
: :
: : and this code in the form
: :
: : '------------------------------------------------------------
: : Option Explicit
: :
: : Private Sub Command1_Click()
: : Dim myrecord As t_type
: : Dim temp As String
: : Dim i As Long
: :
: : Call CVF_VBNET_DLL(myrecord)
: :
: : With myrecord
: : For i = 1 To .npts
: : temp = temp & .myarray(i) & vbCrLf
: : Next i
: : Text1.Text = .mystring
: : Text2.Text = .myval
: : Text3.Text = .npts
: : Text4.Text = temp
: : End With
: : End Sub
: : '------------------------------------------------------------
: :
: : How can I do the same thing using Visual Basic .NET instead of VB 6 ???
: :
:
:
: For starters your going to run into the fact that .NET doesn't support fixed length strings natively and uses zero-based arrays....
:
: Here is how u can re-declare your t_type in VB.NET....
:
: [code]
: Public Structure t_type
: Public mystring As String
: Public myval As Double
: Public myarray As Array
: Public npts As Integer
: End Structure
: [/code]
:
: To simulate 1 based VB6 arrays your going to have to declare the structure then set the myarray value like so ...
:
: [code]
: Dim t As t_type
: t.myarray = Array.CreateInstance( _
: GetType(Double), _
: New Integer() {100}, _
: New Integer() {1})
: [/code]
:
: However, if possible convert the array to zero-base instead and reword the Structure like so ....
:
: [code]
: Public Structure t_type
: Public mystring As String
: Public myval As Double
: Public myarray As Double(99)
: Public npts As Integer
: End Structure
: [/code]
:
: VBFixedString attribute object changes the default behavior of .NET strings to a fixed length string rather than the typical variable length string. In order to use this attribute, you must have the Microsoft.VisualBasic namespace imported. In fact to do anything u could originally do in VB6, I would completely investigate that namespace. Hopefully this gets u started.
:
After you declare the t_type variable type, try calling the initialize method that was defined in the structure before using myarray:
[code]
Dim myrecord As t_type
' Then Call the Initialize method (Which internally Redim's myarray)
myrecord.Initialize()
[/code]
Now this compiles fine, however I use Visual Studio's .NET. Before I added the call to the Initialize function, I was getting a NullException error.
[code]
"An unhandled exception of type 'System.TypeLoadException' occurred in Project1.exe
Additional information: Can not marshal field myarray of type t_type: This type can not be marshaled as a structure field."
[/code]
:
: After you declare the t_type variable type, try calling the initialize method that was defined in the structure before using myarray:
:
: [code]
: Dim myrecord As t_type
:
: ' Then Call the Initialize method (Which internally Redim's myarray)
: myrecord.Initialize()
: [/code]
:
: Now this compiles fine, however I use Visual Studio's .NET. Before I added the call to the Initialize function, I was getting a NullException error.
:
: [code]
: "An unhandled exception of type 'System.TypeLoadException' occurred in Project1.exe
:
: Additional information: Can not marshal field myarray of type t_type: This type can not be marshaled as a structure field."
:
: [/code]
: :
: : After you declare the t_type variable type, try calling the initialize method that was defined in the structure before using myarray:
: :
: : [code]
: : Dim myrecord As t_type
: :
: : ' Then Call the Initialize method (Which internally Redim's myarray)
: : myrecord.Initialize()
: : [/code]
: :
: : Now this compiles fine, however I use Visual Studio's .NET. Before I added the call to the Initialize function, I was getting a NullException error.
: :
:
:
Just for a joke try changing the Redim statement in the Initialize routine to:
[code]
ReDim myarray(99)
[/code]
If that dont work then im out of ideas. But just to let you know the TypeLoadException is thrown in response to an HRESULT being thrown from the COM runtime. This means its something on the FORTRAN end throwing the error. Meaning you are passing in a data type that was not compatible with the FORTRAN data type. But im just theorizing a bit as well.
My guess is that FORTRAN arrays are one-based (correct me on that) and your array expects 100 elements on the FORTRAN side. However, on the VB.NET side your passing in an array with 101 elements because VB.NET arrays are zero-based.
: : [code]
: : "An unhandled exception of type 'System.TypeLoadException' occurred in Project1.exe
: :
: : Additional information: Can not marshal field myarray of type t_type: This type can not be marshaled as a structure field."
: :
: : [/code]
: : :
: : : After you declare the t_type variable type, try calling the initialize method that was defined in the structure before using myarray:
: : :
: : : [code]
: : : Dim myrecord As t_type
: : :
: : : ' Then Call the Initialize method (Which internally Redim's myarray)
: : : myrecord.Initialize()
: : : [/code]
: : :
: : : Now this compiles fine, however I use Visual Studio's .NET. Before I added the call to the Initialize function, I was getting a NullException error.
: : :
: :
: :
:
: Just for a joke try changing the Redim statement in the Initialize routine to:
:
: [code]
: ReDim myarray(99)
: [/code]
:
: If that dont work then im out of ideas. But just to let you know the TypeLoadException is thrown in response to an HRESULT being thrown from the COM runtime. This means its something on the FORTRAN end throwing the error. Meaning you are passing in a data type that was not compatible with the FORTRAN data type. But im just theorizing a bit as well.
:
: My guess is that FORTRAN arrays are one-based (correct me on that) and your array expects 100 elements on the FORTRAN side. However, on the VB.NET side your passing in an array with 101 elements because VB.NET arrays are zero-based.
:
Well, I already tried to type
[code]
ReDim myarray(99)
[/code]
and the TypeLoadException is unfortunately always present...
The fortran dll I wrote simply gives as output that data type (no input arguments) which VB.Net doesn't seem to accept...
However you are right: fortran arrays are 1-based and it's important that the maximum number of elements is the same both for VB and fortran. The fact I don't understand is that I used to do everything in a trivial way with VB6 and know instead something is going wrong.
By the way thanks a lot for trying to help me!
[b][red]This message was edited by iwilld0it at 2003-5-20 12:12:14[/red][/b][hr]
: : : I have already tried the initialize method and I agree with you that everything compiles fine, but when call my dll I get always that error:
: : : [code]
: : : "An unhandled exception of type 'System.TypeLoadException' occurred in Project1.exe
: : :
: : : Additional information: Can not marshal field myarray of type t_type: This type can not be marshaled as a structure field."
: : :
: : : [/code]
: : : :
: : : : After you declare the t_type variable type, try calling the initialize method that was defined in the structure before using myarray:
: : : :
: : : : [code]
: : : : Dim myrecord As t_type
: : : :
: : : : ' Then Call the Initialize method (Which internally Redim's myarray)
: : : : myrecord.Initialize()
: : : : [/code]
: : : :
: : : : Now this compiles fine, however I use Visual Studio's .NET. Before I added the call to the Initialize function, I was getting a NullException error.
: : : :
: : :
: : :
: :
: : Just for a joke try changing the Redim statement in the Initialize routine to:
: :
: : [code]
: : ReDim myarray(99)
: : [/code]
: :
: : If that dont work then im out of ideas. But just to let you know the TypeLoadException is thrown in response to an HRESULT being thrown from the COM runtime. This means its something on the FORTRAN end throwing the error. Meaning you are passing in a data type that was not compatible with the FORTRAN data type. But im just theorizing a bit as well.
: :
: : My guess is that FORTRAN arrays are one-based (correct me on that) and your array expects 100 elements on the FORTRAN side. However, on the VB.NET side your passing in an array with 101 elements because VB.NET arrays are zero-based.
: :
:
: Well, I already tried to type
: [code]
: ReDim myarray(99)
: [/code]
: and the TypeLoadException is unfortunately always present...
: The fortran dll I wrote simply gives as output that data type (no input arguments) which VB.Net doesn't seem to accept...
: However you are right: fortran arrays are 1-based and it's important that the maximum number of elements is the same both for VB and fortran. The fact I don't understand is that I used to do everything in a trivial way with VB6 and know instead something is going wrong.
: By the way thanks a lot for trying to help me!
:
:
1 more thing ... have you tried catching the TypeLoadException and querying its properties for more information. Maybe it will give more clues.
[code]
Try
' Call External DLL
Catch Err As TypeLoadException
' Query Err properties here (Their Should Be A TypeName Property)
End Try
[/code]
you should also be able to find out which HRESULT it maps to and then you could look up the info under COM documentation. Good luck from here on. Let me know if u ever figure it out. I am very curious to how silly and stupid the solution probably really is.
-----------------------------------------------------------------------
Actually i thought of one more thing. When you leave out ByVal or ByRef under VB.NET (contrary to VB6) ByVal is assumed. Try adding the ByRef keyword before the t_type argument in the prototype of your External dll function. Because ByVal would attempt to Marshal data instead of passing the exact reference.
[code]
Public Declare Sub CVF_VBNET_DLL Lib "CVF_VBNET_DLL" (ByRef myrecord As t_type)
[/code]