Calling VB.NET dll from Intel Visual Fortran Console Application

Calling VB.NET dll from Intel Visual Fortran Console Application

Dear All,

I'm trying to call a VB.NET dll (written in Visual Studio 2010 Professional) using a Fortran console application (Intel Parallel Studio XE 2011), but am struggling.

To try and work out where I am going wrong, I tried setting up a simple VB.NET dll which takes an input and multiplies it by 2, returning the result. I did this as a ComClass in VB, code as follows:

<ComClass(Testdll.ClassId, Testdll.InterfaceId, Testdll.EventsId)> _
Public Class Testdll

#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "e448881b-810c-45fe-99d7-f52d33b07ff5"
Public Const InterfaceId As String = "0f182101-be2a-48b9-b38e-3d0b1821e9b6"
Public Const EventsId As String = "1a6ce0ff-660b-4d56-9d2d-65c02571df5f"
#End Region

' A creatable COM class must have a Public Sub New()
' with no parameters, otherwise, the class will not be
' registered in the COM registry and cannot be created
' via CreateObject.
Public Sub New()
MyBase.New()
End Sub

Public Function TimesTwo(ByRef dInput As Double) As Double

Return (2 * dInput)

End Function

End Class

I built this to C:\Windows\System32\ and registered it by opening a command prompt and browsing to the folder containing RegAsm.exe and typing "RegAsm.exe TestdllVB.dll /tlb:TestdllVBdll.tlb /codebase" (i.e. register it, export the type library and add it to the codebase).

I then used the Fortran module import wizard and imported it as a COM object by browsing to the tlb file. I added some code for the console window and to tried to call the function in the dll as follows:

! TestdllVB.f90

! This module contains the COM interfaces of the objects defined in
! C:\Windows\System32\TestdllVB.tlb
! Generated by the Fortran Module Wizard on 03/26/13

MODULE TestdllVB
USE IFWINTY
USE IFCOM
IMPLICIT NONE

