Regarding DLL's, I have good experiences with encapsulating Fortran using
the CPPF77.ZIP technique, and then exporting C++ functions and classes from
the DLL, instead of the "raw" Fortran functions. That way, your application
will be unaware of the fact that Fortran is being used. I find this to be a
clean (and simple) approach.
Good Luck,
Carsten Arnholm
arn...@online.no
Sherman Chan <sc...@aspeninc.com> wrote in article
<6895ot$9va$1...@gte1.gte.net>...
In article <6895ot$9va$1...@gte1.gte.net>, Sherman Chan <sc...@aspeninc.com>
writes:
|>I am looking for an example program showing how to creat a FORTRAN DLL
|>that is called by a Windows program written in Visual C++. I would like
|>to see the source code in FORTRAN and in C++.
|> I am aware that there are a couple of FORTRAN DLL examples that came
|>with VDF, but I find them very confusing. In addition, it's not clear
|>what I'll have to do in VC++ to get the whole thing working.
|> Can anyone suggest where I can find such code examples?
Here's an example I recently cooked up. We'll make this more widely available
soon. Be aware that there are multiple approaches to this one could take,
including making the Fortran code more "C-like", but I didn't want to
complicate things.
1. Create a "Win32 Console Application" project for the main program, I
called it DLLSAMPLE.
2. In this project, create a C/C++ source file with the following code:
/* Main program written in C that calls a Fortran DLL */
#include <stdio.h>
#include <string.h>
/* Declare the Fortran routine. The following items
are of note:
The "C" attribute prevents C++ name mangling
The dllimport specification is required
Fortran routines use the _stdcall interface by default
Fortran character arguments have a hidden length
argument following the address
Routine name must be in uppercase to match Fortran.
*/
extern "C" __declspec(dllimport) void _stdcall DLL_ROUT
(int *INT_ARG,
char *STR_IN,
int STR_IN_LEN,
char *STR_OUT,
int STR_OUT_LEN);
void main (int argc, char *argv[])
{
char instring[40];
char outstring[40];
int intarg;
strcpy(instring,"Testing...");
intarg = 123;
/* Call Fortran routine - pass intarg by reference,
pass length of outstring explicitly */
DLL_ROUT(&intarg,instring,strlen(instring),outstring,40);
printf("%s\n",outstring);
}
3. Create a new "Dynamic Link Library" project, check "Add to current workspace"
and "Dependency of". I called it FORTDLL.
4. In this workspace, create the following Fortran Free-form source file:
! Fortran part of a C-Fortran DLL example. This
! routine DLL_ROUT is called from a C executable program.
SUBROUTINE DLL_ROUT (INT_ARG, STR_IN, STR_OUT)
IMPLICIT NONE
! Specify that DLL_ROUT is exported to a DLL
!DEC$ ATTRIBUTES DLLEXPORT :: DLL_ROUT
INTEGER INT_ARG
CHARACTER*(*) STR_IN, STR_OUT
! This routine converts INT_ARG to a decimal string.
! appends the string value to STR_IN and stores it
! in STR_OUT. A trailing NUL is added to keep C
! happy.
!
! Note that there are implicit length arguments following
! the addresses of each CHARACTER argument.
CHARACTER*5 INT_STR
WRITE (INT_STR,'(I5.5)')INT_ARG
STR_OUT = STR_IN // INT_STR // CHAR(0)
RETURN
END
5. Select Build..Set Active Configuration and set your executable project
as active. Click on the Build button. It should build
the Fortran code first, then the C code and link it. (Note that because the
DLL project is a subproject, it is built first AND its .LIB is automatically
included with the main program.)
6. Manually copy the .DLL file from the \Debug (or \Release) subdirectory of
the DLL project to the directory containing the .EXE file.
7. Run the program. It should display "Testing...00123" in the console
window.
--
Please send DIGITAL Visual Fortran support requests to dvf-s...@digital.com
Steve Lionel mailto:Steve....@digital.com
Fortran Development http://www.digital.com/info/slionel.html
Digital Equipment Corporation
110 Spit Brook Road, ZKO2-3/N30
Nashua, NH 03062-2698 "Free advice is worth every cent"
For information on DIGITAL Fortran, see http://www.digital.com/fortran
[snip]
>
> Here's an example I recently cooked up. We'll make this more widely
available
> soon. Be aware that there are multiple approaches to this one could
take,
> including making the Fortran code more "C-like", but I didn't want to
> complicate things.
>
> 1. Create a "Win32 Console Application" project for the main program, I
> called it DLLSAMPLE.
> 2. In this project, create a C/C++ source file with the following code:
>
> /* Main program written in C that calls a Fortran DLL */
> #include <stdio.h>
> #include <string.h>
>
> /* Declare the Fortran routine. The following items
> are of note:
>
> The "C" attribute prevents C++ name mangling
> The dllimport specification is required
> Fortran routines use the _stdcall interface by default
> Fortran character arguments have a hidden length
> argument following the address
> Routine name must be in uppercase to match Fortran.
> */
Why are you confusing people by talking about C++ name mangling when you
say that the main program is written in C? The truth here is probably that
your program is compiled as a C++ source file (*.cpp instead of *.c) ?
Since you already use the C++ compiler, I think your example would be
improved using some suitable C++ classes to hide the inelegant hidden
character string lengths (see the CHARACTER class in CPPF77.ZIP at
http://home.sol.no/~arnholm/)
>
> extern "C" __declspec(dllimport) void _stdcall DLL_ROUT
> (int *INT_ARG,
> char *STR_IN,
> int STR_IN_LEN,
> char *STR_OUT,
> int STR_OUT_LEN);
my C++ prototype for the routine would be:
#include <fortran.h>
__declspec(dllimport)
SUBROUTINE DLL_ROUT(INTEGER& INT_ARG, CHARACTER STR_IN, CHARACTER STR_OUT);
>
> void main (int argc, char *argv[])
> {
> char instring[40];
> char outstring[40];
> int intarg;
>
> strcpy(instring,"Testing...");
> intarg = 123;
> /* Call Fortran routine - pass intarg by reference,
> pass length of outstring explicitly */
> DLL_ROUT(&intarg,instring,strlen(instring),outstring,40);
> printf("%s\n",outstring);
> }
my C++ main program would be something like this :
#include <iostream.h>
void main()
{
const size_t outlen=40;
char outstring[outlen];
CHARACTER STR_IN("Testing ...");
INTEGER INT_ARG=123;
// Call Fortran routine, pass INT_ARG by reference (not pointer as
above !)
// Let the CHARACTER class deal with hidden string length issues
{
CHARACTER STR_OUT(outstring,outlen);
DLL_ROUT(INT_ARG,STR_IN,STR_OUT);
}
cout << outstring << endl;
}
>
> 3. Create a new "Dynamic Link Library" project, check "Add to current
workspace"
> and "Dependency of". I called it FORTDLL.
>
> 4. In this workspace, create the following Fortran Free-form source file:
>
> ! Fortran part of a C-Fortran DLL example. This
> ! routine DLL_ROUT is called from a C executable program.
> SUBROUTINE DLL_ROUT (INT_ARG, STR_IN, STR_OUT)
> IMPLICIT NONE
>
> ! Specify that DLL_ROUT is exported to a DLL
> !DEC$ ATTRIBUTES DLLEXPORT :: DLL_ROUT
>
> INTEGER INT_ARG
> CHARACTER*(*) STR_IN, STR_OUT
>
> ! This routine converts INT_ARG to a decimal string.
> ! appends the string value to STR_IN and stores it
> ! in STR_OUT. A trailing NUL is added to keep C
> ! happy.
> !
> ! Note that there are implicit length arguments following
> ! the addresses of each CHARACTER argument.
>
> CHARACTER*5 INT_STR
>
> WRITE (INT_STR,'(I5.5)')INT_ARG
>
> STR_OUT = STR_IN // INT_STR // CHAR(0)
>
> RETURN
> END
Unfortunately, you have to mess with the Fortran code this way. Usually,
people are calling Fortran from C or C++ because they have some existing
(working !) Fortran code they want to re-use. I consider it a better idea
to deal with DLL imports/exports and the like in one language only (and I
prefer C++ for that task).
>
> 5. Select Build..Set Active Configuration and set your executable
project
> as active. Click on the Build button. It should build
> the Fortran code first, then the C code and link it. (Note that because
the
> DLL project is a subproject, it is built first AND its .LIB is
automatically
> included with the main program.)
You could also put the following line of code in your C++ code, so that if
you move the code to some other project, you wouldn't have to explicitely
model such project dependencies:
#pragma comment (lib,"FORTDLL.LIB")
>
> 6. Manually copy the .DLL file from the \Debug (or \Release)
subdirectory of
> the DLL project to the directory containing the .EXE file.
Or use the 'Post-build Step' under Project->Settings .... to ensure that
this hapens EVERY time you build the DLL.
>
> 7. Run the program. It should display "Testing...00123" in the console
> window.
> --
Carsten Arnholm
arn...@online.no
In article <01bd1a2e$919c2870$3b434382@rustad1>, "Carsten Arnholm"
<arn...@online.no> writes:
|>Why are you confusing people by talking about C++ name mangling when you
|>say that the main program is written in C? The truth here is probably that
|>your program is compiled as a C++ source file (*.cpp instead of *.c) ?
Here's why. If in Developer Studio you ask it to create a
"C/C++ Source File", it creates a .cpp file (unless you explicitly give the
file type as .c, which most people don't seem to.) That means you need to
worry about name mangling. This is a common problem we hear about,
which is why I took care to deal with it in my example.
|>Since you already use the C++ compiler, I think your example would be
|>improved using some suitable C++ classes to hide the inelegant hidden
|>character string lengths (see the CHARACTER class in CPPF77.ZIP at
|>http://home.sol.no/~arnholm/)
|>
If I *REALLY* wanted to confuse people, I'd have them use C++. Then we
wouldn't hear from them again for five years until they realized they had
built elaborate and elegant class libraries but got nothing productive
done :- The usual request is to use C. If MSVC didn't make it so easy to
create a .cpp file, then I wouldn't have bothered with the "C" attribute, but
it does and so I did.