PDA

View Full Version : Run time loading of dll


gentelam
06-18-2004, 11:02 PM
Hi,

Any one has an simple example showing run time loading of lahey fortran dll into C using -ml msvc .

I am trying to create one, but I am having a problem in supplying symbol name to "GetProcAddress()" to get the address from DLL.. No matter what symbol I use I get NULL pointer back. I have used dll_export for the subroutine in DLL.

Thanks for help,
Madhav.

tzeis
06-21-2004, 06:40 PM
I have an example in Fortran, but not in C. You can find a C example on the MSDN website if you look up the GetProcAddress method. In the Fortran example, the proc name is specified exactly the same as it appears in the Fortran dll_export statement, paying attention to case, and it is null terminated. You should also make sure that you have a valid handle for the DLL to send to GetProcAddress.

gentelam
06-23-2004, 03:27 PM
Hi,

Thank you for offering the example. I have got an example in FORTRAN. I just learned the symbol names should be as follows:

if the subroutine name is "dll_proc", symbol name for GetProcAddress need to be "_dll_proc@0" Pay close attention to numeric after "@" The value of numeric after @ is based on the arguments. If I have 1 int and 1 doulbe then the value is 8.

My new problems are :
1. If one of the argumets of dll proc is character string.

e.g.,
subroutine bar(c, i, d)
implicit none
dll_export :: bar
integer, intent(in) :: i
real, intent(in) :: d
character(len = *), intent(in) :: c

open(10,file="bar.dat")
write(10,*) "In bar!"
write(10,*) " i = ", i
write(10,*) " d = ", d
!write(10,*) " c = ", c
close(10)
end subroutine

lf95 foodll -dll -ml msvc
gives following error
2151-S: "foodll.f90", line 1: Dummy argument of procedure 'bar' which changed mixed language target is invalid.

If I remove the argument "c" then it works fine. What's wrong with character strings?

2. I have taken same dll and have written c main to call above proc from c. I have provided same symbol name. C gets the symbol correctly, but during run time when I call the dll proc I get exception error. I am not sure what is going on here?

Thanks
Madhav.

tzeis
06-23-2004, 07:25 PM
Since the CHARACTER argument is declared with an assumed length, the Fortran side expects there to be size information passed in, but the C side does not know about this, so the size information is missing. This is what is causing the compile time error. The variable should have a fixed length.
When character variables are passed as arguments, Fortran always adds a hidden length argument to the calling stack. The program is probably crashing because Fortran wants to see the hidden length argument, but it is not there. In the case of LF95, the hidden argument is added to the end of the stack, and it is passed by value. Other Fortran compilers will not necessarily do things that same way as LF95. When creating mixed language procedures, it is necessary to somehow account for this hidden argument. If you add the hidden argument on the C side, you will break any DLL calling conventions that add a stack size decoration, because Fortran counts the character argument as one item, but C will count it as two items. You are better off by synthesizing the character variable in your Fortran code. For example, if you use a Fortran INTEGER(kind=1) dummy argument to contain the string data, the hidden argument problems will not be encoutered, and you can convert from integer to character using the TRANSFER function. Here is a skeletal example of what the code might look like:
subroutine chararg(i)
dll_export :: chararg
integer(kind=1) :: i(10)
character(len=10) :: c
c = transfer(i,c)
write(*,*) c
end subroutine
You can get fancier by adding an integer argument indicating that size of the string, or by checking for a null terminator in the string:
subroutine chararg(i)
dll_export :: chararg
integer(kind=1) :: i(10)
integer :: l(1)
character(len=10) :: c
l = maxloc(i,i==0) - 1
if(l(1) <= len(c)) then
c(1:l(1)) = transfer(i(1:l(1)),c)
write(*,*) c
else
write(*,*) "Won't fit"
end if
end subroutine

gentelam
06-24-2004, 12:46 AM
Hi,

Thanks for your explanation. Now I understand what is happening behind the scenes. Mean time I have tried

subroutine bar(c, clen, i, d)
integer, intent(in) :: clen, i
real(8), intent(in) :: d
character(len = clen), intent(in) ::c

end subroutine bar

This worked just fine. I think the problem is with -ml msvc. If I remove -ml msvc then '*' also worked just fine. LF95 trying to be smart, when -ml msvc is specified, LF95 expects it to be called from C only so give an error at compile time about missing length argument. If I remove -ml msvc then LF95 knows that it is called from other Fortran and expects hidden length will be there, so there is no compiler error. That is why I assumed with -ml msvc I don't need to worry about extra hidden length argumet as LF95 is taking care behind the scenes. To get rid of error I have passes explicitly length as one extra argument and using it in argument declaration. The problem seem to work just fine.

