;(c) copyright Barry Kauler 1997 ;Companion Disk, "Windows Assembly Language & Systems programming". ;W32DEMO.ASM --> W32DEMO.EXE Windows 95 demo program. ;This skeleton assembly language program has been written for TASM5.0. ;It has the startup code built-in, rather than as a separate object file. ;THIS PROGRAM IS WRITTEN TO WORK WITH A 16-BIT WINAPP, AND HAVE ;ACCESS TO ITS DATA SEGMENT. ;Barry Kauler's program GOOFEE.EXE (bundled with "Flow Design for ;Embedded Systems") is an example. It is a 16-bit Winapp, that copies ;almost the entire data segment (0--F000) to below 1M, then writes the data ;selector to the file GLOBALDS.DAT. When GOOFEE.EXE terminates, it ;zeroises the word value in GLOBALDS.DAT. ;Explanation how GOOFEE.EXE does this is given in the "Windows Assembly ;Language & Systems Programming" book. ;http://www.arrowweb.com/goofee/ ;***TO USE*** ;Start GOOFEE.EXE first, then W32DEMO.EXE, arrange so both windows show ;side-by-side. Draw a simple diagram, with node 1 having at least one ;wire attached. With W32DEMO window active, press any key. The demo then ;puts GOOFEE through these steps: ;1. Simulate a right mouse button on node 1 ;2. Move node 1 to the right (plus anything attached) ;3. Zoom out ;4. Pan right, by a small amount ;5. Save the file to disk --- 4-97, bypassed ;6. Modify color of node 1 ;Pressing any key again -- after focus returns to the window of this app-- ;will repeat the demo. ;step 6 above is a "stress test", as it puts this app in a tight loop ;writing a sequence of colors to node 1. To evaluate multitasking, you ;might like to work on the diagram in the GOOFEE window while W32DEMO ;is simultaneously changing node color. Try zooming in, to see node 1 ;enlarged .... .386 .MODEL FLAT,STDCALL UNICODE = 0 INCLUDE W32.INC IDM_QUIT EQU 100 ;menu-identifiers -- must be IDM_ABOUT EQU 101 ;same as defined in .RC file. .DATA ;-------------------------------------------------------------------------- hInst DD 0 mainhwnd DD 0 s1 WNDCLASS s2 MSG s3 PAINTSTRUCT szTitleName DB "Win32 Assembly Language Demo Program",0 szClassName DB "W32DEMO",0 sziconname DB "ICON_1",0 ;name of icon in .RC file. g_hwnd DWORD 0 g_message DWORD 0 g_wparam DWORD 0 g_lparam DWORD 0 szaboutstr DB "This is an about-box",0 ;messagebox sztitlestr DB "Barry Kauler 1997",0 ;/ hglobalds DD 0 ;handle for GLOBALDS.DAT szglobalds DB "GLOBALDS.DAT",0 globalds DW 0 ;contents read from GLOBALDS.DAT szerr DB "ERROR",0 szgerr1 DB "Could not open GLOBALDS.DAT. Terminating.",0 szgerr2 DB "No selector in GLOBALDS.DAT. Terminating.",0 bytesread DD 0 ;number of bytes read from file. szokay DB "OKAY",0 szinstruct DB "When this window has keyboard focus, press any key...",0 thisfocus DD 0 pnode1 DW 0 ;ptr to node 1. .CODE ;----------------------------------------------------------------------------- start: call GetModuleHandle, NULL mov hInst, eax ; initialize the WndClass structure mov s1.w_style, CS_HREDRAW + CS_VREDRAW + CS_DBLCLKS mov s1.w_lpfnWndProc, offset ASMWNDPROC mov s1.w_cbClsExtra, 0 mov s1.w_cbWndExtra, 0 mov eax, hInst mov s1.w_hInstance, eax ; call LoadIcon, NULL, IDI_APPLICATION ;loads default icon. ;let's load a custom icon.... call LoadIcon, hInst, OFFSET sziconname mov s1.w_hIcon, eax call LoadCursor,NULL, IDC_ARROW mov s1.w_hCursor, eax mov s1.w_hbrBackground, COLOR_WINDOW + 1 mov s1.w_lpszMenuName, OFFSET szClassName mov s1.w_lpszClassName, OFFSET szClassName call RegisterClass, OFFSET s1 call CreateWindowEx,0,OFFSET szClassName,OFFSET szTitleName, \ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT, \ CW_USEDEFAULT,CW_USEDEFAULT, 0, 0, hInst, 0 mov mainhwnd, eax call ShowWindow, mainhwnd,SW_SHOWNORMAL call UpdateWindow, mainhwnd ;4-97 ;To directly access data segment of another application, it has exported ;a selector in file GLOBALDS.DAT... ; call _lOpen, OFFSET szglobalds,OF_READ call CreateFile,OFFSET szglobalds,GENERIC_READ,0,0,OPEN_EXISTING,\ FILE_ATTRIBUTE_NORMAL,0 cmp eax,INVALID_HANDLE_VALUE je gerr1 mov hglobalds,eax ; call _lRead, hglobalds,OFFSET globalds,2 ;read 2 bytes, to globalds call ReadFile, hglobalds,OFFSET globalds,2,OFFSET bytesread,0 ; call _lClose, hglobalds call CloseHandle, hglobalds cmp globalds,0 ;does file have a valid selector? je gerr2 jmp SHORT msg_loop gerr1: call MessageBox,mainhwnd,OFFSET szgerr1,OFFSET szerr,MB_OK+MB_ICONEXCLAMATION jmp end_loop gerr2: call MessageBox,mainhwnd,OFFSET szgerr2,OFFSET szerr,MB_OK+MB_ICONEXCLAMATION jmp end_loop msg_loop: call GetMessage, OFFSET s2, 0,0,0 cmp ax, 0 je end_loop call TranslateMessage, OFFSET s2 call DispatchMessage, OFFSET s2 jmp msg_loop end_loop: call ExitProcess, s2.ms_wParam ;----------------------------------------------------------------------------- PUBLIC ASMWNDPROC ASMWNDPROC proc STDCALL, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam:DWORD USES ebx, edi, esi LOCAL hDC:DWORD mov eax,hwnd ;useful to make these static. mov g_hwnd,eax mov eax,wmsg mov g_message,eax mov eax,wparam mov g_wparam,eax mov eax,lparam mov g_lparam,eax xor eax,eax mov ax,WORD PTR g_message .IF ax==WM_DESTROY call wmdestroy .ELSEIF ax==WM_RBUTTONDOWN call wmrbuttondown .ELSEIF ax==WM_SIZE call wmsize .ELSEIF ax==WM_CREATE call wmcreate .ELSEIF ax==WM_LBUTTONDOWN call wmlbuttondown .ELSEIF ax==WM_PAINT call wmpaint .ELSEIF ax==WM_COMMAND call wmcommand .ELSEIF ax==WM_CHAR call wmchar .ELSE call DefWindowProc, hwnd,wmsg,wparam,lparam ret .ENDIF xor eax,eax ret ASMWNDPROC ENDP ;----------------------------------------------------------------------------- wmcommand PROC mov ax,WORD PTR g_lparam .IF ax==0 mov ax,WORD PTR g_wparam .IF ax==IDM_QUIT call PostQuitMessage,0 .ELSEIF ax==IDM_ABOUT call MessageBox, g_hwnd, OFFSET szaboutstr, OFFSET sztitlestr, MB_OK .ENDIF .ENDIF ret wmcommand ENDP ;------------------------------------------------------------------------ wmpaint PROC call BeginPaint, hwnd,OFFSET s3 mov hDC, eax ;One thing to be very careful about, is Windows can renter ASMWNDPROC ;-- for example, calling UPDATEWINDOW() will immediately execute ;ASMWNDPROC, with WM_PAINT -- even if execution is currently inside ;ASMWNDPROC! Therefore, make sure WM_PAINT is reentrant. ;another one to watch is WM_DESTROY, that is generated if user ;selects System-menu/Close. ;The situation in this program is that UPDATEWINDOW() is in WINMAIN() ;and ASMWNDPROC is called immediately, *not* when we get down to the ;message-loop -- i.e., the message queue is bypassed. ;Therefore, this procedure, wmpaint, gets entered before globalds ;has a valid selector, because of the order in which I wrote the ;code in WINMAIN() -- so, check for zero. ;... however, to generate another WM_PAINT message, to reenter here ;after globalds has a valid selector, you need to do something to the ;window, like resize it by dragging a side. ;globalds has a selector to data segment of GOOFEE.EXE. this is only valid ;if GOOFEE.EXE was started before this application, and is still running. .IF globalds==0 ;see above note. .ELSE mov es,globalds ;the info in the data segment of GOOFEE.EXE is published on-line, at: ;http://www.arrowweb.com/goofee/ ;offset 00+32 has the value 01... let's find out... mov si,32 xor eax,eax mov al,es:[si+00] ;NOTE: could use equate "xversion EQU 00" .IF al==01 call TextOut, hDC,20,20,OFFSET szokay,4 call TextOut, hDC,20,40,OFFSET szinstruct,53 .ENDIF ;right, we have access to GOOFEE's data segment (you should see "OKAY" ;on the screen). .ENDIF call EndPaint, hwnd,OFFSET s3 ret wmpaint ENDP ;........................................................................ wmchar PROC ;let's send a command to GOOFEE upon receipt of any keypress... ;as some steps below will require keyboard input to the GOOFEE program, ;first make GOOFEE the active window... mov es,globalds mov si,32 mov eax,es:[si+63] ;get hwindow1 call SetForegroundWindow, eax ;A value of 80h in the control1 field (at offset 48+32) ;means GOOFEE is ready for a command, that you write to bits 0--6 ;of control1... ;NOTE: one thing to be careful about, is API functions destroy ES. mov es,globalds mov si,32 ;offset of start of database. wmc3: mov al,es:[si+48] ;note you could use an equate "xcontrol1 EQU 48" cmp al,80h jne wmc3 ;ok, assuming that there **already exists** node 1 in the window ;of GOOFEE Diagrammer... ;let's simulate a right-mouse-button press... mov es:[si+49],WORD PTR 01 ;number of object (onumber field) mov es:[si+48],BYTE PTR 10100001b ;bit5=simulate right button ;bits0,1=01 node array. ;that's it! GOOFEE will clear bits0--6 when done, and will set ;bit7 when ready for another command (i.e., wait for 80h). ;further demo, move node 1 on the screen... wmc4: mov al,es:[si+48] ;note you could use an equate "xcontrol1 EQU 48" cmp al,80h jne wmc4 ;first, white-out its current position (which will automatically ;white-out all attached wires and sub-nodes)... mov es:[si+49],WORD PTR 01 ;number of object (onumber field) mov es:[si+48],BYTE PTR 10010001b ;bit4=1 white-out object. ;bits0,1=01 node array. ;now we will directly write to an object in the database... wmc5: mov al,es:[si+48] ;note you could use an equate "xcontrol1 EQU 48" cmp al,80h jne wmc5 mov ax,1 ;number of object(must exist in GOOFEE window). dec ax mov bx,es:[si+1] ;could use "xsizeobject EQU 1". mul bx add ax,es:[si+13] ;"xpnodeobjects EQU 13". add ax,1024 ;"xobjects EQU 1024". ;...now have absolute offset of object (offset from start of data ;segment) let's move the object right by 30 ... mov bx,ax mov pnode1,ax ;save it for later. mov ax,es:[bx+9] ;"xxtopleft EQU 9". add ax,30 mov es:[bx+9],ax mov ax,es:[bx+42] ;"xxmid EQU 42". add ax,30 mov es:[bx+42],ax ;the above has moved it in the database, now tell GOOFEE to redraw it... ;(GOOFEE will *automatically* redraw all attached wires & sub-nodes!) mov es:[si+49],WORD PTR 01 ;node number 1. mov es:[si+48],BYTE PTR 10000101b ;bit2=1 redraw, bits1,0=01 node. ;continuing the demo, let's control zoom factor... wmc6: mov al,es:[si+48] ;note you could use an equate "xcontrol1 EQU 48" cmp al,80h jne wmc6 mov al,es:[si+62] ;"xzfactor EQU 62" .IF al==0 mov es:[si+62],BYTE PTR 1 ;zoom out .ELSEIF al==1 mov es:[si+62],BYTE PTR 2 ;zoom out again .ELSEIF al==2 mov es:[si+62],BYTE PTR 0 ;back to normal size. .ELSE mov es:[si+62],BYTE PTR 0 .ENDIF ;to get this new zoom-factor to show, must redraw window... mov es:[si+52],BYTE PTR 10000000b ;bit7=1 redraw entire diagram. ;"xcontrol3 EQU 52" mov es:[si+48],BYTE PTR 11000000b ;bit6=1 extended control2/3. ;NOTE that I've set this last, as control1 is what GOOFEE ;monitors, waiting for any bits2--6 to be set. ;pan to the right, by a small amount... mov es:[si+51],BYTE PTR 01000000b ;bit6=1 pan right. ;could use "xcontrol2 EQU 51" mov es:[si+48],BYTE PTR 11000000b ;bit6=1 extended control2/3. jmp bypass1 ;leave it out for now. ;now, save the file... wmc8: mov al,es:[si+48] ;note you could use an equate "xcontrol1 EQU 48" cmp al,80h jne wmc8 mov bx,es:[si+67] ;could have "xpszfile EQU 67" ;pszfile is a pointer to szfile, a 255 character path/filename. ;note that whenever a data file is opened, szfile will have ;this info. mov es:[bx],BYTE PTR "D" ;very crude way to copy a string!!! mov es:[bx+1],BYTE PTR "." ;/ could put entire path and filename, mov es:[bx+2],BYTE PTR "G" ;/ up to 255 characters. mov es:[bx+3],BYTE PTR "O" ;/ this is just D.GOO mov es:[bx+4],BYTE PTR "O" ;/ mov es:[bx+5],BYTE PTR 0 ;/ null termination. mov es:[si+52],BYTE PTR 01000000b ;bit6=1 save file to disk. ;could use "control3 EQU 52" mov es:[si+48],BYTE PTR 11000000b ;bit6=1 extended control2/3. bypass1: ;ok, change fill-color of node 1 .... mov bx,pnode1 ;address of node 1, calc above or es:[bx+5],BYTE PTR 0100b ;bit2=1 turn on color. mov ecx,0FFFFFFFFh L1: wmc9: mov al,es:[si+48] ;control1 cmp al,80h jne wmc9 mov es:[bx+32],ecx ;write to .color field of node 1. mov es:[si+49],WORD PTR 01 ;node number 1. mov es:[si+48],BYTE PTR 10000101b ;bit2=1 redraw, bits1,0=01 node. sub ecx,10000000 ;speed up jnc L1 ;fall out when reach zero. ;when GOOFEE has finished processing last command, change focus back ;to window of this app... wmc7: mov al,es:[si+48] ;note you could use an equate "xcontrol1 EQU 48" cmp al,80h jne wmc7 call SetForegroundWindow, hwnd ret wmchar ENDP ;........................................................................ wmcreate PROC ret wmcreate ENDP wmdestroy PROC call PostQuitMessage,0 ret wmdestroy ENDP wmlbuttondown PROC ret wmlbuttondown ENDP wmrbuttondown PROC call MessageBeep,0 ret wmrbuttondown ENDP wmsize PROC ret wmsize ENDP ;----------------------------------------------------------------------------- ends end start