|
API Example 1
1) "Hello World"
There is the general perception that Win API programming is difficult.
And even more so in Fortran. In reality, nothing can be further from the
truth. The classic example is "How many lines does it take for a Hello
World application" ? Well, I guess most of you will estimate a couple of
dozen. In reality it takes only 8 lines to create the window shown below,
and that includes variable declarations !

The code for the "Hello World" is shown below :
integer function WinMain( hInstance, hPrevInstance,
lpszCmdLine, nCmdShow )
!MS$ ATTRIBUTES STDCALL, ALIAS : '_WinMain@16' ::
WinMain
!To access win32 definitions
use msfwin
!Function parameters
integer(4), intent(in) :: hInstance,hPrevInstance,nCmdShow,lpszCmdLine
integer(4) :: iret
!Show message
iret = MessageBox(NULL, "Hello world."//char(0),
"KFWIN Window"//char(0), MB_OK)
! Exit program
WinMain = 0
end
At first glance you may not be suitable impressed with the little message
box, but bear in mind that without any coding from you, this window can
(i) be moved around on the screen, (ii) if you hold the mouse button down
on the OK button and then move it off it, the button move up and down and
(iii) if the style was MB_OKCANCEL we would get a tooltip if the cursor
was on the X. Not bad for 8 lines of code.
2) Wipping up something quick
In the previous example we saw that Windows provides us with a very
useful feature for reporting simple information back to the user - the
Messagebox. This feature can be used for quick testing of code without
switching to console applications. The example below performs some mathematical
operations and then report the results back to the users.

The code to create this window is shown below:
integer function WinMain( hInstance, hPrevInstance,
lpszCmdLine, nCmdShow )
!MS$ ATTRIBUTES STDCALL, ALIAS : '_WinMain@16' ::
WinMain
!To access win32 definitions
use msfwin
!Function parameters
integer(4), intent(in) :: hInstance, hPrevInstance,nCmdShow,lpszCmdLine
!Function returns and Window+Message Structure
integer(4) :: iret, iNum1, iNum2
real(4) :: rNum3
character (len=32) :: szNum1, szNum2, szNum3
character (len=*), parameter :: nl=char(13)//char(10)
!Do whatevert calcs you want
iNum1 = 1*100
iNum2 = 2*100
rNum3 = 55.0/3.0
!Convert number to string
write (szNum1,*) iNum1; szNum1 = adjustl(szNum1)
write (szNum2,*) iNum2; szNum2 = adjustl(szNum2)
write (szNum3,*) rNum3; szNum3 = adjustl(szNum3)
!Display result
iret = MessageBox(NULL, "Result 1 = "//trim(szNum1)//nl//
&
"Result 2 =
"//trim(szNum2)//nl// &
"Result 3 =
"//trim(szNum3)//nl// &
""//nl// &
"Save results
to temporary file ? "//nl// &
char(0), "KFWIN
Window"//char(0), MB_OKCANCEL)
! Exit program
WinMain = 0
end
3) Edit "Console"
I guess by now you realize it is very easy to create Window programs
in Fortran. But, you may have notice one (or maybe more) small problems.
We have a method (messagebox) of displaying information to the user, but
how do we input information from the keyboard/screen ?
Unfortunately there is no general "input box" available in Windows.
To get user input into your program (except from files), you have to learn
about the message loop, window procedures or both. Most applications use
Dialog windows with a dialog procedure. But for entertainment, let's do
the opposite and create a simple "console application" where everything
is still in the Winmain function. Not very practical, but educational.
For the "console", we will use a window of the predefined Textbox class.
And we put the textbox inside another window, only to have different text
for the title and text box content. We used a Listbox class for the outer
window, but you can use several of the other predefined window classes.
Using predefined classes saves us from defining a window class of our own.
The other problem is - How do we stop execution to allow the user to
input data ? In the previous examples we stopped execution with the Messagebox,
but we cannot do it this time. The answer is the now infamous message loop.
What we do is to put a Do While loop in our Winmain function. Execution
stays in the loop till you reguest it to stop. How ? Well, with each loop
you query Windows to see if there are any mesages for your program, using
the GetMessage function. This will always return TRUE, except if you posted
a PostQuitMessage(0). This will cause GetMessage to return FALSE and your
Do While loop will come to an end.
But the GetMessage function also returns the message itself, and you
can look at the message and take appropriate action. In our "console" application,
we will only intercept the Right Mouse Button Click event (which will stop
execution) and the Enter key KeyDown event (to read in the line of text).
Once the user input a number and press enter, our program reads it,
do whatever calcs on it, and display whatever information to the user.
The program then waits for the user for more input. To quit, click the
right mouse button or enter "end" or "exit".
Our "console" application looks as follows:

