Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Programmer's Bookshelf


JUL91: PROGRAMMER'S BOOKSHELF

This month we will look at four new books on Microsoft Windows 3.0. When I stepped on the bathroom scale holding these books and subtracted my own weight, they turned out to weigh ten pounds. I guess I'm not the only one who needs to go on a diet.

For the vast majority of programmers, graphics programming is, for better or worse, going to increasing entail mastering -- not VGA palette registers or Bresenham's line-drawing algorithm -- but the ins-and-outs of Application Program Interfaces (APIs) such as the Macintosh Toolbox, Xt Intrinsics, or the Microsoft Windows API. What I'll emphasize here is what these books can teach us about the possibility of simplified programming for the notoriously complex Windows API.

Programming Windows

The definitive book on Windows programming is, of course, Charles Petzold's Programming Windows. Microsoft labels this "the authorized edition." I've also heard Charles's book called "thunk," not because of the essential discussion of "reload thunks" that appears on pp. 282-4, but because thunk is the sound this massive book makes if you drop it on your foot!

In addition to Petzold's wonderfully smooth writing style (of which I am completely jealous) and his ability to take even some of the more twisted aspects of the Windows API and make them sound almost "programmer-friendly," I like this book for the large number of buried treasures it contains. While Petzold lets you know that Windows programming is hard to do -- and that this difficulty is good for you -- the book also contains many ingenious ideas that irresistibly appeal to the lazy, good-for-nothing bum in all of us.

For example: "Perhaps the epitome of lazy programming is the HEXCALC program .... This program doesn't call CreateWindow at all, never processes WM_PAINT messages, never obtains a device context, and never processes mouse messages. Yet it manages to incorporate a ten-function hexadecimal calculator with a full keyboard and mouse interface in fewer than 150 lines of source code" (p. 479). What a great idea!

Likewise, after a 40-page examination of Windows memory management, Charles presents a two-p ragraph note titled "If You Know You're Running in Protected Mode" (p. 302). This is an amazing note because it says, in essence, that you can ignore most of the preceeding 40 pages.

It turns out that, by punting Windows Real mode, you can vastly simplify your memory management: "When allocating moveable global memory, you can lock the memory block immediately to obtain the pointer. Even though the block is locked, Windows can still move it in memory -- and the pointer will remain valid. You don't even have to save the global memory handle." Because Windows 3 Real mode is a complete joke which hardly anyone uses, and which probably ought to have been left out of the product in the first place, you lose nothing by developing only for the Windows protected (Standard and Enhanced) modes. What you gain is some sanity. You can use the RCT switch to mark a Windows application as "protected mode only."

Another gem in Petzold's book is the RANDRECT program on pp. 598-604, which shows how to use the PeekMessage( ) function instead of the more typical GetMessage( ). PeekMessage( ) is the key, not only to using Windows "dead time," but also to simulating a typical DOS application-driven approach to programming on top of Windows's event-driven architecture.

I am particularly fond of two small sections of this great book. Like many programmers, I was completely hopeless with Windows programming, and despaired of ever getting the hang of it, until I read "The Message Box" (pp. 439-441) and the MFCREATE.C program (p. 640). These two small sections are the key to truly simple Windows programming in which, for example, "hello world" takes five lines of code, not the 80 lines you're hit with in Chapter 1 of this book.

Basically, MFCREATE.C shows that Windows programs don't require a window, a window class, a window procedure, a message loop, or any of the other Windows paraphernalia that makes the API so daunting to a newcomer who just wants to display three lines of output on the screen. In fact, Petzold notes that MFCREATE.C, buried on p. 640, is the shortest program in the book. It happens to create a disk-based metafile, which may not be what you're interested in, but the key point is that this little program may open your eyes to a new way of writing simple Windows utilities, or of just plain getting started with Windows programming.

That brings us to discussion of the Windows MessageBox( ) function on pp. 439-441. If, as MFCREATE.C shows, there's no rule stating that WinMain( ) must register a window class, create a window, install a window procedure, and start a message loop, then all we need to write "hello world!", for example, is a call to the handy Windows MessageBox( ) function.

Microsoft's documentation states that the first parameter to MessageBox( ) must be a window handle (an HWND) that identifies the window that owns the message box, so you might think that you still have to go through the Windows rigamarole just to create a window that can "own" the message box. However, Petzold shows that "if you don't have a window handle available," you can use NULL for the handle. When you're new to Window programming, or when you just want to write a simple utility for use by your coworkers, the basic problem is precisely that you don't have a window handle available, and Charles's NULL trick lets you avoid writing the standard 80 lines of Windows boilerplate code in order to get one.

