Microsoft Systems Journal Volume 1 ──────────────────────────────────────────────────────────────────────────── Vol. 1 No. 1 Table of Contents ──────────────────────────────────────────────────────────────────────────── Advanced Reuter Terminal Gives Traders Windows on Financial World The Advanced Reuter Terminal (ART), a dedicated hardware and software system that runs under Microsoft(R) Windows, enables currency traders to view and analyze up-to-the-minute financial data. An exclusive interview reveals the developing team's experiences. DDE: A Public Protocol for Advanced Application Linkages An extension of Microsoft Windows data exchange facilities, the Dynamic Data Exchange (DDE) protocol allows developers to establish sophisticated application linkages, ranging from automatic updating of documents to processing of real-time data. New Intel Graphics Coprocessor Makes Windows Sparkle Intel has a new way to boost the display speed of Windows and other graphics applications: the 82786, a high-powered coprocessor that improves workstation performance, moving graphics functions from software to high-speed hardware. TI's Programmable Graphics Processor Perks Up PC Pixels The first true graphics microprocessor, Texas Instruments' 32-bit TMS34010 executes at 6 MIPS, addresses up to 4 gigabytes of memory, supports displays of up to 64K by 64K pixels, and includes high-speed pixel-processing instructions. Latest Dialog Editor Speeds Windows Application Development An improved version of the Microsoft Windows Dialog Editor (supplied with the software development kit) simplifies designing and testing a dialog box──one of the key elements of a Windows application. Here's how to use it and what to watch out for. Ask Dr. Bob EDITOR'S NOTE This first issue of the Microsoft Systems Journal is devoted in its entirety to Microsoft Windows operating environment, Microsoft's most important system software product since MS-DOS(R). Windows has encountered remarkable success both as a retail product and as a development environment since its release last November. While much of the product's initial popularity can be attributed to its flexibility as a "switcher" for traditional applications and the included sample applications, its ultimate success will depend on the acceptance of native Widows applications. Reuters' ART. As a definitive example of the capabilities of Microsoft Windows and its flexibility to serve particular user interface designs, we examine the Advanced Reuter Terminal (ART), the most sophisticated Windows application to date. A superb example of design and coding craftsmanship, the ART would be worth featuring even if it were not based on Windows. We also present profiles of two graphics processors that will have a significant impact on future graphics systems: the Intel 82786 and the Texas Instruments TMS34010. And we take a look at DDE, a new standard for interprogram data exchange, as well as examine the improved Dialog editor. In addition, Dr. Bob tackles some questions about Windows from developers. In sum, we hope to present a developer's view of Microsoft Windows. Future Issues. In future issues we will focus on other areas of interest to developers, including C programming, device drivers, extended and expanded memory, debuggers, code portability, and the inside view of MS-DOS and Microsoft XENIX(R). We will tackle the subjects that interest serious developers most──from user interface design to guidelines for writing ternimate-and-stay-resident programs. Our aim is to confront the real issues confronting serious developers. Write to Us. We invite you to write to us, to subscribe, and equally important, to tell us what you think we should include in the Journal. Beginning in January 1987, our issues will expand to full size, more than twice the length of this special issue. We hope the Journal will become one of the most important publications you receive.──Ed. JONATHAN D. LAZARUS Editor MICHAEL LONGACRE Design Copyright(C) 1986 Microsoft Corporation. All rights reserved; reproduction in part or in whole without permission is prohibited. Microsoft Systems Journal is published every other month by Microsoft Corporation, 16011 NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H. Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley, President and Chief Operating Officer; Francis J. Gaudette, Treasurer; William Neukom, Secretary. Microsoft Corporation assumes no liability for any damages resulting from the use of the information contained herein. Microsoft, the Microsoft logo, MS-DOS, and XENIS are registered trademarks of Microsoft Corporation. Intel is a registered trademark of Intel Corporation. IBM is a registered trademark of International Business Machines Corporation. Lotus is a registered trademark of Lotus Development Corporation. Texas Instruments is a registered trademark of Texas Instruments, Inc. ──────────────────────────────────────────────────────────────────────────── ART Showcases Microsoft Windows MSJ Interview One of the finest examples to date of software running under Microsoft Windows is a product called the ART, the Advanced Reuter Terminal. When you think of Reuters, you think of news, but to currency traders who buy and sell millions of dollars each day, Reuters means finance. Its financial information service is the lifeblood of their trade, providing the timely and accurate information on which their success depends. The ART is a complete hardware and software solution that supplies a high-resolution color monitor and features multiple windows showing text and graphics displays of up-to-the-minute financial information. It has the equivalent display capacity of ten of the previous Reuter Monitor terminals, which limited users to viewing information on a monochrome text display. What's more, ART users can perform data analyses by choosing options from pull-down menus. ART's Configuration ART hardware consists of an 80286-based (PC-AT compatible) NCR computer with 1.5 megabytes of RAM, a 20-megabyte hard disk, high-resolution (EGA) display, 1200-baud modem, Reuters proprietary communications board, a Reuters programmable keyboard, and, of course, a mouse. But the heart of the system is the Windows-based software. Developed by the Reuters office in London, it offers such capabilities as real-time graphics, user-defined montage pages, limit and news watching, and graphics analysis. Real-time graphics are made possible by calling up a graphics window and linking it to a particular currency value displayed on the screen. Each time the value of the currency changes, a new tick is added to the graph. The ART provides automatic limit watching, using dialogs in which users enter upper and lower limits for any currency displayed on a stored page. When limits are broken, the ART gives audible and visible signals allowing a dealer to react instantly. News watching works the same way; the ART immediately alerts the user when the preselected word or phrase appears in a headline. In sum, the ART system allows the user to determine the screen's content and layout, an asset to active traders. To find out about some of the background development of the ART, we spoke with Paul Lucke, manager of the Reuters micro section in London, and asked him some questions about developing a product under Microsoft Windows. To find out about some of the background and development of the ART, we spoke with Paul Lucke, manager of the Reuters micro section in London, and asked him some questions about developing a product under Microsoft Windows operating environment. Why Windows? MSJ: Why did you choose Microsoft Windows rather than some of the other graphics environments such as GEM? REUTERS: Clearly we wanted a bit-mapped graphics display because a major component of the system is drawing graphs and performing analytical studies. That cut out the non-bit-mapped graphics systems, which brings you down to Windows and GEM. We started out by buying a Laser (computer) as soon as they arrived on the scene. At the time it looked as if that was the way user interfaces were going. After we did some development work on the Laser, we saw the IBM PC running Microsoft Windows and figured that this particular combination was the way to go. We felt that Microsoft had the makings of a market leader, so we took Windows on as an alpha test site and put up some experimental demos using Reuters data. Our objective was to turn data into information, and we believed that Microsoft Windows was the right vehicle for doing so. Clearly, when GEM came out, we had to consider it as well, particularly because an alpha release of any product has its fair share of bugs. But we found GEM's method of window interfacing, at least at the time we reviewed it, to be inferior to Microsoft's tiled windows concept. Think of it as two kinds of desks: the untidy desk and the tidy desk. Tidy is the way we wanted to go. When information changes, traders need to see it as it happens. They can't afford to have it obscured underneath another window. So any kind of obscuring device must be something temporary, like pull-down menus, which disappear once you select an item. Also, some of the simple things were better implemented in Windows──like the accuracy of the pointing. We just found it easier to use than GEM. Microsoft Support MSJ: How did you find Microsoft's support? REUTERS: We took care only to go to our contact at Microsoft when we were dealing with a problem that was clearly a fault of the product. That way we were able to encourage him to listen to us. And he offered good support on a one-to-one basis. Clearly, something like the DIAL system would have been useful, had it been available. We have a North American operation that has a DIAL account, and users report that it has recently improved considerably. One of the things that's been particularly useful is watching the bulletin board as other people's problems are discussed. MSJ: You depend on having information updated instantly. Did you run into any problems when you were working with your implementation? REUTERS: We have to go through a lot of tricks to design the system in such a way that the time-critical aspects of the application are catered to. There must be an easier way. What's also a major problem is 640K restrictions. It's imperative that we're able to access lots of memory. For large data areas you can do something with RAM disks, but we really need MS-DOS 5.0. It seems to be receding into the far distance; every quarter it seems to slip a quarter. The whole point of a windows environment is to have multiple simultaneous applications running, and if memory limitations prevent loading more than two applications, that's really bad news. If Microsoft is going to keep faith with the developers of Windows applications, it has to come out with MS-DOS 5.0──or at least a mechanism for making large chunks of executable memory available. Cautious Interest MSJ: You mentioned that in the course of developing your product you had to come up with some tricks to perform cerain tasks. Do you ever share this information with other developers? Would, for instance, a user group benefit from your experiences, or would you rather keep that kind of information to yourself? REUTERS: I'd characterize our attitude as "cautious interest." You must understand that the major users of Microsoft Windows in the U.K. are either our customers or competitors. And so, while we will certainly chat about our experiences and exchange information on bugs and so on, we would not be quite as forthcoming as perhaps a company that was trying to advertise itself. MSJ: The keyboard used with ART is quite different from the normal PC-style keyboard. Tell us something about it. REUTERS: The keyboard uses an 80186, which gives it more processing power than a PC. It is completely programmable, which we need since we have many different installations, which drive different kinds of terminal keyboards. The keyboard we use lets us emulate any of these. MSJ: What is the function of your proprietary communications board? REUTERS: The board has its own processor and handles the communications link, which is a multi-drop line protocol. The board also de-couples communications processing from Windows processing and does page-caching. Information pages are put into cache memory so that dealers will get a quicker response when changing pages. MSJ: Are you looking at any of the new graphics chips like the Intel 82786 and TI34010 to enhance the system? REUTERS: Yes. We've developed a super-high-resolution graphics board that uses the Intel chip, and we are waiting for Intel to release the final version. We were also impressed with the TI chip, and we're looking at it seriously. We'll use the one that's up and running at board production time. MSJ: Your windows work a little differently from the typical Microsoft windows. Why did you choose to have a common menu bar? REUTERS: We eliminated the menus because we didn't want to waste any precious screen space, and found that it simplified usage. Mouse-o-phobia? MSJ: What has been the reaction of dealers to the mouse? REUTERS: We've had a positive response at demonstrations we've given. No one who has used it has ever let it go. We have found, however, that some people take time to adjust to pointing with it──as long as two to three hours. MSJ: Now that you've had considerable experience with it, what is your overall impression of the Microsoft Windows environment? REUTERS: Windows is an extremely powerful set of tools. We could not have brought to market a product of the quality of the ART in such a relatively short time frame without it. The learning curve is reasonably short, too. An experienced C programmer will take only a matter of days to get a simple application running. And in perhaps four to five weeks he'll become fully productive, partly as a result of support within the team. A large number of people on the team now know how to use Microsoft Windows. Also, the examples of working systems make the learning curve easier. From that point of view, I'm convinced Windows is the right choice for Reuters. As an environment for running third-party applications, Windows might not be the ideal choice, but we're using it for specialized applications within the financial marketplace. The thing that makes it so useful is its ability to adapt quickly to different high-resolution screens. For example, we pulled in a Wyse PC with a 1280 by 786 monochrome screen. In a couple of hours we got the whole system running in a totally different resolution, different character sets, different fonts and looking nice. If you've got a Windows driver for your display, you can port your application onto it and make use of the facilities provided by that display. It's that flexible. MSJ: Is there anything else? REUTERS: One of the most imaginative ideas I've seen yet is that Microsoft has been advertising in a specialist magazine, called DOT.EXE. It's targeted at ISVs and offers, for four hundred pounds, a one-day course in Microsoft Windows, which includes an ISV tool kit and a Microsoft Mouse. We'll probably be plugging our new people into it. The credit for developing ART should go to a team of high-powered programmers led by Chris Oswald. They are the ones who deserve the credit for doing an incredible job. ──────────────────────────────────────────────────────────────────────────── Dynamic Data Exchange Enhances Application Connectivity Harvey Berger One of the most valuable benefits of Microsoft Windows is that enables you to use a number of application programs in combination to solve a problem. With Windows you can transfer text, numbers, or pictures from one application to another. A classic example is the ability of Windows to take a table or a graph from a spreadsheet program and insert it into a word processing document. The Windows Clipboard is what serves as the intermediate repository for such information exchanges among applications. At your direction, information is placed on the Clipboard by a CUT or COPY operation in one application and then inserted into the context of a second application with a PASTE operation. On the surface the process seems straightforward──a selection, a CUT or COPY, and then a PASTE──and manual. But behind the scenes the two applications and Windows combine to effect the actual transfer. The donating application takes control of the Clipboard and places the selected information on it in one or a series of formats. The receiving application then selects a format and requests the data from the Clipboard. In many cases, to save memory and reduce processing overhead, the donating applications may defer actually rendering the selected data until another applications requests it. A Step Forward The Clipboard's facilities are quite flexible, but they require manual intervention for each transfer, and the Clipboard can hold only one "item" at a time. In a multitasking environment like Windows, the need rapidly develops for more sophisticated data exchange facilities. Windows Dynamic Data Exchange (DDE) is intended to facilitate implementation of more complex information transfers than are possible with a single or even multiple clipboards. DDE is a formal, public protocol for data exchange built on Windows messaging, and the shared memory facility. DDE is much more an agreement among cooperating applications than it is a particular body of code or executable routine. It's a special usage of Windows messages. Windows applications routinely receive messages corresponding to user- and system-initiated actions ranging from keystrokes to request to repaint the screen. DDE uses a set of formerly reserved message numbers to implement the protocol. In the Windows environment any application may send a message to any other application, without explicit knowledge of the nature of the other application. DDE is designed to facilitate implementation of a wide variety of data exchanges, ranging from simple one-time transfers of selected data to automatic parallel transfers of real-time data. Since many DDE relationships are likely to be more complicated than those of the Clipboard, DDE is described in terms of a server and client applications rather than in terms of a donor and a receiver, where a single server may be supplying data to many clients and a single client application may be communicating with many servers simultaneously. Data Formats DDE and the Clipboard share a number of common facilities, most notably an extensible set of data exchange formats. Initially Microsoft Windows recognized a limited set of public formats for data exchange (TEXT, BIT MAP, META-FILE, SYLK, DIF, and so forth) and custom formats. Concurrent with the announcement of DDE, Microsoft has announced five additional clipboard data formats (Rich Text, Table, Sub-Table, WKS, and DBF). Windows also allows cooperating applications to register custom formats for private usage. The new formats allow the economical transfer of data with its formatting information. Object Orientation A key distinction between Clipboard and DDE exchanges is DDE's object orientation. The Clipboard is used to transfer a user selection from one application to another. Each Clipboard transfer is independent; the sequence of transfers corresponds to the sequence of user operations. With DDE each transfer consists of an item associated with a "name." Names and Topics When requesting or supplying data, the names are provided to allow the communicating task to identify the source or disposition of the data item. Names describe either the location or the contents of a data item. In a spreadsheet, each cell has an address (for example, R2C3 for the cell in the second row and third column). Some spreadsheets also have named cell ranges (R1:4C5, for example, might be named TOTALS). In either case, a natural relationship exists between names and data items. With other kinds of applications, like word processors, names are likely be to more positionally oriented. The DDE protocol requires a name or a positional reference for each item to be transferred. By definition, each name, as recognized by the server, refers uniquely to a specific data item. The choice of names and topics are the private concern of a server and its clients. Some servers will associate names relevant to a given subject with a topic. Client applications typically ask potential servers if they recognize (and can supply data about) the topic. Where the server's names are in themselves very complete descriptions of data items, a sample name may be used as the topic. Servers that define topics should be worded carefully to avoid accidental duplication. Applications In the first example the Clipboard is used to paste a chart into a word processing document. With DDE it is possible to construct more complex scenarios. In addition to saving the pasted chart, the word processor might store the name of the chart, the file name worksheet it was associated with, and the file name of the application program. Given that information, the word processor could ensure that it had the latest version of the chart. First the word processor could, after verifying existence of the worksheet file, start executing the spreadsheet application, specifying the worksheet file name and a DDE startup parameter. Then, using DDE, the word processor could request retransfer of the graph. The exact procedures to be used are left up to the developers of the respective applications. Microsoft will most likely issue guidelines soon for future application development. Chain Reaction A scenario in which the chart is the result of calculations in a spreadsheet is also reasonable to envision. When printing the document, the word processor might request the chart from the graphics application, which in turn might extract the applicable data from the spreadsheet. The integration becomes involved, but the burden moves from the user repeating manual steps (which he might forget) to the PC using DDE. DDE Sequences A sequence of DDE exchanges between a client and a server are referred to as sessions.(Figures 2, 3, 4, 5, 7, 8, and 9 show portions of sample client and server applications.) A session typically begins with an INQUIRE message. A client application sends a WM_DDE_ INQUIRE to every other active application (potential server) in the system. The INQUIRE message includes a topic or a sample name. If an active server is willing to exchange data related to the topic, it in turn responds with a WM_DDE_ ACK message. After establishing a session, the client and the server exchange messages according to the operating rules of the server. Servers may have implementation restrictions on the number of clients or the types of transfers it will honor. DDE provides eight message types that establish transfer protocols. The terms client and server are largely semantic, since either application can send (or receive) any message type. In practice, a given application may be functioning as a server to one application and as a client to the same or another application. A simple example of a DDE transfer would consist of the following sequence: 1. Application One broadcasts an INQUIRE message to those windows in search of a server that will discuss CHEMICAL FORMULAS. 2. APP2, with a database of chemical formulas, responds to APP1 with an ACK (acknowledgement) message. 3. APP1 then sends a REQUEST message specifying WATER as the desired item name. 4. APP2 responds with a text string UPDATE message pointing to WATER=H2O. 5. APP1 then sends a REQUEST message for SALT. 6. APP2 responds with an UPDATE message specifying SALT=NaCl. Real-Time Data DDE was developed in response to the users' need for the coupling of data from separate applications. The stock market is another classic example of the need for DDE. During trading hours there's a constant flow of data as each transaction is processed by the exchanges. Market watchers need to filter the data stream and isolate the transactions of interest to them. The selected data is then analyzed to determine the required action. One of the first demonstrations of DDE modeled this process. A SignalTM receiver (supplied by Lotus Information Network Corporation) offered a continuous feed of stock prices (for up to 600 selected stocks). Normally the Signal receiver (connected to a serial port) supplies data to special Lotus(R) software. For this demonstration Windows Terminal was modified to detect price changes and respond to DDE messages. A simple bar- charting program was then written to receive the DDE UPDATE messages. Interestingly the SIGNAL monitor program was written in New York City by consultant Louis Cutrona, and the Charting program was written by Mark Taylor of Microsoft. When brought together, the two programs were able to communicate through DDE after being developed 3,000 miles apart. ADVISE-DATA In the first example above, the transfers are relatively simple. For each REQUEST message there's a corresponding UPDATE. The ADVISE message allows for more complex transfers. In the following example it accommodates the automatic transfer of real-time data as it changes. 1. APP3 broadcasts an INQUIRE message to those windows, looking for a server with NY STOCK PRICES. 2. The Signal Monitor Application Four (APP4) responds with an ACK. 3. APP3 sends APP4 an ADVISE message that specifies IBM.(R) 4. APP4 responds with an ACK indicating that it recognizes IBM and can supply data about it. 6. APP4 then sends the current price data for IBM. 7. Steps 3 to 5 are repeated for each stock of interest. 8. Each time APP4 learns of a new price for stock about which it has received an ADVISE, it sends an UPDATE message with the new price. Atom Management To speed processing and simplify the development of DDE Servers, DDE applications exchange "atoms" rather than strings. A central atom manager allows all applications in the system to register strings in exchange for a 16-bit atom. The atom manager (a shared executable library) will also translate the atom to its corresponding string. A single atom pool is maintained for all DDE applications in the system. In anticipation of sophisticated client/server relationships, DDE offers optional specification of the Clipboard format for data exchange, acknowledgement of messages, and shared memory controls. Private client/server priority schemes are also accommodated. Developers interested in the details of DDE, the central atom manager, or the new Clipboard formats can download the specifications documents and sample programs from either DIAL or GENIE. On the basis of early response to the DDE design specification, a significant number of the new Microsoft Windows applications now being developed will incorporate DDE provisions. The nature of the implementation depends on the type of application and its particular features. Macros Many applications, including spreadsheets, databases, and charting programs, include user-definable macros or other programming capabilities. The DDE design anticipates the combination of these applications based entirely on user programming. If the application implements a substantive part of DDE capabilities, the user can supply the topics and item names, constructing data relationships not contemplated by the application developer. Generalized applications can thus be combined into tightly coupled customized applications suites. This process is not as complex as it might sound. In the bar chart example described above, the user can specify, via a dialog box, the particular stocks to be tracked. In a more generalized implementation, the user might also have been allowed to specify the topic, directing the DDE message to a different server. If, hypothetically, another server existed in the PC, it could supply the data to be plotted. Thus one instance of the bar chart program might display stock market data, while another instance displays temperature, pulse, and blood pressure from a server connected to appropriate sensors. This example may be far-fetched in the choice of data to be displayed side by side, but it's reasonable to imagine the development of common analytical software that can be applied to disparate tasks. Microsoft Windows and DDE expand the potential market for analytical software by providing a protocol that is truly application independent. Outside Influence So far this discussion has been limited to communications among applications running on the same PC. The principles of DDE are, however, applicable to transfers of data across machines, even computers of different types. The DDE protocol assumes that (if possible) a request for data exchange will be satisfied by "broadcasting" a request to all other tasks in the PC and that one of those contacted will supply the desired data. If the data desired is not available locally, DDE provides for the use of "agents." An agent is an application that serves as the importer/exporter of data. When an agent application receives an INQUIRE request for data about a given topic, it determines whether it can satisfy the request by in turn sending the message to its "correspondents" in other machines, which may be able to satisfy the request. A given agent might use a local area network, micro-mainframe link, or other communications facility to reach its correspondents. The correspondent agent may then issue a DDE INQUIRE (if it is located in a PC) to locate the ultimate server. The exchange of data then proceeds without either the client or the server being aware of the agents. Access Control While DDE does not explicitly provide for access control or data security, a server application has a number of alternatives at its disposal to validate the authority of a given client before exchanging data with it. The server could require a password or other access parameter to be sent along with the plete communication faciT message back to the client for the password, or using the window handle provided, access a lot of information (from Windows) about the client. The DDE protocol is a framework on which software developers can construct private or public exchange mechanisms. When exchanging data with private (well-known) clients, servers can supplement DDE with message priority, data queueing, and private message protocols. In addition to Microsoft Windows' built-in facilities for inter-application communication, very complete communication facilities designs can be implemented. The Future The fact that Microsoft Windows is independent of any physical device permits applications to take full advantage of the capabilities of a wide variety of current and future displays, printers, and pointing devices. Application developers are freed from having to be concerned in their programs with the peculiarities of particular devices. DDE extends the same concept to data exchange. If an application honors DDE requests, it's then able to communicate with all other applications that do so, without having to consider their peculiarities. DDE is an important step forward in inter-application communication, since it provides a public protocol that applications can subscribe to rather than requiring each application developer to have to consider all the possible applications that users might want to exchange data with. The user can then combine programs and achieve results that no single application can offer. Figure 1: DDE Messages ╓┌─────────────────┌───────────────────┌──────────────────┌──────────────────╖ DDE Messages Purpose Argument 1 Argument 2 WM_DDE_INITIATE Ask if a window Atom ─► Topic will exchange items related to a topic WM_DDE_TERMINATE End the exchange Atom ─► Topic of items related to the topic WM_DDE_ACK Response to a Atom Acknowledgement DDE message code WM_DDE_REQUEST Request a given Atom ─► Item name Desired data item format WM_DDE_DATA Contains a Atom ─► Item name Handle to requested data data object item WM_DDE_ADVISE Request a given Atom ─► Item name Desired data item and automatic format updating DDE Messages Purpose Argument 1 Argument 2 updating WM_DDE_UNADVISE Request end of Atom ─► Item name Desired data automatic updating format for a given time WM_DDE_POKE Contains an Atom ─► Item name Handle to unsolicited data data object item WM_DDE_EXECUTE Request execution Handle to data of indicated object containing command command Figure 2: Client's Window Procedure typedef struct { WORD wDDEMODE; WORD wFormat; } DDEADVISEBLOCK; typedef struct { WORD wClientRelease; WORD wFormat; WORD hServerWnd; WORD ClipData; } UPDATEINFO; ATOM aCompare; BOOL bAccepted; long FAR PASCAL SampleRcvrWndProc(hWnd, message, wParam, lParam) switch (message) { ∙ ∙ ∙ case WM_DDE_ACK: aCompare = HIWORD(lParam); bAccepted = LOWORD(lParam); /* Is this acknowledgement of first inquire message? */ if ((aCompare == aStockInfo) && bAccepted) hwndServer = wParam; else { /* is this the acknowledgement of an advise message? */ for (i = 0; iwDDEMODE = 0; /* I'm not going to ACK his updates */ lpmem->wFormat = CF_TEXT; GlobalUnlock(hmem[i]); PostMessage( (HWND) hwndServer, WM_DDE_ADVISE, hwnd, MAKELONG(hmem[i],stockinfo[i].Atom) ); } Figure 4: Client's Data Update Procedure ProcessNewData(hwnd, lStuff) HWND hwnd; DWORD lStuff; { ATOM aData = HIWORD(lStuff); HANDLE hDDEData = LOWORD(lStuff); UPDATEINFO far *lpDataBlock; LPSTR lpData; HDC hdc; int i,c; /* Make sure we're getting info only from our server */ /* Note: hwndServer is a global variable */ if (hwnd != hwndServer) return; lpDataBlock = (UPDATEINFO far *)GlobalLock(hDDEData); for (i = 0; iwFormat == CF_TEXT ) { for( i = 0; i < MAXHOTITEMS; i++ ) { if( DDE[i].aSymbol == atom ) { for( j = 0; j < MAXCLIENTS; j++ ) { if( DDE[i].hwndClient[j]==(HWND)hwndClient){ b = TRUE; /* Already there */ break; } } if( !b ) /* if atom is there but client isn't */ { for( j = 0; j < MAXCLIENTS;j++ ) { /* Look for an empty slot */ if( DDE[i].hwndClient[j]==(HWND)NULL ) { DDE[i].hwndClient[j]= (HWND)hwndClient; DDE[i].cActive++; b = TRUE; break; } } } } if( b ) break; } if( !b ) /* If atom is not already in the table */ { for( i = 0; i < MAXHOTITEMS; i++ ) { if( DDE[i].cActive <= 0 ) { DDE[i].aSymbol = atom; DDEGetAtomName( atom, (LPSTR) DDE[i].szSymbol, MAXSYMCHARS - 1 ); DDE[i].szSymbol[MAXSYMCHARS - 1 ] = '\0'; DDE[i].hwndClient[ 0 ] =(HWND) hwndClient; DDE[i].cActive = 1; b = TRUE; break; } } } GlobalUnlock( gh ); } /* Acknowledge one way or the other */ if( IsWindow( (HWND) hwndClient ) ) SendMessage( (HWND) hwndClient, WM_DDE_ACK, hwnd, MAKELONG( b, atom ) ); if( b ) { /* Transmit latest data we received */ if( ( gh = GlobalAlloc( GMEM_MOVEABLE, (DWORD) sizeof(DDEUPDATEBLOCK))) && ( lps = (LPSTR) GlobalLock( gh ) ) ) { for( p = (char *) &DDEUpdateBlock, k = 0; k < sizeof( DDEUPDATEBLOCK ); *lps++ = *p++, k++ ); GlobalUnlock( gh ); PostMessage( (HWND) hwndClient, WM_DDE_DATA, hwnd, MAKELONG( gh, atom ) ); } } return; } ──────────────────────────────────────────────────────────────────────────── New Intel Graphics Coprocessor Makes Windows Sparkle ─────────────────────────────────────────────────────────────────────────── Also see related tables: 82786 Features 82786 Graphics Instruction ─────────────────────────────────────────────────────────────────────────── The new graphics coprocessor from Intel, the 82786, is expected to substantially enhance the performance of Microsoft Windows and other graphics applications on the IBM PC, XT, AT, and compatibles. The chip performs near-instantaneous screen updates, draws graphic primitives such as lines and circles at more than 2 million pixels per second, and creates text at a rate of 25,000 characters per second. It also supports multitasking and ultra-high-resolution displays. The 82786 has four major components: a graphics processor, display processor, bus interface unit, and dynamic RAM controller. These parts of the chip communicate over a built-in asynchronous bus. The graphics processor accepts commands from applications programs to draw bit maps, which create objects in memory. The processor contains a set of on-chip graphics commands that draw points, lines, rectangles, circles, polygons, and other primitives, to relieve the host CPU of the task of creating these figures from scratch. To display any of these figures on the screen, the CPU sends a single 16-bit request to the 82786 graphics coprocessor. To handle specialized drawing functions, the 82786 includes commands such as BitBLT (bit block transfer), ChaBLT (character-block transfer), Incremental Point, and Fill. BitBLTs are performed upon request from the CPU at a rate of 24 megabits per second. ChaBLTs occur at the rate of 25,000 characters per second. The Incremental Point command is used to draw more complex figures. An incremental point list describes the figure's shape, and, once the CPU sends the list to the 82786, the graphics processor draws the shape at a rate of 2 million pixels per second. The Fill command instructs the graphics processor to fill in any shape with horizontal lines. The coprocessor also has non-drawing instructions. For example, the Link command executes an unconditional jump to another command, and Enter Macro and Exit Macro are used to call and return, respectively, from a subroutine. The 82786 can receive a block of commands at one time from the CPU, and do it while executing other commands. Many of the graphics processor's functions are implemented in hardware rather than software. These include clipping, mouse support, character-set support, logical operations, and bit-plane masking. The amount of display memory is the only limitation on the number of character sets that the 82786 can support. Any one can be activated by a single command. All character sets can have proportional spacing or kerning. The next major functional block of the 82786 is the display processor. This part of the 82786 is independent of the graphics processor. Microsoft Windows would use the display processor to control display contents. The display processor transfers multiple bit maps from memory to window areas on the screen. It also provides a hardware cursor and colored borders around the windows. An almost unlimited number of windows can be displayed on the screen. To create windows, the display processor divides the screen into strips and tiles. A strip is one or more horizontal scan lines, and a tile is a section of a strip. A collection of tiles forms a window. Unlike the graphics and display processors, the bus-interface unit and dynamic RAM controller are invisible to the CPU software. The bus interface unit maintains the integrity of a display by arbitrating requests for graphics memory. The dynamic RAM controller handles up to 4 megabytes of graphics memory and screen rereshing. 82786 and Windows What will the 82786 do for a product like Microsoft Windows? According to Richard Bader of Intel, "The 82786 will make Windows so responsive that a computer like the IBM PC will no longer stand in the way of what the user wants to do. Operations that took seconds to execute previously will now happen instantaneously. The 82786 enhances the intimacy between user and machine, and thus enhances productivity." Will Windows developers need to understand the intricacies of the 82876 in order to achieve this high performance? Says Bader, "When an 82786 board- level product comes to market, it will have a Windows driver, which would then make the 82786 virtually transparent to the applications programmer──as transparent as the Microsoft Graphics Device Interface allows. When an applications programmer uses any graphics board, he needs to interrogate the GDI interface to find out specific characteristics of the board such as the resolution it supports and whether or not it supports color. The application is then adapted to those physical characteristics. But for the most part there is a great deal of transparency provided by GDI, and this will be applicable to new 82786 graphics boards." An Ideal Application One application that stands to gain significantly from the appearance of 82786 boards is desktop publishing. Intel's Bader points out, "As Windows evolves, we expect that it will be ideally suited for desktop publishing. And since the 82786 is so well suited as a GDI device, we believe that the 82786 is also applicable to that market." Many of the desktop publishing packages currently on the market take quite a long time to reformat information on the screen. And displaying a variety of fonts on the screen is time consuming and awkward. Continues Bader, "The speed of the 82786 will change all that. Microsoft Windows in conjunction with the 82786 will give a good performance." The 82786 supports a wide range of monitors, which is also a plus for desktop publishing. It easily supports systems that are 640 by 480 with 64 colors. Additionally, Bader points out, "We can also support large-format screens, full-page displays, with a resolution in the order of 1200 by 800, 1400 by 1400, or 1000 by 800 with gray scales. What we've found in a number of instances is that people are looking for that 17-inch high-resolution monochrome monior. They're looking for extremely crisp, stable, high- performance text displays, and that's exactly what the 82786 is capable of providing." Intel and Microsoft have entered a cooperative program to provide Microsoft Windows software driver technology for systems designs using the Intel 82786. ─────────────────────────────────────────────────────────────────────────── 82786 Features ─────────────────────────────────────────────────────────────────────────── ■ High-performance graphics ■ Fast polygon and line drawing ■ High-speed character drawing ■ Advance DRAM controller for graphics memory up to 4 megabytes ■ Supports up to 200-MHz CRTs ──up to 640 by 480 by 8 bits (1K∙1K∙8) or 1400 by 1400 by 1 bit (2K∙2K∙2) ■ Up to 1,024 simultaneous colors ■ Integral DRAM controller, shift registers, and DMA Channel ■ IBM PC Computer Graphics Adaptor compatbility ■ Hardware windows ■ Fast bit-block copies between system and bit-map memories ■ Integral video DRAM support──up to 1900 by 1900 by 8 ■ Multitasking support ■ International character support ■ Provides support for rapid filling with patterns ■ 88- pin grid array leadless chip carrier ■ Advanced CHMOS technology ─────────────────────────────────────────────────────────────────────────── Graphics Instructions ─────────────────────────────────────────────────────────────────────────── 64 Bit Block Transfer within bit-map AE Bit Block Transfer across bit-maps 06 Define Texture: Opaque 0A Define Character Set: Word 64 Bit-Block Transfer Within Bit-Map AE Bit-lock Transfer Across Bit-Maps 06 Define Texture: Opaque 07 Define Texture: Transparent 0A Define Character Set: Word 0B Define Character Set: Byte 1A Define Bit-Map 3D Define Colors 41 Define Logical Combination 46 Define Clip Rectangle 4D Define Inter character Space 4E Define Character Orientation 68 Draw Arc: Exclusion 69 Draw Arc: Inclusion 53 Draw Point B4 Draw Incremental Points BA Draw Series of Horizontal Lines 54 Draw Line 58 Draw Rectangle 73 Draw Polygon 74 Draw Polyline 8E Draw Circle A6 Draw Character String: Opaque A7 Draw Character String: Transparent 44 Enter Pick Mode 45 Exit Pick Mode 4F Move to Absolute Position 52 Move to Relative Position ──────────────────────────────────────────────────────────────────────────── Programmable Graphics processor from TI Perks up PC Pixels ─────────────────────────────────────────────────────────────────────────── Also see the related TMS34010 tables: TMS34010 Features TMS34010 Graphics Instruction ─────────────────────────────────────────────────────────────────────────── Joe Desposito Texas Instruments(R) has developed the first graphics microprocessor, the TMS34010. This is a significant development in the world of graphics chips, which to date has been populated with less powerful graphics coprocessors and controllers. The TMS34010 is a 32-bit processor that executes 6-million instructions per seconds (MIPS), addresses up to 4-gigabytes of memory, and i a general-purpose instruction set. The chip, which will enhance all graphics applications, is of particular interest to developers using Microsoft Windows on the IBM PC and compatibles, where up until now performance has been less than spectacular. The TMS34010 will be contained in an add-on board that will handle all the graphics processing, replacing the monohrome color graphics or enhanced graphics display adaptor. Inside the TMS34010 Like most microprocessors, the TMS34010 has an ALU, general registers, stack pointer, program counter, instruction decoder, and control unit. However, it also contains components that optimize the chip for graphics processing. It has a far greater number of general registers contained in the microprocessor than that found in general-purpose processors. Thirty 32-bit registers are divided into an A file and a B file. This large number prevents the shifting of bits between registers that often occurs with graphics processing. It uses a barrel shifter for high-speed bit rotations and can rotate a field of up to 32-bits in a single instruction cycle. The microprocessor uses a four-segment instruction cache for loading instructions from memory before they are executed. Each segment has eight subsegments, each holding eight bytes of code. This 256-byte instruction cache speeds up the execution of graphics algorithms by capturing loops of instructions. Once a loop is captured, "fetch" memory cycles in the loop halt. Loop execution is fast because the chip can access instruction cache, internal register files, and external memory simultaneously instead of sequentially. The chip's input/output registers include interrupt registers, host interface registers (for communicating with a host processor), video timing registers (to drive a CRT), and local memory control registers. To further increase speed, the TMS34010 employs some of the principles of reduced-instruction-set computer (RISC) chips; for example, it executes most of its instructions in a single 160-ns cycle. Pixel Processing In addition to approximately 100 general-purpose instructions, the TMS34010 uses special high-speed instructions called PixBLTs (pixel block transfers) to handle character display and bit-mapped graphics. These instructions are an extension of BitBLTs (bit-block transfers) found in some graphics controllers. Transfers are made between source and destination arrays using linear addressing or X-Y coordinates. PixBLT instructions include Boolean and arithmetic operations, expansion of a single bit to one of two colors, transparency detection, and plane- masking. These types of instructions can also be combined to perform a much larger set of operations. The TMS34010 operates on single-bit pixels, which are usually associated with monochrome graphics, by using its Boolean operations on source and destination arrays. The common combining operations are Replace, OR, NOT- AND, AND, and XOR. For multiple-bit pixels, which are usually associated with color and gray- scale graphics, the processor uses arithmetic operations such as Maximum, Minimum, Add, Add with Saturate, Subtract, and Subtract with Saturate. The Maximum and Minimum functions are used to create a sense of depth and to smooth jagged lines, a technique called antialiasing. Add with Saturate and Subtract with Saturate are useful for dedicating multiple color planes to each of the three primary colors. A PixBLT instruction can expand a bit to one of two colors by taking one bit per pixel data from memory, expanding it to multiple-bit color data, and placing it anywhere in the frame buffer. PixBLT instructions also handle windowing, allowing the placement of windows of variable size and content anywhere on the display, and clipping, which prevents drawing outside the boundaries of a window. The way the TMS34010 handles text generation is quite different from other processors. First, a display list, which defines what is to be displayed, appears. For normal block text, the list is simple; say, a list of ASCII character codes. As text becomes more complex, requiring, for example, proportional spacing and kerning, the display list becomes more complicated. The TMS34010 general-purpose instructions typically break down the display list, and then a PixBLT is used to move this image from off-screen memory onto the screen. Texas Instruments is supporting the TMS34010 with a line of software development and support products, including low-level assembly language tools, a C compiler with a run-time support package, a function library, and support of the CGI (computer graphics interface) standard. Hardware and software development systems are also available. Building Applications Since the TMS34010 is a microprocessor, a software developer has a great deal of flexibility in determining what runs on the graphics processor and what runs on the host processor. Since the chip will also support a number of graphics standards such as CGI and Microsoft's GDI (graphics device interface), many applications can be ported by using these standards. One way that developers can tap the potential of the chip is to port a graphics environment like Microsoft Windows to it at GDI levels. Thus any software application running through Windows could be accelerated by a 32- bit graphics processor. However, many of the graphics cards being developed are relatively "soft" cards, in that they are RAM based. In effect, the command set for the board will be downloaded to it. As Kevin McDonough of Texas Instruments points out, "This will give the cards tremendous flexibility and tremendous performance advantages. For example, a program such as AutoCAD running under Microsoft Windows will run much faster in the Windows environment. You can probably gain another performance factor of 10 by doing specific algorithms and downloading them to the graphics processor for specific applications." According to McDonough, graphics boards incorporating the TMS34010 processor are currently under development at Video 7, Number 9, and other vendors. The distinguishing feature of the TMS34010 is that the processor is not limited to a few algorithms. Rather, any algorithm, be it a line, ellipse, circle, or whatever, can be programmed. And similarly, for text, the chip can be programmed for any format. Karl Guttag, also of Texas Instruments, points out that the chip is also excellent at performing data compression algorithms. "You know that when you draw images, they have to be stored on disk. And since the images take up a lot of disk space, it makes sense to compress them. Data compression mostly involves bit manipulation, which the TMS34010 excels at, so it turns out that this chip is better than any general-purpose processor for doing image compression." Texas Instruments and Microsoft have entered a cooperative program to provide Microsoft Windows software driver technology for systems designs using the TMS34010. With graphics boards that incorporate the TMS34010 processor now under development, a new world of graphics will soon open up to personal computer users. ─────────────────────────────────────────────────────────────────────────── TMS34010 Features ─────────────────────────────────────────────────────────────────────────── ■ 160-ns instruction cycle time ■ Fully programmable 32-bit general-purpose processor with 128-megabyte address range ■ Pixel processing, X-Y addressing, and window clip/pick built into the instruction set ■ Programmable 1,2,4,8, or 16-bit pixel size with 16 Boolean and 6 arithmetic pixel processing options (Raster-Ops) ■ 31 general purpose 32-bit registers ■ 256-byte LRU on-chip instruction cache ■ Direct interfacing to both conventional DRAM and multiport video RAM ■ Dedicated 8/16-bit host processor interface and HOLD/HLDA interface ■ Programmable CRT control (HSYNC,VSYNC,BLANK) ■ High-level language support ■ Full line of hardware and software development tools including a C compiler ■ 68-leaded packaging (PLCC) ■ 5-volt CMOS technology Graphics Instruction ─────────────────────────────────────────────────────────────────────────── TMS34010 Graphics Instruction ─────────────────────────────────────────────────────────────────────────── ADDXY RS,RD Add Registers in XY Mode CMPXY RS,RD Compare X and Y Halves of Registers CPW RS,RD Compare Point to Window CVXYL RS,RD Convert XY Address to Linear Address DRAV RS,RD Draw and Advance FILL L Fill Array with Processed Pixels: Linear FILL XY Fill Array with Processed Pixels: XY MOVX RS,RD Move X Half of Register MOVY RS,RD Move Y Half of Register PIXBLT B,L Pixel Block Transfer: Binary to Linear PIXBLT B,XY Pixel Block Transfer: Binary to XY PIXBLT L,L Pixel Block Transfer: Linear to Linear PIXBLT L,XY Pixel Block Transfer: Linear to XY PIXBLT XY,L Pixel Block Transfer: XY to Linear PIXBLT XY,XY Pixel Block Transfer: XY to XY PIXT RS,*RD Pixel Transfer: Register to Indirect PIXT RS,*RD.XY Pixel Transfer: Register to Indirect XY PIXT *RS,RD Pixel Transfer: Indirect to Register PIXT *RS,*RD Pixel Transfer: Indirect to Register PIXT *RS.XY,RD Pixel Transfer: Indirect XY to Register PIXT *RS.XY,*RD.XT Pixel Transfer: Indirect XY to Indirect Y SUBXY RS,RD Subtract Registers in XY Mode LINE Z Line Draw Key to abbreviations: S (source register), D (destination register), R (register file select), K (constant), Z (draw option) ──────────────────────────────────────────────────────────────────────────── Developers' Aid: Dialog Editor Charles Petzold The Microsoft Windows dialog box is both one of the most important elements of a Windows application and the most difficult element to design. Since dialog boxes allow the user to select or enter information too complex for an application's menu, they must be clearly and logically organized. Yet the graphical image of the dialog box is normally derived from a template in a Resource Script (.RC) file, where all the coordinates and sizes are specified as numbers. For the programmer, editing the .RC file to get these coordinates and sizes right (both in terms of clarity and aesthetics) is mostly a matter of trial and error. New Dialog Editor The only help the original Microsoft Windows Software Development Kit offered in designing dialog boxes was the barely adequate DLGEDIT.EXE. If you've tried DLGEDIT and rejected it, don't overlook version 2 of the dialog editor, now called DIALOG.EXE. It's a complete rewrite and a major improvement. A beta test version of the new dialog editor is distributed at Windows Development Seminars and can also be downloaded from the Microsoft SIG on CompuServe. Aside from it's practical value in helping you create attractive dialog boxes for Windows applications, DIALOG is also an excellent tool to learn about the various styles, controls, and options of dialog boxes. The only problem is that it is distributed without documentation. But like most Windows applications, some creative playtime unlocks most of it's treasures. DIALOG saves files in two different formats: a .RES format (the compiled format that it uses for later editing) and a human-readable ASCII format with the extension DLG. The .DLG file contains the complete dialog template as it would normally appear in a .RC file, with style parameters expressed in terms of the macro definitions from WINDOWS.H (such as BS_PUSHBUTTON) and IDs identifying each control as specified in a header file for the application. One .RES file created by DIALOG can contain many different dialog templates for the application. You can begin a new file within DIALOG by selecting New from the File menu. To start a new dialog template, select New Dialog from the Edit menu and switch between dialog templates by using the View Dialog option of the File menu. To fill up the dialog box with various controls, use the Control menu, which lists 12 basic types of controls (such as Check Box, Radio Button, Push Button). If the control uses text, it will simply show the word TEXT. You can select the control for editing simply by clicking on it. By hooking the sides or corners you can stretch and size it, and by hooking the center you can move it around. A Matter of Style The Control menu lets you select from the basic types of controls. The Styles menu allows you to further refine them, replace the word TEXT with something else, and assign an ID to each control. These IDs are usually macro definitions from the application's header (.H) file. Your source program can then reference each control by these names. Although DIALOG can read a header file and let you use the name (rather than the number), it asks for the header file only when reading an existing RES file. So, before you get too far, you'll want to save a new file using the Save As option from the Files menu, create a header file with define statements for the IDs, read the file back into DIALOG with File Open, and enter the name of the header file when DIALOG asks for it. From the Styles menu you have a choice of Class Styles and Standard Styles. The options here let you refine the way the control appears on the screen and how it works. For instance, you might select Push Button from the Control menu, which will display text in a rounded rectangle and is often used for things like OK and CANCEL If you want a default setting for the push button (which has a bold outline), you first select the push button control for editing, bring down the Class Styles option of the Styles menu, and select Def Push Button. Another example: The dialog box itself is initially shown with a simple frame. You can add a caption bar and a system menu box by selecting those options from Standard Styles. Controls in a Windows dialog box are generally organized by "groups" and "tab stops." Controls marked as tab stops can be navigated using the tab key. Within a group, however, you can use the arrow keys. You can mark controls as groups and tab stops from the Standard Styles option of the Styles menu. However, if you've been designing your dialog box in a free- form manner, you'll find that the tab stops will bounce around from item to item in the order that you've created them rather than a visually logical order. Fortunately, you can both re-order the controls and select groups and tab stops through the Order Groups option of the Options menu. This shows a list of all the controls you've created. Selecting a control with the mouse and placing the horizontal black bar cursor in a different location moves the control. To define a group, you have to mark only the first and last control of the group. You would usually flag the first control of the group as a tab stop also, but you can pick another control as the tab stop if you'd like the cursor to jump to the middle of a group. Make Sure It Works Of course, now you'll have to recompile to see if the thing works right. Well, not quite yet. You can now pick Test Mode from the Options menu and test the tab stops and groups from within DIALOG. This test mode is one of the nicest features of the program. DIALOG is not perfect. You should watch out for a few things: If you want to delete a specific control from the dialog box, first select it and then pick Clear Control from the Edit menu. The Cut Dialog option of the Edit menu deletes the whole dialog box and everything in it. You can get it back by selecting Paste Dialog from the Edit menu, but it's a little disconcerting to see the whole thing disappear. Icons do not work right. You should be able to identify an icon by typing the name in the Text field of the Class Styles box. However, DIALOG will ignore this name. You can either avoid using icons in dialog boxes or enter in the name later using a text editor. The worst problem does not lie with DIALOG. Preferably, you should be able to include the .DLG file created by DIALOG in the application's Resource Script (.RC) file, as: #include "myprog.dlg" However, the RC.EXE resource compiler version 1.00 will not correctly process an include file in the resource script unless the include file contains only define statements. Until this problem has been fixed, you'll probably have to copy the .DLG file into your .RC file manually. You'll also want to use a different name for the .RES file that is created by DIALOG and used for future editing. Otherwise, DIALOG's .RES file will be overwritten when you run the RC resource compiler. Although DIALOG can read and properly modify a RES file that it did not create and which contains information other than the dialog boxes, it would probably be safer to let DIALOG work with its own .RES file. Required Reading Chapter 7 (pages 147 to 174 of the Microsoft Windows Software Development Kit Programming Guide (for a general discussion of controls and dialog boxes of controls and dialog boxes). Section 6.2.6 (pages 292 to 307 of the Programmer's Reference (for the format of the DIALOG template). Figure 1: TRYOUT DIALOG 11, 18, 208, 165 CAPTION "Mythical Windows Spreadsheet" STYLE WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_POPUP BEGIN CONTROL "Number Format" 11,"button", BS_GROUPBOX | WS_CHILD, 12, 4, 64, CONTROL "General" ID_GEN, "button",BS_RADIOBUTTON | WS_GROUP | WS_TABSTO CONTROL "Fixed" ID_FIX, "button", BS_RADIOBUTTON | WS_CHILD, 16, 28, 4 CONTROL "Scientific" ID_SCI, "button", BS_RADIOBUTTON | WS_CHILD, 16, 40, 5 CONTROL "Currency" ID_CUR, "button", BS_RADIOBUTTON | WS_CHILD, 16, 52, 4 CONTROL "Comma" ID_COM, "button", BS_RADIOBUTTON | WS_CHILD, 15, 65, 4 CONTROL "Default Column Width:" 8, "static", SS_LEFT | WS_GROUP | WS_CHILD, CONTROL " 9" ID_DCW, "edit", ES_LEFT | ES_RIGHT | WS_BORDER | WS_TABSTOP | CONTROL "Decimal Places:" 17, "static", SS_LEFT | WS_CHILD, 88, 28, 66, 10 CONTROL " 2" ID_DPL, "edit", ES_LEFT | ES_RIGHT | WS_BORDER | WS_TABSTOP | CONTROL "Fast Entry Mode" ID_FEM, "button", BS_AUTOCHECKBOX | WS_TABSTOP | CONTROL "Flag Circular Calcs" ID_FCC, "button", BS_CHECKBOX | WS_TABSTOP | CONTROL "Text Format" 19, "button", BS_GROUPBOX | WS_CHILD, 12, 84, 64, CONTROL "Left" ID_LEF, "button", BS_RADIOBUTTON | WS_GROUP | WS_TABST CONTROL "Right" ID_RIT, "button", BS_RADIOBUTTON | WS_CHILD, 16, 108, CONTROL "Centered" ID_CEN, "button", BS_RADIOBUTTON | WS_CHILD, 16, 120, CONTROL "Recalculation" 3, "button", BS_GROUPBOX | WS_GROUP | WS_CHILD, 88, CONTROL "Auto" ID_AUT, "button", BS_RADIOBUTTON | WS_GROUP | WS_TABST CONTROL "Manual" ID_MAN, "button", BS_RADIOBUTTON | WS_CHILD, 96, 108, CONTROL "Row-Wise" ID_ROW, "button", BS_RADIOBUTTON | WS_CHILD, 148, 96, CONTROL "Col-Wise" ID_COL, "button", BS_RADIOBUTTON | WS_CHILD, 148, 108, CONTROL "Natural" ID_NAT, "button", BS_RADIOBUTTON | WS_CHILD, 148, 119, CONTROL "OK" 0, "button", BS_DEFPUSHBUTTON | WS_GROUP | WS_TABSTOP | WS CONTROL "Cancel" 0, "button", BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD, 120, 1 END NULL ); } } return 0L; case WM_CREATE: ──────────────────────────────────────────────────────────────────────────── Ask Dr. Bob! Multitasking? Dear Dr. Bob, I'm confused: Is Microsoft Windows a multitasking environment or not? Sometimes it seems like it is, and sometimes it seems like it isn't. What's the story, Dr. Bob?──Confused Dear Confused, Most people think of multitasking as a process requiring time-slicing. In a traditional multitasking environment, the operating system switches between different tasks by allocating short periods of time to each. Microsoft Windows does not yet time-slice between applications. Windows will probably use time-slicing for multitasking only when MS-DOS also does so. Instead, Windows has a nonpreemptive multitasking scheduler for running Windows applications (programs written specifically for Microsoft Windows). A Windows application will not be interrupted if it does not want to be interrupted; only when it specifically relinquishes control do other applications get a chance to run. A Windows application normally sits in a loop waiting to receive messages, usually executing a GetMessage call. The messages it receives cover all the events (keystrokes, mouse movements, window resizing, and so forth) that affect the application. When it gets a message, the Windows application dispatches it to the appropriate procedure in the program. This procedure can either act on the message or ignore it. Microsoft Windows switches between applications when an application polls for a message by using the GetMessage call or the similar WaitMessage call. Once it has the message, the application can take as long as it wants, without interruption. In the interim other applications (for instance, the CLOCK) will stop working. Polite Windows applications check frequently for messages while performing lengthy chores. This way they can yield control to other applications and give them an opportunity to run. However, if a Windows application needs an unbroken chunk of time (say, for high-speed real-time data acquisition), it can take it. The application can interrogate the mouse and the keyboard, write to the screen, and do everything it needs to do──all without interruption. It just has to avoid looking for more messages. So Microsoft Windows is multitasking, but it's up to the individual Windows applications when to allow multitasking with other applications and when not to. For "good old applications" (MS-DOS applications that don't write directly to the display and that can run in a window), Windows yields control to other applications during keyboard input and screen output. But for old applications that do write directly to the display, Microsoft Windows suspends all multitasking but still allows the user (in most cases) to switch among applications. Compilers Dear Dr. Bob, Why do I need to use Microsoft compilers for developing Windows applications?──Puzzled Dear Puzzled, Right now the only compilers that support Microsoft Windows are the Microsoft C and Pascal compilers. You can also program Windows applications in assembler, but the most convenient way to do that would be to use assembler routines called from a C program. Microsoft has made public the relatively minor support for Windows that a C compiler requires, and so other compilers may also support Microsoft Windows. A C compiler for Windows requires procedures to be "Pascal" (arguments are pushed on the stack from left to right and that the procedure itself adjusts the stack). Normally, C pushes arguments right to left and adusts the stack after returning from the procedure. The C compiler must also allow mixed- model programming with the "near" and "far" key words. Procedures called from Microsoft Windows must have a small prolog and epilog so that Windows can adjust data segment references after moving data. No Software Interrupt Dear Dr. Bob, Is there some kind of software interrupt or other hook I can use to access Microsoft Windows procedures from within a program I write in another language, such as BASIC or Turbo Pascal? Can I get at Windows graphics functions any other way?──Frustrated Dear Frustrated, No. Windows functions are not accessed through a software interrupt. The Windows loader resolves all function references in the new format executable at load time. This means that only programs in the new EXE format can access Microsoft Windows functions. It might be possible to write a shell program as a Windows application that would provide a path for other programs that are not Windows applications to call Windows procedures. But this task would be very difficult. Moveover, these programs would almost certainly have to be fixed in memory and could not be moved. The WINOLDAP module allows some old applications to run in a window, by providing one type of shell that intercepts DOS and BIOS calls and translates these calls into Windows functions. This shell is one of Windows' more complex features, since it must deal with many of the nasty things that old applications do. A more extensive shell──even one that included only graphics support──would have to be more complex. Executive Dear Dr. Bob: Can I execute a Windows application from within another Windows application?──Stumped Dear Stumped, Sure. That's what the MS-DOS Executive does. The recommended method for executing a program from a Windows application is with an assembly language subroutine using the EXEC function call 0x4B. Windows intercepts this MS-DOS call and can correctly load the new format executable. The EXEC function call returns to the calling program right after the child process has been loaded. Under MS-DOS, the calling program doesn't get control back until the child process has terminated. You can even execute an old application from a Windows application or vice versa. You can get a little sense of what happens in this latter case by running COMMAND.COM from Microsoft Windows. If you lie in the PIF file and say that COMMAND.COM writes directly to the screen, you'll find that executing a Windows application from the MS-DOS command level results in the "This program requires Microsoft Windows" message. If you run COMMAND.COM in a window and enter the name of a Windows application, however, it will load correctly and take over the area of the screen occupied by COMMAND.COM. When you bring up COMMAND.COM again, you'll see the cursor sitting at the MS-DOS prompt. The Windows application has been loaded, and Windows has returned control back to COMMAND.COM. Expanded RAM Dear Dr. Bob, Can Microsoft Windows use an expanded memory board?──Fuzzy Dear Fuzzy, Yes and no. Microsoft Windows can take advantage of a RAM-Disk on an expanded memory board but not of expanded memory itself. However, Windows supports the use of expanded memory by old applications by preserving and restoring the state of the Expanded Memory Manager. Let's look at these two topics separately. The presence of any high-speed disk storage will improve Windows' performance. Windows uses the disk for storing temporary files from Windows applications and for swapping old applications in and out of memory. The disk used for these purposes can be any real or virtual disk on your system, including any type of RAM-Disk. A RAM-Disk installed in expanded memory is ideal. If your RAM-Disk is installed as drive D: (for instance), put the following statement in your AUTOEXEC.BAT: SET TEMP=D: This statement directs Microsoft Windows applications to store temporary files (for instance, those created by WRITE) on the RAM-Disk. These temporary files begin with a tilde (~) and have the extension TMP. If you do Windows program development, you should also include the following statement in your AUTOEXEC.BAT: SET TMP=D: This statement directs the Microsoft C Compiler to use the RAM-Disk for its temporary files. To use drive D: as a swapping disk for old applications, replace the line in your WIN.INI file that reads Beyond using a RAM-Disk in expanded memory, neither Microsoft Windows nor Windows applications use expanded memory directly. (A future version of Windows may have more support for expanded memory.) However, Microsoft Windows can detect the presence of an expanded memory board. If it finds one, Windows can let old applications use expanded memory in the multitasking environment without memory conflicts. Windows will save the contents of the expanded memory board registers and restore them when switching among applications. Some early Expanded Memory Managers did not include full support of the "Get/Set Page Map" (function 15) of the Expanded Memory Specification Release 3.20. This call is the one Microsoft Windows uses to save the contents of the expanded memory board registers. For this reason, Release 3.20 versions of the Expanded Memory Manager are included with Windows. These drivers are called EMM.PC and EMM.AT; they work only with the Intel Above Boards. You can also use any Expanded Memory Manager, Release 3.20 or later, supplied with an Intel Above Board. Before using any other expanded memory board, check with the manufacturer to determine if the Expanded Memory Manager provides Release 3.20 support of the "Get/Set Page Map" call. Math Coprocessor Dear Dr. Bob, Can Windows applications use an 8087 or 80287 math coprocessor? ──Perplexed Dear Perplexed, Yes. Microsoft Windows provides full support for saving the state of the math coprocessor and restoring it when switching among applications. That's the good news. However, Windows applications cannot yet use the 8087 floating point libraries provided with the Microsoft C Compiler (version 3 or 4). This problem lies with the libraries themselves rather than with Windows support of the 8087. If you want to use floating point in a Windows application written in C, you must currently use the /FPa compiler switch and link with the SLIBFA.LIB, MLIBFA.LIB, or LLIBFA.LIB──depending on your memory model──alternate math library. The alternate math library, which uses a subset of the IEEE standard, is faster and smaller (but less accurate) than the 8087-emulation code of the other floating point math libraries. The alternate math library does not use an 8087 even if one is present. When the 8087 floating point libraries are eventually modified for Windows, upgrading the program should only require relinking it with LINK4. In the interim, you could define your own floating point routines that use either the regular C library routines or the assembly language subroutines using the 8087, depending on the presence of the math coprocessor. Printf Dear Dr. Bob, What happens ins a Windows application if I try to write to the Display in the conventional way using the C function printf?──Stymied Dear Stymied, Nothing. Microsoft Windows intercepts all DOS and BIOS output headed for the display and dumps it into the bit bucket, where it is never seen again. If you need to write formatted text output to the screen, you can use sprintf and display the resultant string with Windows TextOut procedure. Instances Dear Dr. Bob, What happens when I run the same program simultaneously in Microsoft Windows? Are the two versions entirely distinct or do they share code and data?──Unclear Dear Unclear, When an application is loaded more than once, the loadings of the application are referred to as different "instances." Generally, multiple instances of the same application share the same code segment but have different data segements. Windows applications also share resources such as menus, icons, cursors, and bitmaps, since such resources are read-only data. During initialization, an application can determine whether or not it is the first instance of that application. If it is not the first instance, it can retrieve some data that must be shared from the previous instance. ════════════════════════════════════════════════════════════════════════════ Vol. 1 No. 2 Table of Contents Aldus: Preparing PageMaker for the Move to Windows The success of Aldus Corporation's PageMaker(TM) has made this program practically synonymous with desktop publishing. Now, Aldus is moving PageMaker to Microsoft(R) Windows and rewriting the program in C for both the PC and the Macintosh. Moving Toward an Industry Standard for Developing TSRs Terminate-and-stay-resident programs such as Borland International's SideKick(TM) have become popular tools for many PC users. A report from the May gathering of software developers attempting to formulate guidelines for peaceful coexistence among TSRs. A Step-by-Step Guide to Building Your First Windows Application At first, creating a Microsoft Windows-based application seems to be a complex process involving an oddly structured C program that uses Windows functions and macros. This simple program, WSZ, provides a model for building a Windows application. New XENIX Version Will Be First to Run On the Intel 80386 In early 1987, Microsoft will release XENIX(R) System V/386, the first version of XENIX to make full use of Intel's new 32-bit 80386 microprocessor. In this exclusive interview, XENIX Development Manager Martin Dunsmuir tells what to expect. A New Generation of Debugging Arrives with CodeView The CodeView(TM) debugger included with the Microsoft(R) C Compiler 4.00, the latest debugging tool to become available, marks improvement over SYMDEB by including symbolic local variable and subroutine names with additional debugging capabilities. Ask Dr. Bob EDITOR'S NOTE Welcome to Issue 2 of Microsoft Systems Journal, up in size from the last issue of MSJ, but already too small to accommodate the problem-solving and problem-avoiding technical articles that many of you have already come to expect──judging from the first subscription mailing response. We are, of course, delighted by your interest and approval. There's still time to become a charter subscriber to Microsoft Sytems Journal and avoid missing the solutions, insights, and inspirations we're dedicated to bringing you. Consider what's coming up: a closeup of the IRMA 3270 board (from a developer's point of view, of course), a comparison of page description languages, details of the MS-DOS CD ROM extensions, and a step-by-step debugging session using CodeView. Down the road: authoritative examinations of operating systems developments and language enhancements. Wish List. How many times have you said to yourself, "How could they have gone to all the trouble to design such a useful product and left out _____?" MSJ is so committed to the principle of product improvement that we'll pay you a $100 for your idea if we publish it, and you can be sure that the manufacturer will hear of its sins of omission directly from the editors of Microsoft Systems Journal. Write to Us. Our aim at Microsoft Systems Journal is to tackle the issues confronting serious developers, but we need your help. Write us, or send us a message on MCI Mail (MSJ). Tell us what you want to see in the Journal. Or let us pass on your most recent programming snafu to Dr. Bob. And, of course, we'd like your feedback on these first two issues. Whether you've got a question, comment, or complaint, we'd like to hear from you.──Ed. Masthead JONATHAN D. LAZARUS Editor BARRY OWEN Managing Editor CHRISTINA G. DYAR Assistant Editor MICHAEL LONGACRE Art Director VALERIE MYERS Associate Art Director WILLIAM B. GRANBERG Circulation Manager L. PERRIN TOMICH Office Manager Copyright(C) 1986 Microsoft Corporation. All rights reserved; reproduction in part or in whole without permission is prohibited. Microsoft Systems Journal is a publication of Microsoft Corporation, 16011 NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H. Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley, President and Chief Operating Officer; Francis J. Gaudette, Treasurer; William Neukom, Secretary. Microsoft Corporation assumes no liabilty for any damages resulting from the use of the information contained herein. Microsoft, the Microsoft logo, MS-DOS, and XENIX are registered trademarks and CodeView is a trademark of Microsoft Corporation. Apple is a registered trademark and Macintosh is a trademark of Apple Computer, Inc. IBM is a registered trademark of International Business Machines Corporation. PageMaker is a registered trademark of Aldus Corporation. Lotus and 1-2-3 are registered trademarks of Lotus Development Corporation. UNIX is a registered trademark of AT&T. Compaq is a registered trademark of Compaq Computer Corporation. Intel is a registered trademark of Intel Corporation. ──────────────────────────────────────────────────────────────────────────── Aldus: Preparing Pagemaker(TM) for the Move to Windows Kevin Strehlo Several years after the end of Ethel Merman's heyday as the queen of musical comedy on Broadway, a reporter said to her, "Broadway has been very good to you." Merman was quick to retort, "Yes, and I have been very good for Broadway." Such is the mutually advantageous relationship between the Apple Macintosh(TM) and Aldus Corporation's PageMaker, the leading desktop publishing software. While the Macintosh has certainly been very good for PageMaker, PageMaker has been very good for the Mac. Pagemaker's featured position in Apple advertising has helped push it to the top ten in the bestseller charts, while it returned the favor by selling lots of Macs (Aldus research shows that one out of three of Macintosh users bought their computers primarily to use PageMaker). Indeed, the PageMaker program has been such a success that the Mac and desktop publishing have become practically synonymous. It comes as little surprise that Aldus is attempting to repeat its success by making a move to Microsoft Windows(R), the logical vehicle for cracking the lucrative PC marketplace. What may be surprising, however, is the Seattle-based company's decision to throw away the code of its original Pascal-based version of PageMaker in favor of a complete rewrite in C that will run on both the PC and the Macintosh. From the start, Aldus Corporation had planned to merge the Macintosh version of the PageMaker page layout program with the version it was developing under Microsoft Windows. Rather than release the Macintosh version first, Aldus decided to wait until the PC version of PageMaker was out the door. A Rare Problem In Aldus's effort to share source code between the Mac and the PC, however, the decision to wait was reversed by a problem in one of the environments. It was a rare case where the problem was located in the Mac. "We had made all the data structures in the original Mac version of PageMaker 'resources' and saved them out to files using the Mac's Resource Manager," says Jeremy Jaech, programming manager for Aldus. "That turned out to be a mistake." The Resource Manager was designed to read small, indexed data structures (such as fonts and dialog boxes) into memory. It wasn't intended to continually write large pieces of data back to disk. Aldus found itself plagued with less-than-optimum performance and occasional file corruption caused by persistent bugs in the Resource Manager's garbage collection routines. Fortunately the PC team was already working on the solution. "There is no equivalent to the Resource Manager under Windows, so we spent a lot of time up front designing and implementing a file system that did exactly what we wanted to do on the PC," says Jaech. But because they had not cleanly separated the file system layer from the rest of the Mac code, sliding that file system underneath the Mac code would necessitate a major rewrite. They decided to bite the bullet and merge the Mac and Windows versions right away. The Edge The project was organized into three teams: the core code team and──because of the significant differences between the two environments──two teams that would each write the unavoidable environment-specific modules, dubbed the Macintosh edge code and the Windows edge code. One of the first choices was fundamental. Building for the lowest common denominator of the Macintosh and Windows would result in failure to take advantage of the most appealing features of both. Instead, the Aldus team decided to write for the union of the two environments' capabilities. That often meant that the edge code for one environment had to emulate a capability that was missing from the other. For example, the Mac provided continually scalable fonts and Windows did not. "Microsoft went for the concept of readability," explains Dave Walter, the lead programmer for the core code group. "But a lot of things PageMaker does involve showing the form rather than the content. We need to be able to get a piece of type up on the screen the actual size it's going to be. We don't really care if it's readable." Thus, given a request from the core code drawing routine for a particular size and face of type, the Windows edge code read the character metrics of the printer font and returned the appropriate bit map for screen display. Fortunately, Windows did make provisions for handling fractional character values──a feature missing from the Mac Toolbox until the 128K ROM upgrade──so the PC version of PageMaker was able to portray line lengths accurately on-screen despite a screen resolution that is much lower than (and not necessarily an even divisor of) the final 300 DPI printer output. When possible, the environments were equalized in the core code rather than in the edge code. When the user drags a guide on PageMaker's page layout board, for example, Aldus's Walter wanted the code that actually moved it to be part of the core. That meant accommodating the differences between the way the two environments handled the pattern used to represent that guide. The Windows GDI (graphics device interface) drawing routines automatically align a pattern so that it begins and ends at the same point, ensuring it is "in phase." The Mac Quickdraw routines do not align patterns for you; the application must rotate patterns into proper phase for drawing. To deal with the discrepancy, Walter says, "We just used an 'if def Mac' or 'if def Windows' clause." Complex Code Sometimes, however, the environments' differing approaches made the patch code so complex that it was forced out of the core and into the edges, Walter says. For example, under Windows, scrolling involved handling a series of messages that described the user clicking on the cursor keys or clicking the thumb, a simple procedure. But tracking a scroll bar on the Mac required calling the Mac Toolbox's TrackControl function. Unfortunately, the arguments TrackControl passed were different depending on whether the user was clicking on the thumb or a cursor key. "You actually have to have two different scrolling procedures on the Mac," Walter explains. In other ways, Windows is less orthogonal and therefore more complex to work with than the Mac environment. For example, under Windows the function GetMessagePos fetched the position of the mouse, while GetCurrentPos fetched the cursor position. "But the first returns the value to you as a Long (pointer), while the other gives you a pointer to a pointer," says Walter. Clyde McQueen, who wrote the shared memory management and file handling code for the two environments, had to deal with a number of similarly disconcerting choices as he strove to keep as much of the code in core as possible. "On the Mac it's neat and clean to dereference from your handle to get a pointer," says McQueen. Doing a dereference once outside a loop would be far more efficient than doing it inside a thousand times, of course, and it would be perfectly safe as long as he knew the object pointed to would not be moved. Since times of memory movement are pretty well defined on the Mac, he wouldn't have to lock the object down. Under Windows, however, an object is automatically locked whenever you dereference its handle to get a pointer. (This will allow Microsoft to flesh out Windows in the future with preemptive scheduling of tasks.) McQueen had a choice: he could add overhead by locking memory "needlessly" on the Mac or move that central part of the memory management system out of the core code. He decided that the lesser of these evils was locking memory on the Mac. Low Memory In another case, the problem was the lack of an early warning under Windows that you were about to run out of memory. "I can get a ballpark," says McQueen, "but it's not reliable." A clause in the core code lets the program know it's operating in the Windows environment and thus has to be careful about running out of memory. "That's why a lot of user operations under the Windows version of PageMaker result in a little disk hit," McQueen says. Such behavior is less acceptable for PageMaker on the Mac, where the program is much more likely to be running on floppy disk drives. Thus, if the clause senses it is on the Mac, writing data out to disk or purging code segments is deferred until a global memory warning is received. Fixing the Bugs To work out the early kinks in his memory management and file handling routines, McQueen used Heapwalker, the Windows utility designed to track objects in the global heap. Since he knew the number and approximate sizes of objects he was dealing with, Heapwalker let him see instantly when something had gone awry. But discovering a bug was not the same as fixing it. For bug hunting, McQueen turned to home-brewed tools he called "scopes" (see Figure 1☼). "The scopes were a way to quickly examine the memory structures I was using," McQueen says. The scopes were modeless dialog boxes, written as an excuse to learn the structure of a Windows application. They proved exceedingly useful. Although they could alter as well as display memory, viewing turned out to be far more valuable than altering. McQueen could often tell what had gone wrong simply by examining his memory structures. Clicking on the "next" button he had built into the scope would display the next record, allowing him to follow whatever errant trail he had blazed through memory. "It sure beat trying to figure out what was going on from SYMDEB's hexadecimal dump," he says. Working Environments Once the Mac and Windows projects merged, however, the development of the core code could be done in either environment. For Windows, the Microsoft C Compiler and the Make program were used; on the Mac, Lightspeed C and Polytron's Polymake were used. "Some of the problems we've got when we're dealing with an Intel processor are the segmentation of memory and the existence of two different pointers, near and far," says Walter. To avoid such processor-specific dependencies, he says, "We found that using warning level 2 on the Microsoft C Compiler was crucially important for our development." The Aldus team found the Microsoft debugger for Windows helpful. "But what we're really looking forward to is a future version of CodeView(TM) that works with Windows applications──it's certainly a big help with DOS apps," says Ted Johnson, lead programmer on the Windows edge code. To an outsider, it might seem Aldus had something to lose by going with the common code approach. The marketplace for PC page layout programs was wide open, after all, and an early presence in the market would be a great advantage. So why waste time making sure that the Windows code already under development would also work on the Mac? One reason was to get the PC product out sooner. It was simply too hard to find good Windows programmers at such an early stage in the game. "The PC project was definitely resource starved," Johnson explains, "until the Mac team's talents were brought to bear." Common Benefits The benefits to Aldus of a common code approach, says Jaech, are several. "Since 80 percent of the code is shared, you usually implement a feature just once, not twice. Also you only have to fix a bug once, unless it's environment specific. And from a maintenance point of view, people over in customer service trying to deal with phone calls won't have to remember two sets of technical issues and problems." Finally there's the advantage of file compatibility. PageMaker files will be interchangeable between the Mac and the PC, as will import filters for key word processing products. "We didn't plan it that way," says Jaech. But, as with most of the spinoffs of the decision to write common source code, it seems to have worked out for the best. ──────────────────────────────────────────────────────────────────────────── Moving Toward an Industry Standard for Developing TSRs Nancy Andrews Terminate-and-stay-resident programs──"pop ups"──are like people: they can't live together but they can't live alone either. If you are running Microsoft Word, for example, you cannot check spelling interactively with Turbo Lightning. If you interrupt Lotus(R) 1-2-3(R) with SideKick, strange things can happen when you return. If you run XyWrite you can't use any TSRs. And if you run two or more TSRs, there's no knowing how they will interact. Currently all software developers program their TSRs a little differently. There is no established set of programming rules to ensure smooth interaction with other TSRs and with the operating system itself. To make a TSR work with everyone else's requires an enormous programming commitment──it must be tested against everything in the marketplace. This includes not only other TSRs but normal applications as well. Some normal applications make life difficult for TSRs. XyWrite, for example, steals the hardware keyboard interrupt, thus locking out any TSR that also requires INT 9. Are there "offenders," TSRs that purposely try to be exclusive, taking the system resources they need without regard for others? John Socha, chief programmer at Peter Norton Computing, says no. Different software vendors have depended upon different approaches to solving problems in a world where there are no rules. Others who are not so kind cite Borland as an offender; Borland's SideKick takes the keyboard interrupt, for example, and then at each timer tick checks to see if anyone else has taken it. If another program has it, then SideKick will steal it back. These factors complicate writing a TSR, making it a tough job to deliver a good and flexible product. One of the reasons all TSRs work differently is that TSR controls are not built into MS-DOS(R). There was no such thing as a TSR in the UNIX(R) world. "It was a radically new idea," says Socha, "to be able to change the behavior of an existing program──a dynamic rather than a background process──and to add new features without having the source code." Later operating systems like the Apple(R) Macintosh did incorporate TSR controls into the operating system. The Mac makes its desk accessories constantly available on the Apple menu and handles their interactions for you. Because current versions of MS-DOS do not provide similar capabilities, Microsoft is part of the team working toward developing standards or guidelines. According to Adrian King, Microsoft's Director of Operating Systems, future versions of MS-DOS will address this at the outset. King, however, declined to comment about which versions, when, and how. The Solution A group of software developers co-chaired by Adrian King of Microsoft and Tony Middleton of Softronics, and hosted by Softronics, met in Colorado in May. King said that his goal was to put together standards "of maximum benefit to the software community that could be wholeheartedly endorsed by as many organizations as possible." Lynn Stricklan from Softronics said that what he wanted was standards that would make TSRs "clean and nonviolent." Participants at the conference agreed to agree that they were not able to issue a standard──a formal definition of rules. Instead, they aimed for the creation of a set of programming guidelines, and they called their efforts "Programming Guidelines for MS-DOS Co-Resident Software." The Guidelines Microsoft produced a draft of these guidelines. The original draft included the following: a set of operational rules for TSRs, specifications for their order of loading, a set of data records that describe operational characteristics, and a program interface to allow other applications to avail themselves of a TSR's functions. Two parts included in the original draft but recognized as unfeasible were a mechanism for TSRs to uninstall and a procedure for loading TSRs into extended memory, leaving memory below 640K available for normal applications. Operational rules include two major areas: interrupts and maintaining software variables for changes that are made directly to the hardware. Major problems have occurred with the keyboard interrupts INT 9 and INT 16h. Some applications hook INT 9 and bypass INT 16h, thus preventing others from seeing the keyboard request. Other applications take complete control over the keyboard with INT 9, presumably to trap keystrokes that the BIOS does not generate, such as Ctrl- Up Arrow. The proposed way to handle interrupt conflicts is to always use INT 16h to get a keystroke and to chain the interrupts. Essentially, chaining stores the old interrupt vector inside the program before replacing it with its own vector. The code sample in Figure 1 illustrates how to chain into INT 9. This routine doesn't do anything useful now──you can insert your own code for that. It simply passes control back to the old INT 9 routine, which can be either the ROM BIOS routine or some other memory-resident program like Prokey. But as long as everyone does, in fact, chain interrupts, all should function properly and it shouldn't matter who gets control next. Other proposed interrupt operational rules prohibit setting a timer to retrieve an interrupt vector and using a stack only when interrupts are off or in a protected or non-reentrant area. The remaining operational rules require that the BIOS data area be updated in order to reflect any of the following alterations: changing video modes, setting the high-order attribute bit for background intensity, using a software cursor, or changing the EGA palette directly. If your program is interrupted by another TSR, it ought to know what to restore when it relinquishes control. The proposed data records to describe operational characteristics of TSRs include a Program Identification Record and an Interrupt Information Record (see Figure 2). Each TSR will have only one Program Information Record but will have an Interrupt Information Record for each interrupt vector it intercepts. The record includes a pointer to the old routine. This allows any program to trace through the chain of installed programs. The Interrupt_Priority value in the Interrupt Information Record will be assigned based on the type of program, and it can be used to control the order of loading TSRs. Keyboard macro processors such as SuperKey and Prokey need to be last in the chain and will be given high priority numbers. The guidelines also propose a TSR program interface. Such an interface would permit users to write programs that call functions provided by a TSR. This way a Turbo Pascal program could, for example, run a SuperKey macro. Proposed functions include Get Program Information By Name or Number functions, Activate and Deactivate functions, and Enable and Disable functions. This interface would be implemented by a set of subfunctions to function 52h on INT 15h, the cassette interrupt. INT 15h was chosen because it's used infrequently and has a lot of unused function numbers. More Details To Come The participants generally concurred with the proposed guidelines and determined that more detailed work was needed in four areas. These include: 1. A revised set of rules for interaction with DOS. Currently TSRs depend on several undocumented MS-DOS features such as the IN_DOS flag (see Figure 3), the critical error flag, and some undocumented system calls. Microsoft's Adrian King has agreed to provide this information. Both Borland and Lotus say that this information is critical for TSRs to work consistently. 2. A standard model for keyboard handling. This will most likely include scan codes for keys (such as Ctrl-Cursor Up) that don't currently exist. David Rose of Rosesoft and Roger Schlafley of Borland have been put in charge of this area. 3. More extensive work on data structures. This will refine and expand the data structures section in the original guidelines. Norton's John Socha is in charge of data structures. 4. A kernel to be added to DOS to take charge of RAM-resident programs. This kernel will tell the operating system what programs to install and where to install them, and it will allocate resources based on the priority of the requester. Lynn Stricklan of Softronics is investigating the feasibility of the kernel approach. Softronics currently uses a kernel to manage its RAM-resident programs. Participants agreed to have work completed in 90 days and to publish results on the CompuServe Microsoft SIG. Unfortunately, the 90 days have elapsed, and the work has not yet been completed. But King reports that the guidelines are "not dead." He expects to finish his part, and he encourages others to do the same. He says he hopes that we'll see a new draft on the SIG in early November. But Will They Work? Participants in the drafting of the TSR guidelines qualify their work. They want it to be understood that guidelines are "evolving," that right now they are a "moving target" and "will definitely change," that this is not a "be all and end all" solution, and that "enforcement is not possible." They caution against unmitigated optimism, pointing out that even if the guidelines are completed and generally accepted, developers will still have to deal with users who choose not to upgrade. But even with these qualifications, the possibility that both software developers and end users will benefit looks good. Writing a TSR will still be a difficult and complex task, but once the TSR is written, if it plays by the rules, software developers will know that it will work with any other TSR or application that also plays by the rules. If the big players──Lotus, Microsoft, Borland──agree to support the guidelines, others will as well, and the guidelines can do the job they're intended to do. So far this looks like a real possibility. David Reed from Lotus acknowledged that there are some incompatibilities between 1-2-3 and the guidelines, and said that once a consensus document has been produced, Lotus will make 1-2-3 and Spotlight, its TSR, conform. He points out that it is to their advantage to have their applications work with everyone else's and that this will directly reduce their support costs. Microsoft has taken an active role in this. It co-chaired the conference and agreed to provide the necessary information about DOS. Beyond that the company has also made some changes to its standard applications so that they will work better with TSRs. David Intersimone at Borland says that when everyone else writes well- behaved applications──that is, when the guidelines have been agreed to and implemented──Borland will too. He claims that SideKick programmers had no choice but to grab INT 9 back because some of the applications SideKick was expected to work with were ill-behaved and users expected SideKick to appear when they pressed Ctrl-****. And he says that Borland has gone out of its way to help developers work around the idiosyncrasies of SideKick. Phillipe Kahn says, "The whole issue of standardization should not be driven by the software publishers or the press but by the users." And when standardization actually happens, end users can't help but be the ultimate beneficiaries. They'll be able to choose from the full range of available software──to work with any combination of TSRs and applications without conflict. Figure 1: Code Sample for Chaining Interrupts This code sample illustrates how to chain into INT 9. This routine won't be useful until you insert your own code; it just passes control back to the old INT 9 routine. CODE_SEG SEGMENT ASSUME CS:CODE_SEG ORG 100h BEGIN: JMP INIT_VECTORS ;Initialize vectors and attach ;to DOS OLD_KEYBOARD_INT DD ? ;Address for ROM routine ;-----------------------------------------------------------------; ; This procedure intercepts the INT 9 keyboard vector. Right now it ; just calls the old INT 9 routine. ; ;-----------------------------------------------------------------; INTERCEPT_KEYBOARD_INT PROC NEAR ### ;Put your code here CLI ;Disable interrupts JMP OLD_KEYBOARD_INT ;Chain back to the old Int 9 INTERCEPT_KEYBOARD_INT ENDP ;-----------------------------------------------------------------; ; This procedure initializes the interrupt vectors and installs this ; program. This routine also throws itself away after the install. ; ;-----------------------------------------------------------------; INIT_VECTORS PROC NEAR MOV AH,35h ;Get the old keyboard interrupt vector MOV AL,9 ;This is INT 9 INT 21h ;Old vector now in ES:BX MOV Word Ptr OLD_KEYBOARD_INT,BX MOV AX,ES MOV Word Ptr OLD_KEYBOARD_INT[2],AX MOV AH,25h ;Set the new KEYBOARD_INT vector MOV AL,9 ;This is INT 9 LEA DX,INTERCEPT_KEYBOARD_INT INT 21h MOV AX,3100h ;Terminate but stay resident ;with ;return code of 0 LEA DX,INIT_VECTORS ;End of resident portion ADD DX,15 ;Round up paragraph number MOV CL,4 ;Convert from bytes to ;paragraphs SHR DX,CL ;Divide by 16 INT 21h ;Terminate but stay resident INIT_VECTORS ENDP CODE_SEG ENDS END BEGIN Figure 2: Proposed Data Structures for TSRs These are the proposed data records to describe operational characteristics of TSRs. Each TSR will have one Program Information Record but will have an Interrupt Information Record for each interrupt record it intercepts. Program Identification Record -- PIDR Program_Record_ID DB "PIDR" Version_Number DW BCD version number Interrupt_List DW Near Pointer to list of interrupts Startup_Shift_Keys DW Near Pointer to shift-pairs Startup_Key_Codes DW Near Pointer to startup codes Options_Area DW Near Pointer to local data area Program_ID_String DB ASCIIZ string Interrupt Information Record -- IINR Down_Pointer DD Far pointer to old interrupt routine Program_ID DW Near Pointer to PIDR Interrupt_priority DB Between 0 and 255 giving priority Function_Numbers DB Null-terminated function list To correlate interrupts with the associated IINR, each interrupt procedure must be preceded by a 6-byte record. The first four bytes identify it as a valid interrupt record, and the last two bytes point to the IINR for more information. DB "SRID" DW Offset Interrupt_Record Intercept_Interrupt Proc Far ### ### Intercept_Interrupt Endp ----------------------------------------------------------------- Figure 3: The IN_DOS Flag The IN_DOS flag is an undocumented MS-DOS feature that TSRs depend on. IN_DOS_FLAG DD ? ;Address of IN_DOS flag MOV AH,34h INT 21h ;Get pointer to IN_DOS flag MOV Word Ptr IN_DOS_FLAG,BX MOV Word Ptr IN_DOS_FLAG[2],ES ;Above four lines are init code -- they ;are done once to get address of flag ;Following 7 lines done each time to ;check if IN DOS PUSH BX ;See if you can popup PUSH DS LDS BX,IN_DOS_FLAG ;Set DS:BX to point to IN_DOS flag CMP Byte Ptr [BX],0 ;Are we inside DOS? POP DS POP BX JNZ DO_NOT_POPUP_NOW *This routine is not foolproof. For example, your program would never popup if you were at the DOS prompt. Information to make it usable in all situations will be provided with the revised guidelines (on CompuServe in the Microsoft SIG). ──────────────────────────────────────────────────────────────────────────── A Step-by-Step Guide to Building Your First Windows Application Charles Petzold The notion of "building" a Windows application rather than simply "writing" one implies that the job involves more than just cranking out hot C code. This is true. While the core of most Microsoft Windows applications certainly is a C program, it is a C program that is oddly structured and makes heavy use of Windows functions, macros, and type definitions. A typical Windows application also requires several other files besides the C program. Much of the difficulty for programmers first approaching Microsoft Windows is the large amount of overhead and the new concepts needed just to get a small program off the ground. To clear away some of the mystery that surrounds building a Windows application, we'll put together a fairly simple program called Whatsize, or WSZ. WSZ displays the dimensions of its own window in units of pixels, millimeters, or inches. The program also allows you to switch this display between black text on a white background and white text on a black background. Despite its limited functionality, WSZ illustrates some basic Microsoft Windows concepts; demonstrates the use of such familiar Windows accessories as menus, icons, and dialog boxes; and offers some insight into the nature of device-independent applications. Getting Set Up I will assume that you have a Microsoft Windows Software Development Kit, the Microsoft C Compiler 4.00, a hard disk, and a mouse. Some of the procedures in building the Windows application are slightly different from the earlier C compiler included with the Software Development Kit. The files you'll need for this project are shown below in one possible subdirectory organization. Notations in parentheses indicates whether a file comes from the C Compiler 4.00 (C4) or the Software Development Kit (SDK). \MSC CL.EXE (C4) P1.EXE (C4) P2.EXE (C4) P3.EXE (C4) MAKE.EXE (C4) RC.EXE (SDK) RCPP.EXE (SDK) LINK4.EXE (C4) WINSTUB.EXE (SDK) MAPSYM.EXE (SDK) SYMDEB.EXE (SDK) \MSC\INC WINDOWS.H (SDK) (and regular C header files from C4 if needed) \MSC\LIB SLIBC.LIB (C4) SLIBW.LIB (C4) SLIBFA.LIB (C4) LIBH.LIB (C4) (and other memory models from C4 if needed) \WINDOWS Installed version of Windows (SDK) ICONEDIT.EXE (SDK) DIALOG.EXE (optional) \WINDOWS\WSZ All WSZ files The environment variables necessary for the C compiler to get at the libraries and the include files are SET PATH=\MSC SET LIB=\MSC\LIB SET INCLUDE=\MSC\INC Microsoft Windows programmers generally do most of their development work (particularly editing source code and compiling) outside of Windows in regular old MS-DOS. This is due partly to the inadequacy of current Windows text editors and the problems that occur when running the C compiler in a limited memory environment. Creating an Icon We'll begin with one exception to this rule. Let's get into Microsoft Windows and start with a part of WSZ where you can ignore what I've done and exercise your own creativity. Most Windows applications have an icon that Windows displays when the program is parked downstairs at the bottom of the screen. You can create an icon for WSZ by using ICONEDIT (see Figure 1☼). Choose Icon from the mode menu, pick a pen color, a pen size, and then use the mouse to draw. For the icon I created for WSZ, I painted the surface white and then drew a question mark in black. The resolution of the bit-mapped icon you created in ICONEDIT will be considerably reduced when Windows displays the icon when your program runs. This is why ICONEDIT shows the icon in a reduced size at the left of the screen. If you use thin lines (three or fewer pixels wide) in your icon, they could possibly disappear when the icon is displayed. You'll eventually want to see how the icon looks on both a low-resolution (IBM CGA or equivalent) and medium-resolution (EGA or Hercules) display. When you finish creating your miniature masterpiece, save the icon as WSZ.ICO in the subdirectory WSZ. Resources That icon you just created is called a "resource." It will eventually become part of the WSZ.EXE executable file, but it is neither part of your program's code nor part of your program's data. Icons are not the only type of resource you can have in your .EXE file. Resources also include dialog boxes, menus, and (most preferably) all displayable text. Think of a resource as read-only data that is conveniently stored right in the application's .EXE file. In most cases, resources are not loaded from the disk into memory until they are needed. (This is why you'll often see a disk access when you pop up a dialog box.) Simple Microsoft Windows calls retrieve these resources from the disk so your program can use them. Because resources are read-only, Windows may discard them from memory when they are no longer needed and then reload them later if necessary. In an operating system that implements virtual memory, a resource currently in memory would not have to be saved to the disk because it would still be available in the same form from the .EXE file. Resources are defined in a resource script (.RC) file, which is an ASCII file you create in a normal text editor. The resource script for WSZ is called WSZ.RC and is shown in Figure 2. This file includes definitions for the icon, all of the displayable text strings used in the C program, a menu, and two dialog boxes. The dialog boxes are usually the most complex part of the resource script file. You can either work out the dialog box coordinates manually (as I've done here) or by using DIALOG.EXE (see Figure 3☼; also see "Latest Dialog Editor Speeds Windows Application Development," MSJ Vol. 1, Issue 1). The resource script file also contains all the displayable text strings that the C program will use. You are not required to put them in the resource script. In fact, defining text strings in the C program usually makes both the source file and the .EXE file somewhat smaller. The program will run faster (since it doesn't have to retrieve strings from disk) but will require more memory space because the strings have to remain in memory rather than reside on the disk. However, if you want to adapt Windows applications for foreign-language markets, collecting all displayable text into one relatively small file will be enormously helpful. The STRINGTABLE option in the resource script file can handle only single lines. If you need to include larger text strings in your program (for instance, for a "Help" window like PIFEDIT's), put this text into separate files and use the "user-defined" resources in the resource script file. You can then use the FindResource, LoadResource, and LockResource functions within your program to get pointers to those resources. The user-defined resource is a general-purpose facility that allows you to imbed any read- only data into the .EXE file and access it very easily with just three simple Microsoft Windows calls. Avoid Header Loss The names in the WSZ.RC file that begin with the letters "ID" are really just numbers that are defined as macros in the WSZ.H header file shown in Figure 4. It's possible to use macro names here in a resource script because the RC.EXE resource compiler has its own C-like preprocessor. Using names instead of numbers makes both the resource script and your C program easier to understand and maintain. The WSZ.RC file also has an include statement for WINDOWS.H, which comes with the Windows Software Development Kit. WINDOWS.H is a massive 80K header file that is much more important for compiling Windows C programs than resource scripts. Here it is used only to define the macro names beginning with "WS" (window style). The C Program Now that we've gotten some of the preliminaries out of the way, we're ready to start looking at some C code. The complete WSZ.C file is shown in Figures 5, 6, 7, 8 and 9. This file functions: WinMain, Initialization, MainWndProc, AboutWndProc, and UnitsWndProc. WSZ.H and WINDOWS.H are specified as include files at the top of the program. You will often find that it is helpful when you are programming Windows applications to consult the WINDOWS.H header file. It contains declarations for every Windows function and loads of #define and typedef definitions that ought to be used within Windows applications. Every Windows application requires a WinMain procedure. This is the entry point to the application. The WinMain procedure for WSZ is shown in Figure 5. The input parameters to WinMain include two "handles," one to the current instance and another to the previous instance. When you load a Windows application, that's the first "instance" of the program, and there is no previous instance. If you load the same application without terminating the first, the previous instance handle will refer to the copy loaded previously. You'll run across handles a lot in Windows programming. Almost everything in Microsoft Windows (menus, windows, memory) is represented by a handle. If you look at the WINDOWS.H file you'll discover that the various handle data types are just plain old integers. Handles are just numbers that Windows uses to refer to things. WinMain also receives a command line parameter string. If the user of your application entered a parameter in the File Run box, this is where your application will get the parameter. The fourth parameter to WinMain ("nCmdShow") indicates the initial state of the application. It could be a window (if the application was invoked by File Run) or an icon (if File Load was used). After WinMain calls the Initialization routine, it sits in a small loop polling for messages from Microsoft Windows. When it gets a message, WinMain dispatches it off to somewhere else. Right now, this looks a little mysterious. Let's leave WinMain in this loop while we take a look at the Initialization function. Initialization The Initialization function (see Figure 6) does several chores necessary to get the application moving. The two most important chores are defining a "window class" and a "window." More than one window may use the same class. The window class defines such things as the standard cursor, the icon, and the window procedure. Creating the window defines the style and title of the window. If you run more than one copy of WSZ in Windows, different instances of the application will use the same code segment, but different data segments. This reduces memory use considerably. Different instances of the same application will have different windows, but the windows will use the same window class. This is the reason we define the window class only if this is the first instance of the program. Although each instance of a Windows application will generally have its own data segment, the instances are able to share some data. This is illustrated by the two GetInstanceData calls. Window Procedure When we last left WinMain, it was sitting in a loop getting messages from Microsoft Windows and dispatching them somewhere. Where these messages ultimately go is the MainWndProc procedure shown in Figure 7. This is a window procedure. It receives messages from Windows regarding everything that affects the WSZ window. Every keystroke, every mouse click, even every movement of the mouse is a message. Within a window procedure, you can ignore many of these messages by passing them on to the Windows function DefWindowProc. The message handling in window procedures makes Windows programming seem very strange to programmers who have previously worked in more traditional environments. Usually a program makes a call to the operating system to find out what's going on. Microsoft Windows is different. From the viewpoint of the window procedures, it seems like Windows is calling your program to tell you what's going on. If you feel comfortable with that concept, stick with it. Otherwise, you can think of DispatchMessage as initiating the call to the window procedure. MainWndProc handles only a few of the messages that are sent to it by Windows. The WM_CREATE message is sent by Windows as a result of the CreateWindow call in Initialization. WSZ takes this opportunity to retrieve some of the information that will not be changing during the Windows session. This information includes the size (in both millimeters and pixels) of the entire display surface and the dimensions of a character that is in the default system font. Microsoft Windows sends a WM_SIZE message to the window procedure when the window is first displayed and whenever the window is resized. The new dimensions of the window are encoded in the lParam variable. The dimensions refer only to the "client area," which excludes the caption bar, menu, and scroll bars. This is the area that a Windows application is free to write in. A window procedure will receive a WM_ERASEBKG any time Windows wants it to erase the background of the window client area. The color of the background is defined as part of the window class. Since WSZ will be coloring the background itself, all we need to do is tell Windows not to erase the background. Windows sends window procedures a WM_PAINT message whenever the client area needs repainting. This can happen after the window has been resized (in which case it has been preceded by a WM_SIZE message) or after a pop-up menu has been removed. This is where WSZ will do most of its work. It calculates values to write the display, colors the whole client area with PatBlt, loads strings from the resource table with LoadString, and then writes text to the screen with a combination of DrawText and the regular C sprintf function. Generally, Windows applications use TextOut rather than DrawText for simple text strings. However, DrawText provides such amenities as centering and word-wrap options. The text is drawn in a rectangle indicated by the rect structure. The rectangle is defined to allow a top and bottom border equal to the height of a character and a left and right border equal to the width of a character. Creating a device-independent application requires that you not make any assumptions about the size of the display or the client area or the character sizes. You will probably find that it works best to dynamically design window displays within your program based around the size of a default character rather than around hard-coded constants. Dialog Boxes The WM_SYSCOMMAND message means that the user of the application has selected something from the system menu. Normally we wouldn't care, except that we've added "About..." to the system menu. If About... has been selected, then WSZ calls DialogBox to indicate that control should now transfer to the procedure called AboutWndProc (see Figure 8). Because the About dialog box is fairly simple (just some text and an "Ok" button), AboutWndProc is also simple. Microsoft Windows is responsible for actually displaying the dialog box and passing messages back to AboutWndProc. The only message that we really care about is a WM_COMMAND message, which means that the Ok button has been hit. When the MainWndProc receives a WM_COMMAND message, it knows that the user has selected something from the window menu (rather than from the system menu). The two choices from the Color menu option (ID_BLACK and ID_WHITE) simply require changing a checkmark and then saving the CurrentColor. The Units menu selection then passes control to UnitsWndProc (see Figure 9), another dialog box. Note that both the menus and dialog boxes make full use of the same mouse and keyboard interface as in other Windows applications. Yet nowhere does WSZ make any explicit keyboard or mouse calls. Definitions Finally (well, almost finally), a Windows application requires a Definition (.DEF) file. The Definition file contains information about the program segments and procedures that LINK4 (the Windows linker) needs in order to construct the .EXE file. WSZ.DEF is shown in Figure 10v1n2a3fa. The code and data segments are both defined as MOVEABLE, which means that Microsoft Windows can move them around in memory in order to make room for other programs. The idea that Windows may be moving your program around in memory may give you the willies, and it should. Many Windows bugs result from doing such things as saving far pointers (where they become invalid) or not properly locking and unlocking allocated memory. The MainWndProc, AboutWndProc, and UnitsWndProc routines are very special because they are called by Windows. These routines must be specified as EXPORTS in the WSZ.DEF file. The STUB called WINSTUB.EXE is a small program that LINK4 embeds in the .EXE file. Try running WINSTUB.EXE on the DOS command level to see what it does. Putting It All Together So far we've created five different source files with the extensions .ICO, .RC, .H, .C, and .DEF──all for just one application. Now we have to run these files through the RC.EXE resource compiler, the CL.EXE C compiler, and the LINK4 linker. You may be wondering how you're going to keep this mess straight. If you decide to change the icon file, what do you have to do to bring everything up to date? It's a problem, and that's why all good Windows programmers use MAKE. MAKE.EXE is a program maintainer. The "make-file" for an application contains sections that look like this: target: dependent command If the file change date of any "dependent" file is later than the file change date of the "target" file, then the "command" is run. Otherwise, it is not run. The make-file for WSZ is shown in Figure 11. By convention, make-files have no extension, so the file is just called WSZ. By executing MAKE WSZ after making changes to your source files, you can bring the whole thing up to date. The make-file also serves as a form of documentation of the different pieces that contribute to the application and indicates how to recreate the program from the source files. From Source to .EXE Figure 12 shows a system flowchart of the various WSZ files and how they eventually get into the WSZ.EXE executable file. Some of this should look normal. The CL.EXE C Compiler uses the WSZ.C source code, and two header files to create a WSZ.OBJ object file. The command line options may look excessive, but they are all there for a reason. The -Gw option, for instance, adds some special Windows Prolog code to far procedures. LINK4.EXE is much like the normal LINK program. It uses the WSZ.OBJ file and C .LIB library files and creates an executable WSZ.EXE file. But this .EXE file is not the same format as the .EXE files we've been using for 5 years with MS-DOS. These .EXE files have expanded header information, and some of this information will come from the WSZ.DEF definition file. One apparent oddity in this flowchart is that the resource compiler (RC.EXE) must be run twice, once at the beginning and again at the end. This is because the resource compiler has to do two things: First, it must compile your ASCII .RC file into a binary .RES file, and second, it must append data from the .RES file to your .EXE file and set up pointers in the .EXE file header to reference each resource. To do both these steps at once (assuming LINK4 has already created an .EXE file), you would execute RC FILENAME [.RC] This creates the compiled .RES file from your .RC file and copies the contents of the .RES file to the .EXE file. However, this approach creates problems with program maintenance. If you need to relink following a C source code change, you don't need to recompile the resource script. All you need to do is add the existing .RES data to the new .EXE file. For this reason it's better to use RC.EXE in two separate steps: RC -r FILENAME [.RC] creates the compiled .RES file from the .RC file, and -RC FILENAME.RES copies the data from the .RES file to the .EXE file. If you change the .RC file now, you'll have to recompile it with RC.EXE, relink, then use RC.EXE to add the new compiled resources to the .EXE file. But if you change something that affects only the C compilations (which is more often the case), then you need only relink and add the .RES file to the .EXE file. You don't have to recompile the resource script. The Rewards Building a Windows application up from the beginning is no picnic. Yet neither is writing your own graphics routines, menu logic, and dialog boxes──and Microsoft Windows does all that for you. Figure 2: WSZ.RC Th resource script for WSZ, called WSZ.RC. This file includes definitions for the icon, all of the displayable text strings used in the C program, a menu, and two dialog boxes. #include #include "WSZ.h" WhatSize ICON WSZ.ico STRINGTABLE BEGIN IDS_NAME, "WhatSize" IDS_ABOUT, "About..." IDS_TITLE, "What Size is the Window?" IDS_FORMAT, "%.3G %s by %.3G %s" ID_MILLIM, "millimeters" ID_INCHES, "inches" ID_PIXELS, "pixels" END WhatSize MENU BEGIN POPUP "Colors" BEGIN MENUITEM "Black on White", ID_BLACK MENUITEM "White on Black", ID_WHITE END MENUITEM "Units", IDM_UNITS END AboutBox DIALOG 20, 20, 144, 75 STYLE WS_POPUP | WS_DLGFRAME BEGIN CTEXT "What Size?", -1, 37, 5, 68, 8 ICON "WhatSize", -1, 9, 23, 0, 0 CTEXT "A Windows Application", -1, 0, 14, 144, 8 CTEXT "Version 1.00", -1, 38, 34, 64, 8 DEFPUSHBUTTON "Ok", IDOK, 53, 59, 32, 14, WS_GROUP END UnitsBox DIALOG 20, 20, 100, 90 STYLE WS_POPUP | WS_BORDER CAPTION "What Size Units" BEGIN RADIOBUTTON "Pixels",ID_PIXELS,20,5,60,15,WS_GROUP | WS_TABSTOP RADIOBUTTON "Millimeters", ID_MILLIM, 20, 25, 60, 15 RADIOBUTTON "Inches", ID_INCHES, 20, 45, 60, 15 DEFPUSHBUTTON "Ok", IDOK, 10, 70, 32, 15, WS_GROUP PUSHBUTTON "Cancel", IDCANCEL, 58, 70, 32, 15 END Figure 4: WSZ.H Header File The WSZ.H header file contains numbers that are defined as macros, which become the names in the WSZ.RC file that begin with the letters "ID". #define IDS_NAME 101 #define IDS_ABOUT 102 #define IDS_TITLE 103 #define IDS_FORMAT 104 #define ID_PIXELS 105 #define ID_MILLIM 106 #define ID_INCHES 107 #define ID_BLACK 108 #define ID_WHITE 109 #define IDM_ABOUT 110 #define IDM_UNITS 111 Figure 5: WinMain Function for WSZ The WinMain procedure for WSZ. Every Windows application requires such a procedure, which is the entry point to the application. /* whatsize -- Windows application in C */ #include #include "wsz.h" long FAR PASCAL MainWndProc (HWND, unsigned, WORD, LONG) ; BOOL FAR PASCAL AboutWndProc (HWND, unsigned, WORD, LONG) ; BOOL FAR PASCAL UnitsWndProc (HWND, unsigned, WORD, LONG) ; FARPROC lpAbout ; FARPROC lpUnits ; HANDLE hInst; int CurrentUnits = ID_PIXELS ; int CurrentColor = ID_BLACK ; int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow) HANDLE hInstance, hPrevInstance; LPSTR lpszCmdLine; int nCmdShow; { MSG msg; if (!Initialize (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)) return FALSE ; while (GetMessage ((LPMSG)&msg, NULL, 0, 0)) { TranslateMessage ((LPMSG)&msg) ; DispatchMessage ((LPMSG)&msg) ; } return (msg.wParam) ; } Figure 6: Initialization Function for WSZ The Initialization function performs several of the chores necessary to get the application moving. The two most important chores are deining a "window class" and a "window." BOOL Initialize (hInstance, hPrevInst, lpszCmdLine, nCmdShow) HANDLE hInstance, hPrevInst ; LPSTR lpszCmdLine ; int nCmdShow ; { char *szAppName [10] ; char *szAbout [10] ; char *szTitle [30] ; WNDCLASS wndclass ; HWND hWnd; HMENU hMenu; LoadString (hInstance, IDS_NAME, (LPSTR) szAppName, 10) ; LoadString (hInstance, IDS_ABOUT, (LPSTR) szAbout, 10) ; LoadString (hInstance, IDS_TITLE, (LPSTR) szTitle, 30 ) ; if (!hPrevInst) { wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = MainWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, (LPSTR) szAppName); wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH); wndclass.lpszMenuName = (LPSTR) szAppName; wndclass.lpszClassName= (LPSTR) szAppName; if (!RegisterClass ((LPWNDCLASS) &wndclass)) return FALSE; } else { GetInstanceData(hPrevInst,(NPSTR)&CurrentUnits,sizeof(int)); GetInstanceData(hPrevInst,(NPSTR)&CurrentColor,sizeof(int)); } hWnd = CreateWindow ( (LPSTR) szAppName, /* class name */ (LPSTR) szTitle, /* caption title */ WS_TILEDWINDOW, /* windows style */ 0, /* x (ignored) */ 0, /* y (ignored) */ 0, /* width (ignored) */ 0, /* height (ignored) */ (HWND) NULL, /* parent (none) */ (HMENU) NULL, /* menu (use class) */ (HANDLE) hInstance, /* instance handle */ (LPSTR) NULL) ; /* parameters */ hInst = hInstance ; lpAbout = MakeProcInstance ((FARPROC) AboutWndProc, hInstance) ; lpUnits = MakeProcInstance ((FARPROC) UnitsWndProc, hInstance) ; hMenu = GetSystemMenu (hWnd, FALSE) ; ChangeMenu (hMenu, 0, NULL, 999, MF_APPEND | MF_SEPARATOR) ; ChangeMenu (hMenu, 0, (LPSTR) szAbout, IDM_ABOUT, MF_APPEND | MF_STRING) ; hMenu = GetMenu (hWnd) ; CheckMenuItem (hMenu, CurrentColor, MF_CHECKED) ; ShowWindow (hWnd, nCmdShow) ; UpdateWindow (hWnd) ; return TRUE ; } Figure 7: MainWndProc Procedure for WSZ MainWndProc is a window procedure that receives messages from windows on everything that affects the WSZ window. long FAR PASCAL MainWndProc (hWnd, message, wParam, lParam) HWND hWnd; unsigned message; WORD wParam; LONG lParam; { HMENU hMenu ; static int CharHorzPix, CharVertPix ; static int ScrnHorzPix, ScrnVertPix ; static int ScrnHorzMil, ScrnVertMil ; static int WindHorzPix, WindVertPix ; switch (message) { case WM_CREATE: { TEXTMETRIC tm ; HDC hDC = GetDC (hWnd) ; GetTextMetrics (hDC, (LPTEXTMETRIC) &tm) ; CharHorzPix = tm.tmAveCharWidth ; CharVertPix = tm.tmHeight ; ScrnHorzPix = GetDeviceCaps (hDC, HORZRES) ; ScrnVertPix = GetDeviceCaps (hDC, VERTRES) ; ScrnHorzMil = GetDeviceCaps (hDC, HORZSIZE) ; ScrnVertMil = GetDeviceCaps (hDC, VERTSIZE) ; ReleaseDC (hWnd, hDC) ; } break ; case WM_SIZE: WindHorzPix = LOWORD (lParam) ; WindVertPix = HIWORD (lParam) ; break ; case WM_ERASEBKGND: return TRUE ; case WM_PAINT: { PAINTSTRUCT ps ; char szFormat [20] ; char szUnits [20] ; char szBuffer [60] ; float nHorz = (float) WindHorzPix ; float nVert = (float) WindVertPix ; RECT rect ; if (CurrentUnits != ID_PIXELS) { nHorz *= (float) ScrnHorzMil / ScrnHorzPix ; nVert *= (float) ScrnVertMil / ScrnVertPix ; } if (CurrentUnits == ID_INCHES) { nHorz /= 25.4 ; nVert /= 25.4 ; } BeginPaint (hWnd, (LPPAINTSTRUCT) &ps) ; PatBlt (ps.hdc, 0, 0, WindHorzPix, WindVertPix, (CurrentColor == ID_WHITE) ? BLACKNESS : WHITENESS) ; if (CurrentColor == ID_WHITE) { SetTextColor (ps.hdc, RGB (255, 255, 255)) ; SetBkColor (ps.hdc, RGB (0, 0, 0)) ; } LoadString (hInst, IDS_FORMAT, (LPSTR) szFormat, 20) ; LoadString (hInst, CurrentUnits, (LPSTR) szUnits, 20) ; rect.bottom = WindVertPix - (rect.top = CharVertPix) ; rect.right = WindHorzPix - (rect.left = CharHorzPix) ; DrawText (ps.hdc, (LPSTR) szBuffer, sprintf(szBuffer,szFormat,nHorz,szUnits,nVert,szUnits), (LPRECT) &rect, DT_CENTER | DT_WORDBREAK) ; EndPaint (hWnd, (LPPAINTSTRUCT) &ps) ; } break; case WM_SYSCOMMAND: switch (wParam) { case IDM_ABOUT: DialogBox(hInst,(LPSTR)"AboutBox",hWnd,lpAbout); break; default: return DefWindowProc(hWnd,message,wParam,lParam); } break; case WM_COMMAND : switch (wParam) { case ID_BLACK: case ID_WHITE: hMenu = GetMenu (hWnd) ; CheckMenuItem(hMenu,CurrentColor,MF_UNCHECKED); CheckMenuItem (hMenu, CurrentColor = wParam, MF_CHECKED) ; InvalidateRect (hWnd, (LPRECT) NULL, TRUE) ; break ; case IDM_UNITS: if (DialogBox (hInst, (LPSTR) "UnitsBox", hWnd, lpUnits)) InvalidateRect (hWnd, (LPRECT) NULL, TRUE) ; break ; default : return DefWindowProc (hWnd, message, wParam, lParam) ; } break ; case WM_DESTROY: PostQuitMessage (0) ; break; default: return DefWindowProc (hWnd, message, wParam, lParam) ; break; } return (0L) ; } Figure 8: AboutWndProc Procedure for WSZ AboutWndProc is the window procedure for the dialog box called up by selecting the About option from the menu. BOOL FAR PASCAL AboutWndProc (hDlg, message, wParam, lParam) HWND hDlg ; unsigned message ; WORD wParam ; LONG lParam ; { switch (message) { case WM_INITDIALOG : return TRUE ; case WM_COMMAND : EndDialog (hDlg, TRUE) ; return TRUE ; default: return FALSE ; } } Figure 9: UnitsWndProc Procedure for WSZ UnitsWndProc is the window procedure for the dialog box called up by selecting the Units option from the menu. BOOL FAR PASCAL UnitsWndProc (hDlg, message, wParam, lParam) HWND hDlg ; unsigned message ; WORD wParam ; LONG lParam ; { static int NewUnits ; switch (message) { case WM_INITDIALOG : CheckRadioButton (hDlg,ID_PIXELS,ID_INCHES,CurrentUnits); SetFocus (GetDlgItem (hDlg, NewUnits = CurrentUnits)) ; return FALSE ; case WM_COMMAND : switch (wParam) { case ID_MILLIM: case ID_INCHES: case ID_PIXELS: NewUnits = wParam ; CheckRadioButton (hDlg,ID_PIXELS,ID_INCHES,NewUnits); break ; case IDOK: CurrentUnits = NewUnits ; EndDialog (hDlg, TRUE) ; break ; case IDCANCEL: EndDialog (hDlg, FALSE) ; break ; default: return FALSE ; } break ; default: return FALSE ; } return TRUE ; } Figure 10: WSZ.DEF Definition File The Definition file for WSZ, called WSZ.DEF contains information about the program segments and procedures that LINK4, the Windows linker, requires for constructing the .EXE file. NAME WhatSize DESCRIPTION 'A Windows Application' STUB 'WINSTUB.EXE' CODE MOVEABLE DATA MOVEABLE MULTIPLE HEAPSIZE 1024 STACKSIZE 4096 EXPORTS MainWndProc @1 AboutWndProc @2 UnitsWndProc @3 Figure 11: Make-File for WSZ The make-file for WSZ is a program maintainer that keeps everything up to date. It serves as a record of the different pieces that contribute to the application and indicates how to recreate the program from the source files. wsz.res: wsz.rc wsz.ico wsz.h rc -r wsz.rc wsz.obj: wsz.c wsz.h cl -d -c -W2 -AS -Gs -Gw -Oas -Zpd -FPa wsz.c wsz.exe: wsz.obj wsz.res wsz.def link4 wsz, wsz/align:16, wsz/map, slibw, wsz.def mapsym wsz rc wsz.res Figure 12: This system flowchart of the various WSZ files shows how the files eventually get into the WSZ.EXE executable file. ┌──────────┐ ┌───────────┐ ┌─────────────┐ ┌─────────┐ │ WSZ.RC │ │ WSZ.ICO │ │ WINDOWS.H │ │ WSZ.C │ └─────┬────┘ └─────┬─────┘ │ WSZ.RC │ └─┬───────┘ └──────────┐ │ └─┬────┬──────┘ │ ╔════════════╗ │ │ ┌───┘ │ ┌───┘ ║ P1.EXE ║ ╔════════════╗ ╔▼═══▼═══════▼═╗ ╔═══▼════════▼═╗ ┌─► P2.EXE ║ ║ RCPP.EXE ◄──╢ RC.EXE(-r) ║ ║ CL.EXE ╟──┘ ║ P3.EXE ║ ╚════════════╝ ╚═══════╤══════╝ ╚══════╤═══════╝ ╚════════════╝ ┌─────▼─────┐ ┌─────▼─────┐ ┌───────────┐ │ WSZ.RES │ │ WSZ.OBJ │┌─────┤ WSZ.DEF │ └─────┬─────┘ └─────┬─────┘│ └───────────┘ │ ╔════════════▼══╗ │ ┌───────────────┐ └───► ◄───┘┌┤ WINSTUB.EXE │ ║ LINK4 ◄────┘└───────────────┘ ║ ◄───────────┐ ╚═╤═══════════╤═╝ ┌──┴─────────┐ ┌────────▼──┐ ┌─────▼─────┐ │ SLIBC.LIB │ ┌────────┤ WSZ.EXE │ │ WSZ.MAP │ │ SLIBW.LIB │ ╔═════▼════╗ └───────────┘ └─────┬─────┘ │ SLIBFA.LIB │ ║ RC.EXE ║ ╔═════▼════╗ │ LIBH.LIB │ ╚═════╤════╝ ║ MAPSYM ║ └────────────┘ ┌─────▼─────┐ ╚═════╤════╝ │ WSZ.EXE │ ┌─────▼─────┐ └───────────┘ │ WSZ.SUM │ └───────────┘ ──────────────────────────────────────────────────────────────────────────── New XENIX Version Will Be First to Run On the Intel 80386 Joe Desposito Microsoft's XENIX(R) System V/386 is expected to be released during the first quarter of 1987. This version of the product is especially significant, as it is the first version of XENIX to take full advantage of a 32-bit microprocessor──namely, Intel's 80386. To learn more about XENIX 386 we went straight to the source: Martin Dunsmuir, Microsoft's XENIX Development Manager. MSJ: We're here to talk about Microsoft XENIX System V/386. But could you first give us a brief background on the other versions of XENIX? MARTIN DUNSMUIR: The product out there at the moment is XENIX 286, for which there is a number of large OEMs (original equipment manufacturers), particularly IBM(R), Compaq(R), Altos, Tandy, and Intel(R). There have been two releases of XENIX 286: System 3, which went out in the '84 time frame, and System V, which came out in '85. The first release, System 3, was really targeted at the Intel 310 Machine. With the exception of IBM──we made a version available on the AT──most other versions of XENIX System 3 were on proprietary architectures. Then, when we went to System V, the emphasis moved to the AT architecture and its clones. Except for the Altos, which has its own architecture and is intended to be used in a turnkey environment, all of the XENIX 286 versions are on the IBM AT or clones──for example, IBM, Compaq, the TI Business Pro, and the Sperry IT. In the last year, the Intel 80386 microprocessor has appeared. And the machines that are currently being manufactured or are on the verge of being shipped──which use the 80386──are AT clones that replace the 80286 with the 80386. So what we're aiming at is a release of XENIX 386 that will run on these machines. Now that's not to say that somebody like Altos, for example, can't take the source of what we produce and make it work on their own proprietary architecture. In fact, Altos is planning to do that──they've already started. MSJ: You mentioned 80386 machines. One that has already hit the marketplace is the Compaq 386. What's your opinion of it? DUNSMUIR: That's the machine that we've been using for development. We actually had some of the earliest prototypes and now we have some real models of it. It's really very good. It's very fast──about three times the speed of the AT. The only slow part of it is the disk, which, of course, operates at the same speed as it does on the AT. When you speed up the processor and you don't speed up the disk in proportion, the effective speed of the whole system obviously doesn't increase as much as that of the processor. The 386 Explosion MSJ: At the fall Comdex, there is expected to be an explosion of 386 accelerator cards for the IBM AT and other 286 machines. Have you worked with any of them? DUNSMUIR: Well, the question really is: What machines will XENIX run on, or rather, what does it run on so far? It runs on any machines containing an 80386 that have an AT compatible architecture. This includes the Compaq Deskpro 386, of course, but also machines running the Intel motherboard and 386 turbo cards plugged into ATs. MSJ: In terms of the microprocessor itself, the 80386──what are some of the advantages that it has over the 80286, and how do these advantages relate to XENIX? DUNSMUIR: One advantage the 80386 has over the 80286 is that it is much faster──about three times the speed in terms of raw CPU power. And it also has this large address space. On the 286 your program can be very large, but basically it is composed of 64K segments. The problem with this is that people who have written applications for UNIX(R) in general find it difficult to bring those applications down onto XENIX 286. In other words, the applications are bigger than the 64K limit that you can get with one segment. Originally we hoped to put a real UNIX machine on everybody's desktop──or a small multiuser UNIX machine──but the number of ISVs (independent software vendors) who actually have gone to XENIX 286 has been somewhat less than we had hoped. That's not to say that the volumes aren't very good. XENIX 286 is probably the single most- installed derivative of UNIX ever. But the number of applications that have been written specifically for XENIX 286 is smaller than we had hoped at the outset. With the 386, you can easily bring your application down from, say, a VAX or a Sun or whatever, because you don't have this problem with these segments getting in the way of your large programs. Possible Problems MSJ: That sounds good in theory, but in practice, won't there be some snags when you try to port a UNIX program running on a VAX to a microcomputer like the Compaq 386 running XENIX? DUNSMUIR: On the VAX, the pointers and the integers in your program are all 32 bits. Now, that means that if you simply compile your program on the 286, then it breaks because the integers on the 286 are 16 bits and the addresses are, too──unless you use multiple segments. So you're forced to use multiple segments. Now, when you use multiple segments, you've got a choice: You can go to what's called the large model, which means that the compiler automatically generates the instructions that change the segments when it needs to. This simulates the large address space in the 286 but reduces performance by about 20 percent. The alternative is to rewrite your application to optimize the use of the segments. On the 80386, however, segments are so large that there are essentially no segments. As a result, all you need to do is recompile, and the thing will run. And XENIX provides the right set of subroutines for the application to call, because it's a standard UNIX programming interface. A caveat to this arises, for example, if the program does graphics through some known standard interface. For instance, on the Sun they may be driving the graphics interface directly. Then the programmer is going to have to emulate that or provide support. But in terms of some straightforward application like a compiler or anything that's terminal-based, the program will just recompile and run. A Big Change MSJ: All the recent Intel processors like the 8088, 8086, and 80286 use 64K segments. The 80386 processor represents a radical change, doesn't it? DUNSMUIR: It certainly does. On the 286 you have these segments, and each segment has a number that you load into the segment register. The segments themselves can be up to 64K. So to actually address a piece of memory you specify the segment and the offset, which is a 16-bit number. On the 386, segments exist, too──except that the offsets are 32-bit numbers. This means that once you've loaded the segment register, you can address 4 gigabytes of memory, rather than 64K, without reloading the segment register. On the 386 you can emulate 286 programs. You can actually make a segment look like a 16-bit segment or a 32-bit segment. Basically, the operating system sets up the segment descriptor table as appropriate, depending on whether you're running a 286 or a 386 program. MSJ: Are there other characteristics that distinguish the 80386 from its predecessors? DUNSMUIR: Underneath the segmentation of the 386 is also paging, which doesn't exist on the 286. Underneath the 4-gigabyte segment are page tables, which map each 4K piece of that address space onto some piece of physical memory──or not, as the case may be. You don't have to actually have all of the physical paging present in that 4-gigabyte segment. Typically, you have only a few. A big program might have 4 megabytes, say, of actual paging mapped. But the advantage of having the very large address space is that you can actually put different parts of the program at completely weird, enormous addresses. So you can say: Okay, shared memory regions, for example, start at 2 gigabytes. But then you must make sure that the operating system maps the shared memory regions up to the 2-gigabyte virtual address used in the program. This is very similar to the way it works, for example, on the VAX or a 68020 machine like the Sun. Any of these 32-bit minicomputers really do it the same sort of way. And that's what the 386 brings you on a chip. It brings you the minicomputer architecture. Microsoft hopes that the people who write UNIX applications for 32-bit minicomputers like the VAX will bring their application, which is presumably written in C, down onto XENIX 386 and then make a low-end version available. That's one of the main thrusts. MSJ: Where does that leave users with XENIX 286 applications? DUNSMUIR: There are, of course, applications that have been written specifically for XENIX 286. These will be available on XENIX 386 simply because it runs all the 286 software. One of the advantages of the 386 is its downward compatibility. If somebody wants a higher-performance multiuser solution, he can just use his old XENIX-286-based programs on the 386 machine, and they'll run just fine. MSJ: XENIX 286 was primarily aimed at the multiuser market. Will the same hold true for XENIX 386? DUNSMUIR: The market position of XENIX 386 will be the small, multiuser marketplace. That's the main thrust. And that's where XENIX 286 has been successful. However, we now have the ability to create, if you like, a workstation with the 386, which we didn't have on the 286. So we will also be trying to address the workstation marketplace. I know that OEMs are planning to compete with the low-end VAX and Sun stations, and they'll offer a very cost-effective solution if, for example, you want to do CAD/CAM or desktop publishing, or anything like that. DOS Overlap MSJ: Doesn't this overlap with the potential market for MS-DOS 5.0? DUNSMUIR: Yes. When it becomes available, MS-DOS 5.0 will offer a very good, single-user workstation environment that will be very similar to a UNIX workstation in many ways. The main difference between an MS-DOS 5.0 and a XENIX 386 workstation is the existence of a UNIX market that is distinct from any other single-user market. UNIX allows you to have an application that will run on anything from a mainframe to a micro. In contrast, applications designed to run under MS-DOS 5.0 won't run under any other version. Hence, there will probably always be a UNIX workstation market unless UNIX disappears completely. This single-user workstation group is a separate group that we'll address with XENIX. MSJ: Will XENIX 386 have any brand-new features? DUNSMUIR: For the single-user workstation group, we've got to provide new features with XENIX. The features that make a good multiuser operating system──for example, supporting terminals──are not the sort of features. . . well, they're a subset, but they're not really the features that you need for a good, single-user workstation. The features that spring to mind for a single-user workstation are windowing, some sort of graphics capability, the ability to run 32-bit applications (which is what you get with the 386), and a networking facility so that you can connect your workstation to a larger machine, whatever that might be. What we're trying to do is to put together all of these various pieces in XENIX 386. MSJ: So XENIX System V/386 is slated to appear in the first quarter of 1987? DUNSMUIR: Right. The beta version will be shipped to OEMs in late November. We plan to ship them the final product in early January. Now, the time delay for any of those OEMs to get a product out in the field is about 2 months at least. So you probably won't see any XENIX 386 operating systems appearing from our OEMs until something like April. But in the meantime, the Santa Cruz Operation (SCO) will be making available a retail version of the same software. I don't know exactly what their plans are, but I imagine that they will make it available very early in January close to the same time that we ship the product to the OEMs. MSJ: But isn't SCO one of your OEMs? DUNSMUIR: We have what's called a second-source agreement with SCO. What that actually means is that we work together to create XENIX. They have access on a daily basis to the code, and therefore they are in a much better position to put together a product. In fact, in the case of XENIX 386, they're actually doing a bit of device driver work, and so forth. One other thing needs to be said about SCO. At the present time, if you buy XENIX directly from SCO, you can get certain added features that we don't provide. This added value is being incorporated into XENIX 386, and we are committed to having functionally identical products from now on. This should silence some criticism in the press that SCO and MS XENIX versions are not compatible. We are very concerned about providing ISVs and end-users with a compatible platform for their XENIX applications whether they choose to buy XENIX from SCO or one of our other OEMs. V/386 Toolkit MSJ: Although XENIX 386 isn't available right now, a XENIX System V/386 Toolkit is. Can you tell us something about it? DUNSMUIR: The XENIX System V/386 Toolkit is a special upgrade package for Microsoft XENIX System V/286. It's for independent software developers who want to write XENIX 386 applications now. The Toolkit includes both the program development tools needed to create XENIX 386 applications and the execution environment needed to run them. Virtually all of the 386 applications developed under the Toolkit will execute without recompilation under XENIX 386. Program development tools include a Microsoft C compiler for the 386; a new version of the Microsoft Macro Assembler, extended to support the 386 instruction set and addressing modes; and a debugger for 386 programs. These tools can be installed on any computer running XENIX 286. But the execution environment portion of the Toolkit requires not only XENIX 286, but also a system with a 386 processor. ──────────────────────────────────────────────────────────────────────────── A New Generation of Debugging Arrives with CodeView ─────────────────────────────────────────────────────────────────────────── Also see the related articles: A Look at DEBUG and SYMDEB Codeview Command Summary ─────────────────────────────────────────────────────────────────────────── Charles Petzold Debugging is such an awful, dirty, unglamorous, time-consuming job that nobody likes to think much about it. Perhaps for this reason, good debugging tools have always lagged behind other advances in language technology. It wasn't until the introduction of SYMDEB (SYMbolic DEBugger, first included with the Macro Assembler 3.0) that Microsoft offered programmers an alternative to the inadequate DEBUG. The CodeView(TM) debugger included with Microsoft's C Compiler Version 4.00, however, represents a new generation. Just as SYMDEB added to DEBUG's features by reading in source code and displaying symbolic references to global variables and subroutines, CodeView advances over SYMDEB by including symbolic local variable and subroutine names, new data display and enter types, an 8087 math coprocessor dump, and some additional debugging techniques. CodeView uses a full-screen windowed display and includes a mouse, menu, and command line interface. It can do C expression evaluation and UNIX-like regular expression evaluation. For now, CodeView is limited to Microsoft C 4.00 and cannot yet be used for debugging Microsoft Windows applications, but it will eventually be adapted for other languages. Prepararation The mechanics of preparing a program for CodeView are simple. When compiling a program, use the /Zi switch MSC PROGRAM /Zi; This option directs the compiler to include in the .OBJ file the names, addresses, and types of all global and local variables; the names, addresses, and return values of all global and static functions; and source code line number references. If you don't need all this information (perhaps for a module that's already in good shape), you can specify that only global functions and variables and line number information be included in the .OBJ file by using the /Zd switch MSC MODULE /Zd; This is the same switch used for preparing programs for SYMDEB. In either case, you might also want to disable optimization with the /Od switch. This will make it easier to correlate source code and the compiled assembly language. When you link, use the /CO (CodeView) switch LINK PROGRAM /CO; The /CO switch directs LINK to include the debugging information from each .OBJ file in the .EXE file together with directory paths of the .OBJ files. CodeView uses these directory paths to search for the C source code file. You will want to help CodeView out by keeping source code and object files in the same subdirectory. Both CodeView and SYMDEB are easier to use if you follow certain programming rules. For instance, since SYMDEB can handle only global functions and variables, you might be less inclined to use local variables or functions defined as static. With CodeView, however, you don't have to accommodate the debugger in this way. The rules for programming for CodeView are generally the same as those for writing structured readable C. You should use small modular functions. You should keep source code statements to one line each since multiple-line source code statements do not show up well in CodeView's combined source and assembly display. You can get a better feel for the relationship between source and compiled code by avoiding long, complex C statements. On the other hand, CodeView does not show source from include files and macros. Also, since LIB strips out the debugging information, CodeView cannot show source code for functions retrieved from libraries, even if the modules were compiled with the /Zi switch. For this reason you will want to wait until certain modules have been debugged before putting them in libraries. Letting MAKE Help Out The symbolic information that LINK embeds into an .EXE file for use by CodeView can make the .EXE much larger than normal. You will not want to distribute an .EXE file with this information because anybody looking at the program in CodeView can learn a great deal about its inner workings from the variable and procedure names that CodeView displays. You can use MAKE to help out with the chore of compiling a program with or without CodeView information. At the top of the make-file for a particular program, define several macros CVC = /Zi /Od CVL = /CO COMP = msc $* $(CVC) ; LINK = link $** $(CVL) ; where CVC and CVL are the CodeView switches for the compile and link steps, respectively. After the definition of these macros, you can set up the make- file like this: prog.obj : prog.c $(COMP) module1.obj : module1.c $(COMP) module2.obj : module2.c $(COMP) prog.exe : prog.obj\module1.obj\module2.obj $(LINK) When it is time to prepare the final .EXE file without embedded debugging information, just undefine the macros on the MAKE command line MAKE prog /DCVC= /DCVL= Alternatively, you can use the EXEPACK utility to strip debugging information from the .EXE file. Starting It Up In many cases you can bring up CodeView for debugging your program with the command line CV PROGRAM However, certain situations (such as using a PC compatible or a black-and- white monitor on a color adapter) will warrant CodeView command line switches. These switches must be placed before the name of the program that you're debugging. The most important command line switches relate to how CodeView uses the display. Because CodeView is a full-screen debugger, it must share the display with the out-put from the program you are debugging. Depending on the hardware installed on your system, you have several options for handling this problem. For systems with one video adapter, CodeView will use either video "swapping" or video "flipping" to keep the CodeView and program output separate. If you have only a monochrome adapter on your system, CodeView will use the "swapping" method. CodeView allocates some memory to save the contents of the display. It swaps screen displays between this memory and the video adapter memory when switching back and forth between CodeView and your program. If you have only a Color Graphics Adapter (CGA) or Enhanced Graphics Adapter (EGA) on your system, CodeView will, by default, use the "flipping" option. CodeView will use different video pages for its own display and the display of the program being debugged. Flipping between these video pages is faster than swapping video contents in and out of memory. You cannot use screen flipping with a monochrome adapter because it has only 4K of memory and one video page. You also cannot use flipping on a CGA or EGA if you are debugging a program that uses multiple video pages or graphics. In this case, you must override the default and specify screen swapping with the /S parameter. Both screen flipping and screen swapping have some problems. Every time you trace a statement, CodeView switches the display to program output and back again. You're likely to find your head aching before you find the bug. If you are debugging a program that directly accesses output ports on the video adapter, CodeView may not be able to restore the exact video state when it returns to your program output display. But there is really no alternative to this when debugging programs using a single monitor. These problems can be alleviated with a second display adapter. If you have two adapters on your system, you can specify the /2 option when you bring up CodeView, and CodeView will use the second adapter. (This option is not in the manual, but it's mentioned in the C 4.00 README.DOC file.) Unfortunately (as also mentioned in this README file), the /2 option does not allow the mouse to work on the debugging display. As with SYMDEB, you can redirect CodeView input and output through a serial port and use a separate debugging terminal. However, you lose many of the advantages of working with CodeView in a full-screen mode. Menus and the Mouse Although the pull-down menu interface and mouse support in CodeView make it easier to use than most debuggers, early encounters with CodeView can be quite confusing. There are actually several user interfaces──a SYMDEB-like command line, a Windows-like menu, the PC function keys, and the mouse. Each of these different interfaces has some distinct features and also some overlap. You can usually do things in several different ways, and it is precisely this flexibility that makes CodeView a bit awkward at first. On entry to CodeView, the screen is divided into two windows called Display and Dialog. The Display window shows either your source code, or source code combined with the compiled assembly language code. The Dialog window at first appears to be SYMDEB running in a window. However, you'll find that CodeView is missing a few DEBUG and SYMDEB commands (Fill, Move, Compare) and adds other valuable functions, such as expression evaluation, symbol examination, a file browser with a search capability, and additional methods to set breakpoints. Everything you can do in CodeView is done by entering commands in the Dialog window. This allows you to use CodeView in a SYMDEB-like teletype mode and still retain full functionality. Some commands (Dump, Enter, Assemble) must be entered in the Dialog window and cannot be done any other way. The menu, mouse, and function key interfaces duplicate many of the more common commands. They do not add functionality but they do provide ease of use. Let's explore some of the ways you can use CodeView by examining the guts of debugging──tracing through code. Trace, Go, and Break Users of DEBUG and SYMDEB are familiar with the Trace and Go commands. DEBUG 3.x and SYMDEB also include a Proceed command. CodeView implements all three of these methods to trace code. The Trace command uses the Intel microprocessors' single-step trap to single-step through instructions. Just as in DEBUG and SYMDEB, you can do a one-instruction trace in CodeView by entering T in the Dialog window. If you're in assembly mode, you'll step through assembly language instructions. In source code mode, you'll step through source code instructions. You can also do a one-instruction trace by pressing the F8 function key or clicking Trace! on the menu with the left mouse button. A multiple-instruction trace is possible only through the Dialog window by including a value after the T. The Program Step command (which was called the Proceed command when introduced in DEBUG 3.0) is similar to the Trace command except that it will not trace through subroutine calls or software interrupts. This is good for skipping through BIOS calls or subroutines that you know work. You can Program Step by entering P in the Dialog window, pressing F10, or clicking Trace! on the menu with the right mouse button. Note that Program Step works by setting a breakpoint following the call or interrupt that it skips through. This breakpoint will not work in ROM or with calls and interrupts to routines that adjust the return address. The Go command just executes code until it hits a breakpoint. You can set a breakpoint at the same time you do a Go by entering G in the Dialog window with a breakpoint expressed as an address, line number, or symbolic label. Alternatively, you can position the cursor in the Display window at the break line and press F7. For a Go command with no break, press F5. With the mouse, you can Go to a particular line by clicking the line with the right mouse button. For a no-break Go, click Go! on the menu. Users of SYMDEB are also familiar with setting explicit breakpoints. The advantage of this method over specifying a breakpoint in the Go command is that you can set more than one of them, and they are sticky──the breakpoint will remain in effect until you disable it or clear it. The breakpoint command in CodeView works like the one in SYMDEB. Twenty breakpoints are available. Setting a breakpoint with the mouse is very easy──you just click on the line with the left mouse button. You can also use the keyboard to move the cursor to the break line and press F9. Beyond SYMDEB Now let's look at some of the ways that CodeView goes beyond the functionality of SYMDEB. We have watches, watchpoints, and tracepoints. They all appear on the same menu, they are listed in the same window, and they have confusingly similar names. They are not the same. A "watch" is a C expression using any global or currently available local variable. The expression is displayed in the Watch window along with the calculated value. For instance, if you have a routine that writes directly to the video display memory using two variables called row and col, you might want to keep a watch on the implied display address referenced by these two variables. Your watch would look like this: 2 * (row + 80 * col) The C expression evaluator can even handle calls to your program's C functions. This lets you test individual functions by passing experimental values to them. A "watchpoint" is generally a Boolean expression, again expressed using C syntax with global and currently available local variable names. When the watchpoint becomes true (i.e., is nonzero), CodeView will break and return control to you. For both watches and watchpoints, CodeView must repeatedly evaluate the expression while single-stepping through code. For watches, it just displays the new value. For watchpoints, it breaks if the expression evaluates to a nonzero value. A "tracepoint" is similar to a watchpoint in that it can cause a break in program execution. But while a watchpoint depends on the evaluation of an expression, a tracepoint will break when an area of memory changes. This is much easier and faster for CodeView to do, but is not quite as flexible for the user. For instance, suppose you had a for loop that increments the variable i, such as for (i = 0; i < 100; i++) If you wanted to break every time that i changed value, you could set a tracepoint that is simply the expression i But if i is a registered variable, the tracepoint would not work, because i does not refer to an area of memory. Also, if you wanted to break every ten cycles through, you could not set a tracepoint that looks like i % 10 because "i % 10" does not refer to a memory location. What you really need is a watchpoint that looks like this: i % 10 == 0 Watching Tracepoints In debugging any program it is advisable to try to localize your problem before setting either watchpoints or tracepoints. Watchpoints take longer to evaluate than tracepoints, but they do not need to refer to memory locations. Here's a simple rule: When you think you need to set a watchpoint or tracepoint, try to do it as a tracepoint first. If you can't, use a watchpoint. Watchpoints and tracepoints both require some time at every program step. For real-time applications, you may need a hardware debugger. Null Pointers The ability to test the contents of any variable or any region of memory continually is a very powerful debugging tool. For instance, you may be familiar with the run-time message error 2001: Null pointer assignment At the very beginning of your program's data segment is a small area of memory containing some zeros and the Microsoft copyright notice. The clean- up exit code calculates a checksum on this memory. If the memory has been changed, the "Null pointer assignment" message is displayed. In C, a null pointer is an invalid pointer. If the beginning of the data segment has been changed, then it may very well be the result of using a null pointer. The message you get doesn't mean you've just assigned 0 to a pointer. It means that you've actually used that pointer to write something into memory. You can find the buggy code that does this by setting a tracepoint on this memory. From the command line of the Dialog window, you can enter TPB 0 L 8 This means "Set a Tracepoint of type Byte, starting at address 0 for a Length of 8." CodeView displays the current values in a Dump-like format. Or you can do essentially the same thing as an expression from the Tracepoint option of the Watch menu *((double *) 0) The contents of this double pointer to zero are the eight bytes starting at address 0. When this memory is changed, CodeView will break and show you where it happened. If you have a particular character string getting chewed up or an array picking up some strange values or even code that gets destroyed, you can set a tracepoint at the variable or the explicit address, and CodeView will break when the damage happens. The Panic Button CodeView also supports dynamic breaking using the SysReq key on the PC-AT. The SysReq key usually works to regain control when your program seems to avoid the breakpoints you set for it. The Calls option on the menu (or the K command in the Dialog window) displays a stack trace. The stack trace is good for answering the question "Where am I and how did I get here?" It displays a list of nested subroutines, the parameters passed to them, and (when the command is entered in the Dialog window) the line numbers where these routines were called. The Caveats Some warnings about CodeView: First, if you've been using DEBUG or SYMDEB, you should be aware that all the CodeView commands use a civilian form of notation called base-10 or decimal. If you enter D 197F, you'll get an error message. You should have typed D 6527 or D 0x197F instead. You can change the radix to hexadecimal if you can't get accustomed to this. (Dumps, assembly language listings, and register contents are displayed in hexadecimal regardless.) However, be aware that watch, watchpoint, and tracepoint expressions also use the radix. If you set the radix to 16, the numbers you type in the expressions will also be interpreted as hexadecimal. For instance, this is the watch I showed earlier: 2 * (row + 80 * col) After radix is set to 16, the 80 is interpreted as hexadecimal and would actually be the decimal value 128. If you'll be changing the radix, write expressions with explicit hexadecimal or decimal prefixes, such as 2 * (row + 0x50 * col) or 2 * (row + 0n80 * col) Second, CodeView 1.00 has at least one bug in its assembly and disassembly logic regarding the DWORD data type (typically used in floating point arithmetic). It treats DWORDs as WORDs. Finally, don't get rid of SYMDEB. CodeView itself uses 160K of memory, and .EXEs prepared for CodeView are larger than normal to include the symbol information, so you may have problems with large programs. Go back to SYMDEB for those. SYMDEB is also easier to use for some chores, such as taking a quick look at some disassembled ROM code, printing a disassembly, or creating short assembly language .COM files. For exploration and experimentation, you'll still think SYMDEB. For serious debugging, you'll definitely want to use Code ─────────────────────────────────────────────────────────────────────────── A Look at DEBUG and SYMDEB ─────────────────────────────────────────────────────────────────────────── Remember the "good" old days? you'd start off with and innocent looking peice of C code: mainmenu () { int i, code, good, donw = 0 ; do { topline (atl) ; menutype ("MAIN MENU", at3) ; clrwin (6, 10, 24, 69, at2) ; clrwin (6, 0, 24, 9, at3) ; clrwin (6, 70, 24, 79, at3) ; writestr ( 7, 21, "Press:", at2) ; But when it came time to do a little debugging in DEBUG, all you saw is something that just doesn't look the same: -u 24a 1434:024A 55 PUSH BP 1434:024B 8BEC MOV BP,SP 1434:024D V80800 MOV AX,0008 1434:0250 E8881C CALL 1EDB 1434:0253 57 PUSH DI 1434:0254 56 PUSH SI 1434:0255 C746FE0000 MOV WORD PTR [BP-02],0000 1434:025A FF36A241 PUSH [41A2] 1434:025E E8B304 CALL 0714 1434:0261 83C402 ADD SP,+02 1434:0264 FF36A641 PUSH [41A6] 1434:0268 B86E01 MOV AX,016E 1434:026B 50 PUSH AX 1434:026C E8E004 CALL 074F 1434:026F 83C404 ADD AP,+04 1434:0272 FF36A441 PUSH [41A4] Are we really sure it's the same program? Fortunately, SYMDEB took some of the guesswork out of debugging (below). Today, the CodeView debugger included with the Microsoft C Compiler 4.00 makes debugging even less painful. -u _mainmenu 12: { _TEXT:_mainmenu: 1AA4:024A 55 PUSH BP 1AA4:024B 8BEC MOV BP,SP 1AA4:024D B80800 MOV AX,0008 1AA4:0250 E8881C CALL _chkstk 1AA4:0253 57 PUSH DI 1AA4:0254 56 PUSH SI 20: int i, code, good, done = 0 ; 1AA4:0255 C746FE0000 MOV Word Ptr [BP-02],0000 24: topline (atl) ; 1AA4:025A FF36A241 PUSH [41A2] 1AA4:025E E8B304 CALL _topline 1AA4:0261 83C402 ADD SP,+02 ─────────────────────────────────────────────────────────────────────────── Codeview Command Summary ─────────────────────────────────────────────────────────────────────────── ╓┌─────────────┌────────────────────┌────────────┌────────────┌──────────────╖ Command Dialog Window Menu Mouse Function Key Assemble A (address) Breakpoint BC list:* Run/Clear Left click F9 (cursor on Clear Breakpoints break line) Breakpoint BD list:* Disable Breakpoint BE list:* Enable Command Dialog Window Menu Mouse Function Key Enable Breakpoint BL List Breakpoint BP (address) Left click F9 (cursor on (passcount) break line break line) (commands) Dump Data D (type) (range) Enter Data E (type) address (list) Execute E Run/Execute Go G Go! Click Go! F3 Goto G (break Right click F7 (cursor on address) break line break line) Command Dialog Window Menu Mouse Function Key address) break line break line) Help H View/Help F1 Stack Trace K Calls Restart L Run/Restart (arguments) Restart & Go Run/Start Radix N (radix) Program step P (count) Right click F10 Trace ! Quit Q File/Quit Register R Options/ F2 Display Registers Command Dialog Window Menu Mouse Function Key Display Registers Register R (register) Click on Change ((=) Flag expression)) Mnemonic Set Mode - S + View/Source F3 Source Set Mode - S - View/ F3 Assemble Trace T (count) Trace! Left click F8 Trace! Tracepoint TP? expression Watch/ (,format) Tracepoint TP (type) range Unassemble U (range) Command Dialog Window Menu Mouse Function Key Unassemble U (range) View File V (. (filename:) File/Load line) V (expression) Watch W? expression Watch/Add (,format) Watch W(type) range Watch List W Watchpoint WP? expression Watch/ Watchpoint Examine X? (module!) Symbol (function.) (symbol) (*) Examine X* Command Dialog Window Menu Mouse Function Key Examine X* Module Names Delete Watch Y number Watch/Delete Watch 8087 Dump 7 Display ?expression View/ Evaluate Expression (,format) Display . Location Redraw .@ Screen Screen \ View/Output Exchange Command Dialog Window Menu Mouse Function Key Exchange Search /(expression) Search/Find Search Next / Search/Next Search Search/ Previous Previous Search Label V (expression) Search/Label Shell to DOS ! File/Shell Tab Size # number ──────────────────────────────────────────────────────────────────────────── Ask Dr. Bob! Peeks and Pokes Dear Dr. Bob, In my programs I often have to get at memory outside my program segment, say to retrieve values from the BIOS data area or to write directly to the display. In BASIC I can use PEEK and POKE. How can I do PEEKs and POKEs in C?──"C"urious Dear "C"urious, Very easily. The flexibility you have using pointers in C makes this job almost trivial. For instance, if you need to retrieve the current video mode (located at address 0040:0049 in the BIOS data area), you can use the statement in Figure 1. The 0x400049L is a long integer containing the segment and offset address of the value you want to access. The (char far *) casts this number into a far pointer to a character. The indirection operator * retrieves the 1-byte value stored at this pointer. You can also use a similar construction to store values, as shown in Figure 2. Obviously, in this example, you would not do this unless your program has manually changed the video mode and you want the BIOS data area to agree with what you have done. If you have to reference data outside your program frequently, you might want to set up a couple of macros, as shown in Figure 3, where s and o are the segment and offset parts of the far address. You would use MEMC for 1- byte characters and MEMI for integers. The examples in Figure 3 would then look like Figure 4. If you need a far pointer that accesses the IBM Color Graphics Adapter, you could define it as shown in Figure 5. Interrupts Dear Dr. Bob, I've been using the intdos and int86 functions included with the Microsoft C 3.0 compiler to call DOS and BIOS routines directly. I haven't experienced many problems. However, I recently tried to use int86 for the DOS Interrupts 0x25 and 0x26 (which read and write absolute sectors on a disk) and I can't get it to work. For instance, this program should read the first sector of a drive A: disk into the array called buffer: #include main () { char buffer [512]; union REGS regs; regs.h.al=0; regs.x.bx=(int)buffer; regs.x.cx=1; regs.x.dx=0; int86 (0x25, ®s, ®s); } But this program crashes. What's the problem?──Puzzled Dear Puzzled, Interrupts 0x25 and 0x26 are a little different from the other DOS and BIOS interrupts because they leave the original flags on the stack. The stack must be adjusted after returning from the interrupt. The int86 function included with C 3.0 did not make this adjustment. The solution to your problem is as simple as an upgrade. The int86 function in Microsoft C 4.0 specifically checks for Interrupts 0x25 and 0x26 and adjusts the stack after the interrupt function returns control. Arrays and Pointers Dear Dr. Bob, I've been programming in C for a while now, but I still get confused about the relationship between arrays and pointers. For instance, I can define two variables like this: char str1[] = "This is a string" ; char *str2 = "This is a string" ; For some purposes, str1 and str2 appear to work the same. Other times, however, it's obvious that they are not the same. What is the difference between a character array and a pointer to a character string, and when should I use one instead of the other.──Unsure Dear Unsure, Both str1 and str2 may be used as pointers that address the first element of the string. Both puts (str1) ; and puts (str2) ; print the string on the display. It may help to think of str2 as a pointer to the string and str1 as shorthand notation for &str1[0], which is a pointer to the first element of the array. You can see what the compiler does with these two definitions by compiling with the /Fc switch. The compiler creates a combined source code and assembly code listing created as a file with the .COD extension. If both str1 and str2 are defined at the external level (outside of any function), then str1 is compiled into the assembly language statement shown in Figure 6, while str2 is compiled in the manner shown in Figure 7. The pointer definition takes up 2 additional bytes of memory. When you define and initialize str1, you are allocating 17 bytes to hold the string and the terminating zero. When you define str2, however, you are really only allocating 2 bytes for the pointer. The initialization directs the compiler to store in str2 the address of the string. In theory, this string should be treated as "read-only" (but nothing bad will happen if you try to write into it). For str1, you've defined the whole string, so you can read from it and write to it. Again in theory, if you also define char str3[] = "This is a string" ; and char *str4 = "This is a string" ; then str1 and str3 must define two separate strings. However, it would be legitimate for a compiler to initialize str2 and str4 as pointers to the same string. (The Microsoft C Compiler will not do this.) For both str1 and str2, you can reference characters in the strings using either array notation or indirection. All of these statements will then put the character 's' into the character variable ch: ch = *(str1 + 3) ; ch = str1[3] ; ch = *(str2 + 3) ; ch = str2[3] ; The assignment statements using str1 are compiled into assembly language statements that look something like this: mov al,[_str1 + 3] mov [_ch],al However, an extra assembly language step is required for the assignments using str2 because the address of the string must be retrieved from str2 before the string can be referenced: mov bx,[_str2] mov al,[bx + 3] mov [_ch],al If str1 and str2 are defined inside a function, then str1 must be defined as static: static char str1[] = "This is a string" ; because arrays can't be initialized inside functions. However, you can define str2 either way: char *str2 = "This is a string" ; or static char *str2 = "This is a string" ; In the first case, 2 bytes will be allocated on the function's stack frame for str2, and the function will store the address of the string in that location. In the second case, the str2 pointer variable is stored in static memory just as if it were defined outside the function. So far it looks like the array definition is preferable to the pointer definition, but this is not always the case. One important distinction is that str2 is an lvalue, but str1 is not; that is, you can assign another pointer to str2. You can say str2 = str1 ; but you cannot say str1 = str2 ; If you go back and look at the assembly language code for the str1 definition, you will see that this second statement makes no sense. Initialized strings are more efficient when defined as an array. However, if you need to use the variable name as a pointer to other strings, then you must use a pointer definition. Recognizing Windows Dear Dr. Bob, When I try to execute a Windows application on the DOS command level, I get the message "This program requires Microsoft Windows." How does a Windows application determine whether it's running under Windows or running under plain MS-DOS?──Wondering Dear Wondering, It doesn't. Although Windows programs have an .EXE extension, the format of the .EXE file is different from the DOS .EXE format. This "new format executable" is created by LINK4, the Microsoft Windows linker. Only the Windows loader knows about the new format of the .EXE file; MS-DOS does not. But the new format executable is constructed in such a way that it appears to MS-DOS to be an old format executable. The program that runs when Microsoft Windows loads the .EXE file is the Windows application. The program that runs when MS-DOS loads the .EXE file is a small piece of code that prints the message "This program requires Microsoft Windows." The new format executable is required to give Microsoft Windows more information about the program. Windows must know which segments of the program are code and which are data, which segments it can move around in memory and which segments must be fixed, whether a segment can be loaded immediately or only when it is needed, and so forth. The new format executable also contains support for resources (menus, icons, cursors, bitmaps, dialog boxes, etc.) that Windows loads from the disk as the application requires them. All calls to Windows functions from the application appear in the new format executable as dummied-out far calls. The Microsoft Windows loader resolves these calls based on a table that is also within the .EXE file. The program that runs when MS-DOS attempts to execute the Windows application can be any .EXE file and not necessarily the standard one. You could even use a non-Windows version of your application for that file. Figure 1: videomode = *(char far *)0x400049L ; Figure 2: *(char far *)0x400049L = videomode ; Figure 3: #define MEMC(s,o) (*(char far *)((long)(s)<<16|(o))) #define MEMI(s,o) (*(int far *)((long)(s)<<16|(o))) Figure 4: videomode = MEMC (0x40, 0x49) ; MEMC (0x40, 0x49) = videomode ; Figure 5: int far *colorscreen = 0xB8000000L ; Figure 6: _str1 db "This is a string", 0 Figure 7: label db "This is a string", 0 _str2 dw label