So, You Want To Write Windows Apps.
• • • A step-by-step guide
By Jim Guerber • • •



Intro to Windows Programming


    |    


 So, you want to write Windows Apps. Well, you are not alone. We have noticed more requests lately for help making those old text-based applications more up to date and better looking. After seeing those beautiful programs that Barbara did for the Comet Data Express Application, and realizing that they were written entirely in Internet Basic, more and more developers have said



Since I did not know how to do it myself, I decided to try to take a common program written with Comet Screens, and convert it to Windows. This is the story of how I did it. Keep in mind that this was an informal test, and thus I will write about it informally. As you will see, I did not do some things right. I later corrected some of my mistakes, but I did not lay out the look of the window before I started the task. That was probably my biggest mistake.

As many of you know, I have programmed many things, but I never programmed a windows control before. So, I am like many of you. I have used windows quite a bit these last few years, but never actually implemented anything in it. I do know the basics of windows programming:

• Everything is done with call-backs.

• The user can click anywhere in the window. The program does not determine the order that information is presented to it, the user decides that.

I also know some things most of you don't know yet.  Some from reading books like "About Face" by Alan Cooper, some from almost daily contact with people such as Brian Levantine and Barbara Brazil.

• There are minor programs and there are "Cardinal Apps". Cardinal Apps are main screens that users live in all day long. Other screens come and go, but Cardinal Apps take up most of the users screen real estate. There are other features about Cardinal apps mentioned in Cooper’s book -- you should read it. Your Comet Applications are examples of Cardinal Apps.

• To do a really good professional looking program in Windows requires much more time thinking about the screen interface and the look-and-feel of the user experience than those old text based programs did.

• The ONLY control in windows that requires user input and can accept random data is the edit control. The Comet input statement is a form of edit control. All other windows controls can and should be pre-formatted by the application to remove randomness, ambiguity, and the need for validation. This is a key point.

I took the familiar "DIRCAT" application from the utilities as my goal. As all of you know, it is currently written requiring nine transmits from the keyboard to get a simple directory listing to the screen! Yes I counted them, since I always select the directory of my choice and then hit enter 9 times to see my listing.

 

I will show you how I took this program (messy as it is internally) and converted it into a good (well, acceptable anyway) windows program. Each step of the way, I saved the source code and will show you my progress with  Internet Basic files that you can download, compile and run.

Here are some words of advice at the outset:

• Windows controls provide a lot of power. There are many methods, properties and messages associated with each control. That power brings Microsoft complexity. . STAY AWAY FROM THAT.

•This is especially true if (not if, but when) you want those programs to function over the internet with Comet Anywhere. USE ONLY THE MOST MINIMAL FEATURES OF A WINDOWS CONTROL.

•USE THE FEWEST CALL BACKS.  Think of a control as a really slow file. You do not want to deal with it much for performance reasons.

