USE OF WINDOWS 3.1 API CALLS FROM A FORTRAN DLL TO A VISUAL BASIC PROGRAM
=========================================================================
Tony Lewis, September 1994, Rev. 1
CIS 73357,1730

ABSTRACT
--------
These files demonstrate how a Dynamic Link Library (DLL) written in Fortran
can use Windows 3.1 Applications Programming Interface (API) calls to send
status messages to a Visual Basic program while the DLL is executing.  The
demonstration program uses API calls as follows:

> Create a Message Box to communicate with the program user
> Send a status message string to a VB text box (current status message)
> Send a status message string to a VB list box (status message log)
> use of the UpdateWindow API call to refresh the VB form display

This method of using API calls to communicate to the main Visual Basic program 
may be applicable for DLL's written in languages besides Fortran.  Note that
this method of using API calls with a Fortran DLL may not be optimal, but
it does appear to work without errors. 


KEYWORDS
--------
Visual Basic, Fortran, DLL, API, Windows, SendMessage, Message Box,
UpdateWindow


LANGUAGES
---------
Microsoft Visual Basic 3.0, Fortran 5.1


BACKGROUND
----------
Although the Advanced Topics manual for MS Fortran 5.1 states that Fortran
cannot be used to call Windows API functions, other references provide
examples of how it can be done [see the REFERENCES section].  Windows API
calls can be utilized to build a complete Windows application (not a QuickWin
application), complete with menus, dialog boxes, etc.  This functionality also
extends to Fortran DLLs as well.

Use of Windows API calls in a Fortran program to create a stand alone Windows
application can be a challenging task, particularly if one knows little or
nothing about using the API calls.  Many programmers have found it
advantageous to use Visual Basic for the user interface, and convert the
Fortran "number crunching" program to a DLL, which is called by VB.  For short
functions and subroutines, execution time is not significant, and the end user
probably never knows (or cares) that a DLL was used.  However, if the Fortran
DLL is large, and performs numerous tasks (disk I/O, numeric intensive
calculations, etc.), then the user may wind up staring at a "Calculating;
please wait..." message while the DLL goes about its many tasks.  These files
demonstrate how a Fortran DLL can utilize Windows API calls to pass status
messages to a Visual Basic text box and list box while the DLL is executing. 
The text box is used to show the current status message, while the list box is
used to store all status messages received, for later review.  In addition,
examples of generating Message Boxes and updating the VB window display are
included.  By using these API calls, one may create VB/Fortran DLL programs
that allow the DLL to pass status, warning, or error messages from the DLL to
the VB form.  [Note that this method is a 'one way street' in that VB cannot
pass messages back to the DLL during execution; it is assumed that the
interface between VB and the subs/functions in the DLL will pass all needed
information when the DLL is called.]


WINDOWS API CALLS
-----------------
Windows 3.1 can be considered to be a large collection of dynamic link
libraries, which are used to perform various functions, such as creating
windows, copying information to the clipboard, and managing printers.  A lot
of programs written for Windows are done in C, which is not surprising since
it provides a lot of flexibility.  But it does require the programmer to
become intimate with the internals of Windows itself.  Visual Basic provides
most of the functionality of a 'real' Windows program, while shielding the
programmer from the API calls.  Microsoft Fortran 5.1 does not provide any
support for using API calls, so it is up to the programmer to add the various
interfaces to the Fortran program or DLL.  Consult the references given at the
end, the Windows Software Development Kit (SDK), or other third party books to
learn more about API calls.  The Professional version of VB 3.0 provides
several help files on use of API calls.


WINDOWS API CALLS - MessageBox function
---------------------------------------
Fortran 5.1 provides the MESSAGBOXQQ subroutine to generate a message box from
a program.  This could, in theory, be used to transmit status messages from a
running DLL to a VB program. However, if the possibility of several messages
exist, the user may tire quickly from clicking the "OK" button over and over. 
The MessageBox API call is provided in the DLL to provide an example of its
usage, and to provide notification of the start and end of the DLL execution.

