Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
17 Views

Subroutine in DLL library accesing data in main program

Hi,

I'm trying to move some content into a dll but having problems with data access

 

1) Can a subroutine in the DLL (with ATTRIBUTES DLLEXPORT) access data in modules within the main program? Module Apples has private variable ICNT, and functions to modify/get the value::

MODULE Apples
 
  INTEGER, PRIVATE :: ICNT =0
 
  CONTAINS
 
  INTEGER FUNCTION howmany()
  HOWMANY=ICNT
  END FUNCTION howmany
 
  INTEGER FUNCTION add()
  ICNT=ICNT+1
  ADD=ICNT
  RETURN
  END FUNCTION add
 
END MODULE Apples

- In the dll, subroutine testdll wants to access the function in module:

SUBROUTINE testdll
  !DEC$ ATTRIBUTES DLLEXPORT::testdll

  USE Apples
  INTEGER I
 
  I=add()

END SUBROUTINE testdll

- and the main program, which accesses the functions in the module, as well as calls the subroutine in the dll.

PROGRAM test
    USE Apples
    implicit none
    INTEGER :: I
    
    I=add()
    I=add()
    I=howmany()
    
    !DEC$ ATTRIBUTES DLLIMPORT::testdll
    CALL testdll

     I=howmany()

    END PROGRAM test

I have put the Apples module in a static library, which is used by both main and testdll.

The problem is, the main program can access and iterate the module variable, whereas when the subroutine in the dll makes the call, the variable is zeroed again, as if the dll has its own copy of the module data.

So, how can the subroutine in the dll access the data in a module in the main program?

I have attached the solution.

2) A possibly related question is regarding what could be passed to the subroutine in dll. While there's no issue with simple data types, it was not possible to pass a pointer to a TYPE.

 

Thanks, Deniz


   

0 Kudos
5 Replies
Highlighted
Black Belt Retired Employee
17 Views

The answer to your first question is "You can't do that" with the approach you're taking. The way you did it, the main program and the DLL each have their own separate copy of Apples, which is why they don't talk to each other.

There are two approaches one can take:

1) Put any data that is to be shared in the DLL. If a module defines the shared data (the right approach) and DLLEXPORTs the variable(s), when the main program USEs the module the DLLEXPORT will turn into a DLLIMPORT. Note that you may get an informational message when you link the DLL that locally defined data is DLLIMPORTed, but you can ignore that.

2) Add a routine to the DLL which the main program calls to pass a pointer to the shared data, and the DLL saves it, referencing the pointer when needed. This is awkward and can also create reentrancy issues.

I don't understand your second question - one can certainly pass a pointer to a derived type, as long as both the caller and callee have that type defined. But to do that both need to be referencing the SAME type - that is, the same declaration, not another copy of it.

0 Kudos
Highlighted
Beginner
17 Views

Thanks Steve, this is much appreciated. Moving all shared data into dll's will be a big task (for the actual program), but clearly that's the proper path.

On my second question (and your second suggestion), I have attached a sample project, where module Pears defines a type and object of that type, with function to get a pointer to the object:

MODULE Pears
  TYPE, PUBLIC :: dat_Pears
    INTEGER, PUBLIC :: IPears=0
  END TYPE dat_Pears
  TYPE ( dat_Pears ), TARGET, PRIVATE :: Data_Pears
 
  CONTAINS
 
  FUNCTION Get_pear_ptr()
  TYPE ( dat_Pears ), POINTER :: Get_pear_ptr
  Get_pear_ptr => Data_Pears
  END FUNCTION Get_pear_ptr
 
END MODULE Pears
 

in the main program, I can get a pointer and modify etc..

    PROGRAM test
    USE Pears
    IMPLICIT NONE
    TYPE ( dat_Pears ), POINTER :: dat_Pears_ptr
    
    dat_Pears_ptr=>Get_pear_ptr()
    dat_Pears_ptr%IPears=dat_Pears_ptr%IPears+1
    
    !DEC$ ATTRIBUTES DLLIMPORT::testdll
    CALL testdll(dat_Pears_ptr)

    END PROGRAM test

 

but when I pass the pointer into the subroutine in dll:

SUBROUTINE testdll(dat_Pears_ptr)
  !DEC$ ATTRIBUTES DLLEXPORT::testdll
  USE Pears
  TYPE ( dat_Pears ), POINTER :: dat_Pears_ptr
  dat_Pears_ptr%IPears=dat_Pears_ptr%IPears+1
END SUBROUTINE testdll

dat_Pears_ptr becomes null. dat_Pears_ptr pointer in the main and dll refer to different TYPEs?

 

Best regards,

Deniz

0 Kudos
Highlighted
Black Belt Retired Employee
17 Views

The problem here is that routine testdll has a dummy argument that is a POINTER, and this requires that an explicit interface be visible to the caller. You didn't provide one, so the compiler dereferenced the pointer before sending it to testdll. If you replace the main program with the version I attached, it works.

I will comment that you REALLY, REALLY want to put module Pears in the DLL project and not the main program, as otherwise you end up with a circular build dependency.

0 Kudos
Highlighted
Beginner
17 Views

Thanks a lot, explicit interface it is then, good enough for a quick fix.

For the proper treatment, a lot of modules will need to be moved into dlls, which will take some time and commitment.

Best regards,

Deniz

 

 

0 Kudos
Highlighted
Black Belt Retired Employee
17 Views

Incidentally, if the called procedure was in the same project as the caller, the compiler could have alerted you to this error. My general advice is to always put procedures in modules, which then requires a USE to get at them and, incidentally, provide an explicit interface. With some unusual exceptions (generics, user-defined derived type I/O procedures, submodules), if you write an INTERFACE block for a Fortran procedure, you're doing it wrong.

0 Kudos