Chapter 15: Multitasking

In the previous chapter, you considered the possibilities of using timers in applied task. Having specified one or more timers, you force the system to call one or more procedures in an automatic mode. Thus, using timers, you can implement multitasking mode within a single process. Furthermore, it was shown that such subtasks could communicate with each other. This is natural, because they share the same address space; consequently, information can be passed through global variables from subtask to subtask. This problem is interesting and complicated. I cover it later in this book. For the moment, consider the multitasking environment in the Windows operating system from the beginning.

Creating a Process

The process is the kernel object (see Chapter 13) created by the Windows operating system when loading an executable module. It has the following at its exclusive disposal:

  • Virtual memory allocated to it by the operating system.

  • Handles of the files opened by it.

  • The list of dynamic link libraries (DLLs) loaded by it into its own memory.

  • Subprocesses, known as threads, which it has created. Threads execute independently in the memory owned by the process.

I hope that this definition clearly reflects the idea of the process concept. However, for most problems considered in this chapter, an even simpler definition would be sufficient every executable module (EXE) started in the Windows environment becomes the process.

Now, consider subprocesses. This concept is also simple: Every process can generate other processes within the address space allocated to it. These processes execute independently of each other and of the process that generates them. However, the generating process, if needed, can forcibly terminate (kill) any process it has generated. Such processes are also called threads. Now, it is clear that in the previous chapter I used the timer to create an analogue of threads. However, in the Windows operating system, there are special tools for creating threads.

Now, it is necessary to describe the types of multitasking. In the old 16-bit Windows version, switching between tasks was done only when the task returned control to the operating system. This type of multitasking is called nonpreemptive. In a certain sense, this situation was even worse than that in MS-DOS. There, the elements of multitasking were implemented using so-called terminate-and-stay resident programs. Such programs intercepted interrupts from the timer, the keyboard, or any other device; from time to time, they could gain control in response to events related to those devices.

The situation that existed in the old Windows operating system required the programmer to observe a gentleman's agreement that consisted of avoiding capturing the processor for a long time. The use of timers provided a kind of solution for this problem (as you have seen already). Another solution was the use of the PeekMessage function instead of GetMessage . The PeekMessage function, in contrast to GetMessage , returns control immediately, even if there are no messages in the queue.

In 32-bit operating systems of the Windows family (Windows 9 x , Windows NT, Windows 2000, etc.), the preemptive multitasking is implemented. According to this method, switching between processes and threads is delegated to the operating system. If the process spends too much time on a certain operation, then the cursor over the process window will turn into an hourglass. In this case, other processes will continue to execute, and you'll be able to switch to them. However, access to the window of the current process can be difficult. This problem can be solved using the previously mentioned approachnamely, replacing the GetMessage function with the PeekMessage function is the waiting loop. However, dividing the process into several threads would be a better solution.

The next few sections will be dedicated to the creation of threads. As relates to the remaining part of this section, it will concentrate on creation of processes. Your application can create processes by starting a specific executable program. These processes will run independently of the main application. At the same time, your application can delete the application that it has started. The process can be created using the CreateProcess function. Here is a brief description of parameters accepted by this function:

  • First parameterThis specifies the name of the program being started. The name can contain the full path to the program.

  • Second parameterIts value depends on whether or not the first parameter is set to NULL . If the first parameter points to a string, then this parameter is interpreted as a startup command line (without the program name). If the first parameter is NULL , then this parameter is interpreted as a command line whose first element represents the program name. If the path to the program is not specified, then the CreateProcess function searches for the program according to a certain algorithm:

    • Search within the directory, from which the program was started.

    • Search within the current directory.

    • Search in the system directory (which can be obtained by calling the GetSystemDirectory function). As a rule, the system directory's name is C:\WINDOWS\SYSTEM or C:\WINDOWS\SYSTEM32.

    • Search in the Windows directory (the actual name can be obtained by calling the GetWindowsDirectory function). Usually, this is the C:\WINDOWS directory.

    • Search in directories listed by the PATH environment variable.

  • Third and fourth parametersThese parameters are used for specifying access attributes of the process being generated. Usually, they are set to zero.

  • Fifth parameterIf this parameter is zero, then the process being generated doesn't inherit descriptors of the generating process. If the parameter has a nonzero value, the process being generated inherits descriptors.

  • Sixth parameterThis parameter allows you to change the properties of the process being generated. If this parameter is set to zero, then the properties are specified by default. Because this parameter can take a range of values, I won't provide them here. You can find this information in appropriate manuals.

  • Seventh parameterThis is a pointer to the buffer that contains environment parameters. If this parameter is set to zero, then the process being generated inherits environment parameters of the generating process.

  • Eighth parameterSpecifies the current drive and directory for the process being generated. If this parameter is set to null , then the process being generated inherits the current drive and directory of the generating process.

  • Ninth parameterThis is a pointer to the structure that contains information about the window of the process being created. The fields of this structure will be considered later.

  • Tenth parameterThis points to the structure filled when the application is started for execution. Here is this structure:

     PROCINF STRUC         HProcess    DD ?   ; Descriptor of the created process         HThread     DD ?   ; Descriptor of the primary thread                            ; of the new process         Idproc      DD ?   ; Identifier of the new process         IdThr       DD ?   ; Identifier of the primary thread of the                            ; new process     PROCINF.ENDS 