Is this safe assumption or I am just lucky that this worked?

I have another concern related to runtime DLL (calling Fortran DLL from C)

for the same subroutine (shown above) I have Fortran DLL. Now I try to call this from C main.

I am able to load DLL and get the function pointer and call the function just fine. At the time of leaving my C function I get access violation error.

My C function looks like :

function pointer for DLL subroutine is :
void (*bar_c) (char *c, int *clen, int *i, double *d);

void __stdcall load_symbols(void)
{
dllPtr = LoadLibrar("foo.dll");

bar_c = (void *) GetProcAddress("_bar@16", dllPtr);

}

void __stdcall user_bar(char *c, int *clen, int *i, double *d)
{
bar_c(c, clen, i, d);

=========> control comes back here and just before it leaves user_bar()
=========> Access violation occurs.
}

int main()
{
char c[36] = "initialized in c main \0";
int clen, i;
double d;

load_symbols();

clen = strlen(c);
i = 10;
d = 100.0;

user_bar(c, &clen, &i, &d);

===========> control never comes back here.
}

Can you please tell me what is going on here? If you want I have simple tar file (210K) to run this problem I can mail you directly. Initially I have suspected char * argument and hidden length so I removed it and used only int, but I get same error. Fortran DLL is doing some thing here.

Thanks for your help.
Madhav.

tzeis
06-24-2004, 07:30 PM
I would say it is time to do some debugging by elimination. If you omit the call to the DLL does the program still crash?

gentelam
06-24-2004, 08:13 PM
Hi,
I have tried all combinations etc. As I said, The program works just fine if there are no arguments in fortran subroutine in dll i.e., subroutine bar (NO ARGUMENTS).

If I add one argument integer, then program crashes.

I just revised MSDN, it says in MSVC++/VC caller clears the stack allways, and In old MSVF called routine clears the stack just before it returns to the caller function.

Is this true in Lahey also, that called routine clears the stack (related to arguments) before control goes back to caller? If this is true then it explains part of the reason for crash.

One more strange thing is If I declare an local variable in user_bar() say int li; then program works just fine. I am sure some thing is happening with the memory. I am not sure what?

Thanks
Madhav.

tzeis
06-24-2004, 08:30 PM
The behavior depends on the calling convention specified in the -ml option. If you don't specify an -ml option, or specify -ml lf95, Fortran assumes that the caller will be cleaning up the stack. If you specify -ml msvc or -ml bc, then Fortran assumes that it (callee) will be responsible for cleaning up the stack.

gentelam
06-24-2004, 10:22 PM
Hi,

I am using -ml msvc option.
Microsoft default is Caller clears the stack which is same as -ml lf95. Why is it opposite for -ml msvc. Is there any particular reason whe it is done this way?

Also one more puzzling thing here is, If I declare a loca variable int i; in user_bar() function, my program seems to work fine. What made the difference here?

Thanks
Madhav.

tzeis
06-25-2004, 12:11 AM
The -ml msvc option maps to the stdcall2 convention while -ml bc maps to stdcall. I understand that these conventions tell the callee to cleanup. The default C convention that is usually not used with dlls is cdecl, which tells the caller to clean.
The only reason I can imagine why a local variable causes things to work is that its presence rearranges memory so that something non critical gets trashed.

gentelam
06-25-2004, 12:25 AM
Hi,

Thanks for your explanation. That is exactly what I was thinking. Now I have compiled whole of C code with /Gz which __stdcall convention. The code seems to work just fine.

I have also tried is removed the -ml msvc and took care of name mangling my self and passed the extra hidden argumet for character from C and it works like charm. Now I can use len = * for character dummy arguments also. I am getting all the values correctly.

I don't know if you work in Lahey, but can you please tell me if what I did is wrong here. (2nd method removing -ml msvc and took care of name mangling)

Thanks for your help though, it was good to have this kind of forums.

Regards,
Madhav.

tzeis
06-25-2004, 05:15 PM
When it comes to mixed language programming, there is usually more than one way to do things. Saying something is right or wrong is not really appropriate, because there is no standard or rules governing how the interfaces should appear. What is more important is understanding the underlying mechanisms and coming up with something that works. Here, the tests for "good" and "bad" are does it work, is it robust, and does it continues to work when you take the executable to a different machine and O/S.
That said, I will mention that the next Fortran standard will set rules for Fortran - C interoperability. At that point, it will be more useful to talk about doing things the right way.

gentelam
06-25-2004, 06:07 PM
thank you. It was a nice to understand what is happening behind the scenes for mixed language applications. I think now I have fair idea of what is happening.

Madhav.