The code to create this window is shown below:
integer function WinMain( hInstance, hPrevInstance,
lpszCmdLine, nCmdShow )
!MS$ ATTRIBUTES STDCALL, ALIAS : '_WinMain@16' ::
WinMain
!To use win32 definitions
use msfwin
implicit none
!Function parameters
integer(4), intent(in) :: hInstance, hPrevInstance,nCmdShow,lpszCmdLine
!Function returns and Window+Message Structure
logical(4) :: bret
integer(4) :: hWnd_Static, hWnd_Control, iret,
iErr=0
real(8) :: nRead
character (len=1024) :: szText="", szRead=""
character (len=*), parameter :: nl=char(13)//char(10)
type (T_RECT) :: rectStatic
type (T_MSG) :: iMsg
!Display main window (only for title)
hWnd_Static = CreateWindow("listbox"C, &
"KFWIN Window (Right
Click to Exit)"//char(0), &
IOR(WS_CAPTION, IOR(WS_VISIBLE,
WS_DLGFRAME)), &
50, 50, 400, 400,
NULL, NULL, hInstance, NULL)
!Get size
iret = GetClientRect (hWnd_Static, rectStatic)
!Display text box with initial text
hWnd_Control = CreateWindow("edit"C, "Right
Click to Exit (or type exit)"//nl//nl// &
"Enter number : "//nl//char(0),
&
IOR(WS_CHILD, IOR(WS_VSCROLL,
IOR(ES_AUTOVSCROLL, IOR(ES_MULTILINE, WS_VISIBLE)))), &
0, 0, rectStatic%Right,
rectStatic%Bottom, hWnd_Static, NULL, hInstance, NULL)
!Set focus to Edit box
iret = setfocus(hWnd_control)
iret = SendMessage(hWnd_Control, WM_GETTEXTLENGTH,
0, 0)
iret = SendMessage(hWnd_Control, EM_SETSEL,
iret, iret)
!Start simple message loop (can only access
POSTED messages)
do while (GetMessage (iMsg, NULL, 0, 0))
if (iMsg%message == WM_RBUTTONUP)
then
!This is to QUIT -
other, better mesages are only SEND to proc
bret = DestroyWindow(hWnd_Static)
call PostQuitMessage(0)
elseif (iMsg%message == WM_KEYDOWN)
then
!See if Enter was pressed
if (iMsg%wParam == 13) then
!Read last line
iret = SendMessage(hWnd_Control,
EM_GETLINECOUNT, 0, 0)
iret = SendMessage(hWnd_Control,
EM_GETLINE, iret-1, loc(szText))
szText(iret+1:len(szText))
= ""
!First see if
"exit" or "end"
if (trim(szText)
== "exit" .or. trim(szText) == "end") then
bret = DestroyWindow(hWnd_Static)
call PostQuitMessage(0)
end if
!Internal read
into Double
read(szText,
*, IOSTAT=iErr) nRead
if (iErr /=
0) then
iret = MessageBox(NULL, "Error"//char(0), "KFWIN"//char(0), MB_OK)
szRead = ""
else
!Now do calcs on input.
nRead = (nRead + 1.0)**2
!Convert result to string fro display
write (szRead,*) nRead; szRead = adjustl(szRead)
end if
!Display results
at end of existing text and ask for next input
iret = GetWindowText(hWnd_Control,
szText, len(szText))
szText(index(szText,char(0)):len(szText))
= ""
iret = SetWindowText(hWnd_Control,
&
trim(szText)//nl// &
"Result of (x+1)^2 = " // &
trim(szRead)//nl// &
"Enter next number below : "//nl//char(0))
!Reset focus
in case of msgbox, set caret, and scroll to it
iret = setfocus(hWnd_control)
iret = SendMessage(hWnd_Control,
WM_GETTEXTLENGTH, 0, 0)
iret = SendMessage(hWnd_Control,
EM_SETSEL, iret, iret)
iret = SendMessage(hWnd_Control,
EM_SCROLLCARET, 0, 0)
else
!Do default
processing so that Edit acts normally other keys
bret =
TranslateMessage(iMsg)
bret =
DispatchMessage(iMsg)
end if
else
!Do default processing
so that Edit acts normally
bret = TranslateMessage(iMsg)
bret = DispatchMessage(iMsg)
end if
end do
!Show end of loop
iret = MessageBox(NULL, "Message loop terminated"//char(0),
"KFWIN"//char(0), MB_OK)
! Exit program
WinMain = iMsg%wParam
end
This code is much longer than the previous examples, but then this is
no longer a trivial application. It does (hopefully) show the true power
of Windows - with relatively few commands you can create good looking applications.
|