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

Access violation when calling callback function

I have a legacy application that is mixed language C++ / Fortran and have upgraded from CVF to Intel Fortran and from Visual Studio 6 to VS 2015.

There is an OLE Server (in C++ with cdecl calling convention) and a bunch of Fortran functions as static LIBs and 2 Fortran DLLs. All Fortran LIB project files are set to use C, REFERENCE calling convention as well as the DLLs

The problems is that I am getting access violation in a Fortran DLL when calling a function pointer that was passed in from the OLE Server.

Here is the sequence with some code samples:

1. C++ OLE Server creates thread using createthreadex.

2. The thread calls Fortran DLL function 'A'

    SUBROUTINE A(index)
    IMPLICIT NONE
    INTERFACE
        SUBROUTINE dllFunction (name,
    2                    environ,
    3                    addr_rto_get_integer)

            !DEC$ATTRIBUTES DLLIMPORT :: dllFunction

            !DEC$ATTRIBUTES VALUE :: addr_rto_get_integer

            CHARACTER*(*)    name

            INTEGER*4    environ

        END SUBROUTINE dllFunction
    END INTERFACE

    EXTERNAL    rto_get_integer

    INTEGER*4    addr_rto_get_integer

    addr_rto_get_integer = %LOC(rto_get_integer)

    CALL dllFunction (name,

    2            environ,
    3            addr_rto_get_integer)

    RETURN
    END 


    SUBROUTINE dllFunction(name,
    2                    environ,
    3                    addr_rto_con_setup)

    !DEC$ATTRIBUTES DLLEXPORT :: dllFunction

    !!DEC$ATTRIBUTES REFERENCE :: addr_rto_get_integer

    INTEGER*4    addr_rto_get_integer

    addr_get_integer = addr_rto_get_integer

    opti.print = new_rto_get_integer(' ', name, 'print', 0)

    END ! optim

2.  Common block definition:

    INTEGER*4    addr_get_integer

    COMMON /opt_addresses/ addr_con_setup,

    1        addr_get_integer

    POINTER (addr_get_integer, new_rto_get_integer)

    INTERFACE
        FUNCTION new_rto_get_integer(list, item, field, sub)
            INTEGER*4    new_rto_get_integer
            !DEC$ATTRIBUTES C, alias:'_NEW_RTO_GET_INTEGER':: new_rto_get_integer
            CHARACTER*(*)    list
            CHARACTER*(*)    item
            CHARACTER*(*)    field
            INTEGER*4        sub
        END FUNCTION new_rto_get_integer
    END INTERFACE


4. When the dllFunction tries to use the function pointer addr_rto_get_integer an access violation occurrs.


opti.print = new_rto_get_integer(' ', name, 'print', 0) <<<--- Access violation 

Any ideas?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


An example of the function in the common block is shown below:


    INTERFACE
        FUNCTION new_rto_get_integer(list, item, field, sub)
            INTEGER*4    new_rto_get_integer
            !DEC$ATTRIBUTES C, alias:'_NEW_RTO_GET_INTEGER':: new_rto_get_integer
            CHARACTER*(*)    list
            CHARACTER*(*)    item
            CHARACTER*(*)    field
            INTEGER*4        sub
        END FUNCTION new_rto_get_integer
    END INTERFACE

0 Kudos
13 Replies
Highlighted
Black Belt
74 Views

One of the issues in porting from CVF to Intel Fortran is that of how to pass the hidden arguments containing the lengths of character string arguments. Your DLL function may expect the CVF calling convention to be used and, if so, you need to specify /iface:cvf. For details, see https://software.intel.com/en-us/node/525130 .

0 Kudos
Highlighted
Beginner
74 Views

Not quite sure why that would cause an access violation when calling a callback function. Don't really want to revert the calling convention back to make it compatible with the old CVF as that seems a step back.

 

0 Kudos
Highlighted
Black Belt
74 Views

Is the callback function written in C++?  If so, as mecej4 says, then it needs to be altered to account for the change in calling convention associated with the change in compiler (or tell the Fortran compiler to use the previous calling convention), otherwise it will be looking for things in its argument list that are not the things it expects (rather than getting a character length, it will get a machine pointer value, or rather than getting a machine pointer value it will get a character length).  Regardless of the language it is written in, perhaps show the relevant declarations for the actual call back procedure.

Perhaps things have been elided for the sake of example, but there are a number of other issues. Where is `index` declared in subroutine `A`?  In dllFunction, none of the dummy arguments are declared, and `addr_rto_get_integer` appears to be referenced when undefined.  What is `opti`?

0 Kudos
Highlighted
Black Belt
74 Views

Mark I. wrote:

Not quite sure why that would cause an access violation when calling a callback function. Don't really want to revert the calling convention back to make it compatible with the old CVF as that seems a step back.

The callback function, if it has character string arguments and is written in Fortran, expects the string lengths to be passed to it in a certain way (CVF convention, probably). Unless you change the source code of the caller (in the OLE server?), you cannot use a new callback function that follows the Intel Fortran default calling convention.

0 Kudos
Highlighted
Beginner
74 Views

I edited the code to reduce size.

The callback function is a Fortran function with the default calling convention.

    SUBROUTINE rto_put_integer(list, object, field, sub, value)

    CHARACTER    list*(*), object*(*), field*(*)

    INTEGER*4    sub
    INTEGER*4    value

'opti' is a record in a common block:

'index' is n INTEGER*4 - guess I removed it when editing the code for publishing..

    STRUCTURE    /OPT_INTS/
        INTEGER*4    nv                ! number of variables
        INTEGER*4    nc                ! number of constraints
        INTEGER*4    type            ! Optimiser Type
        INTEGER*4    lp_option        ! LP Option
        INTEGER*4    max_opt            ! Max Optimisation Iterations
        INTEGER*4    max_fps            ! Max Feasible Point Iterations
        INTEGER*4    max_iter        ! .. used for either (dynamic)
        INTEGER*4    max_relax        ! Max Relaxation Steps
        INTEGER*4    update            ! Update B or H matrix
        INTEGER*4    formula            ! Use DFP or BFGS
        INTEGER*4    print            ! Diagnostic print controller
        INTEGER*4    vdid            ! Virt. disp. ID of "Running..Optimiser" box
        INTEGER*4    liwork            ! Size of Integer Work Space
        INTEGER*4    lwork             ! Size of Real Work Space
        INTEGER*4    iter_opt        ! Optimisation Iterations
        INTEGER*4    iter_fps        ! Feasible Point Iterations
        INTEGER*4    max_hess        ! Max Hessian Resets
        INTEGER*4    ncf_type        ! Type used by E04NCF
        INTEGER*4    num_qpa            ! No. of calls to qpapprox
        INTEGER*4    num_opm            ! No. of calls to opt_plant_model
        INTEGER*4    lun                ! LUN used for diagnostics
        INTEGER*4    phase            ! Current Phase
        INTEGER*4    NNZ                ! No of non-zeros in Jacobian
        INTEGER*4    environ            ! Underlying Model Package
        INTEGER*4    naughty            ! Pick on a bad constraint
        INTEGER*4    verify            ! Verify level
        INTEGER*4    dv_level        ! Derivative level
        INTEGER*4    scale_opt        ! Scale option
        INTEGER*4    max_cold        ! No. of COLD starts, before we go WARM
        INTEGER*4    spare(3)        ! 
    END STRUCTURE

    RECORD    /OPT_INTS/    opti

In the solution there are lot of Fortran files that declare the dummy arguments and others that don't. This worked on CSV without issue (It's a program ported from VAX, then MS Fortran, then CVF and now Intel :-)). I did want to make then all the same but ended up leavinng them alone. I only have to worry about dummy string lengths with mixed language calls rather than same language calls, right?

I could try creating a parameter less callback and see if that gets the access violation too to check that it's not a parameter problem.

0 Kudos
Highlighted
Black Belt
74 Views

If your OLE server was written to work with CVF, its expected function prototype is

void rto_put_integer(
    char *list,      int l_list,
    char *object,    int l_object, 
    char *field,     int l_field, 
    int *sub,        int *value
   );

With Intel Fortran, with the default calling convention, the actual function prototype is

void rto_put_integer(
    char *list, char *object, char *field, int *sub,  int *value, 
    int l_list, int l_object, int l_field
    );

The "hidden length arguments" are l_list, l_object and l_field.

You can use the /iface:mixed_str_len_arg compiler option to make Intel Fortran use the CVF convention. See https://software.intel.com/en-us/node/579741 . Since you are already able to link, STDCALL and the @nn name decoration of CVF are probably irrelevant.

This may be a good time to replace the nonstandard STRUCTURE and RECORD features of VAX Fortran, using the standard "derived data types" of Fortran 90 and later.

0 Kudos
Highlighted
Beginner
74 Views

The 'rto_get_integer' function is defined in the same DLL as the function that calls the callbacks and not in the OLE server. I didn't think  you needed to add the dummy arguments if Fortran is calling Fortran?

0 Kudos
Highlighted
Black Belt
74 Views

Mark I. wrote:

I didn't think  you needed to add the dummy arguments if Fortran is calling Fortran?

Correct (for the string length arguments).

0 Kudos
Highlighted
Beginner
74 Views

Any more ideas guys? I'm really stuck on this.

0 Kudos
Highlighted
Valued Contributor II
74 Views

Not sure if I can help and the answer to my question is probably in code you haven't shown, but I do not see the variable name defined in your routine A. Is it a module variable or am I simply overlooking it?

 

0 Kudos
Highlighted
Valued Contributor III
74 Views

I found the code snippets a a bit confusing to follow. The heading "2.  Common block definition: " has  the interface to new_rto_get_integer is this "common block" actually part of an include file?  When we have the call:

opti.print = new_rto_get_integer(' ', name, 'print', 0) <<<--- Access violation  

can we actual see the explicit interface of new_rto_get_integer, we need to? Clearly you will also get an access violation if this function tries to modify parameters 1,3 and 4 as they are passed as constants (yes I know stating the obvious but we can all sometimes suffer "code blindness" and non of the calls you show specify INTENT).

0 Kudos
Highlighted
Beginner
74 Views

Hi Andrew,

The common block is in a seperate include file.

The function new_rto_get_inteteger never gets executed as the access violation occurrs when trying to call the function but the new_rto_get_integer doesn't modify the params.

We all get code blind at times :-))

0 Kudos
Highlighted
Black Belt
74 Views

Mark I. wrote:

Any more ideas guys? I'm really stuck on this.

I'm afraid that this thread is too fragmented and disorganized. At best, we could add to the confusion by doing more speculation.

If you want more useful comments, you can post complete simplified example code(s) that we can compile and run ourselves, and (i) works with CVF and (ii) does not work with IFort. It may be best to start a new thread with that.

0 Kudos