Example 1 shows the simple code you can write by applying some of the ideas buried in Petzold's book. This program displays information on what the Windows Program Manager's About box likes to call "System Resources." No one knows what these system resources are, but at least we know what percentage of them is free! I had read on BIX that "System Resources" are in fact nothing more than the local heaps of the USER and GDI modules, and that Program Manager gets this information with an undocumented Windows call, GetHeapSpaces( ). I wanted to write a simple test program to see if this was true.

Example 1: A simple Windows program, SYSTRES.C, consists of little more than the OkMsgBox( ) function from Petzold's Programming Windows.

  /* SYSTRES.C -- System Resources
  Borland C++:
  bcc -W systres.c
  rc systres.exe */

  #include <windows.h>

  /* undocumented Windows call: KERNEL.138 */
  extern DWORD FAR PASCAL GetHeapSpaces (WORD hModule);
  void heap_info (char *module, WORD *pfree, WORD *ptotal, WORD *ppercent)
  {
       DWORD info = GetHeapSpaces (GetModuleHandle(module));
       *pfree = LOWORD (info);
       *ptotal = HIWORD (info);
       *ppercent = (WORD) ((((DWORD) *pfree) * 100L) / ((DWORD) *ptotal));
  }
  #define PROGMAN_EXE "progman.exe"
  #define PROGMAN_CLASS "Progman"
  #define HELP_ABOUT 0x387 //subject to change!!!

  //run the Program Manager Help menu About... box
  void progmgr_aboutbox(void)
  {
       WORD progmgr;
       // use class name ("Progman"), not window title ("Program Manager"):
       // see Richter, Windows 3: A Developer's Guide, p. 80
       if (! (progmgr = FindWindow(PROGMAN_CLASS, 0)))
       {
           // WinExec () is async: equivalent of spawn () P_NOWAIT
           WinExec (PROGMAN_EXE, SW_SHOWMINIMIZED);
           if (! (progmgr = FindWindow (PROGMAN_CLASS, 0)))
               return; // couldn't find or load Program Manager
       }
       // use PostMessage instead of SendMessage so we run async
       PostMessage (progmgr, WM_COMMAND, HELP_ABOUT, 0L);
  }
  // from Petzold, Programming Windows, p. 441
  void OkMsgBox (char *szCaption, char *szFormat, ...)
  {
       char szBuffer [256] ;
       char *pArguments ;
       pArguments = (char *) &szFormat + sizeof szFormat ;
       wvsprintf (szBuffer, szFormat, pArguments) ; // changed from
       vsprintf
       MessageBox (NULL, szBuffer, szCaption, MB_OK) ;
  }
  int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR
                                                           lpszCmdLine, int
  nCmdShow)
  {
      WORD user free, user_total, user_percent;
      WORD gdi_free, gdi_total, gdi_percent;
      heap_info ("USER", &user_free, &user_total, &user_percent);
      heap_info ("GDI", &gdi_free, &gdi_total, &gdi_percent);
      progmgr_aboutbox ();
      OkMsgBox ("System Resources",
           "USER heap: %u bytes free out of %u (%u%% free)\n"
           "GDI heap: %u bytes free out of %u (%u%% free)\n"
           "Free system resources: %u%%\n",
               user_free, user_total, user_percent,
               gdi_free, gdi_total, gdi_percent,
               min(user_percent, gdi_percent));
  }

The resulting program, SYSTRES.C, is built around the OkMsgBox( ) function from p. 441 of Petzold's book. I made one change to this function: Rather than call vsprintf( ), it calls wvsprintf( ), which is declared in WINDOWS.H and built into Windows. This partially explains why the resulting executable, SYSTRES.EXE, is less than 3 Kbytes, much smaller than the typical character mode DOS utility, in a program which is a lot nicer to look at.

To see if the GetHeapSpaces( ) information really is equivalent to the numbers reported by Program Manager, we of course need to pull down the Program Manager Help menu and look at the About box. But there's really no reason why the program can't do this for us, so SYSTRES.C also contains a function, progmgr_aboutbox( ), which will do just that. It first sees if Program Manager is already running; if it's not, it uses WinExec( ) to launch it. It then sends Program Manager a message -- the same message that gets sent if you manually click on the About ... menu item. It's amazing that we can perform this type of interprocess communication with so few lines of code; the ability to post messages from one program to another shows, by the way, that the message-based architecture of Windows that Petzold describes is not just arbitrary object-oriented terminology, but a true description of how Windows works.