But, I am getting ahead of myself. As I said earlier, I extracted the DirCat code from our standard utility source (I don’t know the version...it hasn't changed much in the last 10 years). It was in one of those xtil90x files, and the file included other utilities as well. I made DirCat run as a standalone program and removed some of the extraneous code.  The screen interface is just as you know it, and the code is pretty messy, (just like some of your own programs, I bet).

You can download the code here:
DirCat00.ibs

Click on the link, download it, edit it, compile it and run it.

Barbara used Microsoft DEV Studio to make the dialogs she used to build the Comet Data Express. Brian really recommends that as well. Being ornery and contrary as I am, I chose NOT to follow that advice, and here are the reasons:

Dev Studio Cons:
• Dev Studio only lets you build Dialogs. Comet Applications are Cardinal Apps, like I said above.

•There is not much control of the look of a dialog as there is on a regular window. The color is "dialog grey" for example.

•I know that all of your programs were written using screen positioning. Yes, it would be good to have a Comet visual development studio program, but I am just too cheap to spend the money for one at this time. Bad as it is, Screen positioning was good enough for us for the past many years, it should be good enough for us now (right??!!)

•Dev Studio costs an arm and a leg from Microsoft.

•Dev Studio is just another complex Microsoft product to deal with. It’s part of Microsoft Visual C++ -- you know what that means.

 

It’s only fair to say what’s good about the Dev Studio approach. 

Dev Studio Pros:
•It’s a graphical interface. You click on a control, drag it around a window, resize it, specify some of its visual properties, and Boom there is a window!

•I can't think of any more...

 

Back to my story.

 

The only example program in the DLG directory that did not deal with dialogs is "direct". You know the one. It’s the one with a picture of a much younger me and calls me BWANA -- how –tacky!

So, I decided to take the DirCat00 program and just replace the directory input with a combo box. Just one control: How bad could that be?  I took direct.ibs and tried to extract the combo box code from it and insert that code in my DirCat program. Let me tell you, it was frustrating. I worked for hours and could not even get the program to compile! There are so many controls in the direct program, and there are so many other things going on that it is way too confusing. I could not tell what variables were constants, what were needed for the WDL library to function, which ones were just for the structure of the application, and what went where. I gave up in disgust.

 

I did not even mention this attempt at programming windows for about a week. I knew I needed help, but couldn't afford a psychiatrist. By now you must know that I did get help, or I would not be writing this. My help came from several valuable talks with Brian. He said that the direct program was not one to be used as an example of how to program windows controls. He built it as a test program when writing the Comet code that implemented the controls in the first place. It is more of a capability demonstration than a learning tool and it shows that Comet is VERY capable of running real windows programs written entirely in Internet Basic.

Brian agreed to build me a simple program that implemented only one control. A couple of days later, he came up with an even better suggestion A month or two earlier, Brian wrote a sample program called “LookDemo”. It implemented a list box that displayed some stuff provided by a routine outside of its display code. This code was made to be adapted for other uses, and was MUCH better suited to my purposes. Brian also pointed me to a couple of documentation files that were handed out in that dealer meeting in San Francisco many years back. You can find the doc files in your downloaded WDL directory.

WDL is one of three Comet directories you should download and include in your configuration. At the time of this writing, here are the latest versions of these files: http://www.signature.net/download/WinDev/wdl0305.zip
http://www.signature.net/download/WinDev/dlg0305.zip
http://www.signature.net/download/WinDev/dmw0305.zip



You can find LookDemo here:
lookdemo.ibs

I decided to start again. This time I made a program that implemented two push button controls only. From Lookdemo I knew that these were simple controls that would not take much to program. Using the files supplied by Brian, this program was not difficult to write. I would place the buttons at the bottom of the screen and use them as the only controls that the user would use to signal the program. All other controls would exist to transmit information to the program when the ok button was clicked. I was having fun again!

Here is the first program in the series:
win00.ibs

This program simply puts 2 buttons on the screen - "OK" and "CANCEL". It is about 140 lines long. Half of those lines are empty or comments, which makes reading it easier. That means that it took about 70 lines of code to put up two controls on a Comet Screen.

This is not much when you think of it.

Since this is the first program I wrote like this, lets go through it almost line-by-line so I can show you what I learned. The Comet Windows stuff is written in a series of use files. These files represent a source library. They should be thought of like some sort of DLL.  DON'T LOOK AT THEM. I mean, sure you can look at them, but, they will only get you confused. The actual internal windows interface is done way deep inside of this code using obscure Pseudo DOS calls and other contrivances. This is because we never implemented the windows API in our compiler, or even as mnemonics. “Some day we will... “. Brian wrote the library about seven years ago, and it is HUGE! And, yes, that’s right -- seven years! And the rest of us are just catching up now!

The original WDL library (prior to version 03.04) provided everything you needed to create your windows controls. Following the examples we provided, you could figure out which of the many API include files you needed to use in your program, which of the three message handlers you had to write to process the events for your controls, and how to handle the reporting of error messages to the user. Version 03.04 has simplified that considerably. So, while Windows programming was doable before, now it's really easy!

For most programs, you'll need only one API include file, CometWin.Inc. It is smart enough to determine which other pieces of the API your program will need and it will include them for you. You'll need to include it twice - once in the declarative section and once the executable section. ALL routines that you can call within the API start with the characters "CW."   (that’s C W DOT) or "COS."   (that’s C O S DOT). Any other label you see in one of these programs is part of the program, not part of the library. Likewise, all variables supplied by the library begin with the characters "CW." or “cos”. Examples are CW.Color and cosCtlId.

 Now let's look at my program. At the top I include the main API file, CometWin.Inc. Next I've used some SET statements to define some of the attributes for my Comet window.

Include "CometWin.Inc", "Wdl"

Set ScreenWidth = 80     
Set ScreenHeight = 30

Set DefBkRed = 255
Set DefBkGreen = 254
Set DefBkBlue = 236

ScreenWidth and ScreenHeight define the size of the Comet window in number of characters. DefBkRed, DefBkGreen, and DefBkBlue define the wallpaper color to be used. As you will see, these colors define a sort of parchment color. I like this as a change from the traditional white or those ugly DOS colors some of your users still demand. Yuck! Anyway, all of these settings may be omitted and you'll get a Comet window of the normal size and color as you do now, but this sure is an easy way to customize your screen.

All that's required to finish the declarative section of my program is the declaration of my variables. This program needs only one, Program$, which I use to display a message telling you which button you pressed when running the program.

Now on to the exectuable section... Escapeto Shutdown is kind of important. You see, unlike Comet QCRT screen contents, Windows controls are NOT automatically cleared when a program ends or a clear screen mnemonic is executed. If my Shutdown routine did not destroy the controls, they would still be there for any subsequent program that is run. The call to CW.Close cleans up any controls that I have built, removes my wallpaper, and reinstates the default font. Don't worry, we'll talk about fonts later.

The first thing I need to do is to initialize my Comet Windows environment. I do this by calling CW.Open. It will clear my screen, size my window, create my wallpaper, and hide the cursor. Remember, unless I'm using an edit control for data entry, I don't need a cursor and if I do use an edit control, I'll get the cursor for free.

Before the call to CW.Open, I can specify values to be used for the initialization process. I set CW.Width, CW.Height, and CW.Color to customize my screen size and wallpaper color.

The next several lines comprise the essence of my whole program. I make an OK button, set the focus to it, make a cancel button, and wait for the user to click a button. I use EventSub to tell me when a click occurs. I'll show you how to set up the Event Handler in a bit. So far, it's a piece o’ cake!

Lets look at MakeOkButton. For every control I need to tell windows where to locate it and what size I want it to be. That takes care of the assignment statements CosWidth, CosHeight, cosRow and cosCol. Note that I used 2 set statements for my width and height. That was only because I want to make all of my buttons the same size. You obviously do not need to do it this way, but I think it’s a good idea to use symbolic constants in cases like this. They take no extra code space, and when you want to change them, you only need to change them in one place.

A note about measurement:

In these programs, all control and GDI measurements are in "CentiUnits". There are 100 CentiUnits of width and height for each Comet character. This is a feature of Comet that allows me to locate and size controls using fractional character sizes as the basis. If you wanted to position your control at row 5, column 10, you'd set cosRow to 500 (5*100) and cosCol to 1000 (10*100). I put my OK button at row 21.5, column 27.5. I encourage you to take this program and play with it, changing cosRow, BtnHeight etc.

Every control within windows has an ID. This is the way windows tells itself and my program what control I'm talking about. All IDs are pre-defined. Some, such as IDOK and IDCANCEL are defined for you in the API. They are standard IDs used by windows everywhere. Its up to you to assign whatever ID you want in your program for each control. There is a range of IDs which Comet has reserved for itself between 41000 and 44999 that you can feel free to use. Otherwise one of your IDs may collide with one of the windows IDs. I know, I am not really clear on this, but you get the gist of this, right?

The last thing you need to set for a button is the text to display inside it. Note the ampersand before the O in OK. That tells windows to simulate this button push if the user types an alt O. Just another one of those tricky windows features.

I have grouped all of the other housekeeping into a subroutine called MakePushButton. Please, for now, just copy that routine into your code and call it to make each button you want. Those settings allow different behaviors and looks than I need to bother about. It IS important to know that for almost all of those variables you see in the WDL, if you substitute an underline for the periods in them, they are exactly the same item used by windows itself. Thus you can understand their meaning from any textbook on windows programming. You can also look them up on the internet. I would suggest you use google to do this on the Microsoft msdn web site. Here is a fun exercise. Notice that our program uses bs.pushbutton as part of the style definition for our buttons. Go to google and type in this search:

Bs_pushbutton site:msdn.microsoft.com

Just copy the above line and paste it into the google search edit box. In this way you can learn all about those things in our windows.inc file.

Now, just how does Windows tell us if the user has clicked one of those buttons? That has to do with windows messaging. There are thousands of messages flying around windows every second. There are timer messages, drawing messages, and many more. If you had to write your program to take care of all of these it would be huge and perform very poorly. That’s where COSW comes in. COSW intercepts most of those windows messages and takes care of them for you. All you have to do is tell COSW which messages you DO want to receive, and you can be assured that those will be the only ones you have to deal with.

In our programs I tell COSW which messages I want for each control when I create it. Look at MakePushButton. The three lines of code after the call to COS.CreateButton say that I want to know about the BN.CLICKED message for the push button I'm creating. BN.CLICKED is the message sent by Windows when the user clicks the button. Each “notification” from windows is composed of two items – the control ID and the action by the user that caused the notification. So before our call to COS.AppendCmdFilter I set cosCtlText$ to a concatenation of the control id and the message id. By the way, the other line of code, cosCWnd$ = cosDlghDlg$, tells the API which window I'm talking about. cosDlghDlg$ is the handle to my window. You'll see this before lots of the API calls. Don't worry about this for now, just do it. Trust me. It works!

OK, so now what happens when one of our events occurs? Here's how it works... Remember the EventSub at line #53 of my program? Its event handler (I conveniently called it "EventHandler") gets called for each event that occurs. All I need to do is identify which event it was and handle it appropriately. My API call at line #111 to CW.ParseEvent returns cosCtlId (which tells me what control had the event) and cosCommand (which tells me which event it was). Cool! My Select clause sorts this all out easily. In this case, all I want to do is set Program$ with the message I will display at the end of the program telling you which button you pressed. As if you didn't know?? I also need to indicate whether I want to wait for more events after this or if I'm done. I do this by setting CW.ExitEvent to either TRUE or FALSE. In this case, once you click a button, I'm done so I've set it to TRUE. Check out lines 127 thru 131 to see how this value is used to re-establish the EventSub or not. If the EventSub is re-established, my program goes back to waiting for another event. Nothing else can interrupt it except F3/XMIT which is why I put the EscapeTo at the top of my program. Since I am terminating the event handler, my program will resume execution at the first statement after the EventWait (line 54). This drops me right in to my Shutdown code.

That's it! I'm done! My first windows program works. Wow, maybe I can do this!

Please, download this program, compile it, run it, and by all means please change some of those flags, styles etc. and see what happens.

Download win00.ibs

    |