Freeware package for making Win32 programs using A386 V2.5 June 20, 2003 Copyrights: ALINK Linker is Copyright 1998-2000 Anthony A.J. Williams GORC Resource Compiler is Copyright 1998/2000 Jeremy Gordon RSXNT.DLL is Copyright 1995-1999 Rainer Schnitker, distributed under the GNU General Public License; source to RSXNT is at ftp://ftp.leo.org/pub/comp/os/os2/leo/gnu/emx+gcc/rsxnt AWBAT assembly tool is Copyright 2003 Eric Isaacson A386 source files in this module are Copyright 2001-2002 Eric Isaacson This package was put together by: Eric Isaacson Software 1-812-339-1811 416 E. University Ave. http://eji.com Bloomington, IN 47401-4739 eric@eji.com To use this package, you need to have my A386 assembler; V4.04 or later. If you have never purchased A386 before, you can do so online at http://eji.com/a86/order . If you are a registered A386 user, you may purchase the update by contacting me directly. New for V2.5 NOTE: You must delete your old AW.COM file for this new version to work! In this release I have adjusted AW so that the package works under Windows XP. For some reason, XP does not let AW.COM call the GORC and ALINK programs directly. To fix this, AW now generates a batch file JMAKE.BAT which makes all the necessary program calls. I have renamed AW.COM to AWBAT.COM. I now have an AW.BAT file, that calls AW.COM and then calls the just-created JMAKE.BAT file to run the programs specified by AWBAT. I have also provided an AWD.BAT file, that you can use to fire up a GOBUG session with the program. To download GOBUG, so to Jeremy Gordon's Web site, as mentioned below. Again, NOTE that if you are careless and leave the old AW.COM file in your path, you will invoke that old AW.COM when you issue an AW command, and this newer version will thus be ignored! V2.2, 2.3, 2.4 were private releases, addressing minor problems. New for V2.1 In this release I have included RSXNT.DLL which is needed to run ALINK, and I have fixed a bug that caused the ;asm and ;link feature not to work properly. There are also minor adjustments to a few of the library source files, as they have evolved during my continued foray into 32-bit programming. I have also added an example CONSOL32 of non-GUI 32-bit programming. Overview of this package This package has all the additional elements needed for users of my A386 assembler to create 32-bit programs that run under Windows 95 or later (95, 98, NT, 2000, ME, XP, etc.). For compiling resource scripts, I have included the freeware resource compiler GORC, written by Jeremy Gordon. The home page for Jeremy's programming tools is http://www.godevtool.com, which includes a number of useful tools. I particularly recommend the debugger GoBug, which works quite well with the A386-based programs produced by this package. For producing the final EXE file, I have included the freeware linker ALINK, written by Anthony Williams. The home page for ALINK is http://alink.home.dhs.org . This is a "beta test" version 1.7: although I have found it to work quite solidly in the context of linking my A386-based programs, Anthony reports that it still has some bugs in other contexts. The version provided by his page is still V1.6 at the time of this writing. ALINK requires the module RSXNT.DLL, which was written by Ranier Schnitker and is distributable under the GNU General Public License. The source for RSXNT is available at the FTP site given in the copyright notices above. The *.LIB files are catalogs used by ALINK, of all the functions provided by the Win32 API. For some reason I do not fathom, the link can fail if the files are listed in a random order. The file ALINK.DEF, which AW incorporates into the link by default, includes an ordering of the .LIB files that works. To bring together A386, GORC, and ALINK, I have implemented an AW command, which stands for "Assemble for Windows". AW allows you to write both the resource script and the assembler source code in a single A386 source file. You can feed the source file to AW, and it will cause all three programs to be called, to produce the working EXE file from the source. I have also provided the source code for a 32-bit program template called HELLO32. HELLO32 places the classic "Hello, World!" message in a Window on the screen, as well as demonstrating a number of Win32 features: the menu, status-bar messages, a toolbar with tooltips, open/save dialog boxes, an About box, etc. If you have this package and my A386 assembler, you just type AW HELLO32 to produce the program from its source files. Thanks to Jeremy, Anthony and Ranier for allowing me to include their programs in this package! Overview of AWBAT Assemble-for-Windows Tool AWBAT will scan your assembler source file, use specially-coded comment lines and A386 macro lines (that you have placed into the file) to create an .RC file, and a batch file JMAKE.BAT which calls the assembler, resource compiler, and linker to produce an executable program file from a single program-specific source file. With AW, you do not need to manually create and maintain batch files or MAKE files to assemble simple programs for Windows. The default setting for AW is to produce resource files with the fixed names J.RC and J.RES -- this effectively makes them temporary files, in that they will be overwritten when you assemble another source file. Thus, if you have lots of different Windows assembler programs, there will be fewer files you need to keep track of and maintain. If AW sees that there are no RC specifiers in the source file, AW will execute the alternate program sequence, in which no .RES file is produced or used. If AW sees that the RC file has not changed contents from its last output, it will avoid calling RC again to produce the .RES file, thus saving you time. AW allows you to declare identifiers for resources, in such a way that the assignment of constants for those identifiers is handled automatically by AW and A386. AW similarly handles the coordination of menu-action case numbers, between the RC file and the assembler source file. To understand how to use AW, you must be familiar with Windows programming: this package will be fairly incomprehensible to you if you don't know what RC files are, and if you don't know what Callback functions are. Terms and Conditions This package is available for download from my Web site at http://eji.com/a86/aw.htm . You may link to this address but you should not post or distribute the package itself anywhere else, unless I have given you explicit permission to do so. You are allowed to use this package without any additional licensing charges, beyond those that you pay for A386. If you use this package to develop an A386-coded application, and you are selling the source code to a third party (or were hired by them to write the source code), you may provide this package to that third party, subject to the condition (which already exists in the A386 terms) that they must be licensed to execute the A386 assembler. AW Operating Instructions AW is invoked from the DOS command prompt by typing AW followed by the name of your assembler source file, followed optionally by the name of the output files to be produced. For example, AW HELLO.8 processes the file HELLO.8, producing the file J.RC and its compiled J.RES if there are any resource-compiler lines in the file. If you wish to produce program-specific RC and RES files, you type AW HELLO.8 HELLO to produce HELLO.DEF and HELLO.RC. If you leave off the file extension for the A386 source file name, the extension .8 is assumed. AW does not change your assembler source file: it merely reads it to figure out what the .RC file will be, then assembles, resource-compiles, and links it. Your source file can have INCLUDE directives, but AW does not act upon those: all AW-recognized lines must appear in the top-level source file. If your assembler source file has a comment line beginning with ";asm ", AW will take this as the command line template it should use to invoke the assembler for this file. This line can contain the following substitution characters: % for the .RC output name (J unless you specify another name) @ for the program source-file name (e.g., HELLO32) ! preceding the assembler name, if AW is to look in the PATH for it ^ to literalize the following character if it is in this list "A for the string following /A in the AW command tail, if provided If no ;asm line is given the following default template is used: !A386.COM +ocs+I157+W4+L55-T "A @.8 Similarly, if your assembler source file has a comment line beginning with ";link ". AW will take this as the command-line template it should use to invoke the linker for this file. The line contains the same subsitition characters as ;asm , except "L for the string following /L (instead of "A and /A). If no ;link line is given the following default template is used: !ALINK.EXE -o @.exe @.obj "L %.res ^@!alink.def .RC File Specifications If there is to be an RC file associated with your program, you give it as a sequence of lines in your source file, each beginning with one of the specifiers ";rc ", "rc", "rcdef", or "MENUITEM". The last three specifiers are A386 macros. If A86.LIB and all the *.8 library files included in the package are in the current directory or in a directory named by the A386LIB environment variable, the file W32MAC.8 will automatically be included in the assembly, and the proper macro definitions for rc, RCDEF, and MENUITEM will be loaded. ;rc Line The ;rc line indicates a resource-compiler line requiring no action by A386. The entire remainder of the line is included in the .RC file. rc Line The rc line (without the semi-colon) indicates a resource-compiler line that begins with a resource name. The line has the form: rc RESOURCENAME, rest of line which generates two lines in the .RC file: #define RESOURCENAME nnn RESOURCENAME rest of line and causes the A386 macro to generate the one line: RESOURCENAME EQU nnn , where nnn is the next constant in an incrementing sequence maintained by both AW and the A386 macros. NOTE that, due to the syntax of A386 macros, there is an extra comma that does NOT appear in an ;rc comment line. This comma is removed by AW when it produces the line to be fed to the resource compiler. Thus, if that line requires no commas following the initial symbol name, you put a single comma in the rc line. If that line requires a comma following the initial symbol name, you put TWO commas at that spot in the rc line. RCDEF line The RCDEF line defines a resource name, but does not generate any additional .RC line. Thus the line RCDEF RESOURCENAME places the line #define RESOURCENAME nnn into the .RC file, and the line RESOURCENAME EQU nnn into the assembly, where nnn is the next constant in an incrementing sequence maintained by both AW and the A386 macros. MENUITEM line Most RC files specify the structure of drop-down menu items, accessible by clicking on keywords (e.g., "Open", "Save As", etc.) in the menu bar just below the title-bar of the program's window. Each such menu item requiring action on your program's part is specified by a MENUITEM line in the RC file, which gives the name if the item as displayed on the menu bar, followed by the index number to be passed as a parameter to a Callback function to your program. Your program is supposed to recognize the index number and perform the tasks demanded by the corresponding menu item. In my A386/AW system, if you wish to implement a menu in your resource script, your sequence of MENUITEM lines in your A386 source file should be preceded by the label "Menu:" and followed by the declaration "NMenu EQU ($-Menu)/4". For each item in the Menu having a corresponding A386 action, you code a line of the form: MENUITEM "display name", ItemName which generates the two .RC lines MENUITEM "display name",nnn #define IDM_ItemName nnn and the two assembler lines DD menuItemName IDM_ItemName EQU nnn , where nnn is the next constant in an incrementing sequence maintained by both AW and the A386 macros. Your A386 program should then include a procedure named "menuItemName", which will be jumped to when the menu item indicated by "display name" is selected by the program's user. If your MENUITEM line does not have a corresponding A386 action (e.g., it is a SEPARATOR), then you code the line as an ;rc , for example: ;rc MENUITEM SEPARATOR This system is much easier to maintain than the standard method, in which you must manually generate and maintain an elaborate sequence of symbol declarations in two different places: at the top of the RC file there are lines like #define IDM_QUIT 500; #define IDM_ABOUT 501; etc. At the top of the assembler source file is a similar set of declarations IDM_QUIT EQU 500; IDM_ABOUT EQU 501, etc.. Then the index numbers are referred to within the bodies of the respective files via their IDM_ symbols, with the source code typically comparing a message number to each symbol name in succession, and conditionally jumping to the appropriate code if there is a match. My Windows programming system eliminates that red tape, by coding RC index numbers to be positions within a jump-table of pointers to action routines. My standard menu-handling Callback routine accepts the menu-index number passed to it, and simply jumps to the routine "menuItemName" pointed to by the corresponding slot in an array called Menu. The HELLO32 and CONSOL32 Programs Included in this package is a set of source files comprising my Win32 versions of a "Hello World" program. The main file for the GUI version is HELLO32.8. There is also a much simpler console-only version whose source file is CONSOL32.8. All the other *.8 files in the package are source-file library files, gathered via A86LIB into the catalog file A86.LIB, which A386 automatically consults, causing the library files to be included in the assembly. The HELLO32.8 file describes the Win32 features demonstrated by the program, and gives a rudimentary description of how the features are implemented. General Win32 Programming Tips The *.LIB files in this package give you access to the hundreds of function calls provided by the Win32 API. The documentation for these functions is contained in a giant Help file distributed by Microsoft. To find this file on the Web, feed the string "Win32 helpfile" to one of the search engines. At the time of this writing, the file was available at: ftp://ftp.cs.virginia.edu/pub/lcc-win32/win32hlp.exe The program names in the API are case-sensitive, so the A386 switch +c must be used (and is hence invoked by AW by default). In general, all parameters to the API functions are doubleword quantities, including parameter types such as BOOLEAN that you'd think might occupy less space. Parameters are passed by pushing the doubleword values onto the stack before making the CALL to the function. The pushes are made in the REVERSE order from that given in the helpfile decriptions for the function. The functions return with the parameters popped from the stack. If the function has a return value, it is placed into the EAX register upon return. All functions preserve the values of EBX, ESI, EDI and EBP across the call; they may clobber the values EAX, ECX, and EDX. Some Win32 programming systems require "mangled" names: a sequence of characters is added to the published API name, to indicate the number and type of the parameters. This system does not require such codes, so you should not include them. However, many of the function names require the additional single letter "A" appended to the name. I do not have any documentation saying exactly which functions they are: you just have to find out via trial and error. If a function is in the API but the linker doesn't find it, try appending the A to the name. There are thousands of system constants provided in the Win32 API, which are almost always referred to by an all-caps name, without providing the underlying constant value. For your convenience, I have compiled a list W32DEFS.TXT of all such names I could find, together with their constant values. Values are decimal unless preceded by 0x which denotes hexadecimal. I have also provided, in ERRNUMS.TXT, a listing of the meanings of the error codes returned by the GetLastError function. If you wish to use Jeremy Gordon's GoBug debugger with this system, you should have no PUBLIC directives whatsoever in your source file, or any of the library files. This will cause all symbols in the assembly (except local labels such as L1) to be public. You must also include the -debug switch in the ALINK command line, which will cause all the symbols to be included in debug records within the EXE file. This in turn will cause all the symbols to appear in the disassembly produced by GoBug. To assemble and debug, I have a batch file AWD.BAT, with the following lines: awbat /L-debug /A"=DEBUG" %1 call jmake if errorlevel 1 goto exit2 del jmake.bat gobug %1 %2 %3 %4 %5 %6 %7 :exit2 Then I can type, for example, AWD HELLO32 to assemble and debug my HELLO32 program. If HELLO32 had had any command-line parameters, I ould have included those on the AWD line as well. Again, the GoBug tool can be downloaded from Jeremy's site at: http://www.godevtool.com You may move the program files A386.COM AW.COM, GORC.EXE, ALINK.EXE, and the GoBug files to any directory named by your PATH environment variable. You can also move the *.LIB files there. If you do so, you should edit the file ALINK.DEF to give the full path names of the .LIB files, and then move ALINK.DEF into the same directory as ALINK.EXE. You can also move A86.LIB and the *.8 library files to a directory named by the A386LIB environment variable. Once you have done all that, you may write A386 Win32 source files in any directory, and run the AW MYPROG command from that directory. Finally, a word about event-driven programming: it's tricky. The callback mechanism can be confusing if you're not used to it. For example, in one of my first attempts to expand HELLO32 into something non-trivial, my CALL CreateWindowExA was failing, and the window refused to appear. I spent many hours puzzling over the failed call, which occurred near the very beginning of execution, and whose parameters appeared identical to HELLO32. How could there be an error in the call, when the program hasn't yet gotten to any of the new code? Eventually I realized that the OS was making a callback to my cbCreate code *before* returning from my own CreateWindowExA call, so that my new code *was* being executed without my realizing it.