GetHeapSpaces( ) does turn out to provide the same numbers for System Resources as Program Manager. The discrepancy of a percentage point or two seems to be due to differences in how the two programs do integer-based percentages. Incidentally, Windows programmers will soon not have to rely on this undocumented function. A forthcoming Microsoft library called Toolhelp, to be provided as part of its "Open Tools" strategy, will provide the GDI-HeapInfo( ) and UserHeapInfo( ) functions, along with many other much-needed additions to the Windows API. These functions will work in Windows 3.0, and will also be part of the retail release of Windows 3.1.

It may seem as if we've strayed far afield from Petzold's Programming Windows. The point is simply that this book has so much useful information and so many good ideas, that you can even use the book to adapt a style of Windows programming with which Petzold would probably disagree. This is some book. Thunk! Ouch!

Windows 3.0 for BASIC Programmers

Our next book has the unlikely title Windows 3.0 for BASIC Programmers. If you are not a Basic programmer, and even if you are one of the many programmers who despise this language, please keep reading anyway. The title of this book is quite misleading. Let me quote the book's introduction:

This book will teach even the most inexperienced programmer how to write Windows applications. It is unlike any other Windows book available today. You don't need to learn hundreds of complex function calls, new styles of programming, or the C language. Rather, through the use of a powerful development package called Realizer Limited, included with this book, you will learn how to create sophisticated Windows programs, quickly and painlessly. If you can write a Basic program, you will be able to write a Windows program. It's that simple.

This sounds like a piece of marketing literature but, oddly enough, it's all true. For the price of a book, you get a "limited" version of Within Technologies' Realizer Basic development environment for Windows. This is an incredible bargain! You really will be able to write simple Windows applications, and learn a lot about Windows, with this book/disk package.

Hyman had the excellent idea of trying to teach, not only how to use Realizer, but also how Windows works. Several chapters contain "Behind the Scenes" sections. In a few places these are somewhat confused (for example, the explanation of Windows memory management), but the basic idea works well.

The key point is the presentation of a "higher level" on top of the Windows API. For example, where Petzold's book has the reader sweat out 40 pages on Dynamic Data Exchange (DDE), which end in the now somewhat famous statement "The only advice I can offer here is simply to do the best you can," Hyman's book and the Realizer software, like several other Basics for Windows, simply provides a higher-level DDE interface.

Likewise, where most Windows programming books have to devote pages and pages to reinventing the File Open ... dialog box found in every sizeable Windows application (see Petzold, pp. 447-457, for example), the Realizer software simply provides all the standard dialog boxes, built right into the language.

Interestingly, Microsoft is finally coming around to the novel idea of building high-level interfaces. By the time Windows 3.1 is released, developers will have two new dynamic link libraries (DLLs): one called COMMDLG, which provides all the standard dialog boxes, and another called DDEML, which provides a high-level DDE interface which looks surprisingly like that found in WordBasic, the Basic development environment embedded in Microsoft Word for Windows.

The demo programs that come with Hyman's book are impressive: two different charting programs (Realizer specializes in forms, charts, and data analysis), Blackjack and Poker simulations, a DDE "stock watcher" that communicates with Excel, and so on.

Example 2 shows what Basic for Windows looks like. This is the SYSTRES utility again, this time written with Realizer. Note that the Microsoft Software Development Kit (SDK) is not needed for this program -- just Windows itself and a $29.95 book/disk package. This is an interpreted, not a compiled program, so we need some way to link to the Windows API routines. There's no WINDOWS.H here; instead, Realizer lets you dynamic link at runtime to anything in a DLL. That is the purpose of the EXTERNAL FUNC and EXTERNAL PROC statements at the top of Example 2.

