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.

 

 
   
KFWIN © was developed and is maintained and distributed by KORF Software.
Click image (if scripting enabled) to email us any technical or commercial questions.

This product is protected by copyright law and international treaties.