|
forums.silverfrost.com Welcome to the Silverfrost forums
|
View previous topic :: View next topic |
Author |
Message |
BK
Joined: 07 May 2020 Posts: 14
|
Posted: Wed Jul 08, 2020 7:54 pm Post subject: WRITE called in a DLL results in an exception |
|
|
I'm using C++Builder 10.3 to build a GUI for legacy F77 FORTRAN analysis software. A Win32 DLL was built and loads OK. The DLL needs to write results to an external file. The usual WRITE statement with unit=num results in an External exception 80000003. WRITEFA@ can be used for one-line writes, but existing format statements that produce multi-line writes can't be included as arguments. Opening the external file using OPEN with a specific unit number seems to work OK, so it seems that WRITE doesn't have a good handle for the external file. Is there any way that WRITE can be used? |
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1888
|
Posted: Thu Jul 09, 2020 3:17 am Post subject: Re: WRITE called in a DLL results in an exception |
|
|
BK wrote: | ... existing format statements that produce multi-line writes can't be included as arguments. |
Format statements exist only in Fortran source code, and allow the statement label to be used for the format string. There is no way to pass a Fortran statement from your non-Fortran GUI code layer to the Fortran DLL*. You can, however, pass a properly constituted Format string as an actual argument, as long as the GUI code knows how to pass Fortran string arguments while adhering to the conventions of the Fortran compiler and runtime.
BK wrote: | Opening the external file using OPEN with a specific unit number seems to work OK, so it seems that WRITE doesn't have a good handle for the external file. |
I do not see how the second statement follows from the first, and what you mean by "good handle". WRITE statements are converted to a series of calls to I/O routines in the Fortran RTL, and we rarely need to know the details of those calls.
-----
* Leaving out implementations that pass the Fortran code to the compiler, wait for the compilation and linking to complete, and transfer execution to the resulting EXE/DLL.
Last edited by mecej4 on Thu Jul 09, 2020 8:01 am; edited 1 time in total |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7931 Location: Salford, UK
|
Posted: Thu Jul 09, 2020 7:44 am Post subject: |
|
|
What UNIT numbers are you using for file output? |
|
Back to top |
|
|
BK
Joined: 07 May 2020 Posts: 14
|
Posted: Thu Jul 09, 2020 8:28 pm Post subject: |
|
|
mecej4, the FORTRAN source includes several multi-line writes such as a header and a glossary. I wasn't talking abt passing a format statement from C++builder to the DLL. I was talking abt an inability to include in WRITEFA@ a format label or multiple format statement lines. But I have an idea for a work-around.
paullaidler, I was using unit=17 in WRITE. I later used OPENF@ to open the output file. This returns identifier SID which is used in WRITEFA@. In my inexperience, I interpret SID as a handle. |
|
Back to top |
|
|
BK
Joined: 07 May 2020 Posts: 14
|
Posted: Tue Jul 14, 2020 8:20 pm Post subject: |
|
|
Out of curiosity, I pulled my XP machine with an old FORTRAN compiler out of storage, and built the DLL with the same source code (taking out any F95 code). Running the DLL with C++Builder worked just fine, no problems.
Sad to say, I think Silverfrost is not ready for the big leagues.
BTW, when using an internal write, you have to know a priori the width of the value to write. For a one-digit integer, fmt='I1' works, fmt='I2' doesn't. Not too convenient when results are computed and you don't know what the width would be. And I didn't even try it with a floating-point number.
A very disappointed customer. |
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1888
|
Posted: Tue Jul 14, 2020 11:49 pm Post subject: Re: |
|
|
BK wrote: | ... when using an internal write, you have to know a priori the width of the value to write. For a one-digit integer, fmt='I1' works, fmt='I2' doesn't. Not too convenient when results are computed and you don't know what the width would be. |
You can use the I0 format. For example,
Code: | program inwrite
implicit none
integer ijk,i
character*11 str
!
ijk = 1
do i=1,5
write(str,'(I0)')ijk
print '(1x,I2,2x,A)',i,trim(str)//' $'
ijk=ijk*10+1
end do
end program |
gives
Code: | 1 1 $
2 11 $
3 111 $
4 1111 $
5 11111 $ |
|
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1888
|
Posted: Wed Jul 15, 2020 12:18 am Post subject: Multiple lines and writefa@ |
|
|
You can build up a multi-line string in your code before writing that string using WRITEFA@, if you wish.
Code: | program inwrite
implicit none
integer ijk,i
integer*2 sid,ier
character*7 str
character*80 lines
!
ijk = 1
lines = ''
do i=1,5
write(str,'(I0)')ijk
if (i .gt. 1) lines = trim(lines) // char(10)
lines = trim(lines) // str
ijk=ijk*10+1
end do
call openw@('multi.txt',sid,ier)
call writefa@(lines,sid,ier)
call closef@(sid,ier)
end program |
writes the file multi.txt, containing:
Code: | 1
11
111
1111
11111 |
|
|
Back to top |
|
|
BK
Joined: 07 May 2020 Posts: 14
|
Posted: Wed Jul 15, 2020 8:32 pm Post subject: |
|
|
mecej4, tnx for the hints, but it doesn't make sense for me to re-write hundreds of write statements that work perfectly fine when using a DLL created by an old FORTRAN compiler.
I'm hoping that my concerns abt DLL writes get passed on to the developers. I'm also hoping I get a notice abt an updated version of Silverfrost if and when they incorporate fixes. |
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1888
|
Posted: Thu Jul 16, 2020 9:03 am Post subject: |
|
|
I am still in the dark regarding (i) what features, standard or non-standard, that you use in your code and are supported by your anonymous old compiler as well as FTN95 and (ii) the code that fails with the current version of FTN95.
If you can provide a demonstrator, please do; if the bug can be reproduced, we will be half-way to a solution. In the absence of actual code, little progress can be made.
Last edited by mecej4 on Thu Jul 16, 2020 10:49 am; edited 2 times in total |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7931 Location: Salford, UK
|
Posted: Thu Jul 16, 2020 9:55 am Post subject: |
|
|
This is just to endorse mecej4's comment.
Documentation is provided in ftn95.chm in under "Mixed language programming".
But we need some basic sample code from you in order to reproduce the failure - a main program in C calling a subprogram in Fortran with details of how they are linked. |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7931 Location: Salford, UK
|
Posted: Thu Jul 16, 2020 10:33 am Post subject: |
|
|
BK
I have put together a sample based on your description and it works OK for me. So we need a very simple sample from you that illustrates the failure.
This is my C code:
Code: | extern "C" void SUB1(int* k);
int _tmain(int argc, _TCHAR* argv[])
{
int k = 42;
SUB1(&k);
return 0;
}
|
and here is my Fortran code:
Code: | SUBROUTINE sub1(k)
INTEGER k
open(10,file="output.dat")
write(10,*) k
close(10)
END SUBROUTINE sub1 |
|
|
Back to top |
|
|
BK
Joined: 07 May 2020 Posts: 14
|
Posted: Fri Jul 17, 2020 6:54 pm Post subject: |
|
|
Here's the C++Builder code:
typedef void (__cdecl *MYPROC)();
HINSTANCE hndl;
MYPROC ProcAdd;
BOOL fFreeResult, fLinkSuccess = FALSE;
CurrDirec = GetCurrentDir();
hndl = LoadLibraryA("INPUTS.dll");
if (hndl != NULL)
{
ProcAdd = (MYPROC) GetProcAddress(hndl, "INPUTS");
if (NULL != ProcAdd)
{
fLinkSuccess = TRUE;
ShowMessage("link success");
(ProcAdd) ();
} else {
ShowMessage("could not get dll addr");
}
fFreeResult = FreeLibrary(hndl);
}
And here's the fortran:
F_STDCALL Subroutine INPUTS
CFTN95$OPTIONS(FIXED,STDCALL)
(misc comments)
ENTRY SALFSTARTUP
(the rest is F77 common blocks, local variables, & format statements.
The DLL was created using command-line slink since it didn't appear that link options did anything but create a default LibMain.
Here are the slink commands:
DLL
load c:\aa2\Inputs.obj
load c:\aa2\FixAnt.obj
load c:\aa2\GetLwr.obj
load c:\aa2\Stat1.obj
exportall
map c:\aa2\statmap.txt
file c:\aa2\INPUTS |
|
Back to top |
|
|
BK
Joined: 07 May 2020 Posts: 14
|
Posted: Fri Jul 17, 2020 7:03 pm Post subject: |
|
|
Forgot to mention that Sub INPUTS calls three other subroutines, all starting with "F_STDCALL Subroutine" and including "ENTRY SALFSTARTUP.", The on-line help wasn't clear on whether the other subroutines should include F_STDCALL and the ENTRY, so I put them in. But the issues occurred in INPUTS, not the others. |
|
Back to top |
|
|
BK
Joined: 07 May 2020 Posts: 14
|
Posted: Fri Jul 17, 2020 7:21 pm Post subject: |
|
|
After the F77 common blocks, local variables, & format statements, the fortran opens and reads a text file with integer arguments, input data file name, and output data file name. Both files are in the current working directory. I don't pass the file names because past experience shows that the file name strings get garbled.
The open is typical F77:
EXISTS = .FALSE.
INQUIRE(FILE='GUI_data.txt',EXIST=EXISTS)
IF (EXISTS) THEN
OPEN (UNIT=25,FILE='GUI_data.txt',STATUS='UNKNOWN',
& FORM='FORMATTED',ACCESS='SEQUENTIAL')
DO 5 JJ = 1,4
LINE = ' '
READ(25,1009) LINE
IF (JJ .EQ. 1) READ(LINE(1:1),fmt='(I1)') IGUI
IF (JJ .EQ. 2) READ(LINE(1:1),fmt='(I1)') IOUT
IF (JJ .EQ. 3) INPNAM = TRIM(LINE)
IF (JJ .EQ. 4) OUTNAM = TRIM(LINE)
5 CONTINUE
There's a similar section for opening the output data file on unit 17
A typical write (where iw is set to 17) is
write(iw,*) 'igui: ',igui |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7931 Location: Salford, UK
|
Posted: Sat Jul 18, 2020 8:34 am Post subject: |
|
|
BK
The first thing to note is that STDCALL is not a valid option in CFTN95$OPTIONS(FIXED,STDCALL). FTN95 does not fault this and at the moment I don't know if that is deliberate or just an oversight. But this makes no difference because you are attempting something like it with F_STDCALL.
The main concern relates to the matching of __cdel and F_STDCALL or STDCALL. The default calling convention for FTN95 is "__cdel" and presumably this is also the default for C++ Builder. In which case you should not use F_STDCALL or STDCALL in the Fortran code.
More to the point, I strongly recommend that you start with a simple test like the one that I outlined above - just a short main program with a very simple call to a Fortran routine. You will probably need to change from my _tmain() (which is for Microsoft C) to main() and it is OK to use LoadLibraryA and GetProcAddress for the dynamic linking but after that, keep it as simple as possible.
In short, my test demonstrates that you can do what you are aiming for, but you should first sort out the matching of the calling conventions (__stdcall versies __cdel). |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|