The interface for MessageBox is given below (in $FREEFORM format):

interface to integer*2 FUNCTION MessageBox[pascal]-
 (hWndMB,boxtext,caption,boxtype)
    integer*2 hWndMB[value]
    integer*2 boxtype[value]
    character*1 boxtext[reference]
    character*1 caption[reference]
end

The MessageBox function is denoted as PASCAL, as are the SendMessage function
and UpdateWindow subroutine.  The hWndMB value is the Windows handle of the
message box, and is set to zero for this function.  The boxtype value
determines the various features of the message box (buttons and icons); see
the Advanced Topics manual for details.  The actual message is passed via
boxtext, and the caption for the message box is passed via caption.  Note that
the actual length of the text strings are set within the program, and are
passed by reference to the function when it is called.  The strings must also
be "C" strings with a null character as the end character.

When used in a DLL, the MessageBox function provides the same function as
MessageBoxQQ in a program, or MsgBox in Visual Basic.


WINDOWS API CALLS - UpdateWindow subroutine
-------------------------------------------
Most standalone Windows applications due their own housekeeping with regards
to the display of the application's windows, and components within the
windows.  In this application, a Fortran DLL is modifying a text box and a
list box of a Visual Basic form; VB is not 'aware' that the DLL is doing this,
so the display of both boxes may not reflect what the DLL has inserted.  To
get around this, a call to the UpdateWindow subroutine is used after the DLL
has sent the string to the text and list boxes.  This subroutine tells the VB
application that it needs to repaint the VB form, which also refreshes the
display for the list and text boxes.  The UpdateWindow subroutine only needs
the handle of the VB form (Form1.hWnd) to know which window needs refreshing.

The interface to the UpdateWindow subroutine is given below:

interface to SUBROUTINE UpdateWindow[pascal](hWndVBF)
  integer*2 hWndVBF[value]
end


WINDOWS API CALLS - LoadCursor and SetCursor functions
------------------------------------------------------
If the DLL takes a long time to execute, then it may be desirable to change
the cursor to an hourglass shape, to give the user the idea that he or she
needs to wait for a while.  The LoadCursor function will tell Windows what the
cursor shape should be.  In this case, the constant IDC_WAIT (equal to 32514)
is used for the hourglass cursor shape.  The LoadCursor function will return
the handle of the cursor; this is input to the SetCursor function, which
changes the form of the cursor.  SetCursor also returns the old cursor handle,
which is used later to restore the original cursor form.

The interface for LoadCursor is:

interface to integer*2 FUNCTION LoadCursor[pascal](hinst,pCursor)
  integer*2 hinst[value]
  integer*2 pCursor[value]
end

Note that hinst is set to zero for this example.  The pCursor variable points
to individual cursor shape value.

The interface for SetCursor is:

interface to integer*2 FUNCTION SetCursor[pascal](hcur)
  integer*2 hcur[value]
end

The handle of the cursor is hcur; by saving the return value of LoadCursor,
you can easily restore the cursor to the original shape.


WINDOWS API CALLS - SendMessage function
----------------------------------------
The SendMessage function is a versatile way for the various components of
Windows and applications to communicate information and commands.  As the name
implies, it is used to send messages to other applications.  In the example
DLL, we use SendMessage to send a text string to a VB text box, and send the
same text string to a VB list box.  As noted earlier, the text box can be used
to provide status messages to the user while the DLL is running.  The list box
can be used to keep a log of all messages received from the DLL, for review
after the DLL is finished.  Once the text strings are sent to the text and
list box, VB can manipulate the strings as if VB itself had placed the text in
the text and list boxes.

The format for the SendMessage function is given below (in $FREEFORM format):

interface to integer*4 FUNCTION SendMessage[pascal]-
 (hWnd, WinMsg, wparam, lparam)
    character*1 lparam[reference]
    integer*2 hWnd[value]
    integer*2 WinMsg[value]
    integer*2 wparam[value]
end

The hWnd value is the handle of the item to receive the message.  In this
example, we need the handle of the text box and the list box.  For Visual
Basic 3.0, this is easily accomplished by the following code:

LBhandle% = List1.hand
TBhandle% = Text1.hand

The handle is passed to the DLL in its argument list when it is called; in the
example code the handles for the text and list boxes are passed (along with
the VB form handle).  The WinMsg value is the value of the message passed by
SendMessage.  The constants for various messages can be found in the files
included with the Professional version of VB, or other sources. The wparam
value is a 16 bit variable that has specific uses, depending upon which
message that SendMessage is transmitting; in this case, it is zero for both
messages.  The lparam value is a 32 bit (integer*4) value that is used in both
messages as a pointer to the string to sent to VB. It's use also varies
depending upon the message used with SendMessage.  The strings to be sent to
VB have to be null terminated "C" strings, as with MessageBox.  The programmer
should insure that the string length of the text does not exceed the size of
the text box and list box in VB (for readability).

To send a text string to a VB text box, the WM_SETTEXT message (hex 0401)is
sent via SendMessage, after defining the string.  The call to
SendMessage(WM_SETTEXT) can be done where ever the programmer desires to send
a status, warning, or error message during execution of the DLL.  Note that
the time that a message is displayed in the VB text box depends upon when the
DLL issues the next SendMessage call; therefore, it may not be physically
possible for the user to read all messages before they are replaced by the
next message.  A list box can be used to log all the messages. Visual Basic
list boxes automatically add scroll bars when the number of items received
exceed the number of visible lines.  This provides a handy way for the program
or user to review all status messages when the DLL is complete.  To send a
text string to a VB list box, the LB_ADDSTRING message (hex C) is sent via
SendMessage.  In the example code, the same string, text1, is sent to both the
text and list box.  An extra feature of using LB_ADDSTRING for the list box is
that the return value (SendListVal) is the item number of the text added to
the list box in VB.
EXAMPLE PROGRAM - Overview
--------------------------
The example programs consist of a VB program and a Fortran DLL.  The VB
program calls the DLL, which generates an introductory message box, sends nine
status messages to the text box and list box, and generates an 'all done'
message box before returning to VB.  The status messages are received
approximately every 4 seconds, to simulate the effect of a running DLL.


EXAMPLE PROGRAM - testmsg (Visual Basic 3.0)
------------------------------------------------
The VB program testmsg consists of one form, with one text box (text1), one
list box (list1) and three command buttons (CallDLLCmdButton, ClearCmdButton,
and OKCmdButton).  In addition, a label (label3) is provided at the top of the
form to indicate the program status (in VB vs. in DLL).  The Call DLL button
will update the program status label, and call the DLL.  As the DLL runs, the
text in the text box and list box will be updated by the DLL.  When the DLL is
complete, the last DLL status message is shown in the text box, and all nine
messages are in the list box, which now has scroll bars.  The Clear Box button
will clear both boxes of items, if desired.  The OK button terminates the
application.  Note that the VB program does not use any API calls itself.

The interface to the Fortran DLL is given in the testmsg.bas file:

Declare Sub TESTMSG Lib "testmsg.dll"(ListBoxhWnd As Integer, TextBoxhWnd As
Integer, VBFormhWnd as Integer)

Here, the handles to the text box, the list box and the VB form (window) are
passed to the DLL as integers (integer*2).  The example DLL must be in the
PATH for the VB application to run; SETUP will place the DLL in the
\WINDOWS\SYSTEM directory.


EXAMPLE PROGRAM - testmsg.dll (Fortran 5.1)
-------------------------------------------
The testmsg.dll consists of one subroutine, also called testmsg.  The
interface to SendMessage, MessageBox, and UpdateWindow are given up front. 
All variables are explicitly declared, as this practice contributes to better
memory management for Windows (i.e. - reduces chances for General Protection
Faults).  The SendMessage message constants are defined, as well as wparam.  