Example 2: The same utility as in Example 1, but this time written with the Realizer Basic development environment for Windows.

  ' SYSTRES.RLZ -- System Resources
  ' run-time dynamic linking to Windows API
  external "kernel" func GetHeapSpaces (word) as dword
  external "kernel" func GetModuleHandle (pointer) as word
  external "user" func FindWindow (pointer, pointer) as word
  external "user" proc PostMessage (word, word, word, dword)

  func get_free (modname)
    heap_space = GetHeapSpaces (GetModuleHandle (modname))
    free = heap_space mod 65536          ' LOWORD
    total = Floor (heap_space / 65536)   ' HIWORD
    percent = 100 * free / total
    Print #1; modname, "heap: ", free, " bytes free out of ", total;
    Print #1; " (", percent, "%)"
    return percent
  end func

  WM_COMMAND = 273   ' 111h (yuk! no hex!)
  HELP_ABOUT = 903   ' 387h

  ' pull down Program Manager Help About... box
  progmgr = FindWindow ("Progman", Pointer (0))
  if progmgr = 0 then
    Shell "progman", _Minimize
    progmgr = FindWindow ("Program", Pointer (0))
  end if
  PostMessage (progmgr, WM_COMMAND, HELP_ABOUT, 0)

  LogNew (1; "System Resources")
  user_percent = get_free ("USER")
  gdi_percent = get_free ("GDI")
  Print #1; "System Resources: ", min (user_percent, gdi_percent), "% free"
  LogControl (_Show)

This brings out an important point about Windows: If we are linking to these functions from a Basic interpreter, with the Windows SDK nowhere in sight, then all the Windows API routines must be part of Windows itself, and not something that comes with the SDK. All the functionality is built into every copy of Window that someone buys off the shelf at Egghead; the SDK brings remarkably little to the party.

Actually, I did have to refer to WINDOWS.H once in coding up this example, because I needed to know the "magic number" for the WM_COMMAND message. I got the HELP_ABOUT magic number by watching the behavior of Program Manager with Microsoft's SPY utility, and by examining PROGMAN.EXE with the excruciatingly slow but nonetheless useful Whitewater Resource Toolkit (WRT) included with Borland C++ (WRT was apparently built with the Actor language, and its poor performance is about the worst possible form of advertising for Actor one could imagine; this is a shame, because WRT would otherwise be addictive). In both cases, I had to convert hexadecimal numbers to base ten because, incredibly, Realizer does not accept hex numbers.

One final point about the Realizer program in Example 2: Note the SHELL statement which is used to launch Program Manager (if it isn't already running). In Basic for Windows (and remember, there are several other such systems besides Realizer), SHELL is built on top of the Windows WinExec( ) function and is, therefore, fully asynchronous. The program that issues the SHELL continues to run, even while the program it launched runs. This even happens for DOS (non-Windows) applications in Enhanced mode, if the Background bit is set in the DOS program's Settings. This is one of several nice multitasking changes that Basic (and other languages) undergo under Windows.

I generally prefer Microsoft's WordBasic to Realizer; WordBasic has a more intuitive syntax. However, Realizer and Hyman's book try to do several things that WordBasic avoids. For one thing, dynamic linking works better with the Realizer EXTERNAL statement than with the WordBasic DECLARE statement, because Realizer distinguishes between the WORD/DWORD types and the INTEGER/LONG types: WordBasic insists on using signed arithmetic for everything (this makes it difficult to extract the low word and high word from the GetHeapSpaces( ) return value, for example).

Realizer also allows you to write event-driven programs and DDE servers (not just DDE clients). The designers of Realizer thought through the implications of this for the built-in Basic debugger: A menu selection "Full Stop" is sometimes necessary when your program is continuing to be bombarded with DDE messages, even though you think you've halted it in the debugger. This is one of several interesting "Concurrent Basic" issues.

Hyman's chapter on event-driven programming is quite interesting: "Realizer hides most events from the programmer. Realizer programs appear to be state-based, or linear .... Behind the scenes, as each Realizer line is run, Realizer handles a variety of messages and events .... There are some situations in which the event-driven nature shows through. For example, Realizer programs can have their own menus. The user can select a menu item from these menus at any time. This causes an event that the Realizer program needs to handle. Likewise for modeless forms" (p. 188).

Even if you haven't the slightest interest in Basic, definitely check out Hyman's book if you are at all interested in Windows programming. At the very least, you will get a taste of the sort of development environments and languages that can be built on top of Windows, and of the great flexibility which Windows is capable of in the hands of thoughtful third-party developers.

Windows 3: A Developer's Guide

Jeffrey Richter's is the first book to appear on advanced Windows programming. That is, it assumes you've read Petzold's book, and makes no attempt to teach introductory Windows programming. The book has eight lengthy chapters on various aspects of Windows that haven't been properly covered elsewhere. For example, there is an entire 70-page chapter titled "Installing Commercial Applications," an 80-page chapter on "Setting Up Printers," and a 70-page chapter on "Subclassing and Superclassing Windows."

Two chapters present excellent indepth examinations of Windows internals: the opening chapter "Anatomy of a Window," and the chapter on "Tasks, Queues, and Hooks." Together, these provide a coherent explanation of the interrelationships of a program's Instance handle, Task handle, Module handle, PSP, and so on.

I found many useful tips and suggestions here. For example, even the tiny SYSTRES program shown in Examples 1 and 2 try to find Program Manager's window handle by passing a class name, not a window title, to FindWindow( ). This comes directly from p. 80 of Richter's book. I've also been using the VOYEUR utility that comes with the book.

Speaking of utilities, one of the most striking things about Richter's book is the install program for the accompanying disks. It's simply a standard Windows install program (which he describes in chapter 8); nothing very exciting. But if you think about it, here is a $39.95 book, and the accompanying disks have a better, more professional, install program than most commercial character-mode DOS applications. This is typical of the Windows marketplace: Windows raises the level of quality for software, even for the sample programs that accompany computer books.

Windows 3 Power Tools

Okay, so Windows 3 Power Tools isn't a book for programmers, and we all know how bad "user" books can be. (You should see the junk stacked up in my basement: Using Fastback, Learning Fastback, and An Advanced User's Guide to Installing Fastback are a few of the titles that come to mind.)

