;       SCCSID = @(#)smrsevnt.asm       6.4 92/02/28
;       SCCSID @(#)smrsevnt.asm 6.4 92/02/28
PAGE 60,132                             ;
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************

;************************************************************************
;
; Source File Name:     SMRSEVNT.ASM
;
; Subroutine Name:      APPSENDREQUEST
;
; Descriptive Name:     Send an Application Request Block to the
;                       Shell
;
; Function:     This sends the application request to the
;               Shell/Session Manager for processing and waits
;               for notification from the Session Manager that
;               the requested function has been processed.
;
; Notes:
;       Dependencies:   APPSENDREQUEST uses the ReqType to determine
;                       what and how much data resides on the stack
;                       from the orginal application request.
;
;       Restrictions:   None
;
; Change History:
;   @TL6  03/01/90 DCR 704 - Merge SMG/SSW instance data into OS2CHAR
;   @T01  08/06/90 PTR B715020 Clear request block RC to avoid infinite loop
;   D1506 02/25/92 DCR 1506 But really implemented under ptrs (B733789).
;   58348 02/22/93 CJJ Pass current directory to a VDM started VDM session.
;   MDLL  01/10/94 BJN Page Tune segments for Merged DLL.
; @148119 02/09/96 MHW Changed the order of a test
;
;
; Entry Point:
;       Linkage:  EXTRN APPSENDREQUEST:NEAR
;                       CALL    APPSENDREQUEST
; Inputs:
;       ES:DI   ReqBlock    Application Session Request Block
;       BX      BlockSize   Number of bytes in Request Block
;       AX:DX   ObjectBuff  Buffer to copy 'Object Name' from DosExecPgm
;       FS      INSTANCE    Instance Data Segment
;
; Exit Normal:
;       AX      ReturnCode Value = 0, Application request successful
;       SI      NewSession New session ID
;       BX      NewProcess New PID
;
; Exit Errors:
;       AX      ReturnCode DOSEXECPGM    errors
;                          DOSOPENQUEUE  errors
;                          DOSSEMSET     errors
;                          DOSSEMWAIT    errors
;                          DOSWINPROC    errors
;                          DOSWRITEQUEUE errors
;                          WINMSGSEMWAIT errors
;
; Effects:      All other registers preserved.
;
; Internal References:
;          Data:        Reqblock (SMRREQB.INC)
;                       RDATA - Common data segment
;                               appreqqueuename - request block queue name
;          Routines:
;
; External References:
;          Routines:    DOSCLOSEQUEUE
;                       DOSEXECPGM
;                       DOSOPENQUEUE
;                       DOSSEMCLEAR
;                       DOSSEMSET
;                       DOSSEMWAIT
;                       DOSSMFREEMEM
;                       DOSWRITEQUEUE
;
;************************************************************************
ifdef I1
;
;   Save registers
;   DOSOPENQUEUE (AppReqQueueName)      ; Open application request queue
;   If No Error AND
;   If PM is Present
;     If  WinMsgSemWait not loaded AND
;     If  We are at ring 3
;       DOSWINPROC (WinMsgSemWait)      ; Do a loadmodule on it
;     EndIf
;   EndIf
;
;   If No_Error
;     Repeat                            ; Keep trying in case being blocked
;       DOSSEMSET (ReqBlockSem2)        ; Set applications request semaphores
;       DOSSEMSET (ReqBlockSem)
;
;       If No_Error
;         DOSHOLDSIGNAL (Disable)
;         Specify Almost Writen in Request Block Status Byte
;         DOSWRITEQUEUE (Request Block)
;
;         If No_Error
;           If  Request Block Not read yet
;             Set status to Writen
;           EndIf
;
;           Save Registers killed by 'C'
;           Repeat
;             If  WinMsgSemWait has been loaded AND
;             If  We are at ring 3
;               WINMSGSEMWAIT (ReqBlockSem)
;             .Else
;               DOSSEMWAIT (ReqBlockSem)
;             .EndIf
;           Until RC <> Error_Interrupt
;           Restore registers killed by 'C'
;
;           If RequstBlock RC == No Error
;             If Inherited Start
;               Put Object Name on stack
;               If starting a non-VDM program
;                 Put Object Name Length on Stack
;                 If Async Option == 2              ; If special trace option
;                   Put Async Option = 6 on Stack   ; Put normal trace option
;                 Else
;                   Purt Async Option on stack
;                 EndIf
;               Else
;                 Pad Object buffer length to DWORD for VDMs
;               EndIf
;
;               Put @ of Argument String on Stack
;               Put @ of Environment String on Stack
;               Put @ of Program Name on Stack
;
;               If  Not a VDM
;                 DOSEXECPGM
;               Else
;                 Put Session ID on Stack
;                 DOSCREATEVDM
;               EndIf
;
;               Put RC in Request Block
;
;               ***********************************************
;               * Wake up the shell, and wait for it to do    *
;               * its stuff.  It will eventually wake us up.  *
;               ***********************************************
;               DOSSEMSET (ReqBlockSem)
;               DOSSEMCLEAR (ReqBlockSem2)
;
;               Save Registers killed by 'C'
;               Repeat
;                 If  WinMsgSemWait has been loaded AND
;                 If  We are at ring 3
;                   WINMSGSEMWAIT (ReqBlockSem)
;                 .Else
;                   DOSSEMWAIT (ReqBlockSem)
;                 .EndIf
;               Until RC <> Error_Interrupt
;               Restore registers killed by 'C'
;             EndIf                    ; Inherited Start Code
;
;           Else                       ; Error from Shell
;             ***************************************************
;             *  Copy info from our buffer to caller's buffer.  *
;             *  if there was an error on an noninherited start *
;             ***************************************************
;             If Inherited Start
;               ***************************************************
;               *  Copy info from our buffer to caller's buffer.  *
;               ***************************************************
;               If buffer selector <> 0
;                 Copy buffer
;               EndIf                  ; Caller's buffer existence test
;             EndIf                    ; Inherited Start Code
;           EndIf                      ; Check errors from shell
;
;           Get return code from shell out of request block
;         EndIf                        ; DOSWRITEQUEUE worked test
;
;         Save return code
;         DOSHOLDSIGNAL (Enable)
;         Restore meaningful return code
;       EndIf                          ; DOSSEMSET worked test
;     Until RC <> Try_Again
;
;     If No Error
;       ************************************************
;       *  Don't close the queue if this is the shell  *
;       *  (ie. the process that created the queue).   *
;       ************************************************
;       If  ShellPid <> LIS_CurProcID
;         Save return code
;         CALL DOSCLOSEQUEUE
;         Restore meaningful return code
;       EndIf
;     EndIf
;   EndIf
;
;   Save return code
;
;   ******************************************
;   *  Free up any memory that we ALLOCATED  *
;   ******************************************
;   If  Non-InHerited Start
;     If Environment Selector <> 0
;       DOSFREESEG (Environment Selector)
;     EndIf
;
;     If Object Buffer Selector <> 0
;       DOSFREESEG (Object Buffer Selector)
;     EndIf
;   EndIf
;
;   **********************************************************************
;   *  Move addresses of remaining valid request blocks down 1 slot in   *
;   *  the INSTANCE data for this process.  Decrement Next_Slot to       *
;   *  indicate that there is 1 less valid request block - since we're   *
;   *  going to free it immediately after we update INSTANCE data.       *
;   *  Note:  The decremented Next_Slot value indicates the number of    *
;   *  remaining valid request blocks.                                   *
;   **********************************************************************
;
;   DOSSMFREEMEM (Request Block)
;
;  Restore meaningful return code
;  Restore registers
;
;************************************************************************
else

.386C
    EXTRN AppReqQueueName    : BYTE     ; Application request queue name
;   EXTRN WinMsgSemWait_Name : BYTE     ; Name of PMWIN entry           ;@TL6
    EXTRN PM_Present         : BYTE     ; Windowing present
;   EXTRN AppWin_Handle      : WORD     ; Place to store loadmodule handle ;@TL6
    EXTRN LocalSelector      : WORD     ; Selector to LDT
    EXTRN ShellPID           : WORD     ; Shell ID
;   EXTRN WinMsgSemWait      : DWORD    ; Windowing MuxSemWait          ;@TL6
    EXTRN SMG_INSTANCE       : DWORD    ; SMG/SSW instance data @       ;@TL6
    EXTRN VDMCurDir          : DWORD    ; Current VDM directory         ;58348

    EXTRN DOSWINPROC      : NEAR        ; Windowing dynalink load
    EXTRN DOSCREATEVDM    : FAR         ; Start a DOS Mode Session
    EXTRN DOSCLOSEQUEUE   : FAR         ; Close app request queue
    EXTRN DOSFREESEG      : FAR
    EXTRN DOSOPENQUEUE    : FAR         ; Open app request queue
    EXTRN DOSWRITEQUEUE   : FAR         ; Send app request queue
    EXTRN DOSSEMSET       : FAR         ; Set app request semaphore
    EXTRN DOSSEMCLEAR     : FAR         ; Clear app request semaphore
    EXTRN DOSSEMWAIT      : FAR         ; Wait until request processed
    EXTRN DOSSMFREEMEM    : FAR         ; Free app request block
    EXTRN DOSEXECPGM      : FAR         ; Execute a new program
    EXTRN DOSHOLDSIGNAL   : FAR         ; Disallow/allow signals
.xlist
    INCLUDE SMRREQB.INC                 ; Request block definitions
.xcref
    INCLUDE ERROR.INC                   ; Error code equate for ERROR_INTERRUPT
    INCLUDE ERROR2.INC                  ; Error code equates
    INCLUDE INFOSEG.INC                 ; Information segment definitions
    INCLUDE SMRRDATA.INC                ; Router data declarations
    INCLUDE STRUC.INC
    INCLUDE OS2INST.INC                 ; Instance data equates         ;@TL6
    INCLUDE DEMSMG.INC                  ; VDM STRUC                     ;58348
.cref
.list

CODE_TUNE1 SEGMENT WORD PUBLIC 'CODE' USE16                             ;MDLL
    ASSUME CS:CODE_TUNE1,DS:NOTHING,SS:NOTHING,ES:NOTHING               ;MDLL

    PUBLIC APPSENDREQUEST
APPSENDREQUEST PROC NEAR

    Local_Bytes EQU  8
    Local_Var1  EQU  WORD PTR SS:[BP][-2]
    Local_Var2  EQU  WORD PTR SS:[BP][-4]
    Local_Var2p EQU  DWORD PTR SS:[BP][-4]                              ;CJ10
    Local_Var3  EQU  WORD PTR SS:[BP][-6]
    Local_Var4  EQU  WORD PTR SS:[BP][-8]

    ENTER Local_Bytes,0                 ; Create stack frame

    MOV  Local_Var1, AX                 ; Save input AX:DX
    MOV  Local_Var2, DX                 ; Save input AX:DX

    PUSH DS                             ; Save registers
    PUSH DX
    PUSH CX
    PUSH FS
    PUSH GS

;   ************************************
;   *  Open application request queue  *
;   ************************************
    LEA  DX, Local_Var3                 ; Get offset of local variables
    LEA  CX, Local_Var4                 ;  so we can push their address

    MOV  AX, RDATA                      ; Get global data selector
    MOV  DS, AX

    PUSH SS
    PUSH DX                             ; Address to store queue PID owner
    PUSH SS
    PUSH CX                             ; Address to store queue handle
    PUSH DS
    PUSH OFFSET AppReqQueueName
    CALL DOSOPENQUEUE

    LFS  SI, DS:SMG_INSTANCE            ;                               ;@TL6
    .IF  < zero AX > AND                ; Open queue worked             ;@TL6
    .IF  < DS:PM_Present e Yes >        ; windowing present
      PUSH BX                           ; Save Request Block Size       ;@TL6
      MOV  BX, WORD PTR FS:[SI].WINMSGSEMWAIT+2 ; PMWIN already loaded  ;@TL6
      .IF  < zero BX > AND              ; Not loaded                    ;@TL6
      MOV  BX, CS                       ; Check CS for
      TEST BX, 1                        ;   ring 3 caller
      .IF  < nz >                       ; Ring 3 caller?
        MOV  CX, 1                      ; One entry wanted
        PUSH SI                         ; Save instance data offset     ;@TL6
        LEA  BX, [SI].WinMsgSemWait_Name; Name of entry                 ;@TL6
        LEA  DX, [SI].WINMSGSEMWAIT     ; Location to store entry       ;@TL6
        LEA  SI, [SI].Appwin_Handle     ; Offset to store handle        ;@TL6
        CALL DOSWINPROC                 ; Go do load
        POP  SI                         ; Restore instance data offset  ;@TL6
      .ENDIF
      POP  BX                           ; Restore Request Block Size    ;@TL6
    .ENDIF

    MOV  DX, Local_Var3                 ; Get owning process ID --> RETURNED BUT NEVER NEEDED
    MOV  CX, Local_Var4                 ; Get queue handle

    .IF  < zero AX > NEAR                                               ;@TL6
      .REPEAT                           ; Keep trying in case being blocked
        XOR  DX, DX                     ; Clear for testing
        MOV  ES:[DI].ReqReturnCode, DX  ; Make sure RC is cleared       ;@T01
;       ****************************************
;       *  Set applications request semaphores *
;       ****************************************
;@TL6   MOV  AX, DI
        LEA  AX, [DI].ReqBlockSem2      ; Add offset of semaphore       ;@TL6
        PUSH ES                         ; Push Selector of semaphore
        PUSH AX                         ; Push Offset of semaphore
        CALL DOSSEMSET

;@TL6   MOV  AX, DI
        LEA  AX, [DI].ReqBlockSem       ; Add offset of semaphore       ;@TL6
        PUSH ES                         ; Push Selector of semaphore
        PUSH AX                         ; Push Offset of semaphore
        CALL DOSSEMSET

;        *********************************************
;        *  Send the application request to the que  *
;        *********************************************
        .IF  < zero AX > NEAR                                           ;@TL6
          PUSH Disable_Signals          ; Specify to hold off signals
          CALL DOSHOLDSIGNAL            ; Temporarily disable signals

          MOV  ES:[DI].ReqQueueStatus, RqAlmostWritten
          PUSH CX                       ; Que handle from DOSOPENQUEUE
          PUSH 0                        ; Request identification data
          PUSH BX                       ; Size of queue element (Input Parm)
          PUSH ES
          PUSH DI                       ; @ of Request Block to write
          PUSH 0                        ; Element Priority
          CALL DOSWRITEQUEUE

          .IF  < zero AX > NEAR                                         ;@TL6
            .IF  < <ES:[DI].ReqQueueStatus> ne RqRead > ; IF Not Read yet
              MOV  ES:[DI].ReqQueueStatus, RqWritten    ; then set written flag
            .ENDIF
;           *******************************************
;           *  Wait until request has been processed  *
;           *******************************************
;@TL6       PUSH ES                     ; Preserve registers blown by C
            PUSH FS
            PUSH GS
            PUSH BX
            PUSH CX

;@TL6       MOV  DX, DI                 ; @ of request block
;@TL6       ADD  DX, OFFSET ReqBlockSem ; Add offset of semaphore

            .REPEAT                     ; Make sure sem is cleared
              PUSH ES                   ; Must save ES here             ;@TL6
              LEA  DX, [DI].ReqBlockSem ; Get offset of semaphore       ;@TL6

              PUSH ES
              PUSH DX                   ; Address of semaphore
              PUSH DWORD PTR -1         ; Wait on the semaphore         ;CJ10
;CJ10         PUSH -1

;             *******************************************************
;             *  If PM is present, then call WINMSGSEMWAIT          *
;             *  otherwise use old reliable DOSSEMWAIT.             *
;             *  WINMSGSEMWAIT allows the process that is doing the *
;             *  DOSSTARTSESSION to recieve messages while waiting  *
;             *  for the queue semaphore to be cleared.             *
;             *******************************************************
              MOV  AX, WORD PTR FS:[SI].WINMSGSEMWAIT+2 ; Use WIN entry? ;@TL6
              .IF  < nonzero AX > AND   ; Selector exists?              ;@TL6
              MOV  AX, CS               ; Check CS for
              TEST AX, 1                ;   ring 3 caller

              .IF  < nz >               ; Ring 3 caller?
                CALL FS:[SI].WINMSGSEMWAIT ; Call windowing entry       ;@TL6
                ; ********************************************************
                ; * @DB68493 -                                           *
                ; * WINMSGSEMWAIT calls DOSSEMMUXWAIT and can return an  *
                ; * error when too many processes are doing MUXWAITS, we *
                ; * need to recognize this situation and try to deal     *
                ; * with it effectively. Since we are posibly holding up *
                ; * the message queue, we will do a DOSSEMWAIT with a    *
                ; * short timeout and then repeat WINMSGSEMWAIT if needed*
                ; ********************************************************
                .IF < AX eq ERROR_TOO_MANY_MUXWAITERS >
                  .REPEAT
                    POP  ES                             ; Restore saved ES
                    PUSH ES                             ; Resave it
                    LEA  DX,[DI].ReqBlockSem            ; Restore semaphore addr

                    PUSH ES
                    PUSH DX                             ; Try a timed DosSemWait
                    PUSH DWORD PTR 1
                    CALL DOSSEMWAIT
                    .IF < AX ne no_error >              ; Timed out?
                      PUSH ES
                      PUSH DX
                      PUSH DWORD PTR -1                 ; Try WinMsgSemWait again
                      CALL FS:[SI].WINMSGSEMWAIT
                    .ENDIF
                  .UNTIL < AX eq no_error >
                .ENDIF
                ; @DB68493 - End of insertion
              .ELSE                     ; Else if WIN_SEMWAIT is valid
                CALL DOSSEMWAIT         ; use DOSSEMWAIT
              .ENDIF

              POP  ES                   ; Restore ReqBlock selector     ;@TL6
            .UNTIL < AX ne Error_Interrupt > ; In case int. woke us up

            POP  CX                     ; Restore registers killed by 'C'
            POP  BX
            POP  GS
            POP  FS
;@TL6       POP  ES

            .IF  < ES:[DI].ReqReturnCode eq No_Error > NEAR ; Shell error?
;             @148119 Changed the order of the next test
              .IF  < ES:[DI].ReqType eq App_Start > NEAR AND  ; Is this a start
              .IF  < ES:[DI].RqInheritOpt eq Inherited > NEAR
;CJ10           PUSH ES:[DI].RqStrObjName[2]        ; Selector to Obj. Name
                PUSH DWORD PTR ES:[DI].RqStrObjName ; Sel:Off to Obj Name ;CJ10
                .IF  < ES:[DI].RqStrPgmType b VDM_Type >    ; If not a VDM
                  PUSH ES:[DI].RqStrObjBufLen       ; Object Name Bufer Len
                  .IF  <ES:[DI].RqStrAsyncInd e 2>  ; If new trace option
                    PUSH 6                          ; then push normal trace
                  .ELSE                             ; else push
                    PUSH ES:[DI].RqStrAsyncInd      ; Async option
                  .ENDIF
                .ELSE
                  PUSH 0                ; Pad obj buf length to dword for VDMs
                  PUSH ES:[DI].RqStrObjBufLen ; Object Name Bufer Len
                .ENDIF                  ; VDM Check

;@TL6           MOV  AX, RqStrChildPID  ; Get offset to PID
                LEA  AX, [DI].RqStrChildPID    ; Add in offset of RB    ;@TL6

                PUSH ES:[DI].RqStrArgSel       ; Sel. argument strg
                PUSH ES:[DI].RqStrArgOff       ; Offset argument strg
;CJ10           PUSH ES:[DI].RqEnvironment[2]  ; Sel. of env. strg
                PUSH DWORD PTR ES:[DI].RqEnvironment ; Sel:Off of envir ;CJ10
                PUSH ES                        ; PID selector (Ret'd)
                PUSH AX                        ; PID offset (Returned)
                PUSH ES:[DI].RqStrProgSel      ; Selector of prog name
                PUSH ES:[DI].RqStrProgOff      ; Offset of prog name

ifdef gsproblem
                PUSH 0
                POP  GS
endif
                .IF  < ES:[DI].RqStrPgmType ne VDM_Type > AND  ; If not a VDM
                .IF  < ES:[DI].RqStrPgmType ne Windowed_VDM_Type >
                  CALL DOSEXECPGM
                .ELSE                              ; This is a VDM Start
                  MOV  AX, ES:[DI].RqStrChildSesID ; Get session id
                  XOR  AH, AH                      ; Strip off high byte;@TL6,VR10
                  PUSH 0                           ; Pad SG id to dword
                  PUSH AX

;                 ********************************************************58348
;                 *  If this came from our VDM start session, then pass  *58348
;                 *  it's current directory to the DosCreateVDM else     *58348
;                 *  just pass 0.                                        *58348
;                 ********************************************************58348
                  TEST ES:[DI].ReqFlags, VDMStartedSession               ;58348
                  .IF  < nz >                                            ;58348
                    PUSH DS:VDMCurDir   ; Current VDM directory          ;58348
                  .ELSE                                                  ;58348
                    PUSH DWORD PTR 0    ; No current directory           ;58348
                  .ENDIF                                                 ;58348

                  CALL DOSCREATEVDM     ; Convert to 0:32 & call DosCreatVDM
                .ENDIF

                MOV  ES:[DI].ReqReturnCode, AX ; Save RC for shell

;               ***********************************************
;               * Wake up the shell, and wait for it to do    *
;               * its stuff.  It will eventually wake us up.  *
;               ***********************************************
                PUSH ES
                LEA  DX, [DI].ReqBlockSem                               ;@TL6
                PUSH DX
                CALL DOSSEMSET

;@TL6           MOV  DX, DI
                LEA  DX, [DI].ReqBlockSem2                              ;@TL6
                PUSH ES
                PUSH DX
                CALL DOSSEMCLEAR        ; Wake Up Shell

;@TL6           PUSH ES                 ; Preserve registers blown by C
                PUSH FS
                PUSH GS
                PUSH BX
                PUSH CX
;@TL6           PUSH DX

                .REPEAT                 ; Make sure sem is cleared
                  PUSH ES               ; Preserve ES blown by PMWIN    ;@TL6
                  LEA  AX, [DI].ReqBlockSem  ; Add in start of RB       ;@TL6
                  PUSH ES
                  PUSH AX               ; Offset of semaphore
                  PUSH DWORD PTR -1                                     ;CJ10
;CJ10             PUSH -1

;                 *******************************************************
;                 *  If PM is present, then call WINMSGSEMWAIT          *
;                 *  otherwise use old reliable DOSSEMWAIT.             *
;                 *  WINMSGSEMWAIT allows the process that is doing the *
;                 *  DOSSTARTSESSION to recieve messages while waiting  *
;                 *  for the queue semaphore to be cleared.             *
;                 *******************************************************
                  MOV  AX, WORD PTR FS:[SI].WINMSGSEMWAIT+2 ;Use WIN entry?;@TL6

                  .IF  < nonzero AX > AND; Selector exists?              ;@TL6
                  MOV  AX, CS           ; Check CS for
                  TEST AX, 1            ;   ring 3 caller
                  .IF  < nz >           ; Ring 3 caller?
                    CALL FS:[SI].WINMSGSEMWAIT ; Call windowing entry   ;@TL6
                  .ELSE                 ; Else if WIN_SEMWAIT is valid
                    CALL DOSSEMWAIT     ; use DOSSEMWAIT
                  .ENDIF
                  POP   ES              ; PMWIN destroys ES             ;@TL6

                .UNTIL < AX ne Error_Interrupt >
;@TL6           POP  DX                 ; Preserve registers blown by C
                POP  CX
                POP  BX
                POP  GS
                POP  FS
;@TL6           POP  ES
              .ENDIF                    ; Inherited Start Code

            .ELSE                       ; Error from Shell
;             ***************************************************
;             *  Copy info from our buffer to caller's buffer.  *
;             *  if there was an error on an noninherited start *
;             ***************************************************
              .IF  < ES:[DI].RqInheritOpt ne Inherited > NEAR AND
              .IF  < ES:[DI].ReqType eq App_Start > NEAR ; Is this a start
;               ***************************************************
;               *  Copy info from our buffer to caller's buffer.  *
;               ***************************************************
                .IF  < Local_Var1 ne 0 > ; If buffer selector <> 0
                  PUSH ES                ; Save Registers
                  PUSH DI
                  PUSH DS
                  PUSH SI

;                 **************************************************
;                 *  Use only the low word of the DWORD length,    *
;                 *  since the length will never be > 2^16 bytes.  *
;                 **************************************************
                  MOV  CX, ES:[DI].RqStrObjBufLen

;                 ***************************************
;                 *  Set up source selector and offset  *
;                 ***************************************
;@TL6             MOV  AX, ES:[DI].RqStrObjName[2] ; Selector to Obj. Name
;CJ10             MOV  DS, ES:[DI].RqStrObjName[2] ; Source Selector    ;@TL6
                  LDS  SI, DWORD PTR ES:[DI].RqStrObjName ; Sel:Off to Obj Name

;                 ********************************************
;                 *  Set up destination selector and offset  *
;                 ********************************************
;@TL6             MOV  AX, Local_Var1   ; Caller's Selector
;CJ10             MOV  ES, Local_Var1   ; Destination Selector          ;@TL6
                  LES  DI, Local_Var2p  ; Destination Address           ;CJ10

                  CLD                   ; Go forward
                  REPNZ MOVSB           ; Move it!

                  POP  SI
                  POP  DS
                  POP  DI
                  POP  ES
                .ENDIF                  ; Caller's buffer existence test
              .ENDIF                    ; Inherited Start Code
            .ENDIF                      ; Check errors from shell

            MOV  DX, ES:[DI].ReqReturnCode ; Get return code from shell
          .ENDIF                        ; DOSWRITEQUEUE worked test

          PUSH AX                       ; Save return code
          PUSH Enable_Signals           ; Specify to hold off signals
          CALL DOSHOLDSIGNAL            ; Temporarily enable signals
          POP  AX                       ; Restore return code
        .ENDIF                          ; DOSSEMSET worked test
      .UNTIL < DX ne Try_Again >  NEAR  ; Try until not blocked

      .IF  < nonzero DX >               ; If error from shell side      ;@TL6
        MOV  AX, DX                     ; Save request block error
      .ENDIF

      MOV  SI, ES:[DI].RqStrChildSesID  ; get session id
      MOV  BX, ES:[DI].RqStrChildPID    ; get process id

      .IF  < zero AX >                                                  ;@TL6
;       ************************************************
;       *  Don't close the queue if this is the shell  *
;       *  (ie. the process that created the queue).   *
;       ************************************************
;@TL6   PUSH AX                         ; Save return code from request block
        MOV  DX, ShellPid               ; Get the Shell's Pid           ;@TL6
        MOV  GS, DS:LocalSelector       ; @ of LDT

        .IF  < DX ne GS:LIS_CurProcID > ; Shell PID?                    ;@TL6
          PUSH CX                       ; Que Handle
          CALL DOSCLOSEQUEUE            ; Close the queue
          SUB  AX,AX                    ; Clear return code             ;@TL6
        .ENDIF                          ; Check if this is the shell

;@TL6   POP  AX                         ; Restore meaningful return code
      .ENDIF                            ; DOSWRITEQUEUE or DOSSEMSET worked
    .ENDIF                              ; DOSOPENQUEUE worked

    PUSH AX                             ; Save return code

;   ******************************************
;   *  Free up any memory that we ALLOCATED  *
;   ******************************************
    .IF  < ES:[DI].ReqType e App_Start >     ; If DosStartSession call  ;D1506
      .IF  < ES:[DI].RqInheritOpt ne Inherited > OR ; If not inherited  ;D1506
      .IF  < ES:[DI].RqStrPgmType e VDM_Type >  OR  ; or if VDM         ;D1506
      .IF  < ES:[DI].RqStrPgmType e Windowed_VDM_Type > ; or Windowd VDM;D1506
        MOV  AX, ES:[DI].RqEnvironment[2] ; Sel. of possible allocated memory
        .IF  < nonzero AX >               ; See if one exists             ;@TL6
          PUSH AX                         ; Sel. to free
          CALL DOSFREESEG                 ; Free the memory
        .ENDIF                            ; Freeseg test
      .ENDIF                              ; Non-Inherited Start         ;D1506

      .IF  < ES:[DI].RqInheritOpt ne Inherited >   ; If not inherited   ;D1506
        MOV  AX, ES:[DI].RqStrObjName[2]  ; Sel. of possible allocated memory
        .IF  < nonzero AX >               ; See if one exists             ;@TL6
          PUSH AX                         ; Sel. to free
          CALL DOSFREESEG                 ; Free the memory
        .ENDIF                            ; Freeseg test
      .ENDIF                              ; Non-Inherited Start
    .ENDIF                              ; Non-Inherited Start           ;D1506

;   **********************************************************************
;   *  Move addresses of remaining valid request blocks down 1 slot in   *
;   *  the INSTANCE data for this process.  Decrement Next_Slot to       *
;   *  indicate that there is 1 less valid request block - since we're   *
;   *  going to free it immediately after we update INSTANCE data.       *
;   *  Note:  The decremented Next_Slot value indicates the number of    *
;   *  remaining valid request blocks.                                   *
;   **********************************************************************
    PUSH DS                             ; Save registers for REP MOVSW
    PUSH ES
    PUSH DI
    PUSH SI

;   MOV  AX, INSTANCE                   ; Get access to instance data   ;@TL6
    LDS  DI, DS:SMG_INSTANCE            ; Source is INSTANCE            ;@TL6
    MOV  SI, DI                         ;                               ;@TL6
    ADD  SI, 4                          ; Source is INSTANCE:4          ;@TL6
    PUSH DS                                                             ;@TL6
    POP  ES                             ; Destination is also INSTANCE
;   SUB  DI, DI                         ; Destination is INSTANCE:0     ;@TL6

    DEC  [DI].Next_Slot                 ; Indicate 1 less valid slot    ;@TL6
    MOV  CX, [DI].Next_Slot             ; Calculate # of words to move  ;@TL6
    SHL  CX, 1                          ; Since each slot is a DWORD

    CLD                                 ; Make sure we go forward
    REP  MOVSW                          ; Move'm

    POP  SI                             ; Restore registers from REP MOVSW
    POP  DI
    POP  ES
    POP  DS

    PUSH ES                             ; Selector of memory to free
    PUSH DI                             ; Offset of memory to free (Req. Blk)
    CALL DOSSMFREEMEM                   ; Free the request block

    POP  AX                             ; Restore meaningful return code

    POP  GS                             ; Restore registers
    POP  FS
    POP  CX
    POP  DX
    POP  DS

    LEAVE
    RET

APPSENDREQUEST ENDP

CODE_TUNE1 ENDS                                                         ;MDLL
endif
    END
