0000 ;--------------- 0000 ; HELLO32 Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0000 ;--------------- 0000 0000 ; HELLO32 is a template for a 32-bit Windows application, written entirely 0000 ; in A386 assembler. It is inspired by (and has most of the same features 0000 ; of) Steve Gibson's "Small Is Beautiful" program written for MASM, available 0000 ; at http://grc.com/smgassembly.htm . The resource script and system calls 0000 ; made are about the same as in Steve's program, but the source code is 0000 ; almost completely different. The major differences are: 0000 ; 0000 ; * I have a system for bringing the resource script into the assembler source, 0000 ; which automates the generation of the constants that denote resources. I 0000 ; also have brought the code common to most Win32 apps into A386 source 0000 ; library modules: source files INCLUDEd automatically by A386 during the 0000 ; assembly. You assemble, resource-compile, and link this program by issuing 0000 ; the command AW HELLO32 to the DOS prompt. 0000 ; 0000 ; * My callback control mechanism is table-driven, rather than the long 0000 ; sequence of compares and jumps generated by MASM's structured-assembler 0000 ; IF and ELSEIF directives. This allows me to place callback code for the 0000 ; individual messages into different library modules. 0000 ; 0000 ; * The callback mechanism also pops the passed parameters into the 386's 0000 ; registers, instead of being accessed on the stack via EBP addressing. 0000 ; Likewise, my internal routines pass parameters via registers instead of 0000 ; being pushed onto the stack. In my opinion this generally makes the code 0000 ; smaller, clearer, and easier to write. The EXE for my program is about 0000 ; 2K smaller than Steve's. 0000 ; 0000 ; * I do not have a giant INCLUDE file containing hundreds of Win32 system 0000 ; constants. Instead, I prefer to maintain a reference file W32DEFS.TXT with 0000 ; thousands of constants, look up a name in that file when I'm writing a 0000 ; program, and code it as a numeric value, with its name (and often its 0000 ; meaning) provided in the comment field. This reduces the number of lines 0000 ; assembled, and also simplifies the process of verifying the correctness 0000 ; of the code when single-stepping in a debugger. 0000 ; 0000 ; * The format of the source file is of a more old-fashioned style, instead 0000 ; of the structured-assembler format MASM uses. The MASM code generates 0000 ; a lot of wasted JMPs and produces a debugger disassembly that does not 0000 ; resemble the coded source (a sort of poor man's C compiler). My older 0000 ; format requires more lines for system-call parameter passing: I have a 0000 ; sequence of PUSH instructions before the CALL. Again, this makes the 0000 ; debugger disassembly much more recognizable, and it also gives me room 0000 ; in the source file to provide comments for each parameter. 0000 ; 0000 ; * My source-file indenting uses spaces instead of tabs. It does not make 0000 ; any sense to me to have instruction operands in their own column, so 0000 ; there is only a single space between the mnemonic and the operands. This 0000 ; usually makes more room on the line for a more descriptive comment. Most 0000 ; instructions are indented two spaces, to make the (non-indented) labels 0000 ; stick out. There is an extra indentation space for operand PUSHes 0000 ; preceding system calls, to make their association clearer, and to 0000 ; distinguish them from register-saving stack pushes (which will need 0000 ; later balancing POPs). Occasionally, for tighter code, I push a 0000 ; system-call operand prematurely; i.e., so far before its system call that 0000 ; there are one or more other system calls in-between. I flag this 0000 ; trickiness with a second extra indentation space, and a comment that 0000 ; names the later call. 0000 ; 0000 ; 0000 ; 0000 ; This HELLO32 program demonstrates the following Win32 features: 0000 ; 0000 ; * We have an icon built into the program. This is implemented in the 0000 ; resource script, by importing an icon file with the identifier IDI_ICON. 0000 ; Our library code in regclass.8 uses IDI_ICON when registering our program's 0000 ; class, so that the icon appears in our title bar and on the Windows task 0000 ; bar. The icon is also used in the "About" box, defined later in the 0000 ; resource script. 0000 ; 0000 ; * We have a menu bar with a few working menu items that bring up the standard 0000 ; Win32 dialogs for opening and saving files (although once the user has 0000 ; selected a file with those dialogs, this program does nothing other than 0000 ; put the file name in the title bar). The menu is implemeted via the 0000 ; "Menu:" array in this source file, which contains lines seen both by A386 0000 ; to generate a callback control table, and by the resource compiler to 0000 ; create the menu itself. Our library's callback code directs menu 0000 ; invocations to our A386 library routines menuNew, menuOpen, etc. 0000 ; 0000 ; * We have some accelerator keys that call up menu items. This is implemented 0000 ; by defining the symbol IDA_ACCEL in the resource script, which triggers 0000 ; the enabling code in the library file w32main.8 . The resource script 0000 ; has the control tables that define the specific keys. 0000 ; 0000 ; * We have a toolbar with buttons that invoke the menu items. This is 0000 ; implemented by defining the symbol ID_TOOLBAR in the resource script, 0000 ; which triggers the enabling code the library file cbcreate.8, which in turn 0000 ; references the "tbButtons:" table provided in the data area of this 0000 ; source file. The tbButton macro used for table entries is defined in 0000 ; w32mac.8 . 0000 ; 0000 ; * We have tooltips: tiny little annotating windows that pop up when the user 0000 ; pauses the mouse cursor over toolbar buttons and Windows system buttons, 0000 ; before clicking on those buttons. These are implemented by activating 0000 ; the default Notify message handler in the MainCbActs table in this source 0000 ; file, with ID_TOOLBAR defined. This triggers our cbnotify.8 library 0000 ; code, which accesses the tooltip strings declared in resource script. 0000 ; We have simplified our code by manually declaring the tooltip string 0000 ; identifier constants to be 256 more than the menu-item resource constants 0000 ; associated with their buttons. 0000 ; 0000 ; * We have a status bar that provides a one-line annotation for menu items 0000 ; that have been selected but not yet invoked. This is implemented by 0000 ; defining the symbol ID_STATUSBAR in the resource script, which triggers 0000 ; the activating code in the library file cbcreate.8. We must also activate 0000 ; the default MenuSelect message handler in the MainCbActs table in this 0000 ; source file. That code, in statbar.8, references the STRINGTABLE resources 0000 ; in our script, using the identifiers IDS_SYSMENU and IDM_FILEMENU as 0000 ; indexes to identify the strings to display for each menu item. 0000 ; 0000 ; * We have an "About" window that identifies the program, implemented as a 0000 ; menu item that invokes a default library routine that brings up the 0000 ; IDD_ABOUT dialog box defined in our resource script. 0000 ; 0000 ; * Finally, we output "Hello, world!" to the main window display. The code 0000 ; for this is in the CB_PAINT routine in this source file, which is called 0000 ; by our default library code. 0000 0000 0000 0000 ;----------------------------------------------------------------------------- 0000 ; 0000 ; The following macro call causes the A386 library source file w32mac.8 to be 0000 ; included in this spot. References within the following MainCbActs table 0000 ; will trigger the inclusion (after this file) of w32main.8, our standard A386 0000 ; startup code for a Win32 GUI application. The code performs initializations, 0000 ; creates the main program window, and then enters a "message loop", processing 0000 ; events reported to us by the operating system. We define a "callback" 0000 ; procedure for the operating system to call when any event happens. One of 0000 ; the parameters passed is a "message number", identifying the event. The 0000 ; MainCbActs table defines which events we will respond to. Our library 0000 ; provides the handlers, named cbCreate, cbDestroy, etc., for many (in this 0000 ; simple template, all) events in the table. Those handlers in turn call 0000 ; subroutines provided in the application-specific part of the program, which 0000 ; is the last section of this source file. 0000 0000 i ;--------------- 0000 i ; W32MAC Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0000 i ;--------------- 0000 i 0000 i ; This library module contains macros common to all A386 assemblies of Win32 0000 i ; GUI applications. Generally, no object output should be produced by a 0000 i ; library macro file, although in this case, if a program begins with a 0000 i ; Win32 call as it should, no harm would result of such code were added to 0000 i ; this file. 0000 i = : 0001 i LONG_FNAMES EQU 1 ; EILIB routines will assemble for long file names = : 0001 i NO_DOSCHECK EQU 1 ; EILIB routines will not try to call DOS for errors =builtin i @ EQU OFFSET ; a handy abbreviation 0000 i 0000 i 0000 i ; WIN32 is intended to be called as the first assembled line of any A386-coded 0000 i ; Win32 GUI application. The first operand is a string giving the program 0000 i ; name. The second operand, if present, is the class name. Most simple 0000 i ; programs can leave this operand blank (i.e. you can give two commas 0000 i ; following the program-name string) -- this macro will use the program 0000 i ; name as the class name in that situation. There follow the width and 0000 i ; height of the main program window, followed by any number of flag names 0000 i ; that the main program wishes to declare as 1. 0000 i 0000 i WIN32 MACRO 0000 i DATA SEGMENT FLAT 0000 i szProgName: 0000 i ##if #S2 ; if there is a second operand then declare both explicitly 0000 i DB #1,0 0000 i szClassName: 0000 i DB #2,0 0000 i ##else ; if no second operand then declare both to be the first operand 0000 i szClassName: 0000 i DB #1,0 0000 i ##endif 0000 i wWidth = #3 ; declare the main program width 0000 i wHeight = #4 ; declare the main program height 0000 i nClassName EQU $-szClassName ; size needed to register our class 0000 i #RX5L ; loop for all remaining operands 0000 i #X EQU 1 ; each remaining operand is a flag set to 1 0000 i #ER 0000 i #EM 0000 i 0000 i 0000 i ; MENUITEM, RCDEF, and rc are macros used in the resource-script section of 0000 i ; the program, for actions required by A386 as well as the resource compiler. 0000 i ; The first operand to MENUITEM is used by AW to create the resource line, 0000 i ; and ignored by A386. All three macros create symbols equated to constants 0000 i ; that refer to resources. AW maintains its own implicit counter to keep 0000 i ; track of the values of the constants: A386 uses the counter N_MENUITEMS. 0000 i = : 0001 i N_MENUITEMS = 1 ; initialize counter for resource constants 0000 i MENUITEM MACRO ; declare an element of our Menu table 0000 i DD menu#2 ; prepend "menu" to second operand and make ptr 0000 i IDM_#2 EQU N_MENUITEMS ; prepend "IDM_" and declare a resource constant 0000 i N_MENUITEMS = N_MENUITEMS+1 0000 i #EM 0000 i 0000 i RCDEF MACRO ; AW makes only a DEFINE for the given symbol 0000 i #1 EQU N_MENUITEMS ; declare a resource constant 0000 i N_MENUITEMS = N_MENUITEMS+1 0000 i #EM 0000 i 0000 i rc MACRO ; AW makes both a DEFINE and a compiler line 0000 i #1 EQU N_MENUITEMS ; declare a resource constant 0000 i N_MENUITEMS = N_MENUITEMS+1 0000 i #EM 0000 i 0000 i = : 0000 i ntbButtons = 0 ; initialize counter of toolbar buttons 0000 i 0000 i tbButton MACRO ; declare an element of the toolbar buttons table 0000 i DD #1,#2 ; #1 is the bitmap number for the button 0000 i ; #2 is the command indentifer for the button 0000 i DB 4,0 ; toolbar state and style are always the same 0000 i DD 0,0 ; no app-specific data, and no toolbar string 0000 i ntbButtons = ntbButtons+1 ; increment the buttons counter 0000 i #EM 0000 i 0000 i cb MACRO ; declare an element of a Callback table 0000 i DD cb#1 ; prepend "cb" to the first operand and make a pointer to it 0000 i DW #2 ; place the corresponding 16-bit message number into the table 0000 i #em 0000 i 0000 i 0000 i ; DEFF provides a way for a library file to provide a default value for a 0000 i ; symbol that may or may not have been defined by the main program. If 0000 i ; the first operand symbol was already defined, the DEFF line is ignored. 0000 i ; If not, the symbol is equated to the value given by the second operand. 0000 i ; 0000 i ; The need for DEFF is superceded in A386 V4.06 and later, by the built-in 0000 i ; syntax "DEF symbol EQU value" which will do the same thing. 0000 i 0000 i DEFF MACRO 0000 i ##if !DEF #1 0000 i #1 = #2 0000 i ##endif 0000 i #EM 0000 i 0000 i 0000 WIN32 "Hello32",,400,360,CENTERING,ONE_INSTANCE 0000 m DATA SEGMENT FLAT 0000 m szProgName: 0000 m #if 0 0000 m DB "Hello32",0 0000 m szClassName: 0000 m DB ,0 0000 m #else 0000 m szClassName: 0000 48 65 6C 6C 6F 33 m DB "Hello32",0 0008 m #endif = : 0190 m wWidth = 400 = : 0168 m wHeight = 360 = : 0008 m nClassName EQU $-szClassName 0008 m = : 0001 m CENTERING EQU 1 0008 m = : 0001 m ONE_INSTANCE EQU 1 0008 m 0008 ; | 0008 ; v 0008 ; program name 0008 ; class name is the same 0008 ; main window width 0008 ; main window height 0008 ; flag triggering a window-centering mechanism 0008 ; flag inhibiting multiple program instances 0008 0008 MainCbActs: ; table of callback actions and message numbers 0008 cb Create, 1 ; wmCreate: the main window is being created 0008 30 00 00 00 m DD cbCreate 000C 01 00 m DW 1 000E cb Destroy, 2 ; wmDestroy: the main window has been destroyed 000E 1E 01 00 00 m DD cbDestroy 0012 02 00 m DW 2 0014 cb Size, 5 ; wmSize: our window has been resized 0014 43 01 00 00 m DD cbSize 0018 05 00 m DW 5 001A cb Paint, 15 ; wmPaint: the window needs to be repainted 001A 26 01 00 00 m DD cbPaint 001E 0F 00 m DW 15 0020 cb Close, 16 ; wmClose: the main window is being closed 0020 0B 01 00 00 m DD cbClose 0024 10 00 m DW 16 0026 cb Notify, 78 ; wmNotify: an event has occurred over a control 0026 60 01 00 00 m DD cbNotify 002A 4E 00 m DW 78 002C cb Command, 273 ; wmCommand: user has double-clicked a menu item 002C 8E 01 00 00 m DD cbCommand 0030 11 01 m DW 273 0032 cb MenuSelect, 287 ; wmMenuSelect: user has single-clicked a menu item 0032 A1 01 00 00 m DD cbMenuSelect 0036 1F 01 m DW 287 0038 00 00 00 00 DD 0 ; table terminator 003C 003C 003C 003C ;----------------------------------------------------------------------------- 003C ; 003C ; Following is the resource script for our program. The AW tool will scan 003C ; these lines and feed them to the resource compiler. The uncommented lines 003C ; are also processed by A386: the rc and RCDEF lines define symbols that 003C ; equate to resource-identifier constants. The MENUITEM lines generate 003C ; entries for the control table to direct menu callbacks. These A386 lines 003C ; are macro calls; their macro definitions are contained in the w32mac.8 003C ; library file. 003C 003C rc IDI_ICON,ICON "hello32.ico" = : 0001 m IDI_ICON EQU N_MENUITEMS = : 0002 m N_MENUITEMS = N_MENUITEMS+1 003C RCDEF ID_STATUSBAR ; this symbol activates our status bar = : 0002 m ID_STATUSBAR EQU N_MENUITEMS = : 0003 m N_MENUITEMS = N_MENUITEMS+1 003C RCDEF ID_TOOLBAR ; this symbol activates the main toolbar = : 0003 m ID_TOOLBAR EQU N_MENUITEMS = : 0004 m N_MENUITEMS = N_MENUITEMS+1 003C 003C Menu: ; A386-only line to declare the Menu control array 003C rc IDM_MENU,MENU DISCARDABLE = : 0004 m IDM_MENU EQU N_MENUITEMS = : 0005 m N_MENUITEMS = N_MENUITEMS+1 003C ;rc BEGIN 003C ;rc POPUP "&File" 003C ;rc BEGIN 003C MENUITEM "&New\tCtrl+N", New 003C 1D 02 00 00 m DD menuNew = : 0005 m IDM_New EQU N_MENUITEMS = : 0006 m N_MENUITEMS = N_MENUITEMS+1 0040 MENUITEM "&Open...\tCtrl+O", Open 0040 21 03 00 00 m DD menuOpen = : 0006 m IDM_Open EQU N_MENUITEMS = : 0007 m N_MENUITEMS = N_MENUITEMS+1 0044 MENUITEM "&Save\tCtrl+S", Save 0044 D7 02 00 00 m DD menuSave = : 0007 m IDM_Save EQU N_MENUITEMS = : 0008 m N_MENUITEMS = N_MENUITEMS+1 0048 MENUITEM "Save &As...", SaveAs 0048 D0 02 00 00 m DD menuSaveAs = : 0008 m IDM_SaveAs EQU N_MENUITEMS = : 0009 m N_MENUITEMS = N_MENUITEMS+1 004C ;rc MENUITEM SEPARATOR 004C MENUITEM "E&xit", Exit 004C 0B 01 00 00 m DD menuExit = : 0009 m IDM_Exit EQU N_MENUITEMS = : 000A m N_MENUITEMS = N_MENUITEMS+1 0050 ;rc END 0050 ;rc POPUP "&Help" 0050 ;rc BEGIN 0050 MENUITEM "&Help Topics", HelpTopics 0050 0A 00 00 00 m DD menuHelpTopics = : 000A m IDM_HelpTopics EQU N_MENUITEMS = : 000B m N_MENUITEMS = N_MENUITEMS+1 0054 ;rc MENUITEM SEPARATOR 0054 MENUITEM "&About Hello32", About 0054 26 04 00 00 m DD menuAbout = : 000B m IDM_About EQU N_MENUITEMS = : 000C m N_MENUITEMS = N_MENUITEMS+1 0058 ;rc END 0058 ;rc END = : 0007 nMenu EQU ($-Menu)/4 ; A386-only line to declare the size of the Menu array 0058 0058 rc IDA_ACCEL,ACCELERATORS = : 000C m IDA_ACCEL EQU N_MENUITEMS = : 000D m N_MENUITEMS = N_MENUITEMS+1 0058 ;rc BEGIN 0058 ;rc "N", IDM_New, CONTROL, VIRTKEY 0058 ;rc "O", IDM_Open, CONTROL, VIRTKEY 0058 ;rc "S", IDM_Save, CONTROL, VIRTKEY 0058 ;rc END 0058 0058 rc IDD_ABOUT,DIALOG DISCARDABLE 0, 0, 160, 60 = : 000D m IDD_ABOUT EQU N_MENUITEMS = : 000E m N_MENUITEMS = N_MENUITEMS+1 0058 ;rc STYLE 0x80C80080 // WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME 0058 ;rc CAPTION "About Hello32" 0058 ;rc FONT 8, "MS Sans Serif" 0058 ;rc BEGIN // id x y w h 0058 ;rc ICON IDI_ICON, -1, 8, 8, 21, 20 0058 ;rc CTEXT "Hello32", -1, 30, 8, 100, 8 0058 ;rc CTEXT "written in A386 assembler", -1, 30, 18, 100, 8 0058 ;rc CTEXT "Copyright © 2001 Eric Isaacson",-1, 30, 28, 100, 8 0058 ;rc DEFPUSHBUTTON "OK", 1, 101, 43, 50, 14 0058 ;rc END 0058 0058 ;rc STRINGTABLE 0058 ;rc BEGIN // for Status Bar 0058 rc IDS_SYSMENU,,"Contains commands for manipulating this window." = : 000E m IDS_SYSMENU EQU N_MENUITEMS = : 000F m N_MENUITEMS = N_MENUITEMS+1 0058 ;rc 0xF000, "Resizes this window." //SC_SIZE 0058 ;rc 0xF010, "Moves this window." //SC_MOVE 0058 ;rc 0xF020, "Minimizes this window." //SC_MINIMIZE 0058 ;rc 0xF030, "Expands this window to fill the screen." //SC_MAXIMIZE 0058 ;rc 0xF060, "Closes this window." //SC_CLOSE 0058 ;rc 0xF120, "Restores this window to its selected size." //SC_RESTORE 0058 ;rc 0058 rc IDM_FILEMENU, ,"Contains commands for working with files." = : 000F m IDM_FILEMENU EQU N_MENUITEMS = : 0010 m N_MENUITEMS = N_MENUITEMS+1 0058 ;rc IDM_New ,"Creates a new file." 0058 ;rc IDM_Open ,"Opens an existing file." 0058 ;rc IDM_Save ,"Saves the file." 0058 ;rc IDM_SaveAs ,"Saves the file with a new name." 0058 ;rc IDM_Exit ,"Quits this program." 0058 ;rc 0058 rc IDM_HELPMENU, ,"Contains commands for displaying Help." = : 0010 m IDM_HELPMENU EQU N_MENUITEMS = : 0011 m N_MENUITEMS = N_MENUITEMS+1 0058 ;rc IDM_HelpTopics ,"Displays Help Contents and Index (not implemented)." 0058 ;rc IDM_About ,"Displays program information and copyright." 0058 ;rc IDM_New+256 ,"New" // for ToolTips 0058 ;rc IDM_Open+256 ,"Open" 0058 ;rc IDM_Save+256 ,"Save" 0058 ;rc END 0058 0058 0058 0058 ;----------------------------------------------------------------------------- 0058 ; 0058 ; Following are the main data tables and strings for this program. In the 0058 ; FLAT model, these items could just as well have been included in the same 0058 ; segment as the program code, since all segment registers address the same 0058 ; memory. But I am told that data access is faster if the items are in this 0058 ; declared DATA segment. 0058 0058 tbButtons: ; button definitions for the main toolbar 0058 tbButton 6, IDM_New ; STD_FILENEW, IDM_New 0058 06 00 00 00 05 00 m DD 6,IDM_New 0060 04 00 m DB 4,0 0062 00 00 00 00 00 00 m DD 0,0 = : 0001 m ntbButtons = ntbButtons+1 006A tbButton 7, IDM_Open ; STD_FILEOPEN, IDM_Open 006A 07 00 00 00 06 00 m DD 7,IDM_Open 0072 04 00 m DB 4,0 0074 00 00 00 00 00 00 m DD 0,0 = : 0002 m ntbButtons = ntbButtons+1 007C tbButton 8, IDM_Save ; STD_FILESAVE, IDM_Save 007C 08 00 00 00 07 00 m DD 8,IDM_Save 0084 04 00 m DB 4,0 0086 00 00 00 00 00 00 m DD 0,0 = : 0003 m ntbButtons = ntbButtons+1 008E 008E osave_Newfn: ; our window title if no file is currently selected 008E 55 6E 74 69 74 6C DB 'Untitled',0 = : 0008_0000 osave_n EQU ($-osave_Newfn-1) SHL 16 0097 0097 osave_Filter: ; filter strings for the file-selection dialogs 0097 54 65 78 74 20 46 DB "Text Files (*.TXT)",0,"*.TXT",0 00B0 41 6C 6C 20 46 69 DB "All Files (*.*)",0,"*.*",0 00C4 00 DB 0 ; second null terminates the list 00C5 osave_DefExt: 00C5 74 78 74 00 DB "txt",0 00C9 00C9 00C9 00C9 ;----------------------------------------------------------------------------- 00C9 ; 00C9 ; The application-specific code for our program begins here. This code is 00C9 ; called by the A386 library routines, provided as callbacks to respond to 00C9 ; the various events reported to us by the operating system. 00C9 0000 _TEXT SEGMENT FLAT 0000 0000 CB_CREATE: ; the main program window has been created 0000 E9 18 02 00 00 JMP menuNew ; set up an "untitled" file state for initial usage 0005 0005 EXIT_CAUTION: ; application-specific code for program exit 0005 E9 99 02 00 00 JMP SAVE_CHANGES ; if user tries to exit then prompt him for file-save 000A 000A menuHelpTopics: ; "Help" menu item was selected 000A E9 A9 03 00 00 JMP DEFAULT_CB ; we have no help implemented in this template 000F 000F 000F ; CB_PAINT is called when the OS tells us our window needs to be repainted. 000F ; Our calling library code opens the device context before calling us, and 000F ; closes it after. We are called with EDI set to the device context handle, 000F ; and other registers set to the callback parameters as described in the 000F ; callback.8 library module. We can also access the static structure ps, 000F ; located in w32main.8, to get information on the repainting area. 000F 00C9 DATA SEGMENT FLAT 00C9 HELLO: ; our output message 00C9 48 65 6C 6C 6F 2C DB 'Hello, world!' = : 000D N_HELLO EQU $-HELLO ; size of the output message 000F DATA ENDS 000F 000F CB_PAINT: 000F 6A 0D PUSHD N_HELLO ; size of display string 0011 68 C9 00 00 00 PUSHD HELLO ; "Hello, world!" display string 0016 6A 32 6A 1E PUSHD 50,30 ; y and x pixel-coordinates within the window 001A 57 PUSH EDI ; device context handle 001B E8 FC FF FF FF CALL TextOutA ; output the "Hello, world!" message 0020 C3 RET 0021 0021 0021 ; OPEN_OFN_FILE and SAVE_OFN_FILE are the application-specific routines for 0021 ; opening and saving files. In each case, we are called with EDX pointing 0021 ; to the full path name of the file to be opened or saved. Our caller is 0021 ; responsible for managing the dialog boxes that determined the name before 0021 ; calling us, and for placing the name of the file into the title bar, after 0021 ; calling us. We are responsible for actually opening, reading/writing, and 0021 ; closing the file. 0021 0021 OPEN_OFN_FILE: 0021 ; 0021 ; place file-open code here (typically open, read, and generate a display) 0021 ; 0021 83 0D 3C 02 00 00 OR fFileStatus D,2 ; set "Changed" bit, just to demo the "Save Changes?" box 0028 C3 RET 0029 0029 SAVE_OFN_FILE: 0029 ; 0029 ; place file-save code here (typically open, modify/rewrite, and close) 0029 ; 0029 C3 RET 002A 002A 002A ;--------------- 002A ; CBCREATE Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 002A ;--------------- 002A 002A #if ID_TOOLBAR 00D6 DATA SEGMENT FLAT 00DA hToolBar DD ? ; handle for the main toolbar 00DA #endif 00DA 002A _TEXT SEGMENT FLAT 002A 002A L0: ; a sub-bar creation was unsuccessful 002A 48 DEC EAX ; set EAX to -1, to signal something was wrong 002B E9 81 03 00 00 JMP CALLBACK_RET ; return this nonzero value from our callback routine 0030 0030 cbCreate: 0030 #if CENTERING 0030 E8 FC FF FF FF CALL GetDesktopWindow ; set EAX to the handle for the whole desktop 0035 E8 0B 04 00 00 CALL CTR_ESI_IN_EAX ; center the main window within the desktop 003A #endif 003A E8 FC FF FF FF CALL InitCommonControls ; ensure that the common control DLL is loaded 003F #if ID_STATUSBAR 003F 6A 02 PUSHD ID_STATUSBAR ; our identifier number for the status bar (not used) 0041 56 PUSH ESI ; our module handle, owning the status bar 0042 68 CE 01 00 00 PUSHD szStatusBar ; buffer containing our text for the status bar 0047 68 00 00 80 50 PUSHD 05080_0000 ; WS_CHILD + WS_BORDER + WS_VISIBLE 004C E8 FC FF FF FF CALL CreateStatusWindow ; create a status bar at the bottom of our window 0051 A3 C6 01 00 00 MOV hStatusBar D,EAX ; store the handle for the status bar 0056 85 C0 TEST EAX ; was the creation successful? 0058 74 D0 JZ L0 ; return -1 from callback if not 005A #endif 005A #if ID_TOOLBAR 005A 6A 12 PUSHD 18 ; the number of bytes in a tbButton structure 005C 6A 10 6A 10 PUSHD 16,16 ; height and width of the button bitmaps 0060 6A 10 6A 10 PUSHD 16,16 ; height and width of the buttons 0064 6A 03 PUSHD ntbButtons ; number of toolbar buttons 0066 68 58 00 00 00 PUSH tbButtons ; pointer to array of structures for each button 006B 6A 00 PUSHD 0 ; IDB_STD_SMALL_COLOR: standard small color bitmaps 006D 6A FF PUSHD -1 ; HINST_COMMCTRL: we are using system bitmaps 006F 6A 0B PUSHD 11 ; number of button images in above-identifed resource 0071 6A 03 PUSHD ID_TOOLBAR ; our control ID for the toolbar 0073 68 00 01 80 50 PUSHD 05080_0100 ; WS_CHILD _BORDER _VISIBLE + TBSTYLE_TOOLTIPS 0078 56 PUSH ESI ; our module handle, owning the toolbar 0079 E8 FC FF FF FF CALL CreateToolbarEx ; create the toolbar 007E A3 D6 00 00 00 MOV hToolBar D,EAX ; store the handle for the toolbar 0083 85 C0 TEST EAX ; was the creation successful? 0085 74 A3 JZ L0 ; return -1 from callback if not 0087 #endif 0087 #if CB_CREATE 0087 E9 74 FF FF FF JMP CB_CREATE ; jump to application-specific code 008C #else 008C RET ; no no such code then return 008C #endif 008C 008C 008C ;--------------- 008C ; W32MAIN Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 008C ;--------------- 008C 008C ; This module contains the standard startup code for Win32 GUI applications, as 008C ; well as the callback handlers for Destroy, Paint and menu Exit. 008C 00DA DATA SEGMENT FLAT 00DA MSG: ; buffer containing message info 00DE MSG_hwnd DD ? ; window whose window proc receives the message 00E2 MSG_num DD ? ; message number 00E6 MSG_wParam DD ? ; additional info (sometimes the exit code) 00EA MSG_lParam DD ? ; additional info 00EE MSG_time DD ? ; time at which the message was received 00F6 MSG_curpos DD ?,? ; cursor position when the message was received 00F6 00FA hInst DD ? ; instance handle for our program 00FA ps: ; paint structure 00FE ps_HDC DD ? ; device context handle for this paint session 0102 ps_fErase DD ? ; NZ if we need to erase the background 0106 ps_left DD ? ; left coordinate of rectangle that needs repaint 010A ps_top DD ? ; top coordinate of rectangle that needs repaint 010E ps_right DD ? ; right coordinate of rectangle that needs repaint 0112 ps_bottom DD ? ; bottom coordinate of rectangle that needs repaint 01B2 ps_resv DD 40 DUP ? ; remainder of ps used internally by Windows 01B2 #if IDA_ACCEL 01B6 hAccel DD ? ; handle for the accelerator table 01B6 #endif 008C _TEXT SEGMENT FLAT 008C 008C MAIN: ; execution begins here 008C 6A 00 PUSHD 0 ; use default module name to get our module handle 008E E8 FC FF FF FF CALL GetModuleHandleA ; get our module's handle 0093 A3 F6 00 00 00 MOV hInst,EAX ; store our module's handle 0098 #if ONE_INSTANCE 0098 50 PUSH EAX ; save our module's handle 0099 E8 1D 04 00 00 CALL ONE_INSTANCE? ; ensure that we have only one instance 009E 5E POP ESI ; restore hInst to ESI for later pushes 009F 74 64 JZ >L9 ; exit program if there is another instance 00A1 #else 00A1 XCHG ESI,EAX ; swap hInst into ESI for later pushes 00A1 #endif 00A1 #if PROG_INITS 00A1 PUSH ESI ; save our module's handle 00A1 CALL PROG_INITS ; perform program-specific initializations 00A1 POP ESI ; restore our module's hand;e 00A1 #endif 00A1 E8 72 04 00 00 CALL REG_CLASS ; register our program class 00A6 74 5D JZ >L9 ; return failure if the registration failed 00A8 E8 B5 04 00 00 CALL CREATE_WIN ; create our main program window 00AD 74 56 JZ >L9 ; return failure if the creat call failed 00AF #if IDA_ACCEL 00AF 6A 0C PUSHD IDA_ACCEL ; load resource number of our accelerator keys 00B1 56 PUSH ESI ; our module's handle 00B2 E8 FC FF FF FF CALL LoadAcceleratorsA ; load our accelerator table from our resource file 00B7 85 C0 TEST EAX ; did the LoadAccelerators call fail? 00B9 74 4A JZ >L9 ; exit if it failed 00BB A3 B2 01 00 00 MOV hAccel D,EAX ; store the handle of the loaded accelerator table 00C0 #endif 00C0 L1: ; main-program message loop 00C0 2B C0 SUB EAX,EAX ; load zero, for pushing 00C2 50 50 PUSH EAX,EAX ; we will take the full range of messages 00C4 50 PUSH EAX ; take msgs for all windows in our thread 00C5 68 DA 00 00 00 PUSHD MSG ; place the message info into our MSG buffer 00CA E8 FC FF FF FF CALL GetMessageA ; retrieve a message from our queue 00CF 85 C0 TEST EAX ; is it a WM_QUIT message? 00D1 74 2D JZ >L8 ; jump if it is, to exit the program 00D3 #if IDA_ACCEL 00D3 68 DA 00 00 00 PUSHD MSG ; buffer containing the message info 00D8 FF 35 B2 01 00 00 PUSH hAccel D ; handle of our table of accelerator keys 00DE FF 35 DA 00 00 00 PUSH MSG_hwnd ; handle of the window receiving the message 00E4 E8 FC FF FF FF CALL TranslateAcceleratorA ; process any accelerator keys seen 00E9 85 C0 TEST EAX ; was an accelerator key processed? 00EB 75 D3 JNZ L1 ; loop if it was: the message is handled 00ED #endif 00ED B8 DA 00 00 00 MOV EAX,MSG ; load the pointer to buffer containing msg info 00F2 50 PUSH EAX ; push buffer pointer for Dispatch later 00F3 50 PUSH EAX ; push buffer pointer for Translate now 00F4 E8 FC FF FF FF CALL TranslateMessage ; translate virtual-key msgs into character msgs 00F9 E8 FC FF FF FF CALL DispatchMessageA ; dispatch the message to our callback routine 00FE EB C0 JMP L1 0100 0100 L8: ; WM_QUIT message was seen 0100 A1 E2 00 00 00 MOV EAX,MSG_wParam ; load wParam value from MSG buffer: the exit code 0105 L9: ; jump here if INIT failed 0105 EXIT_PROCESS: 0105 50 PUSH EAX ; push the exit code for our program 0106 E8 FC FF FF FF CALL ExitProcess ; execution terminates here 010B 010B 010B menuExit: ; Exit menu item was selected 010B cbClose: ; window closing X was clicked on 010B #if def EXIT_CAUTION 010B E8 F5 FE FF FF CALL EXIT_CAUTION ; if this file has changed prompt user to save 0110 74 0B JZ RET ; return if the user elected to cancel 0112 #endif 0112 FF 35 44 02 00 00 PUSH D hMainWnd ; push our main window handle 0118 E8 FC FF FF FF CALL DestroyWindow ; destroy our main window 011D C3 RET 011E 011E 011E cbDestroy: ; the main window has been destroyed 011E 6A 00 PUSHD 0 ; our program exit code is zero 0120 E8 FC FF FF FF CALL PostQuitMessage ; tell the system to post a termination message 0125 C3 RET 0126 0126 0126 ; cbPaint is the handler for the wmPaint message. We call BeginPaint to fill 0126 ; our static structure ps with the paint information, then call the 0126 ; procedure CB_PAINT supplied by our main program, to paint the window, 0126 ; then close out with EndPaint. 0126 0126 cbPaint: ; our window needs to be painted 0126 68 FA 00 00 00 56 PUSH ps,ESI ; push struc and handle for EndPaint later 012C 68 FA 00 00 00 56 PUSH ps,ESI ; push struc and handle for BeginPaint now 0132 E8 FC FF FF FF CALL BeginPaint ; intialize the painting process 0137 97 XCHG EDI,EAX ; device context handle kept in EDI during paint 0138 E8 D2 FE FF FF CALL CB_PAINT ; call the application-specific paint routine 013D E8 FC FF FF FF CALL EndPaint ; close out the device context handle 0142 C3 RET 0143 0143 0143 0143 ;--------------- 0143 ; CBSIZE Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0143 ;--------------- 0143 0143 _TEXT SEGMENT FLAT 0143 0143 cbSize: ; our window has been resized 0143 #if ID_TOOLBAR 0143 52 53 50 PUSH EDX,EBX,EAX ; lParam,wParam,uMsg for second call 0146 #endif 0146 #if ID_STATUSBAR 0146 52 53 50 PUSH EDX,EBX,EAX ; lParam,wParam,uMsg for first call 0149 FF 35 C6 01 00 00 PUSH hStatusBar D ; push the handle for our status bar 014F E8 FC FF FF FF CALL SendMessageA ; relay the resize message to our status bar 0154 #endif 0154 #if ID_TOOLBAR 0154 FF 35 D6 00 00 00 PUSH hToolBar D ; push the handle for our toolbar 015A E8 FC FF FF FF CALL SendMessageA ; relay the resize message to our toolbar 015F #endif 015F C3 RET 0160 0160 0160 0160 ;--------------- 0160 ; CBNOTIFY Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0160 ;--------------- 0160 0160 #if ID_TOOLBAR 01B6 DATA SEGMENT FLAT 01B6 szToolTip: ; buffer containing the tooltip message 01C6 DB 16 DUP ? 01C6 #endif 01C6 0160 _TEXT SEGMENT FLAT 0160 0160 cbNotify: ; the cursor has passed over an item of interest 0160 #if ID_TOOLBAR 0160 81 7A 08 F8 FD FF CMP D[EDX+8],-520 ; does NMHDR PTR lParam.code = TTN_NEEDTEXT? 0167 75 20 JNE >L1 ; skip if not 0169 C7 42 0C B6 01 00 MOV D[EDX+12],szToolTip ; set (TOOLTIPTEXT PTR lParam).lpszText 0170 8B 42 04 MOV EAX,[EDX+4] ; fetch (TOOLTIPTEXT PTR lParam).hdr.idFrom 0173 FE C4 INC AH ; add 256 to calculate the tooltip resource stringID 0175 6A 10 PUSHD 16 ; size of the receiving szToolTip buffer 0177 68 B6 01 00 00 PUSHD szToolTip ; buffer to receive string 017C 50 PUSH EAX ; resource ID number 017D FF 35 F6 00 00 00 PUSH hInst ; our module handle (can't use ESI toolbar handle) 0183 E8 FC FF FF FF CALL LoadStringA ; load the ToolTip buffer with the resource string 0188 C3 RET 0189 0189 L1: ; we have not seen our own trigger codes 0189 #endif 0189 E9 2A 02 00 00 JMP DEFAULT_CB ; revert to default handling 018E 018E 018E ;--------------- 018E ; WMENU Copyright 2001 Eric Isaacson, Permissions in AWDOC.TXT 018E ;--------------- 018E 018E _TEXT SEGMENT FLAT 018E 018E ; cbCommand is a wmCommand handler for the case in which we have a menu 018E ; only. We jump to the action routine in the Menu table supplied 018E ; by our main program. 018E 018E cbCommand: 018E #if def Menu 018E 2B C0 SUB EAX,EAX ; zero out the EAX register 0190 8A C3 MOV AL,BL ; set EAX to the menu number passed to us 0192 2C 05 SUB AL,IDM_MENU+1 ; convert to an index within our Menu table 0194 72 F2 JB RET ; return, doing nothing, if the index is too low 0196 3C 07 CMP AL,nMenu ; is the menu number too high? 0198 73 EE JAE RET ; return, doing nothing, if it is 019A FF 24 85 3C 00 00 JMP Menu[EAX*4] ; jump to the associated action routine 01A1 #else 01A1 RET 01A1 #endif 01A1 01A1 01A1 01A1 ;--------------- 01A1 ; STATBAR Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 01A1 ;--------------- 01A1 01C6 DATA SEGMENT FLAT 01CA hStatusBar DD ? ; handle for the status bar 01CE bSimpleMode DD ? ; simple Status Bar TRUE/FALSE 01CE szStatusBar: ; buffer containing the status bar display message 020E DB 64 DUP ? 020E 01A1 _TEXT SEGMENT FLAT 01A1 01A1 ; cbMenuSelect is called when the user has single-clicked a menu item (item 01A1 ; is highlighted but the associated function is not yet run). We figure 01A1 ; out the one-line description for that menu item, and place it into the 01A1 ; status bar. 01A1 01A1 cbMenuSelect: 01A1 2B C0 SUB EAX,EAX ; load zero for store and for possible simple-mode 01A3 A2 CE 01 00 00 MOV szStatusBar B,AL ; clear status text 01A8 66 83 F9 FF CMP CX,0FFFF ; is fuFlags the special 0FFFF value? 01AC 75 04 JNE >L1 ; jump if not 01AE 85 D2 TEST EDX ; flags are 0FFFF: is the handle null? 01B0 74 52 JZ SIMPLE_STATUS_BAR ; jump if yes: menu is closed, so blank the status 01B2 L1: ; we have an open menu item 01B2 81 E3 FF FF 00 00 AND EBX,0FFFF ; clear out the upper word of wParam 01B8 F6 C5 08 TEST CH,8 ; check the MFT_SEPARATOR bit of fuFlags 01BB 75 26 JNZ >L4 ; clear status line if we are in a separator line 01BD F6 C1 10 TEST CL,010 ; check the MF_POPUP bit 01C0 74 0E JZ >L3 ; jump if not a popup: EBX is already a resource ID 01C2 F6 C5 20 TEST CH,020 ; check the MF_SYSMENU bit 01C5 74 06 JZ >L2 ; jump if not SYSMENU, to use FILEMENU 01C7 66 BB 0E 00 MOV BX,IDS_SYSMENU ; set EBX to IDS_SYSMENU 01CB EB 16 JMP >L4 ; join common code to output the associated string 01CD 01CD L2: ; POPUP, not SEPARATOR, not SYSMENU 01CD 83 C3 0F ADD EBX,IDM_FILEMENU ; wParam is an index into the FILEMENU array 01D0 L3: 01D0 6A 40 PUSHD 64 ; size of text buffer 01D2 68 CE 01 00 00 PUSHD szStatusBar ; our status bar text buffer 01D7 53 PUSH EBX ; resource ID for the status string 01D8 FF 35 F6 00 00 00 PUSH hInst ; our module's instance handle 01DE E8 FC FF FF FF CALL LoadStringA ; load the status string into its buffer 01E3 L4: 01E3 2B C0 SUB EAX,EAX ; load zero 01E5 40 INC EAX ; load one 01E6 3B 05 CA 01 00 00 CMP EAX,bSimpleMode ; is the status bar already simple? 01EC 74 05 E8 11 00 00 IF NE CALL SIMPLE_STATUS_BAR ; if not then make it simple 01F3 68 CE 01 00 00 PUSHD szStatusBar ; buffer containing the text we are setting 01F8 68 FF 01 00 00 PUSHD 01FF ; 01 SBT_NOBORDERS by 0FF simple window 01FD 68 01 04 00 00 PUSHD 0401 ; SB_SET_TEXT: set the text of the status bar 0202 EB 0D JMP >L5 ; join common code to send msg to status bar 0204 0204 SIMPLE_STATUS_BAR: 0204 A3 CA 01 00 00 MOV bSimpleMode D,EAX ; store our simple-status mode flag 0209 6A 00 PUSHD 0 ; lParam for SB_SIMPLE message is always null 020B 50 PUSH EAX ; push the fSimple flag we are transmitting 020C 68 09 04 00 00 PUSHD 0409 ; SB_SIMPLE: specify whether status bar is simple 0211 L5: 0211 FF 35 C6 01 00 00 PUSHD hStatusBar ; push the handle for the status bar 0217 E8 FC FF FF FF CALL SendMessageA ; send the message to the status bar 021C C3 RET 021D 021D 021D 021D ;--------------- 021D ; OPENSAVE Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 021D ;--------------- 021D 021D ; This module contains standard callback handlers for the File menu items 021D ; Open, New, Save and Save As. 021D 020E DATA SEGMENT FLAT 020E 020E Q1: 020E 53 61 76 65 20 63 DB "Save changes to the following file?",0A,0A,0 0234 0234 EVEN 4 0238 qSaveChangesP DD ? ; pointer to constructed "Save Changes?" string 023C osave_Title DD ? ; pointer to buffer containing the Window title 0240 fFileStatus DD ? ; 1=Named 2=Changed 0240 0240 ofn: ; structure for the open-file dialogs 0240 4C 00 00 00 ofn_lStructSize DD >S1 ; size of this structure 0244 hMainWnd DD ; our main window aliased here 0244 00 00 00 00 ofn_hwndOwner DD 0 ; window that owns the open-file dialog 0248 00 00 00 00 ofn_hInstance DD 0 ; 024C 97 00 00 00 ofn_lpstrFilter DD osave_Filter ; ptr to the filter-string pairs 0250 00 00 00 00 ofn_lpstrCustomFilter DD 0 ; ptr to user-defined filter buffer 0254 00 00 00 00 ofn_nMaxCustFilter DD 0 ; size of user-defined filter buffer 0258 01 00 00 00 ofn_nFilterIndex DD 1 ; index of currently-selected filter 025C 00 00 00 00 ofn_lpstrFile DD 0 ; ptr to returned full file path 0260 00 00 00 00 ofn_nMaxFile DD 0 ; max # of chars in returned path 0264 00 00 00 00 ofn_lpstrFileTitle DD 0 ; ptr to returned name-and-extension 0268 00 00 00 00 ofn_nMaxFileTitle DD 0 ; max # of chars in name-and-extension 026C 00 00 00 00 ofn_lpstrInitialDir DD 0 ; ptr to initial directory for dialog 0270 00 00 00 00 ofn_lpstrTitle DD 0 ; ptr to title bar string for dialog =id 0034 EDI_ofn_Flags EQU D[EDI+($-ofn)] 0274 00 00 00 00 ofn_Flags DD 0 ; control flags for open-call 0278 ofn_ExtOff DD 0278 00 00 ofn_nFileOffset DW 0 ; offset in lpstrFile of file name 027A 00 00 ofn_nFileExtension DW 0 ; offset in lpstrFile of extension 027C C5 00 00 00 ofn_lpstrDefExt DD osave_DefExt ; ptr to default extension 0280 00 00 00 00 ofn_lCustData DD 0 ; application-defined, used by Hook 0284 00 00 00 00 ofn_lpfnHook DD 0 ; ptr to Hook procedure 0288 00 00 00 00 ofn_lpTemplateName DD 0 ; ptr to name of template resource = : 004C S1 EQU $-ofn 028C 028C 021D _TEXT SEGMENT FLAT 021D 021D menuNew: ; "New" menu item was selected 021D E8 81 00 00 00 CALL SAVE_CHANGES ; if file changed prompt user to save changes 0222 74 F8 JZ RET ; return if the user elected to cancel 0224 #if MENU_NEW_PREP 0224 CALL MENU_NEW_PREP ; optional code for main program to provide here 0224 #endif 0224 56 57 PUSH ESI,EDI ; save registers across call 0226 BE 8E 00 00 00 MOV ESI,osave_Newfn ; point to our default name for new files 022B E8 08 01 00 00 CALL GET_OFN_FILE ; fetch the ofn file-name buffer 0230 97 XCHG EDI,EAX ; swap the buffer pointer into EDI, for output 0231 E8 75 03 00 00 CALL COPYZ ; copy the defult name to the buffer 0236 C7 05 78 02 00 00 MOV ofn_ExtOff,osave_n ; plug file and extension offsets into ofn struc 0240 83 25 3C 02 00 00 AND fFileStatus,NOT 3 ; turn off NAMEDbit 1 and CHANGEDbit 2 0247 5F 5E POP EDI,ESI ; restore clobbered registers 0249 SET_WINDOW_TITLE: ; set the main parent's Window name 0249 FF 35 38 02 00 00 PUSHD osave_Title ; push param for SetWindowTextA later on 024F 56 PUSH ESI ; push hWnd=ESI for SetWindowTextA later on 0250 57 PUSH EDI ; save EDI across call 0251 E8 E2 00 00 00 CALL GET_OFN_FILE ; fetch the ofn file-name buffer 0256 96 XCHG ESI,EAX ; swap the buffer pointer into ESI, for copying 0257 2B C0 SUB EAX,EAX ; load zero into EAX 0259 66 A1 78 02 00 00 MOV AX,ofn_nFileOffset ; load offset in path of the main file name 025F 03 F0 ADD ESI,EAX ; advance ESI to the main file name 0261 2B C9 SUB ECX,ECX ; load zero into ECX 0263 66 8B 0D 7A 02 00 MOV CX,ofn_nFileExtension ; load offset in path of the file extension 026A 29 C1 SUB ECX,EAX ; calculate the length of the file root 026C 8B 3D 38 02 00 00 MOV EDI,osave_Title ; point EDI to our title buffer, for output 0272 F3 A4 REP MOVSB ; output the current file name to the title 0274 80 7F FF 2E CMP B[EDI-1],'.' ; was the last character a period? 0278 75 01 4F IF E DEC EDI ; retract the last char if it was a period 027B F7 05 3C 02 00 00 TEST fFileStatus,2 ; is the CHANGEDbit set? 0285 B0 2A MOV AL,'*' ; load star 0287 74 01 AA IF NZ STOSB ; output star if the CHANGEDbit was set 028A B8 20 2D 20 20 MOV EAX," - " ; load intervening dash 028F AB STOSD ; output intervening dash 0290 4F DEC EDI ; retract the extra blank 0291 BE 00 00 00 00 MOV ESI,szClassName ; point to our program's name 0296 B1 08 MOV CL,nClassName ; load the length of our program's name 0298 F3 A4 REP MOVSB ; output our program's name 029A 5F POP EDI ; restore clobbered register 029B E8 FC FF FF FF CALL SetWindowTextA ; reset the title of our main window 02A0 L9: 02A0 0C 01 OR AL,1 ; set NZ for return from menuSave below 02A2 C3 RET 02A3 02A3 02A3 SAVE_CHANGES: 02A3 F7 05 3C 02 00 00 TEST fFileStatus,2 ; is the CHANGEDbit set? 02AD 74 F1 JZ L9 ; return success if there was nothing changed 02AF 6A 33 PUSHD 033 ; MB_YESNOCANCEL + MB_ICONWARNING 02B1 68 00 00 00 00 PUSHD szClassName ; title of box will just be our program name 02B6 FF 35 34 02 00 00 PUSH qSaveChangesP ; point to "Save changes?" message 02BC FF 35 44 02 00 00 PUSH hMainWnd ; our program owns this box 02C2 E8 FC FF FF FF CALL MessageBoxA ; display the "Save Changes?" warning box 02C7 83 F8 06 CMP EAX,6 ; IDYES: did the user click on the YES button? 02CA 74 0B JE menuSave ; save the file if he did 02CC 83 F8 02 CMP EAX,2 ; IDCANCEL: did the user click on the CANCEL button? 02CF C3 RET ; return Z if yes, NZ if no 02D0 02D0 02D0 menuSaveAs: ; the "Save As" menu item has been selected 02D0 83 25 3C 02 00 00 AND fFileStatus,NOT 1 ; turn off NAMEDbit 02D7 menuSave: ; the "Save" menu item has been selected 02D7 BD 29 00 00 00 MOV EBP,SAVE_OFN_FILE ; load the app-specific save-file routine 02DC B8 06 00 00 00 MOV EAX,6 ; OFN_HIDEREADONLY + OFN_OVERWRITEPROMPT 02E1 B9 00 00 00 00 MOV ECX,GetSaveFileNameA ; load pointer to Save-file dialog routine 02E6 F7 05 3C 02 00 00 TEST fFileStatus,1 ; does the file need a name? 02F0 75 14 JNZ >L9 ; skip if it does not need a name 02F2 L8: 02F2 BF 40 02 00 00 MOV EDI,ofn ; point EDI to our open-file-name structure 02F7 57 PUSH EDI ; push the open-file-name structure 02F8 89 47 34 MOV EDI_ofn_Flags,EAX ; store the flag settings in the structure 02FB E8 38 00 00 00 CALL GET_OFN_FILE ; ensure that the buffer pointers are set 0300 FF D1 CALL ECX ; put up the dialog box getting the file name 0302 85 C0 TEST EAX ; did the dialog succeed? 0304 74 C9 JZ RET ; return Z if the dialog did not succeed 0306 L9: 0306 83 0D 3C 02 00 00 OR fFileStatus,1 ; turn on NAMEDbit 030D 83 25 3C 02 00 00 AND fFileStatus,NOT 2 ; turn off CHANGEDbit 0314 8B 15 5C 02 00 00 MOV EDX,ofn_lpstrFile ; point EDX to the full file path 031A FF D5 CALL EBP ; call the main program's routine for the EBX file 031C E9 28 FF FF FF JMP SET_WINDOW_TITLE ; reset our window title and return NZ 0321 0321 0321 menuOpen: ; the "Open" menu item has been selected 0321 E8 7D FF FF FF CALL SAVE_CHANGES ; if old file changed prompt user to save changes 0326 BD 21 00 00 00 MOV EBP,OPEN_OFN_FILE ; load the app-specific open-file routine 032B B8 00 18 00 00 MOV EAX,01800 ; OFN_PATHMUSTEXIST + OFN_FILEMUSTEXIST 0330 B9 00 00 00 00 MOV ECX,GetOpenFileNameA ; load pointer to Open-file dialog routine 0335 75 BB JNZ L8 ; jump to common code if no cancel took place 0337 C3 RET ; return if user elected to cancel this open 0338 0338 0338 ; GET_OFN_FILE sets EAX to the file buffer pointer ofn_lpstrFile. If the 0338 ; buffer has not yet been allocated, we do so from scratchpad memory. We 0338 ; also copy the "Save Changes" message to memory just before the main file 0338 ; name, so that the save-changes dialog can display the file name in a single 0338 ; message string. 0338 0338 GET_OFN_FILE: 0338 57 PUSH EDI ; save caller's EDI 0339 BF 5C 02 00 00 MOV EDI,@ ofn_lpstrFile ; fetch the buffer pointer 033E 8B 07 MOV EAX,[EDI] ; fetch the ofn_lpstrFile pointer 0340 85 C0 TEST EAX ; has the pointer already been set? 0342 75 3E JNZ >L7 ; skip if it has 0344 57 PUSH EDI ; not set: save the structure output pointer 0345 E8 7F 02 00 00 CALL POINT_SCRATCH ; set EDI to scratchpad memory 034A 89 3D 34 02 00 00 MOV qSaveChangesP,EDI ; save the pointer to our copied "Save Changes?" 0350 56 PUSH ESI ; save caller's ESI 0351 BE 0E 02 00 00 MOV ESI,Q1 ; point to "Save Changes?" message 0356 E8 50 02 00 00 CALL COPYZ ; copy the message, to appear before the file name 035B 5E POP ESI ; restore clobbered register 035C 4F DEC EDI ; retract the trailing null 035D 97 XCHG EAX,EDI ; swap scratchpad pointer to EAX 035E 5F POP EDI ; restore the structure pointer 035F 50 53 PUSH EAX,EBX ; save the scratchpad pointer and caller's EBX 0361 BB 04 01 00 00 MOV EBX,0104 ; load MAX_PATH buffer size 0366 AB STOSD ; output the first buffer pointer 0367 93 XCHG EAX,EBX ; swap in the buffer size 0368 AB STOSD ; output the first buffer size 0369 93 XCHG EAX,EBX ; swap back in the buffer pointer 036A 01 D8 ADD EAX,EBX ; advance the scratchpad pointer to second buffer 036C AB STOSD ; output the second buffer pointer 036D 93 XCHG EAX,EBX ; swap in the buffer size 036E AB STOSD ; output the second buffer size 036F 93 XCHG EAX,EBX ; swap back in the buffer pointer 0370 01 D8 ADD EAX,EBX ; advance the scratchpad pointer to third buffer 0372 A3 38 02 00 00 MOV osave_Title,EAX ; store the third buffer pointer 0377 B3 14 MOV BL,014 ; add a little to MAX_PATH 0379 01 D8 ADD EAX,EBX ; advance the buffer pointer by the bigger amount 037B A3 90 02 00 00 MOV SCRATCH_P,EAX ; retain the allocated buffers 0380 5B 58 POP EBX,EAX ; restore caller's EBX and first buffer pointer 0382 L7: 0382 5F POP EDI ; restore caller's EDI 0383 C3 RET 0384 0384 0384 ;--------------- 0384 ; CALLBACK Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0384 ;--------------- 0384 0384 ; MainCallback is the callback function for our program. We are called by 0384 ; the Windows DispatchMessage routine, if it sees fit. If the message 0384 ; number passed to us is not in our CallbackActs table, we return yet again, 0384 ; for default handling of the message. If it is in the table, we call 0384 ; the associated action routine, with various registers set to the 0384 ; parameters passed us by Windows: 0384 0384 ; EDX = lParam 0384 ; EBX = wParam 0384 ; CX = high wParam (a second copy for convenient access) 0384 ; EAX = message number 0384 ; ESI = hWnd 0384 0384 ; The above register settings are made, and the caller's savable registers 0384 ; are pushed onto the stack, via a complicated series of exchanges between 0384 ; registers and stack elements. 0384 028C DATA SEGMENT FLAT 028C 00 00 00 00 hWnd DD 0 ; handle to current window 0290 #if N_CHILDREN 0290 ChildID DD ? ; the child index for the current callback 0290 CHILD_EAX DD ? ; the message number for the child callback 0290 #if N_CHILDREN=1 0290 OldCb DD ? ; the old child callback handler 0290 hChild DD 0 ; the handle of our one child window 0290 #else 0290 nChild DD 0 ; number of the next child window to be opened 0290 OldCb EQU D[$-4] ; array of old child callback handlers 0290 DD N_CHILDREN DUP ? 0290 hChild EQU D[$-4] ; array of handles for child windows 0290 DD N_CHILDREN DUP 0 0290 CHILD_TYPE EQU B[$-1] 0290 DB N_CHILDREN DUP 0 ; number of child windows within our main window 0290 #endif 0290 #endif 0290 0290 #if ref qEDIT 0290 qEDIT: DB 'EDIT',0 ; edit control class name 0290 #endif 0290 0384 _TEXT SEGMENT FLAT 0384 0384 #if ref MainCbActs 0384 MainCallback: 0384 B8 08 00 00 00 MOV EAX,MainCbActs ; point to table of callback action routines 0389 B9 00 00 00 00 MOV ECX,DefWindowProcA ; if we don't handle it, Windows will 038E #endif 038E CallbackCode: ; common callback function code 038E 5A POP EDX ; pop return address into EDX 038F 95 XCHG EBP,EAX ; EAX = caller's EBP EBP = action table 0390 87 F5 XCHG EBP,ESI ; ESI = action table EBP = caller's ESI 0392 87 CD XCHG EBP,ECX ; ECX = caller's ESI EBP = default proc 0394 87 54 24 0C XCHG EDX,[ESP+12] ; push return address and fetch lParam into EDX 0398 87 5C 24 08 XCHG EBX,[ESP+8] ; push caller's EBX and fetch wParam into EBX 039C 87 44 24 04 XCHG EAX,[ESP+4] ; push caller's EBP and fetch message number into EAX 03A0 87 0C 24 XCHG ECX,[ESP] ; push caller's ESI, and fetch Window handle into ECX 03A3 57 55 PUSH EDI,EBP ; push caller's EDI and the default proc 03A5 JumpCallback: 03A5 xxxcb: 03A5 E8 2E 00 00 00 CALL GET_CALLBACK_ACT ; set EBP to the callback action for this routine 03AA 72 0D JC >L3 ; jump if the action routine was not found 03AC L2: 03AC xxxc: 03AC FF D5 CALL EBP ; call the routine in the CallbackActs table 03AE 2B C0 SUB EAX,EAX ; load zero into EAX 03B0 3C DB 03C ; skip over the following POP EDI instruction 03B1 CALLBACK_RET: ; alternate nonzero return for callback functions 03B1 5F POP EDI ; discard the return address of our callback procedure 03B2 5F POP EDI ; discard the default procedure 03B3 L4: ; common exit 03B3 5F 5E 5D 5B POP EDI,ESI,EBP,EBX ; restore clobbered registers 03B7 C3 RET ; return from callback (params already popped) 03B8 03B8 DEFAULT_CB: ; jump point to revert to default callback handler 03B8 5F POP EDI ; discard the return address of our callback procedure 03B9 L3: ; the action routine was not found in the chart 03B9 59 POP ECX ; pop the default handler action (Windows or nothing) 03BA 52 53 50 56 PUSH EDX,EBX,EAX,ESI ; repush the parameters 03BE FF D1 CALL ECX ; invoke default handling of this message number 03C0 EB F1 JMP L4 ; pop registers and return from callback 03C2 03C2 CALLBACK_RET_1: ; jump here from callback to return 1 not zero 03C2 2B C0 SUB EAX,EAX ; load zero 03C4 40 INC EAX ; increment to one 03C5 EB EA JMP CALLBACK_RET ; discard return address and return from callback 03C7 03C7 03C7 #if N_CHILDREN 03C7 ChildCallback: ; Windows subclassing handler for child windows 03C7 PUSHD -12 ; push GWL_ID code 03C7 PUSH D[ESP+8] ; push child's handle 03C7 CALL GetWindowLongA ; fetch the ChildID value of this child 03C7 MOV ChildID,EAX ; store the ChildID 03C7 POP EDX ; save our return address 03C7 POP ECX,EAX ; fetch the window handle and message number 03C7 POP EBX ; fetch wParam 03C7 XCHG EDX,[ESP] ; fetch lParam and push return address onto stack 03C7 PUSH EBP,ESI,EDI ; save registers across call 03C7 PUSH EAX ; push unused value for the default procedure 03C7 MOV CHILD_EAX,EAX ; store the hWnd/message type for OLD_CHILD_ACT 03C7 MOV ESI,ChildCbActs ; point to table of callback action routines 03C7 CALL GET_CALLBACK_ACT ; set EBP to the callback action for this routine 03C7 MOV EDI,ChildID ; set EDI to the ID number for the called child 03C7 JNC L2 ; jump if there was a callback action 03C7 DB 03C ; skip over following POP ECX instruction 03C7 OLD_CHILD_ACT: ; jump here from our callbacks, to revert to old 03C7 POP ECX ; discard our callback's return address 03C7 PUSH EDX,EBX ; repush lParam and wParam 03C7 PUSH CHILD_EAX,ESI ; repush message number and window handle 03C7 #if N_CHILDREN EQ 1 03C7 PUSH OldCb 03C7 #else 03C7 PUSH OldCb[EDI*4] ; push the pointer to the old routine 03C7 #endif 03C7 CALL CallWindowProcA ; call the old routine 03C7 JMP L4 ; restore clobbered registers and return 03C7 03C7 03C7 ; CREATE_CHILD creates a child window of class EDI, style EDX, height and 03C7 ; width ECX, and x and y coordinates EBX, with the EBP-pointed initial 03C7 ; content string. We increment the nChild counter and assign the child 03C7 ; ID index to the incremented value, returning that value in EBP. We 03C7 ; also set up the interception of callbacks to the child window, with 03C7 ; the new handler being our ChildCallback, and the old handler stored 03C7 ; in the OldCb array. We return with EAX set to the child window's 03C7 ; handle, with this value also stored in the hChild handles array. 03C7 03C7 CREATE_CHILD: 03C7 #if N_CHILDREN GT 1 03C7 PUSHD 0 ; there is no extra window creation data 03C7 PUSH hInst D ; handle to our application instance 03C7 PUSH EBP ; save content pointer 03C7 INC nChild D ; increment to the next free child ID 03C7 MOV EBP,nChild D ; fetch our ID for the child window 03C7 MOV CHILD_TYPE[EBP],AL ; store the type of this child window 03C7 XCHG EBP,[ESP] ; restore content pointer and push index onto stack 03C7 SUB EAX,EAX ; load zero, for pushing 03C7 #else 03C7 SUB EAX,EAX ; load zero, for pushing 03C7 PUSH EAX ; there is no extra window creation data 03C7 PUSH hInst D ; handle to our application instance 03C7 PUSHD 1 ; push our ID for the child window 03C7 #endif 03C7 PUSH ESI ; handle of parent window 03C7 PUSH ECX ; push the height and width words of the child 03C7 SUB ECX,ECX ; zero out the high word of ECX 03C7 POP AX,CX ; pop the width and height back into AX and CX 03C7 PUSH ECX,EAX ; doublewords: height and width of the child window 03C7 PUSH EBX ; push the y and x coordinates of the child window 03C7 POP AX,CX ; pop y and x back into AX and CX 03C7 PUSH ECX,EAX ; repush the doubleword versions of y and x 03C7 PUSH EDX ; style 03C7 PUSH EBP ; push pointer to initial content string 03C7 PUSH EDI ; the registered class of this window 03C7 PUSH 0 ; there are no extended style attributes 03C7 CALL CreateWindowExA ; create the child window 03C7 #if N_CHILDREN=1 03C7 MOV hChild,EAX ; store the child handle 03C7 #else 03C7 MOV hChild[EBP*4],EAX ; store the child handle in its array element 03C7 #endif 03C7 PUSH EAX ; save the child handle for return 03C7 PUSHD ChildCallback ; push our callback routine for SetWindowLongA later 03C7 PUSHD -4 ; GWL_WNDPROC for SetWindowLongA later 03C7 PUSH EAX ; child window handle for SetWindowLongA later 03C7 PUSHD -4 ; GWL_WNDPROC index 03C7 PUSH EAX ; child window handle 03C7 CALL GetWindowLongA ; fetch the existing callback procedure for the child 03C7 #if N_CHILDREN=1 03C7 MOV OldCb,EAX ; store the old callback procedure 03C7 #else 03C7 MOV OldCb[EBP*4],EAX ; store the old callback in its array element 03C7 #endif 03C7 CALL SetWindowLongA ; set the child's callback to our ChildCallback proc 03C7 POP EAX ; restore the child window's handle 03C7 RET 03C7 #endif 03C7 03C7 #if ref menuAbout 03C7 AboutProc: ; our callback handler for the ABOUT dialog messages 03C7 B8 F6 03 00 00 MOV EAX,AboutCbActs ; point to our About-box handlers 03CC B9 D3 03 00 00 MOV ECX,NoDefWindowProc ; we will not call the default Windows handler 03D1 EB BB JMP CallbackCode ; join common callback routing code 03D3 03D3 NoDefWindowProc: ; do not call the default Windows handler 03D3 2B C0 SUB EAX,EAX ; return zero 03D5 C2 10 00 RET 16 ; discard parms and return 03D8 #endif 03D8 03D8 03D8 GET_CALLBACK_ACT: 03D8 L1: ; loop here to search our action table 03D8 8B 2E MOV EBP,[ESI] ; fetch an action routine from the table 03DA 85 ED TEST EBP ; was this the terminating null for the table? 03DC F9 STC ; set Carry, in case terminating null was seen 03DD 74 09 JZ >L2 ; return Carry if it was the terminating null 03DF 66 3B 46 04 CMP AX,[ESI+4] ; does this table entry match our message number? 03E3 8D 76 06 LEA ESI,[ESI+6] ; advance pointer to the next table record 03E6 75 F0 JNE L1 ; loop if no match, to scan the next record 03E8 L2: 03E8 8B F1 MOV ESI,ECX ; copy window handle to ESI 03EA 89 35 8C 02 00 00 MOV hWnd,ESI ; also store handle in hwnd, for convenience 03F0 53 PUSH EBX ; push wParam onto the stack 03F1 66 5B 66 59 POP BX,CX ; pop low(wParam) in place; copy high(wParam) to CX 03F5 C3 RET 03F6 03F6 03F6 #if ref menuAbout 03F6 AboutCbActs: 03F6 cb AboutInitDialog, 272 ; wmInitDialog: dialog box is generated 03F6 06 04 00 00 m DD cbAboutInitDialog 03FA 10 01 m DW 272 03FC cb AboutCommand, 273 ; wmCommand: user has clicked a button 03FC 12 04 00 00 m DD cbAboutCommand 0400 11 01 m DW 273 0402 00 00 00 00 DD 0 0406 0406 cbAboutInitDialog: 0406 #if CENTERING 0406 A1 44 02 00 00 MOV EAX,hMainWnd D ; parent window is our main window 040B E8 35 00 00 00 CALL CTR_ESI_IN_EAX ; center the About box within our main window 0410 #endif 0410 L1: ; common exit 0410 EB B0 JMP CALLBACK_RET_1 ; return from callback with a value of 1 0412 0412 0412 cbAboutCommand: 0412 83 FB 01 CMP EBX,1 ; does wParam equal IDOK? 0415 74 03 83 FB 02 IF NE CMP EBX,2 ; if not does it equal IDCANCEL? 041A 75 D9 JNE RET ; return if not IDOK or IDCANCEL 041C 6A 01 PUSHD 1 ; the return value of the dialog box is 1 041E 56 PUSH ESI ; handle of the dialog box 041F E8 FC FF FF FF CALL EndDialog ; destroy the modal dialog box 0424 EB EA JMP L1 ; return from callback with a value of 1 0426 0426 0426 menuAbout: ; "About" menu item was selected 0426 6A 00 PUSHD 0 ; no lParam needs to be passed to InitDialog 0428 68 C7 03 00 00 PUSH AboutProc ; our dialog box callback procedure 042D FF 35 44 02 00 00 PUSH hMainWnd D ; the window that owns the dialog box 0433 6A 0D PUSHD IDD_ABOUT ; resource number of the dialog box template 0435 FF 35 F6 00 00 00 PUSH hInst ; instance of our module owning the dialog box 043B E8 FC FF FF FF CALL DialogBoxParamA ; create a modal dialog box from the template resource 0440 C3 RET 0441 #endif 0441 ;--------------- 0441 ; WCENTER Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0441 ;--------------- 0441 0441 ; CTR_ESI_IN_EAX repositions the child window whose handle is ESI, to be 0441 ; centered within the parent window whose handle is EAX. We allocate 0441 ; coordinate buffers on the stack, and then pop the coordinate values 0441 ; from the stack as we use them. 0441 0441 _TEXT SEGMENT FLAT 0441 0441 L0: ; failure exit 0441 83 C4 20 ADD ESP,32 ; release the stack allocation 0444 C3 RET 0445 0445 CTR_ESI_IN_EAX: 0445 55 56 57 PUSH EBP,ESI,EDI ; preserve registers across call 0448 83 EC 10 SUB ESP,16 ; make room for the parent window's coordinates 044B 54 PUSH ESP ; pass the stack point as the coordinate buffer 044C 50 PUSH EAX ; push the Parent window Handle 044D E8 FC FF FF FF CALL GetWindowRect ; place the parent coordiantes onto the stack 0452 83 EC 10 SUB ESP,16 ; make room for the child window's coordinates 0455 85 C0 TEST EAX ; did the first call fail? 0457 74 E8 JZ L0 ; return, doing nothing, if it did 0459 54 PUSH ESP ; make room for the child window's coordinates 045A 56 PUSH ESI ; push the Child window handle 045B E8 FC FF FF FF CALL GetWindowRect ; place the child coordinates onto the stack 0460 85 C0 TEST EAX ; did the second call fail? 0462 74 DD JZ L0 ; return, doing nothing, if it did 0464 58 5B POP EAX,EBX ; EAX=child left EBX=child top 0466 5F 5E POP EDI,ESI ; EDI=child right ESI=child bottom 0468 29 C7 SUB EDI,EAX ; EDI=child width 046A 29 DE SUB ESI,EBX ; ESI=child height 046C 6A 10 PUSHD 16 ; SM_CXFULLSCREEN 046E E8 FC FF FF FF CALL GetSystemMetrics ; fetch the full screen width 0473 95 XCHG EBP,EAX ; EBP=full screen width 0474 2B EF SUB EBP,EDI ; EBP=maximum left coordinate 0476 6A 11 PUSHD 17 ; SM_CYFULLSCREEN 0478 E8 FC FF FF FF CALL GetSystemMetrics ; EAX=full screen height 047D 2B C6 SUB EAX,ESI ; EAX=maximum top coordinate 047F 5A 59 5B POP EDX,ECX,EBX ; EDX=parent left ECX=parent top EBX=parent right 0482 29 D3 SUB EBX,EDX ; EBX=parent width 0484 2B DF SUB EBX,EDI ; EBX=difference of widths 0486 D1 FB SAR EBX,1 ; EBX=half the difference 0488 01 DA ADD EDX,EBX ; EDX=new left coordinate 048A 79 02 2B D2 IF S SUB EDX,EDX ; zero the new left if it underflowed 048E 39 EA CMP EDX,EBP ; does the new left exceed the maximum left? 0490 76 02 8B D5 IF A MOV EDX,EBP ; if yes then reset the new left to the max left 0494 5F POP EDI ; EDI=parent bottom 0495 2B F9 SUB EDI,ECX ; EDI=parent height 0497 29 F7 SUB EDI,ESI ; EDI=difference of heights 0499 D1 FF SAR EDI,1 ; EDI=half the difference 049B 03 CF ADD ECX,EDI ; ECX=new top coordinate 049D 79 02 2B C9 IF S SUB ECX,ECX ; zero the new top if it underflowed 04A1 39 C1 CMP ECX,EAX ; does the new top exceed the maximum top? 04A3 76 02 8B C8 IF A MOV ECX,EAX ; if yes then set the new top to the maximum top 04A7 5F 5E 5D POP EDI,ESI,EBP ; restore clobbered registers 04AA 6A 05 PUSHD 5 ; SWP_NOSIZE+SWP_NOZORDER ; size and z-order preserved 04AC 6A 00 6A 00 PUSHD 0,0 ; height and width parms are ignored 04B0 51 52 PUSH ECX,EDX ; new top and left coordinates 04B2 6A 00 PUSHD 0 ; window placement order (z-order) parm is ignored 04B4 56 PUSH ESI ; child window's handle 04B5 E8 FC FF FF FF CALL SetWindowPos ; position the child window in the center of the parent 04BA C3 RET 04BB 04BB 04BB ;--------------- 04BB ; ONE_INST Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 04BB ;--------------- 04BB 04BB _TEXT SEGMENT FLAT 04BB 04BB ; ONE_INSTANCE? determines whether another instance of our program is already 04BB ; running. If it is, we restore the existing window to the foreground, 04BB ; and return Z, and EAX=0, to signal this instance to exit. If there is 04BB ; no other instance, we return NZ to signal this instance to continue. 04BB 04BB ONE_INSTANCE?: 04BB 68 00 00 00 00 PUSHD szClassName ; our program is the name of the semaphore object 04C0 6A 01 6A 00 PUSHD 1,0 ; maximum and initial counts 04C4 6A 00 PUSHD 0 ; returned handle cannot be inherited 04C6 E8 FC FF FF FF CALL CreateSemaphoreA ; create a semaphore handle for our program 04CB 96 XCHG ESI,EAX ; store the semaphore handle in ESI 04CC E8 FC FF FF FF CALL GetLastError ; fetch the error code from CreateSemaphoreA 04D1 3D B7 00 00 00 CMP EAX,0B7 ; is it ERROR_ALREADY_EXISTS? 04D6 75 31 JNE >L2 ; jump if not 04D8 56 PUSH ESI ; program already exists: push semaphore handle 04D9 E8 FC FF FF FF CALL CloseHandle ; close the semphore object handle 04DE L1: ; jump here for all errors except SUCCESS 04DE 6A 00 PUSHD 0 ; we will match with any window title 04E0 68 00 00 00 00 PUSHD szClassName ; look for existing window with our program name 04E5 E8 FC FF FF FF CALL FindWindowA ; fetch the handle for our prog's existing window 04EA 85 C0 TEST EAX ; did we find the handle? 04EC 74 CC JZ RET ; return failure if we did not 04EE 50 PUSH EAX ; push the handle for the existing window 04EF E8 FC FF FF FF CALL GetLastActivePopup ; get the active popup handle for existing window 04F4 97 XCHG EDI,EAX ; swap the existing active handle into EDI 04F5 57 PUSH EDI ; push the existing handle 04F6 E8 FC FF FF FF CALL IsIconic ; is the window minimized (iconic)? 04FB 85 C0 TEST EAX ; set NZ if the window is minimized 04FD 74 10 JZ >L3 ; jump if the window is minimized 04FF 6A 09 PUSHD 9 ; SW_RESTORE: activate the display the window 0501 57 PUSH EDI ; push the handle for the existing window 0502 E8 FC FF FF FF CALL ShowWindow ; restore the window to original size and position 0507 EB 0C JMP >L0 ; return Z to signal window already exists 0509 0509 L2: ; error code was not ERROR_ALREADY_EXISTS 0509 85 C0 TEST EAX ; is it ERROR_SUCCESS? 050B 75 D1 JNZ L1 ; jump if not 050D 40 INC EAX ; created semaphore will prevent other instances 050E C3 RET ; return NZ to continue with this instance 050F 050F L3: ; the existing window is not minimized 050F 57 PUSH EDI ; push the handle to the window 0510 E8 FC FF FF FF CALL SetForegroundWindow ; bring the window to the foreground 0515 L0: ; common exit for existing window 0515 2B C0 SUB EAX,EAX ; set Z to signal the window already exsists 0517 C3 RET ; EAX=0 will be our program's exit code 0518 0518 0518 0518 0518 ;--------------- 0518 ; REGCLASS Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0518 ;--------------- 0518 0518 DEFF IDM_MENU,0 ; we will push a null if there is no menu 0518 m #if !DEF IDM_MENU 0518 m IDM_MENU = 0 0518 m #endif 0518 DEFF hbrBackground,6 ; COLOR_WINDOW, standard background color 0518 m #if !DEF hbrBackground = : 0006 m hbrBackground = 6 0518 m #endif 0518 DEFF ClassStyle,3 ; CS_HREDRAW OR CS_VREDRAW 0518 m #if !DEF ClassStyle = : 0003 m ClassStyle = 3 0518 m #endif 0518 0518 _TEXT SEGMENT FLAT 0518 0518 ; REG_CLASS registers our program class. We create the WNDCLASSEX structure 0518 ; by pushing its elements onto the stack, in reverse order. Those 12 0518 ; pushes are marked with ";--" comments. 0518 0518 REG_CLASS: 0518 #if IDI_ICON 0518 6A 00 PUSHD 0 ; all LoadImageA flags are zero 051A 6A 10 6A 10 PUSHD 16,16 ; height and width of the icon 051E 6A 01 PUSHD 1 ; IMAGE_ICON: we are loading an icon 0520 6A 01 PUSHD IDI_ICON ; push the resource number for our program icon 0522 56 PUSH ESI ; push our module's instance handle 0523 E8 FC FF FF FF CALL LoadImageA ; load our program's icon and set EAX to its handle 0528 50 PUSH EAX ;--hIconSm = our program's icon handle 0529 #else 0529 PUSHD 0 ; if we have no icon then push null instead 0529 #endif 0529 68 00 00 00 00 PUSHD szClassName ;--lpszClassName = our specified program class name 052E 6A 04 PUSHD IDM_MENU ;--lpszMenuName = the resource number of our menu 0530 6A 06 PUSHD hbrBackground ;--hbrBackground = COLOR_WINDOW+1, the standard color 0532 68 00 7F 00 00 PUSHD 07F00 ; IDC_ARROW: the resource number for a standard arrow 0537 6A 00 PUSH 0 ; for a system resource use a null instance handle 0539 E8 FC FF FF FF CALL LoadCursorA ; load the standard arrow as our cursor 053E 50 PUSH EAX ;--hCursor = our loaded cursor's handle 053F #if IDI_ICON 053F 6A 01 PUSHD IDI_ICON ; push the resouce number for our program icon 0541 56 PUSH ESI ; push our module's handle 0542 E8 FC FF FF FF CALL LoadIconA ; load our icon resource 0547 50 PUSH EAX ;--hIcon = the handle for our loaded icon 0548 #else 0548 PUSHD 0 ; if we have no icon then push null instead 0548 #endif 0548 56 PUSH ESI ;--hInstance = our module's handle 0549 6A 00 PUSHD 0 ;--cbWndExtra = 0, no extra bytes to follow instance 054B 6A 00 PUSHD 0 ;--cbClsExtra = 0, no extra bytes to follow class 054D 68 84 03 00 00 PUSHD MainCallback ;--lpfnWndProc = our main callback procedure 0552 6A 03 PUSHD ClassStyle ;--style = CS_HREDRAW OR CS_VREDRAW 0554 6A 30 PUSHD 48 ;--cbSize = the size of this structure 0556 54 PUSH ESP ; push the pointer to our completed structure 0557 E8 FC FF FF FF CALL RegisterClassExA ; register our program's class 055C 83 C4 30 ADD ESP,48 ; release the WNDCLASSEX structure we had created 055F 85 C0 TEST EAX ; did we succeed in registering the program class? 0561 C3 RET 0562 0562 0562 0562 ;--------------- 0562 ; CREATE_W Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 0562 ;--------------- 0562 0562 #if ! ref menuOpen 0562 DATA SEGMENT FLAT 0562 hMainWnd DD ? ; handle for the main window 0562 #endif 0562 0562 DEFF mainX,0 ; default x-coord in case caller did not declare it 0562 m #if !DEF mainX = : 0000 m mainX = 0 0562 m #endif 0562 DEFF mainY,0 ; default y-coord in case caller did not declare it 0562 m #if !DEF mainY = : 0000 m mainY = 0 0562 m #endif 0562 0562 _TEXT SEGMENT FLAT 0562 0562 CREATE_WIN: 0562 6A 00 PUSHD 0 ; NULL lpParam: no window-creation data 0564 56 PUSH ESI ; handle of our application instance 0565 6A 00 6A 00 PUSHD 0,0 ; no menu/child, no parent 0569 68 68 01 00 00 PUSHD wHeight ; height of the main window 056E 68 90 01 00 00 PUSHD wWidth ; width of the main window 0573 6A 00 6A 00 PUSHD mainY,mainX ; y and x coordinates (we will center it later) 0577 68 00 00 CF 00 PUSHD 00CF_0000 ; style = WS_OVERLAPPEDWINDOW 057C #if ref MAIN_TITLE 057C PUSHD MAIN_TITLE ; push the main window title we constructed 057C #else 057C 68 00 00 00 00 PUSHD szProgName ; the window name will be the same as the program name 0581 #endif 0581 68 00 00 00 00 PUSHD szClassName ; pointer to registered class name 0586 6A 00 PUSHD 0 ; extended window style 0588 E8 FC FF FF FF CALL CreateWindowExA ; create our main program's window 058D 85 C0 TEST EAX ; did we succeed in creating the window? 058F 74 D0 JZ RET ; return failure if not 0591 A3 44 02 00 00 MOV hMainWnd D,EAX ; store our main window's handle 0596 BA 0A 00 00 00 MOV EDX,10 ; SW_SHOWDEFAULT: use the default show state 059B SHOW_EDX: 059B 50 PUSH EAX ; push the window handle for CALL UpdateWindow 059C 52 PUSH EDX ; push the new show state 059D 50 PUSH EAX ; push our window handle 059E E8 FC FF FF FF CALL ShowWindow ; set our window's show state 05A3 E8 FC FF FF FF CALL UpdateWindow ; update our client area by sending a WM_PAINT message 05A8 85 C0 TEST EAX ; did the update call succeed? 05AA C3 RET 05AB 05AB 05AB 05AB ;--------------- 05AB ; COPYZ Copyright 2001 Eric Isaacson, permissions in AWDOC.TXT 05AB ;--------------- 05AB 05AB ; COPYZ copies the null-terminated string (including the null) from ESI 05AB ; to EDI. 05AB 05AB COPYZ: ; loop here to copy each character of the path 05AB AC LODSB ; fetch the next character 05AC AA STOSB ; copy it to our stack-image 05AD 84 C0 TEST AL ; is it the null-terminator? 05AF 75 FA JNZ COPYZ ; loop if not, to copy the next character 05B1 C3 RET 05B2 05B2 05B2 05B2 ;--------------- 05B2 ; SCRATCH 05B2 ;--------------- 05B2 05B2 ; OUTPUT_SCRATCH points EDI to scratchpad memory, and then calls the code 05B2 ; following our CALL to output to that memory. We call our caller's code 05B2 ; with all registers except EDI set to the values passed to us. We 05B2 ; return to our grandcaller with EAX pointing to the bytes just output, 05B2 ; EDI set to the output count. and all other registers set to the values 05B2 ; returned from our caller's code. 05B2 05B2 ; This mechanism is intended to be used for relatively small memory allocations 05B2 ; whose total size fits within a reasonable limit known at assembly time. 05B2 ; For example, buffers containing path names of files are ideal for this 05B2 ; mechanism. On our first call we allocate a single memory buffer with the 05B2 ; total limit, and return consecutive chunks of memory, within the one 05B2 ; allocated buffer, on subsequent calls. 05B2 05B2 ; The caller can declare the total limit by defining SCRATCH_ALLOC; we provide 05B2 ; a default value as indicated below. 05B2 05B2 DEFF SCRATCH_ALLOC,32K ; default total size of all allocations we make 05B2 m #if !DEF SCRATCH_ALLOC = : 8000 m SCRATCH_ALLOC = 32K 05B2 m #endif 05B2 0290 DATA SEGMENT FLAT 0294 SCRATCH_P DD ? ; pointer to the allocation for the mext call 0294 05B2 _TEXT SEGMENT FLAT 05B2 05B2 OUTPUT_SCRATCH: 05B2 E8 12 00 00 00 CALL POINT_SCRATCH ; point EDI to our scratchpad buffer 05B7 57 PUSH EDI ; save the starting buffer pointer 05B8 FF 54 24 04 CALL D[ESP+4] ; call our caller's code 05BC 58 POP EAX ; pop the buffer pointer back into EAX 05BD 89 3D 90 02 00 00 MOV SCRATCH_P,EDI ; store the updated scratch pointer for the next call 05C3 29 C7 SUB EDI,EAX ; calculate the number of bytes output 05C5 83 C4 04 ADD ESP,4 ; discard the pointer to the caller's code 05C8 C3 RET ; return to our grandcaller 05C9 05C9 05C9 ; POINT_SCRATCH points EDI to free scratchpad memory, allocating the buffer 05C9 ; if that has not been done yet. The calling program can use POINT_SCRATCH 05C9 ; for temporary usage (memory used can be overwritten by the next scratch 05C9 ; buffer usage). 05C9 05C9 POINT_SCRATCH: 05C9 8B 3D 90 02 00 00 MOV EDI,SCRATCH_P ; fetch our scratchpad buffer pointer 05CF 85 FF TEST EDI ; have we allocated scratch memory yet? 05D1 75 F5 JNZ RET ; return if we have 05D3 50 PUSH EAX ; save the caller's EAX 05D4 B8 00 80 00 00 MOV EAX,SCRATCH_ALLOC ; load the total allocation 05D9 E8 08 00 00 00 CALL ALLOC_OBUFF ; allocate our one buffer for all calls to us 05DE 8F 05 90 02 00 00 POP SCRATCH_P,EAX ; restore EAX and pop the ptr pushed by ALLOC_OBUFF 05E5 C3 RET 05E6 ;--------------- 05E6 ; ALLOC_O 05E6 ;--------------- 05E6 05E6 ; ALLOC_OBUFF allocates EAX bytes of memory as an output buffer, and returns 05E6 ; with EDI pointing to the allocated bytes. We also return with EDI 05E6 ; pushed onto the caller's stack. The caller should CALL LocalFree when 05E6 ; the output process is complete. 05E6 05E6 ALLOC_OBUFF: 05E6 51 52 PUSH ECX,EDX ; save registers across call 05E8 50 PUSH EAX ; push the desired allocation 05E9 6A 00 PUSHD 0 ; no flags: we want a simple fixed memory buffer 05EB E8 FC FF FF FF CALL LocalAlloc ; allocate enough memory to read the file 05F0 5A 59 POP EDX,ECX ; restore clobbered registers 05F2 97 XCHG EDI,EAX ; swap the allocated pointer into EDI, for output 05F3 58 POP EAX ; pop our return address into EAX 05F4 57 50 PUSH EDI,EAX ; push the buffer pointer and return address 05F6 C3 RET Symbols: segment _TEXT builtin @ : 05E6 ALLOC_OBUFF : 03F6 AboutCbActs : 03C7 AboutProc : 000F BeginPaint : 03B1 CALLBACK_RET : 03C2 CALLBACK_RET_1 : 0000 CB_CREATE : 000F CB_PAINT : 0001 CENTERING : 05AB COPYZ : 0562 CREATE_WIN : 0445 CTR_ESI_IN_EAX : 038E CallbackCode : 001F CloseHandle : 001D CreateSemaphoreA : 0004 CreateStatusWindow : 0005 CreateToolbarEx : 0029 CreateWindowExA segment DATA : 03B8 DEFAULT_CB macro DEFF : 0017 DefWindowProcA : 000D DestroyWindow : 0019 DialogBoxParamA : 000B DispatchMessageA id 0034 EDI_ofn_Flags : 0005 EXIT_CAUTION : 0105 EXIT_PROCESS : 0018 EndDialog : 0010 EndPaint : 000C ExitProcess : 0020 FindWindowA : 03D8 GET_CALLBACK_ACT : 0338 GET_OFN_FILE : 0002 GetDesktopWindow : 0021 GetLastActivePopup : 001E GetLastError : 0008 GetMessageA : 0006 GetModuleHandleA : 0016 GetOpenFileNameA : 0015 GetSaveFileNameA : 001B GetSystemMetrics : 001A GetWindowRect : 00C9 HELLO : 0002 ID_STATUSBAR : 0003 ID_TOOLBAR : 000C IDA_ACCEL : 000D IDD_ABOUT : 0001 IDI_ICON : 000B IDM_About : 0009 IDM_Exit : 000F IDM_FILEMENU : 0010 IDM_HELPMENU : 000A IDM_HelpTopics : 0004 IDM_MENU : 0005 IDM_New : 0006 IDM_Open : 0007 IDM_Save : 0008 IDM_SaveAs : 000E IDS_SYSMENU : 0003 InitCommonControls : 0022 IsIconic : 03A5 JumpCallback : 0001 LONG_FNAMES : 0007 LoadAcceleratorsA : 0026 LoadCursorA : 0027 LoadIconA : 0025 LoadImageA : 0012 LoadStringA : 002B LocalAlloc condref MAIN_TITLE condref MENU_NEW_PREP macro MENUITEM : 00DA MSG md 00EE MSG_curpos md 00DA MSG_hwnd md 00E6 MSG_lParam md 00DE MSG_num md 00EA MSG_time md 00E2 MSG_wParam : 0384 MainCallback : 0008 MainCbActs : 003C Menu : 0014 MessageBoxA condref N_CHILDREN : 000D N_HELLO : 0001 NO_DOSCHECK : 03D3 NoDefWindowProc : 0001 ONE_INSTANCE : 04BB ONE_INSTANCE? : 0021 OPEN_OFN_FILE : 05B2 OUTPUT_SCRATCH : 05C9 POINT_SCRATCH condref PROG_INITS : 000E PostQuitMessage macro RCDEF : 0518 REG_CLASS : 0028 RegisterClassExA : 02A3 SAVE_CHANGES : 0029 SAVE_OFN_FILE md 0290 SCRATCH_P : 0249 SET_WINDOW_TITLE : 059B SHOW_EDX : 0204 SIMPLE_STATUS_BAR : 0011 SendMessageA : 0024 SetForegroundWindow : 001C SetWindowPos : 0013 SetWindowTextA : 0023 ShowWindow : 0001 TextOutA : 0009 TranslateAcceleratorA : 000A TranslateMessage : 002A UpdateWindow macro WIN32 md 01CA bSimpleMode macro cb : 0412 cbAboutCommand : 0406 cbAboutInitDialog : 010B cbClose : 018E cbCommand : 0030 cbCreate : 011E cbDestroy : 01A1 cbMenuSelect : 0160 cbNotify : 0126 cbPaint : 0143 cbSize md 023C fFileStatus md 01B2 hAccel md 00F6 hInst md 0244 hMainWnd md 01C6 hStatusBar md 00D6 hToolBar md 028C hWnd condref hello32 : 0426 menuAbout : 010B menuExit : 000A menuHelpTopics : 021D menuNew : 0321 menuOpen : 02D7 menuSave : 02D0 menuSaveAs : 0008 nClassName : 0007 nMenu : 0240 ofn md 0278 ofn_ExtOff md 0274 ofn_Flags md 0248 ofn_hInstance md 0244 ofn_hwndOwner md 0280 ofn_lCustData md 0240 ofn_lStructSize md 0288 ofn_lpTemplateName md 0284 ofn_lpfnHook md 0250 ofn_lpstrCustomFilter md 027C ofn_lpstrDefExt md 025C ofn_lpstrFile md 0264 ofn_lpstrFileTitle md 024C ofn_lpstrFilter md 026C ofn_lpstrInitialDir md 0270 ofn_lpstrTitle mw 027A ofn_nFileExtension mw 0278 ofn_nFileOffset md 0258 ofn_nFilterIndex md 0254 ofn_nMaxCustFilter md 0260 ofn_nMaxFile md 0268 ofn_nMaxFileTitle : 00C5 osave_DefExt : 0097 osave_Filter : 008E osave_Newfn md 0238 osave_Title : 0008_0000 osave_n : 00FA ps md 00FA ps_HDC md 010E ps_bottom md 00FE ps_fErase md 0102 ps_left md 0112 ps_resv md 010A ps_right md 0106 ps_top condref qEDIT md 0234 qSaveChangesP macro rc : 0000 szClassName : 0000 szProgName : 01CE szStatusBar : 01B6 szToolTip macro tbButton : 0058 tbButtons : 03AC xxxc : 03A5 xxxcb