But this really is a pretty good book on the intricacies of using and setting up Windows. Setting up Windows so that it coexists properly with, say, a network and an expanded memory manager, makes programming the Windows API look like child's play. This book has good advice for such seemingly intractable problems as getting QEMM 5.11 and Windows 3.0 Enhanced mode to coexist on a Compaq computer. On the other hand, I saw nothing about the "out of environment space" problem for the DOS box, nothing on the "instancing" problems you can get when you run TSRs (such as the CED command-line editor) before running multiple virtual machines in Windows Enhanced mode, and no mention of the various bugs that plague the Windows 3.0 DOS box. This is simply a decent user's book, by no means a great one.

The disks are the real reason to get this book/disk package. The centerpiece is a graphical scripting language for Windows called "Oriel;" ORIEL.EXE itself is only 18K, but it provides access to most of the Windows graphical device interface (GDI) via an extremely reasonable-looking graphical batch language. The output goes to a resizable, "persistent" window (that is, Oriel takes care of repairing the window whenever it receives a WM_PAINT message). A number of demo scripts show off the language's capabilities; as an example of what the script language looks like, see the "hello world!" program shown in Example 3 (page 144). Oriel stands as a further example of how Windows really can be made easier for programmers as well as for users.

Example 3: "Hello world!" using the Oriel graphical batch language

  { HELLO.ORL -- "hello world" for Oriel }

  UseCaption ("Hello")
  SetMenu ("&File", IGNORE, "E&xit", Do_Exit, ENDPOPUP)
  UseFont ("Roman", 0, 24, BOLD, NOITALIC, NOUNDERLINE, 64, 0, 0)
  DrawText (10, 10, "Hello world!")
  WaitInput ()
  Do_Exit:
    End

Another programmable utility that comes on the disks is Morrie Wilson's CommandPost. This is a replacement for Program Manager. Visually, it resembles the old MS-DOS Executive program for Windows; the difference is the comprehensive scripting language, CommandPost Menu Language (CPML), that lets you customize menus, manipulate windows, create dialogs, run programs, manage files and directories, and so on. CommandPost could use a spiffier interface, now that Program Manager is available, but it's hard to complain since so much of CommandPost is changeable via the script language anyhow.

Just like Richter's book, Windows 3 Power Tools comes with an install program that would put most commercial character-based DOS software to shame. Little details like these convince me that Windows is going to succeed.



_PROGRAMMER'S BOOKSHELF COLUMN_
by Andrew Schulman


Example 1: A simple Windows program, SYSTRES.C, consists of
little more than the OkMsgBox() function from Petzold's Programming
Windows.

/*  SYSTRES.C -- System Resources
Borland C++:
bcc -W systres.c
rc systres.exe */

#include <windows.h>