The main difference between the descriptor (handle) and the identifier lies in that the descriptor is unique only within the limits of a given process and the identifier is the global value. The data area of the current process can be found using an identifier. I assume that you immediately ask the following question: What is the difference between the application descriptor that I get using the GetModulHandle function and the values you have just mentioned? Well, the application descriptor and the handle that you obtain using the GetModulHandle function are the same value. An application descriptor or module descriptor is a local value, which means that it is in force within the limits of a given process. As a rule, it is equal to the address, by which the module is loaded into the virtual address space. Every module loaded into the memory has a module handle, including DLLs.

Now, consider the structure pointed to by the ninth parameter of the CreateProcess function. Here is the structure:

 STARTUP STRUC             cb                 DD 0             lpReserved         DD 0             lpDesktop          DD 0             lpTitle            DD 0             dwX                DD 0             dwY                DD 0             dwXSize            DD 0             dwYSize            DD 0             dwXCountChars      DD 0             dwYCountChars      DD 0             dwFillAttribute    DD 0             dwFlags            DD 0             wShowWindow        DW 0             cbReserved2        DW 0             lpReserved2        DD 0             hStdInput          DD 0             hStdOutput         DD 0             hStdError          DD 0     STARTUP ENDS 

The fields of this structure have the following meanings:

  • cb The size of this structure in bytes; a mandatory field that must be filled

  • lpReserved Reserved; must be zero

  • lpDesktop Desktop (and workstation) name; has meaning only for the operating systems from the Windows NT family

  • lpTitle Window title for console applications that create their own console; must be zero for all other applications

  • dwX X-coordinate of the top left window corner

  • dwY Y-coordinate of the top left window corner

  • dwXSize Window width

  • dwYSize Window height

  • dwXCountChars Size of the console buffer (by the X-coordinate)

  • dwYCountChars Size of the console buffer (by the Y-coordinate)

  • dwFillAttribute Initial color of the text; makes sense only for console applications

  • dwFlags Flag of the field values; possible values are listed in Table 15.1

    Table 15.1: Possible values of the dwFlags field

    Macro value of the flag

    Constant value

    Explanation

    STARTF_USESHOWWINDOW

    1h

    Enable the dwShowWindow field

    STARTF_USESIZE

    2h

    Enable dwXSize and dwYSize

    STARTF_USEPOSITION

    4h

    Enable dwx and dwY

    STARTF_USECOUNTCHARS

    8h

    Enable dwXCountChars and dwYCountChars

    STARTF_USEFILLATTRIBUTE

    10h

    Enable dwFillAttribute

    STARTF_FORCEONFEEDBACK

    40h

    Specify that the cursor is in the feedback mode

    STARTF_FORCEOFFFEEDBACK

    80h

    Specify that the cursor feedback mode is off

    STARTF_USESTDHANDLES

    100h

    Enable kStdInput

  • wShowWindow Defines the window display mode

  • cbReserved2 Reserved; must be zero

  • hStdInput Input descriptor (for the console)

  • hStdOutput Output descriptor (for the console)

  • hStdError Output descriptor for displaying an error message (for the console)

The example shown in listing 15.1 demonstrates the procedure of creating a process. The WINWORD.EXE text editor was chosen as a program that generates a process. To check whether this example operates correctly, you'll have to specify the path to the WINWORD.EXE module on your computer (the PATH variable). Notice that the application appears on screen in a minimized form, and notice how this is achieved. As you can see, the CreateProcess function is much simpler than it seems to be at first glance.

