Fax: +44(0)1865 300 232
Well, no, actually. Your DOS application runs just fine. It's got a perfectly logical interface, nice graphics, and runs pretty fast. Why should it matter that it isn't a native Windows program?
Technically, it probably doesn't. However, the pressure's on to convert from several directions. Your boss keeps showing you the competition, which although it isn't as powerful or fast as yours and many of the features are just chrome, does look rather swishy. Potential customers seem to lose interest when you tell them it's a DOS program, and just don't seem to want to hear why this is a good thing. And those that do buy it complain about the interface - it may be easy to learn, but it's not the standard Windows one that they've come to expect.
It's time to take the plunge and convert to Windows. There are several ways to do this, ranging from "rewrite in C++ using the Windows API" to "recompile using Windows console mode". The first of these is way outside your experience (or you wouldn't have read this far) and the second you suspect won't keep the customers happy and, in any case, your graphics won't work under it. You need something in between, and could have chosen a visual language plus a Fortran DLL or one of several Windows libraries now available. Your code doesn't suit itself to separating out the i/o sufficiently for the DLL route to be preferable, and in any case you prefer programming in Fortran. You chose Winteracter. Good choice.
The first thing you realise is that Winteracter requires you to use Fortran 90. If your existing code and experience is Fortran 77, don't panic. Chances are your compiler vendor now has a more recent compiler which uses the same non-standard extensions and library routines of their old Fortran 77 compiler, along with the Fortran 90 features you need. Compile your code with this, using the fixed form option, and hey presto you have Fortran 90 code. Don't worry about whether it's Fortran 90 or Fortran 95, by the way - the chances of you running into one of the differences are miniscule, and all the compilers on the market today are Fortran 95 in any case. "Fortran 90" tends to get used as the generic term because that's the version when the really big change in the language happened.
You could start by directly changing your own code, replacing old interface routines with calls to Winteracter. In my opinion it's easier and cleaner to start with a completely new main program (one of the Winteracter samples is a good choice) and add routines from your program to it. This avoids having a lot of unused code left around, and also means you at least start off with all the user interface code in one place. For non-Fortran 90 experts, it also gives you a template for the only bits of Fortran 90 you will actually need to use: SELECT CASE, the TYPE statement, and structures.
So, time to start adding your own code? Not yet. First design your interface. If you're lucky, you already had a menu system. This will probably translate across to the new version almost unchanged, but it's worth using as many of the standard Windows menu items as you reasonably can. Your users may expect "Load Data" now, but give them 3 months with the Windows version and they'll be demanding to know why you didn't use the standard "Open" terminology like everyone else. If you had an Edit menu before which bears no resemblance to the standard Windows Edit menu, it might be an idea to rename it. And so on. If you had multiple screen types before, does it make sense to have one window for each screen and allow the user to view them simultaneously, or are you going to stay with a single window with varying contents? Do you want a button bar, and will this duplicate menu functionality or provide something different? There's no one answer to these - it depends strongly on your application, and it is worth knowing exactly what you want before you start. [One exception here might be if you're converting from an INTERACTER program, where I've sometimes found it easier to translate all graphics calls directly into a single window and gradually extract things into their own windows once the program is working.]
Now it's time to make that sample program look more like your own. Fire up the resource editor which comes with Winteracter and input your menu items. When you save the resource script, it will also make a Fortran 90 module containing menu identifiers for you to use. Add the USE statement to your program, and suitable CASE statements for your menu items, and you're up and running. You can now ditch the first part of your old program: that hideously complex routine which got the mouse position when it was clicked, figured out whether to drop down a sub-menu, hide another one or whether the user had done something else entirely, and eventually returned a selected menu item. You don't need it. The call to WMessage is all that you need; Winteracter will return the relevant menu item code and you handle it in the SELECT CASE block. That's all there is to menu items.
Real code time at last. Do you have a data initialisation routine? If so, bring that in first. If not, identify the relevant code and paste it into your new main program - or would it in fact be better in its own routine? If there's no initialisation code and you're assuming all variables are initialised automatically to zero, beware. In my experience this is much less likely to happen under Windows than DOS. There may be a compiler option to do this, but better to do it in code, so you can see exactly what you meant in 5 years time when you're porting to In-Car Windows for Traffic Jams. This is a good time to do tidying-up jobs like this which you always meant to, like commenting the arguments to routines and adding IMPLICIT NONE. It may seem tedious now but you won't believe how much time it will save you later.
Finally you can get down to adding some visibly working code. Pick a menu item - probably Open so you have some real data in the program to play with when you get to the graphics. My method from this point is rather unsubtle: bring in the old routine plus any you can immediately see that it calls (or maybe you have a tool which gives you a call tree for the old program?), compile and link. With any luck it compiled, unless you had to switch compiler vendor and your old code was non-standard. It probably won't link - you'll get calls to missing routines. Some of these will be yours, others from a library you used to use. If you're converting from INTERACTER you probably just found a call to IdFilename. The back of the Winteracter manual contains a helpful list of closely equivalent INTERACTER and Winteracter routines, where you will find WSelectFile. Fix all the linker's complaints and try again. You'll probably get a whole lot more undefined references. You may have to do this several times, but don't despair because you're now bringing across much more basic routines. The next menu item you tackle will also require many of these routines, and you only have to port them once. You've also figured out what Winteracter routines you need to replace your old library calls. It might be worth your while writing an interface layer which contains a set of routines with your old library routine names calling the corresponding Winteracter routines, or it might be tidier for the future if you just replace the calls. This will depend very much on both your code and your old library.
So you've tackled most of the File menu; your program loads and saves data. Maybe you've done some of the other menu options as well - porting the actual computational routines is generally pretty easy as they tend not to require so many library routines. (I'm making no attempt to discuss availability of mathematical libraries here, but this may well have determined your choice of compiler.) Graphics time. INTERACTER users may now look smug; if you're in luck your code will link unchanged. Other library owners will be poring over the Winteracter Subroutine Guide and considering whether to write that interface layer. Even if you have INTERACTER graphics you will need to make some adjustments though: chances are your code does something like leave a space at the top of the screen for the menu, or a gap at the side for another "window". Under Windows you will presumably want your graphics to fill the whole of their window (the menu no longer counts) and will need to adjust the area accordingly. You also need to remember that under Windows you have to handle the window being covered then exposed, or resized by the user. Fortunately Winteracter will inform you of this via a message, just as it does for menu item clicks. Call your graphics routine here and all will be well. If your graphics and slow computation are intermingled you do have a problem - recomputing every time the window needs to be redrawn is not a good idea and will seriously annoy your users. You may be able to get round this with a memory bitmap of the window which you can put back as required - how well this works on a resized window will again depend strongly on your program.
So you can load and display your data. You also need some way to edit it and set computational parameters. You need a dialog box. Even if your DOS program handled this via a configuration file edited externally, it's a good idea to hide it behind a dialog allowing the user to edit the contents. It may be no quicker or easier, but it looks good and users like it, and if you put consistency checks in it can be rather more friendly. Again INTERACTER users have a head start as they have a utility to convert forms into dialogs. These can be loaded into the resource editor for visual editing; the rest of you will have to start from scratch but nothing here is difficult. It's worth noting that you can also define windows here if you like by running them as modeless dialogs. This allows you to display parameters in a grid, for instance, which can look much better than lined-up text. For simple value checking you can define maximum and minimum values allowed for each field directly in the resource editor - no code required. For consistency cross-checking you want the very useful, and unique to Winteracter, semi-modeless dialog type. This allows you to display a dialog with control immediately returning to your code. The user is constrained to the dialog which returns messages to your program about his actions - so you can check the contents of each field as he exits it, for instance, and give him messages about inconsistent values. You add code to shut the dialog if he presses OK or Cancel and it looks to him just like any other modal dialog. Only you will know that you didn't write a single callback function. If you don’t know what a callback function is, this is very good news. Even if you do, it's a whole lot simpler.
You now have menus, graphics and dialogs, so you should be pretty much there. It's worth mentioning printing - you can now use the Windows printer drivers, so if Windows supports it, you do. No more grovelling to your library supplier for a printer driver for something neither you or he has ever heard of but which seems to be all the rage with your Japanese customers.
Your new version is finished! Your customers love it, but have loads of suggestions for improvements. Wouldn't it look better with a real Windows installation program to go with it? How about a proper Windows help system? An interactive tutorial? Drag-and-drop to and from Word? Time to start work on version 2.
This does sound good, but, to be honest, you don't have the time to do it. Your time is much better spent on your own area of expertise: the computational part of the code. What you need is someone else to port the interface while you can continue to improve the technical capabilities. Well, you're in luck - give Polyhedron a call and maybe I can do it for you.
Article written by Catherine Rees-Lay
Cartoons by Jonathan Wallis
(C) Polyhedron Software Ltd. (1999-2004)