! CLSIDs
TYPE (GUID), PARAMETER :: CLSID_Testdll = &
GUID(#E448881B, #810C, #45FE, &
CHAR('99'X)//CHAR('D7'X)//CHAR('F5'X)//CHAR('2D'X)// &
CHAR('33'X)//CHAR('B0'X)//CHAR('7F'X)//CHAR('F5'X))

! IIDs
TYPE (GUID), PARAMETER :: IID__Testdll = &
GUID(#0F182101, #BE2A, #48B9, &
CHAR('B3'X)//CHAR('8E'X)//CHAR('3D'X)//CHAR('0B'X)// &
CHAR('18'X)//CHAR('21'X)//CHAR('E9'X)//CHAR('B6'X))

! Interfaces
INTERFACE
INTEGER(4) FUNCTION $Testdll_TimesTwo($OBJECT, dInput, pRetVal)
INTEGER(INT_PTR_KIND()), INTENT(IN) :: $OBJECT ! Object Pointer
!DEC$ ATTRIBUTES VALUE :: $OBJECT
REAL(8), INTENT(INOUT) :: dInput
!DEC$ ATTRIBUTES REFERENCE :: dInput
REAL(8), INTENT(OUT) :: pRetVal
!DEC$ ATTRIBUTES REFERENCE :: pRetVal
!DEC$ ATTRIBUTES STDCALL :: $Testdll_TimesTwo
END FUNCTION $Testdll_TimesTwo
END INTERFACE
POINTER($Testdll_TimesTwo_PTR, $Testdll_TimesTwo) ! routine pointer

! Module Procedures
CONTAINS
INTEGER(4) FUNCTION $$Testdll_TimesTwo($OBJECT, dInput, pRetVal)
IMPLICIT NONE

INTEGER(INT_PTR_KIND()), INTENT(IN) :: $OBJECT ! Object Pointer
!DEC$ ATTRIBUTES VALUE :: $OBJECT
REAL(8), INTENT(INOUT) :: dInput
!DEC$ ATTRIBUTES REFERENCE :: dInput
REAL(8), INTENT(OUT) :: pRetVal
!DEC$ ATTRIBUTES REFERENCE :: pRetVal
INTEGER(4) $RETURN
INTEGER(INT_PTR_KIND()) $VTBL ! Interface Function Table
POINTER($VPTR, $VTBL)
$VPTR = $OBJECT ! Interface Function Table
$VPTR = $VTBL + 28 ! Add routine table offset
$Testdll_TimesTwo_PTR = $VTBL
$RETURN = $Testdll_TimesTwo($OBJECT, dInput, pRetVal)
$$Testdll_TimesTwo = $RETURN
END FUNCTION $$Testdll_TimesTwo
END MODULE

!****************************************************************************
!
! PROGRAM: TestdllFortran
!
! PURPOSE: Entry point for the console application.
!
!****************************************************************************

program TestdllFortran

USE TestdllVB

implicit none

! Variables
INTEGER(INT_PTR_KIND()) :: $OBJECT
REAL(8) :: Input,RetVal,Output
INTEGER(4) :: Status, iunknown

! Body of TestdllFortran
CALL COMCreateObjectByGUID(CLSID_Testdll,CLSCTX_ALL,IID__Testdll,$OBJECT,Status)
CALL COMQueryInterface (iunknown,IID__Testdll,$OBJECT,status)

Output = $$Testdll_TimesTwo($OBJECT,Input,RetVal)

print *, 'Press any key to exit ...'
read (*,*)

end program TestdllFortran

When I build it and try to run it, I get the following error message "Unhandled exception at 0x001bfbf8 in TestdllFortran.exe: 0xC0000005: Access violation" and the debugger stops on the line that reads "CALL COMQueryInterface (iunknown,IID__Testdll,$OBJECT,status)"

If I comment out this line, I get an error message that reads "Unhandled exception at 0x012a101a in TestdllFortran.exe: 0xC0000005: Access violation reading location 0x00000000" and the debugger stops on the line that reads "$VPTR = $VTBL + 28 ! Add routine table offset"

I think there is an issue around passing $OBJECT, which seems to be passing a value of 0, but I am new to trying to call a VB.NET dll from Fortran.

Can anyone offer any advice or point me in the right direction? I have tried the links at http://software.intel.com/sites/products/documentation/doclib/stdxe/2013... but am still struggling, I can't find much by way of sample code for what I am trying to do.

Thanks!

12 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

I don't see a call to COMInitialize in there.... That is required. You also need a call to COMUninitialize at the end.

Steve - Intel Developer Support

>>...debugger stops on the line that reads "CALL COMQueryInterface (iunknown,IID__Testdll,$OBJECT,status)"

Please verify that functionality available in TestdllVB.dll could be called from another VB .NET test application. Since COMQueryInterface failed something is wrong with registration of the TestdllVB.dll.

Cita:

Steve Lionel (Intel) escribió:

I don't see a call to COMInitialize in there.... That is required. You also need a call to COMUninitialize at the end.

Thanks - I'll give this a try and see how it works out.

Cita:

Sergey Kostrov escribió:

>>...debugger stops on the line that reads "CALL COMQueryInterface (iunknown,IID__Testdll,$OBJECT,status)"

Please verify that functionality available in TestdllVB.dll could be called from another VB .NET test application. Since COMQueryInterface failed something is wrong with registration of the TestdllVB.dll.

I can get the dll to run as a user-defined function in VBA using an Excel 2010 macro-enabled workbook and the following code:

Public Function TestVB(dInput As Double) As Double

Application.Volatile

Dim dllTestVB As TestdllVB.Testdll
Set dllTestVB = New TestdllVB.Testdll

TestVB = dllTestVB.TimesTwo(dInput)

End Function

I'll also try calling from a .NET application as you suggest.

Thanks!

Cita:

Steve Lionel (Intel) escribió:

I don't see a call to COMInitialize in there.... That is required. You also need a call to COMUninitialize at the end.

OK, so I tried adding COMInitialise and COMUnitialise to the code as follows:

! TestdllVB.f90

! This module contains the COM interfaces of the objects defined in
! C:\Windows\System32\TestdllVB.tlb
! Generated by the Fortran Module Wizard on 03/26/13

MODULE TestdllVB
USE IFWINTY
USE IFCOM
IMPLICIT NONE

! CLSIDs
TYPE (GUID), PARAMETER :: CLSID_Testdll = &
GUID(#E448881B, #810C, #45FE, &
CHAR('99'X)//CHAR('D7'X)//CHAR('F5'X)//CHAR('2D'X)// &
CHAR('33'X)//CHAR('B0'X)//CHAR('7F'X)//CHAR('F5'X))

! IIDs
TYPE (GUID), PARAMETER :: IID__Testdll = &
GUID(#0F182101, #BE2A, #48B9, &
CHAR('B3'X)//CHAR('8E'X)//CHAR('3D'X)//CHAR('0B'X)// &
CHAR('18'X)//CHAR('21'X)//CHAR('E9'X)//CHAR('B6'X))

! Interfaces
INTERFACE
INTEGER(4) FUNCTION $Testdll_TimesTwo($OBJECT, dInput, pRetVal)
INTEGER(INT_PTR_KIND()), INTENT(IN) :: $OBJECT ! Object Pointer
!DEC$ ATTRIBUTES VALUE :: $OBJECT
REAL(8), INTENT(INOUT) :: dInput
!DEC$ ATTRIBUTES REFERENCE :: dInput
REAL(8), INTENT(OUT) :: pRetVal
!DEC$ ATTRIBUTES REFERENCE :: pRetVal
!DEC$ ATTRIBUTES STDCALL :: $Testdll_TimesTwo
END FUNCTION $Testdll_TimesTwo
END INTERFACE
POINTER($Testdll_TimesTwo_PTR, $Testdll_TimesTwo) ! routine pointer

! Module Procedures
CONTAINS
INTEGER(4) FUNCTION $$Testdll_TimesTwo($OBJECT, dInput, pRetVal)
IMPLICIT NONE

INTEGER(INT_PTR_KIND()), INTENT(IN) :: $OBJECT ! Object Pointer
!DEC$ ATTRIBUTES VALUE :: $OBJECT
REAL(8), INTENT(INOUT) :: dInput
!DEC$ ATTRIBUTES REFERENCE :: dInput
REAL(8), INTENT(OUT) :: pRetVal
!DEC$ ATTRIBUTES REFERENCE :: pRetVal
INTEGER(4) $RETURN
INTEGER(INT_PTR_KIND()) $VTBL ! Interface Function Table
POINTER($VPTR, $VTBL)
$VPTR = $OBJECT ! Interface Function Table
$VPTR = $VTBL + 28 ! Add routine table offset
$Testdll_TimesTwo_PTR = $VTBL
$RETURN = $Testdll_TimesTwo($OBJECT, dInput, pRetVal)
$$Testdll_TimesTwo = $RETURN
END FUNCTION $$Testdll_TimesTwo
END MODULE

!****************************************************************************
!
! PROGRAM: TestdllFortran
!
! PURPOSE: Entry point for the console application.
!
!****************************************************************************

program TestdllFortran

USE TestdllVB

implicit none

! Variables
INTEGER(INT_PTR_KIND()) :: $OBJECT
REAL(8) :: Input,RetVal,Output
INTEGER(4) :: Status, iunknown

! Body of TestdllFortran
CALL COMInitialise(Status)
CALL COMCreateObjectByGUID(CLSID_Testdll,CLSCTX_ALL,IID__Testdll,$OBJECT,Status)
CALL COMQueryInterface (iunknown,IID__Testdll,$OBJECT,status)

Output = $$Testdll_TimesTwo($OBJECT,Input,RetVal)

print *, 'Press any key to exit ...'
read (*,*)

CALL COMUninitialise()

end program TestdllFortran

And now I am getting these errors:

Description: Error 1 error LNK2019: unresolved external symbol _COMINITIALISE referenced in function _MAIN__

File:TestdllVB.obj

Description: Error 2 error LNK2019: unresolved external symbol _COMUNINITIALISE referenced in function _MAIN__

File: TestdllVB.obj

Description: Error 3 fatal error LNK1120: 2 unresolved externals

File: Debug\TestdllFortran.exe

Am I using COMInitialise and/or COMUninitialise incorrectly?

Thanks

Dave, This is a piece of codes from your initial post:
...
! Body of TestdllFortran
CALL COMCreateObjectByGUID( CLSID_Testdll, CLSCTX_ALL, IID__Testdll, $OBJECT, Status )
CALL COMQueryInterface( iunknown, IID__Testdll, $OBJECT, status )
...

1. When COMCreateObjectByGUID is called it should get IUnknown interface ( this is by design of COM API ). When COMQueryInterface is call try to pass interfaceId instead of iunknown.

2. Verify names for COMInitialise and COMUninitialise functions.

3. Verify a returned value in Status variable after a call to COMCreateObjectByGUID.

The spelling is US, not British.  IZE not ISE.

Steve - Intel Developer Support

Cita:

Steve Lionel (Intel) escribió:

The spelling is US, not British.  IZE not ISE.

D'oh!

I've corrected the spelling and it all works as intended now. Thank-you very much for the help!

>>...
>>! Body of TestdllFortran
>>CALL COMInitialise(Status)
>>CALL COMCreateObjectByGUID(...)
>>...

I wonder why Intel Fortran compiler did Not give a compilation error for a call to COMInitialise function?

Because the rules of the language allow you to reference a procedure with an implicit interface (an interface that the compiler determines based on the way the procedure is invoked).  Prior to F90 all procedure references relied on the equivalent of an implicit interface.

>>...Because the rules of the language allow you to reference a procedure with an implicit interface (an interface that
>>the compiler determines based on the way the procedure is invoked).

Thanks, Ian.

If it was a function reference, you could use IMPLICIT NONE to require a declaration, but for a CALL, there's nothing in the language to help you. There has been a request for an option to require an explicit interface for all procedures, an idea I like.

Steve - Intel Developer Support

Leave a Comment

Please sign in to add a comment. Not a member? Join today