Listing 15.1: Creating a process
image from book
 // The PROCESS.RC file // Constant definitions #define WS_SYSMENU            0x00080000L #define WS_POPUP              0x80000000L #define DS_3DLOOK             0x0004L // The menu resource MENUP MENU {  POPUP "&Start Word"  {  MENUITEM "& Start", 1  MENUITEM "&Close", 2  MENUITEM "E&xit the program", 3  } } // Dialog box definition DIAL1 DIALOG 0, 0, 240, 120 STYLE WS_POPUP  WS_SYSMENU  DS_3DLOOK CAPTION "Example of starting the process" FONT 8, "Arial" { } ; The PROCESS.INC file ; Constants STARTF_USESHOWWINDOW equ  1h SW_SHOWMINIMIZED     equ  2 ; This message arrives when the window is closed NM_CLOSE             equ  10h NM_INITDIALOG        equ  110h NM_COMMAND           equ  111h ; Prototypes of external procedures FDEF MASM ; For MASM         EXTERN TerminateProcess@8:NEAR         EXTERN CreateProcessA@40:NEAR         EXTERN DialogBoxParamA@ 20:NEAR         EXTERN EndDialog@8:NEAR         EXTERN MessageBoxA@16:NEAR         EXTERN ExitProcess@4:NEAR         EXTERN GetModuleHandleA@4:NEAR         EXTERN LoadMenuA@8:NEAR         EXTERN SetMenu@8:NEAR         EXTERN TranslateMessage@4:NEAR ELSE ; For TASM         EXTERN TerminateProcess: NEAR         EXTERN CreateProcessA:NEAR         EXTERN DialogBoxParamA:NEAR         EXTERN EndDialog:NEAR         EXTERN MessageBoxA:NEAR         EXTERN ExitProcess:NEAR         EXTERN-GetModuleHandleA:NEAR         EXTERN LoadMenuA:NEAR         EXTERN SetMenu:NEAR         EXTERN TranslateMessage:NEAR         TerminateProcess@8 = TerminateProcess         CreateProcessA@40 = CreateProcessA         DialogBoxParamA@20 = DialogBoxParamA         EndDialog@8 = EndDialog         MessageBoxA@16 = MessageBoxA         ExitProcess@4 = ExitProcess         GetModuleHandleA@4 = GetModuleHandleA         LoadMenuA@8 = LoadMenuA         SetMenu@8 = SetMenu         TranslateMessage@4 = TranslateMessage ENDIF ; Structures ; Message structure MSGSTRUCT STRUC         MSHWND     DD ?         MSMESSAGE  DD ?         MSWPARAM   DD ?         MSLPARAM   DD ?         MSTIME     DD ?         MSPT       DD ? MSGSTRUCT ENDS ; The structure for CreateProcess STARTUP STRUC         cb                   DD 0         lpReserved           DD 0         lpDesktop            DD 0         lpTitle              DD 0         dwX                  DD 0         dwY                  DD 0         dwXSize              DD 0         dwYSize              DD 0         dwXCountChars        DD 0         dwYCountChars        DD 0         dwFillAttribute      DD 0         dwFlags              DD 0         wShowWindow          DW 0         cbReserved2          DW 0         lpReserved2          DD 0         hStdInput            DD 0         hStdOutput           DD 0         hStdError            DD 0 STARTUP ENDS ; Structure containing information about the process PROCINF STRUC         HProcess      DD ?         HThread       DD ?         Idproc        DD ?         IdThr         DD ? PROCINF ENDS ; The PROCESS.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall include process.inc ; INCLUDELIB directives for the linker to link libraries IFDEF MASM ; MASM directives         includelib c:\masm32\lib\user32.lib         includelib c:\masm32\lib\kernel32.lib ELSE ; TASM directives         includelib c:\tasm32\lib\import32.lib ENDIF ; Data segment _DATA SEGMENT         NEWHWND  DD 0         MSG      MSGSTRUCT <?>         STRUP    STARTUP      <?>         INF      PROCINF <?>         HINST    DD 0 ; Application handle         PA       DB "DIAL1", 0         PMENU    DB "MENUP", 0         PATH     DB "C:\Program Files\Office\WINWORD.EXE", 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Get application descriptor         PUSH 0         CALL GetModuleHandleA@4         MOV   [HINST], EAX ; Create a modal dialog         PUSH 0         PUSH OFFSET WNDPROC         PUSH 0         PUSH OFFSET PA         PUSH  [HINST]         CALL DialogBoxParamA@20         PUSH 0         CALL ExitProcess@4 ;-------------------------------- ; Window procedure ; Position of parameters in the stack ; [EBP+014H]  ; LPARAM ; [EBP+10H]   ; WAPARAM ; [EBP+0CH]   ; MES ; [EBP+8]     ; HWND WNDPROC PROC         PUSH  EBP         MOV   EBP, ESP         PUSH  EBX         PUSH  ESI         PUSH  EDI ; This message arrives when the window is closed         CMP   DWORD PTR [EBP+0CH], WM_CLOSE         JNE   L1 ; Close the dialog         JMP   L5 L1: ; The message for window initialization         CMP   DWORD PTR [EBP+0CH], WM_INITDIALOG         JNE   L3 ; Load the menu         PUSH  OFFSET PMENU         PUSH  [HINST]         CALL  LoadMenuA@8 ; Set the menu         PUSH  EAX         PUSH  DWORD PTR [EBP+08H]         CALL  SetMenu@8         JMP   FINISH ; Check whether some events have happened to the ; menu items in the dialog L3:         CMP   DWORD PTR [EBP+0CH], WM_COMMAND         JNE   FINISH         CMP   WORD PTR [EBP+10H], 3         JE    L5         CMP   WORD PTR [EBP+10H], 2         JE    L7         CMP   WORD PTR [EBP+10H], 1         JE    L6 JMP FINISH ; Close the dialog L5:         PUSH  0         PUSH  DWORD PTR [EBP+08H]         CALL  EndDialog@8         JMP   FINISH ; Start Word L6: ; Fill the startup structure ; The window must appear in a minimized form'         MOV   STRUP.cb, 68         MOV   STRUP.lpReserved, 0         MOV   STRUP.lpDesktop, 0         MOV   STRUP.lpTitle, 0         MOV   STRUP.dwFlags, STARTF_USESHOWWINDOW         MOV   STRUP.cbReserved2, 0         MOV   STRUP.lpReserved2, 0         MOV   STRUP.wShowWindow, SW_SHOWMINIMIZED ; Starting the Winword application         PUSH  OFFSET INF         PUSH  OFFSET STRUP         PUSH  0         PUSH  0         PUSH  0         PUSH  0         PUSH  0         PUSH  0         PUSH  OFFSET PATH         PUSH  0         CALL  CreateProcessA@40         JMP   FINISH ; Remove the process from memory L7:         PUSH  0 ; Exit code         PUSH  INF.hProcess         CALL  TerminateProcess@8 FINISH:         MOV   EAX, 0         POP   EDI         POP   ESI         POP   EBX         POP   EBP         RET   16 WNDPROC    ENDP _TEXT ENDS END START 