The first message box informs the user that the DLL is about to begin to send
status messages.  A DO loop sends nine status messages to the VB text and list
boxes, via the two SendMessage functions.  The UpdateWindow subroutine is
called to refresh the VB form display.  A simple Do While loop generates a 4
second delay between sending status messages to simulate the effect of a
running DLL.  A second 'all done' message box is generated when the nine
status messages have been sent, and the DLL returns to Visual Basic.


COMPILING THE FORTRAN DLL
-------------------------
The testmsg.for file can be compiled from the command line as follows (this is
given in the file compdll.bat):

FL /c /Aw /Gw  a:\testmsg.for

The /c switch compiles without linking (that's next).  The /Aw and /Gw
switches are needed to generate windows compatible code for a DLL.  Note that
the /MW switch is not used for a DLL.


THE DLL DEFINITION FILE - testmsg.def
-------------------------------------
A definition file for the DLL is needed to tell the linker how to assemble the
final product.  The definition file for this example code is given below:

; TESTMSG.DEF for compiling DLL
LIBRARY      TESTMSG
APPLOADER      '__MSLANGLOAD'
EXETYPE        WINDOWS 3.1
PROTMODE
CODE           PRELOAD MOVEABLE DISCARDABLE
DATA          PRELOAD MOVEABLE SINGLE
HEAPSIZE       1024
EXPORTS
  TESTMSG
  WEP

Consult the references for defining the various inputs to the definition file
for a DLL.  Note that the LIBRARY name must match the filename of the DLL, and
the EXPORTS must have the name of the subroutine (also called testmsg) and WEP
for the DLL to work.


LINKING THE OBJECT FILE (linkdll.bat)
-------------------------------------
The DLL may be linked from the command line with the following:

LINK testmsg.obj, testmsg.dll, nul, /NOD /NOE ldllfew.lib noqwin.lib,
testmsg.def

Note that the command line for the linking references the object file created
by the previous compilation of the source code, and names the output with the
DLL extension.  Note also the definition file, which was referenced above. 
The interesting trick to creating Fortran DLLs that use API calls is the
libraries.  The Microsoft Fortran manual states that one should use the 'C'
compatible libraries for mixed language applications.  That is not true here,
so the LDLLFEW.LIB called is the regular version of LDLLFEW, not the 'C'
version (LDLLFEWC.LIB).  The NOQWIN.LIB library is used here since this will
be a dynamic link library, and not a QuickWin application.  If you do not
already have NOQWIN.LIB in your library files, see the Advanced Topics Manual
for details on how to generate it.


SUMMARY
-------
Fortran dynamic link libraries that utilize Windows API calls to send text
strings to a Visual Basic application can be created using Microsoft's Fortran
5.1 compiler, and Visual Basic 3.0.  This example application demonstrates a
relatively simple way to send status or error messages as strings from a
running Fortran DLL to a VB text box and list box to provide information on
the status of the DLL as it executes.  This methodology may be used with large
DLLs written in languages other than Fortran.

CAVEATS
-------
There are no guarantees that this methodology for using API calls from a
Fortran DLL to a Visual Basic program will work for all applications without
errors.  Likewise, there may be better ways to utilize the 700+ API calls to
better refine the interface between the DLL and the VB program.  Feel free to
forward your suggestions, and the author will issue an updated version of 
these files, if warranted.  Note that while the DLL is running, it does not
'yield' to Windows; therefore, there will be no response to keyboard or mouse
events until either the second MessageBox is displayed by the DLL, or the DLL
is exited.  Evidently, the YIELDQQ subroutine (similar to VB's Do Events) is
not available (or might not be applicable) for DLLs; the YIELD API call
apparently does not do anything for a running DLL.  Therefore, running a large
DLL will still 'freeze' Windows until it is complete, but at least the user
will know that the DLL is still active, based upon its status messages.


REFERENCES
----------
Windows Programming for Fortran, J. Ribar
MS Fortran 5.1 manuals (Advanced Topics)
MS Visual Basic 3.0, Professional Edition, manuals
Visual Basic Programmers Guide to the Windows API, D. Appleman
Miscellaneous Fortran/Windows files, available from the MS Languages - Fortran
forum on Compuserve (GO MSLANG)