/* undocumented Windows call: KERNEL.138 */
extern DWORD FAR PASCAL GetHeapSpaces(WORD hModule);
void heap_info(char *module, WORD *pfree, WORD *ptotal, WORD *ppercent)
{
    DWORD info = GetHeapSpaces(GetModuleHandle(module));
    *pfree = LOWORD(info);
    *ptotal = HIWORD(info);
    *ppercent = (WORD) ((((DWORD) *pfree) * 100L) / ((DWORD) *ptotal));
}

#define PROGMAN_EXE     "progman.exe"
#define PROGMAN_CLASS   "Progman"
#define HELP_ABOUT      0x387       // subject to change!!!

// run the Program Manager Help menu About... box
void progmgr_aboutbox(void)
{
    WORD progmgr;
    // use class name ("Progman"), not window title ("Program Manager"):
    // see Richter, Windows 3: A Developer's Guide, p.80
    if (! (progmgr = FindWindow(PROGMAN_CLASS, 0)))
    {
        // WinExec() is async: equivalent of spawn() P_NOWAIT
        WinExec(PROGMAN_EXE, SW_SHOWMINIMIZED);
        if (! (progmgr = FindWindow(PROGMAN_CLASS, 0)))
            return; // couldn't find or load Program Manager
    }
    // use PostMessage instead of SendMessage so we run async
    PostMessage(progmgr, WM_COMMAND, HELP_ABOUT, 0L);
}
// from Petzold, Programming Windows, p. 441
void OkMsgBox(char *szCaption, char *szFormat, ...)
{
    char szBuffer[256] ;
    char *pArguments ;
    pArguments = (char *) &szFormat + sizeof szFormat ;
    wvsprintf(szBuffer, szFormat, pArguments) ; // changed from vsprintf
    MessageBox(NULL, szBuffer, szCaption, MB_OK) ;
}
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine,
                                                                 int nCmdShow)
{
    WORD user_free, user_total, user_percent;
    WORD gdi_free, gdi_total, gdi_percent;
    heap_info("USER", &user_free, &user_total, &user_percent);
    heap_info("GDI",  &gdi_free,  &gdi_total, &gdi_percent);
    progmgr_aboutbox();
    OkMsgBox("System Resources",
        "USER heap: %u bytes free out of %u (%u%% free)\n"
        "GDI heap: %u bytes free out of %u (%u%% free)\n"
        "Free system resources: %u%%\n",
            user_free, user_total, user_percent,
            gdi_free, gdi_total, gdi_percent,
            min(user_percent, gdi_percent));
}





Example 2: The same utility as in Example 1, but this time written with
the Realizer BASIC development environment for Windows.

' SYSTRES.RLZ -- System Resources
' run-time dynamic linking to Windows API
external "kernel" func GetHeapSpaces (word) as dword
external "kernel" func GetModuleHandle(pointer) as word
external "user" func FindWindow(pointer, pointer) as word
external "user" proc PostMessage(word, word, word, dword)

func get_free(modname)
    heap_space = GetHeapSpaces(GetModuleHandle(modname))
    free = heap_space mod 65536         ' LOWORD
    total = Floor(heap_space / 65536)   ' HIWORD
    percent = 100 * free / total
    Print #1; modname, "heap: ", free, " bytes free out of ", total;
    Print #1; " (", percent, "%)"
    return percent
end func

WM_COMMAND = 273     ' 111h (yuk! no hex!)
HELP_ABOUT = 903     ' 387h

' pull down Program Manager Help About... box
progmgr = FindWindow("Progman", Pointer(0))
if progmgr = 0 then
    Shell "progman", _Minimize
    progmgr = FindWindow("Progman", Pointer(0))
end if
PostMessage(progmgr, WM_COMMAND, HELP_ABOUT, 0)

LogNew(1; "System Resources")
user_percent = get_free("USER")
gdi_percent = get_free("GDI")
Print #1; "System Resources: ", min(user_percent, gdi_percent), "% free"
LogControl(_Show)




Example 3: "hello world!" using the Oriel graphical batch language

{ HELLO.ORL -- "hello world" for Oriel }

UseCaption("Hello")
SetMenu("&File", IGNORE, "E&xit", Do_Exit, ENDPOPUP)
UseFont("Roman", 0, 24, BOLD, NOITALIC, NOUNDERLINE, 64, 0, 0)
DrawText(10, 10, "Hello world!")
WaitInput()
Do_Exit:
    End


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.