image from book
 

To translate the PROCESS.ASM program presented in Listing 15.1, issue the following commands for MASM32:

 ML /c /coff /DMASM process.asm     RC process.re     LINK /SUBSYSTEM:WINDOWS process.obj proces.res 

To translate the PROCESS.ASM program presented in Listing 15.1, issue the following commands for TASM32:

 TASM32 /ml process.asm     BRCC32 process.rc     TLINK32 -aa process.obj,,,,,process.res 

I think that the program presented in Listing 15.1 doesn't require any other comment. The window displayed by this program, with the menu, is shown in Fig. 15.1.

image from book
Figure 15.1: Window of the program that starts WINWORD.EXE and removes it from memory

It is necessary to emphasize how the started process can be unloaded from the memory. Naturally, if you start a module written by someone else, you can hope that the author of that module provided a natural method of exiting that program. In this case, the author of the third-party module bears all the responsibility related to application termination. On the other hand, if you use the TerminateProcess function, as was shown in Listing 15.1, then the program may not necessarily terminate correctly. Naturally, the system releases all resources allocated to the process. However, it cannot carry out another component's job. For example, if that program was working with a file, then part of the data might be lost. When using the TerminateProcess function, the system doesn't send any notification to the application. As relates to the ExitProcess function that I widely use in this book, this is a correct way of terminating a process when writing a program in Assembly language. However, avoid using this function when writing programs in high-level languages. Your application might contain objects, and they will be incorrectly removed from the memory (without executing destructors).



The Assembly Programming Master Book
The Assembly Programming Master Book
ISBN: 8170088178
EAN: 2147483647
Year: 2004
Pages: 140
Authors: Vlad Pirogov

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net