page ,132 TITLE $tapii32.asm .listall .386 OPTION READONLY option oldstructs .model FLAT ; .fardata callbacks ;.data CALLBACK SEGMENT PARA USE32 PUBLIC '' public LineCallbackList public PhoneCallbackList LineCallbackList dd 0 PhoneCallbackList dd 0 CALLBACK_NEXT equ 0 CALLBACK_CALLBACK equ 4 CALLBACK_HAPP equ 8 CALLBACK_STRUCT_SIZE equ 0ch CALLBACK ENDS ;GetpWin16Lock proto APIENTRY :ptr dword externDef STDCALL lineInitialize16@20:near32 externDef STDCALL lineShutdown16@4:near32 externDef STDCALL phoneInitialize16@20:near32 externDef STDCALL phoneShutdown16@4:near32 ; ; ;IsBadCodePtr proto APIENTRY :ptr dword ; ; ; ; ; ;GlobalAlloc PROTO NEAR APIENTRY :DWORD, :DWORD ; ; ;GlobalLock PROTO NEAR APIENTRY :DWORD ; ; ;GlobalHandle PROTO NEAR APIENTRY :DWORD ; ; ;GlobalUnlock PROTO NEAR APIENTRY :DWORD ; ; ;GlobalFree PROTO NEAR APIENTRY :DWORD AllocSLCallback PROTO NEAR APIENTRY :DWORD, :DWORD FreeSLCallback PROTO NEAR APIENTRY :DWORD Tapi32ConnectPeerSL PROTO near pszDll16:dword, pszDll32:dword externDef InitThkSL :near32 externDef FdThkCommon :near32 ;TapiThunkInit PROTO near pCB32Tab:dword ;TapiThunkTerminate PROTO near pCB32Tab:dword TapiThkConnectPeerLS PROTO near pszDll16:dword, pszDll32:dword FT_TapiFThkConnectToFlatThkPeer proto near pszDll16:dword, pszDll32:dword NewData PROTO near NewData2 PROTO near FreeLibrary16 PROTO NEAR STDCALL :DWORD ;* externDef TapiCB32BitTable :near32 .data lp16TapiCallbackThunk dd 0 ; ; Pointer to the Win16 heirarchical critical section. ; ; public Win16Lock ;Win16Lock dd 0 pszDll16 db 'TSP3216s.DLL',0 pszDll32 db 'TSP3216l.TSP',0 cProcessAttach dw 0 ;Count of processes attached to our lib. .code THUNK32 ;-----------------------------------------------------------------------; ; Tapi DLL init routine ; We expect lReason to be either DLL_PROCESS_ATTACH/DETACH (we take ; action on these values) or DLL_THREAD_ATTACH/DETACH (we ignore ; and return success). If there is anything else, we need to check ; specifically and take appropriate action. ;-----------------------------------------------------------------------; DllEntryPoint proc near32 hModule:DWORD, lReason:DWORD, lContext:DWORD ;**** invoke GetpWin16Lock, ADDR Win16Lock mov eax, lReason or eax, eax jz dec_cProcess ;DLL_PROCESS_DETACH? cmp eax, 1 ;DLL_PROCESS_ATTACH? jne initok ;DLL_THREAD_ATTACH/DETACH (we don't do anything) inc cProcessAttach ;Keep count of how many processes attach to our lib. ; Do once-only initialization. ; ; mov al, 0 ; xchg al, fFirstTime ; or al, al ; jnz initThunk cmp cProcessAttach, 1 jne initok ; This means that we've done our once only initialization before, but ; that cProcessAttach went down to zero at some point and is now back ; to 1. When cProcessAttach becomes 0, TapiThunkTerminate gets called ; which frees up our thunk table (by calling UnRegisterCBClient). So ; now we need to do that initialization again by calling TapiThunkInit. ; pushd offset TapiCB32BitTable ; call TapiThunkInit ; jmp initok initThunk: invoke TapiThkConnectPeerLS, ADDR pszDll16, ADDR pszDll32 or eax,eax jz exit ; Now call our first thunk to further initialize. ;******************* pushd offset TapiCB32BitTable ;******************* call TapiThunkInit ; Initialize the flat thunks. invoke FT_TapiFThkConnectToFlatThkPeer, ADDR pszDll16, ADDR pszDll32 or eax,eax jz exit ; Initialize the 16->32 thunks. invoke Tapi32ConnectPeerSL, ADDR pszDll16, ADDR pszDll32 or eax,eax jz exit invoke NewData2 mov word ptr lp16TapiCallbackThunk+2, dx mov word ptr lp16TapiCallbackThunk , ax jmp initok dec_cProcess: ;* pushd offset TapiCB32BitTable ;* call TapiThunkTerminate ;needed just for UnRegisterCBClient ; ; Well, we know we have to get rid of all inits and thunk callbacks for ; this process... ; ;********************************** free all the callback thunks ; ; ; ; ; ; ; ; Run the linked list of callbacks & free 'em all. ; ; ; ; ; ; ; ; ; ; push ebx ; Be polite and save this. ; ; ; ; ; ; mov ebx, LineCallbackList ; Get the line list anchor ; ; ; ; ; ;FreeEmAll: ; ; ; or ebx, ebx ; At last entry? ; ; ; jz DoneFreez ; Yup, go away. ; ; ; ; ; ; ; ; ; ; ; Call lineShutdown on this behalf ; ; ; ; ; ; ; push ebx ; Save this - just in case ; ; ; push [ebx+CALLBACK_HAPP] ; Push the hLineApp ; ; ; call lineShutdown16@4 ; Shut em down. ; ; ; pop ebx ; Get back our value. ; ; ; ; ; ; push [ebx+CALLBACK_CALLBACK] ; Push the thunk thing. ; ; ; call FreeSLCallback ; Be free... ; ; ; ; ; ; push ebx ; Use this before freeing it. ; ; ; ; ; ; mov ebx, [ebx+CALLBACK_NEXT] ; Get next node in the list. ; ; ; ; ; ; ; ; ; ; ; Free the node's memory chunk. ; ; ; ; ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; jmp FreeEmAll ; Do the walk of life. ; ; ; ; ; ;DoneFreez: ; ; ; ; ; ; mov LineCallbackList, 0 ; Just in case... ; ; ; ; ; ; ; ; ; mov ebx, PhoneCallbackList ; Get the phone list anchor ; ; ;FreeEmAll2: ; ; ; or ebx, ebx ; At last entry? ; ; ; jz DoneFreez2 ; Yup, go away. ; ; ; ; ; ; ; ; ; ; ; Call phoneShutdown on this behalf ; ; ; ; ; ; ; push ebx ; Save this - just in case ; ; ; push [ebx+CALLBACK_HAPP] ; Push the hPhoneApp ; ; ; call phoneShutdown16@4 ; ; ; pop ebx ; Get back our value. ; ; ; ; ; ; push [ebx+CALLBACK_CALLBACK] ; Push the thunk thing. ; ; ; call FreeSLCallback ; Be free... ; ; ; ; ; ; push ebx ; Use this before freeing it. ; ; ; ; ; ; mov ebx, [ebx+CALLBACK_NEXT] ; Get next node in the list. ; ; ; ; ; ; ; ; ; ; ; Free the node's memory chunk. ; ; ; ; ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; jmp FreeEmAll2 ; Do the walk of life. ; ; ; ; ; ;DoneFreez2: ; ; ; ; ; ; mov PhoneCallbackList, 0 ; Just in case... ; ; ; ; ; ; pop ebx ; Get the saved value. dec cProcessAttach ;Update count of attached processes. cmp cProcessAttach, 0 jne initok ;********************************** ; ; It would have been really cool to have implemented orthogonal thunks, but ; that didn't happen in M7 because of time pressure and because of potential ; destabilizing effects. ; The load count of tapi.dll at this point is 2 (one each for the two ; thunk scripts that we have (flat and 16-bit thunks)). So we need to ; call FreeLibrary16 twice to make sure that tapi.dll gets unloaded after ; this point. ; So why does tapi.dll hang around even after the last app. that attached ; to it is gone? Well, per AtsushiK, that's because it uses the same loading ; technology as kernel, gdi and user. Those libraries never needed to be ; freed, so no one ever wrote the code to free them. That means that the ; following hack will work. What's more, it will always work. ; ---DeepakA ; invoke GetTapiHInst invoke NewData push eax push eax push eax call FreeLibrary16 call FreeLibrary16 call FreeLibrary16 initok: mov eax, 1 ;return success exit: ret DllEntryPoint endp ; end DllEntryPoint ; ; ;;**************************************************************************** ; ; ;;**************************************************************************** ; ; ;lineInitialize proc near32 lphApp:DWORD, hInstance:DWORD, lpfnCallback:DWORD, lpsAppName:DWORD, lpdwNumDevs:DWORD ; ; ; ; ; ; ; ; ; ; ; First, check the code pointer ; ; ; ; ; ; ; push lpfnCallback ; Push the apps callback address ; ; ; call IsBadCodePtr ; Is is a valid code pointer? ; ; ; or eax, eax ; ; ; jnz BadCodePointer ; Nope. Go away. ; ; ; ; ; ; ; ; ; push CALLBACK_STRUCT_SIZE ; ; ; push 42h ;(GMEM_MOVABLE + GMEM_ZEROINIT) ; ; ; call GlobalAlloc ; Get some memory for a new node. ; ; ; push eax ; ; ; call GlobalLock ; ; ; ; ; ; or eax, eax ; Did the call fail? ; ; ; jz No_mo_mem ; Yerp - go away. ; ; ; ; ; ; push ebx ; Be polite and save this. ; ; ; ; ; ; push eax ; We'll need this later (newnode ptr) ; ; ; mov ebx, eax ; We'll also be using it very soon. ; ; ; ; ; ; push lpfnCallback ; Save 32bit address of callback ; ; ; push lp16TapiCallbackThunk ; 16bit callback gotten at init time ; ; ; call AllocSLCallback ; Get a callback ; ; ; ; ; ; ; check return code (0 = failed) ; ; ; or eax, eax ; Did we fail? ; ; ; jz No_mo_callbacks ; Yes, go away. ; ; ; ; ; ; mov [ebx+CALLBACK_CALLBACK], eax ;Save the callback address ; ; ; ; ; ; push lpdwNumDevs ; Prepare to call lineInitialize ; ; ; push lpsAppName ; Prepare to call lineInitialize ; ; ; push eax ; Prepare to call lineInitialize ; ; ; push hInstance ; Prepare to call lineInitialize ; ; ; push lphApp ; Prepare to call lineInitialize ; ; ; call lineInitialize16@20 ; ; ; ; ; ; or eax, eax ; Did lineInit fail? ; ; ; jnz BadReturnCode ; Yup, go away. ; ; ; ; ; ; ; ; ; ; ; Now that everything went well, add the new node to the list. ; ; ; ; ; ; ; pop ebx ; Get the node pointer ; ; ; ; ; ; mov eax, LineCallbackList ; Get pointer to first node ; ; ; mov [ebx+CALLBACK_NEXT], eax ; Fix chain ; ; ; mov LineCallbackList, ebx ; Insert this new node as first ; ; ; ; ; ; mov eax, [lphApp] ; ; ; mov eax, [eax] ; ; ; mov [ebx+CALLBACK_HAPP], eax ; Save the hLineApp ; ; ; ; ; ; xor eax, eax ; Set return code (we got 0 from init). ; ; ; pop ebx ; Get back the saved value. ; ; ; ; ; ; ret ; ; ; ; ; ; ; ; ;BadReturnCode: ; ; ; xchg [esp], eax ; Get the struct pointer ; ; ; ; ; ; push eax ; We're gonna use this in a sec... ; ; ; ; ; ; push [eax+CALLBACK_CALLBACK] ; Push the callback address ; ; ; call FreeSLCallback ; Free the thunk callback ; ; ; ; ; ; ; ; ; ; ; We've already pushed eax (the newnode pointer) so we don't gotta do ; ; ; ; it here. ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Free the newnode chunk of mem. ; ; ; ; ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; pop eax ; Get return code. ; ; ; pop ebx ; Get back the saved value. ; ; ; ret ; ; ; ; ; ; ; ; ;BadCodePointer: ; ; ; mov eax, 80000035h ; ; ; pop ebx ; Get back the saved value. ; ; ; ret ; ; ; ; ; ; ; ; ;No_mo_callbacks: ; ; ; pop eax ; Get out struct pointer ; ; ; ; ; ; ; Free it. ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; pop ebx ; Get back the saved value. ; ; ; ; ; ;No_mo_mem: ; ; ; mov eax, 80000044h ; ; ; ret ; ; ; ; ; ;lineInitialize endp ; ; ; ; ; ; ; ; ;;**************************************************************************** ; ; ;;**************************************************************************** ; ; ;lineShutdown proc near32 hApp:DWORD ; ; ; ; ; ; push ebx ; Be polite and save this. ; ; ; push ecx ; Be polite and save this. ; ; ; ; ; ; push hApp ; ; ; ; ; ; call lineShutdown16@4 ; ; ; ; ; ; push eax ; Save the return code. ; ; ; ; ; ; ; ; ; ; ; Run the linked list of callbacks looking for this hLineApp ; ; ; ; ; ; ; ; ; ; mov eax, LineCallbackList ; Get the list anchor ; ; ; mov ebx, offset LineCallbackList ; The previous node. ; ; ; mov ecx, hApp ; That which we seek. ; ; ; ; ; ;LoopAMundo: ; ; ; or eax, eax ; At last entry? ; ; ; jz NotFound ; Yup. Musta not been found. ; ; ; ; ; ; cmp [eax+CALLBACK_HAPP], ecx ; Is this that which we seek? ; ; ; je GotIt ; Yep - shure is. ; ; ; ; ; ; mov ebx, eax ; Update the "previous" pointer. ; ; ; mov eax, [eax+CALLBACK_NEXT] ; Get next node in the list ; ; ; jmp LoopAMundo ; Do the walk of life. ; ; ; ; ; ;GotIt: ; ; ; push eax ; We're gonna need this in a sec... ; ; ; ; ; ; push [eax+CALLBACK_CALLBACK] ; Push the thunk thing. ; ; ; call FreeSLCallback ; Be free... ; ; ; ; ; ; ; ; ; ; ; Now take it out of the list. ; ; ; ; ; ; ; ; ; ; pop eax ; Get back the node pointer. ; ; ; ; ; ; push [eax+CALLBACK_NEXT] ; Get this "next" pointer. ; ; ; pop [ebx+CALLBACK_NEXT] ; and fix the chain. ; ; ; ; ; ; push eax ; Push the trash node pointer ; ; ; ; ; ; ; Free it. ; ; ; ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; ; ; ;NotFound: ; ; ; ; ; ; ; ; How is this possible? ; ; ; ; Oh well. We can do nothing here. ; ; ; ; ; ; ; ; ; ; ; ; ; pop eax ; Retrieve the return code. ; ; ; ; ; ; pop ecx ; Get the saved value. ; ; ; pop ebx ; Get the saved value. ; ; ; ; ; ; ret ; ; ; ; ; ; ; ; ;lineShutdown endp ; ; ; ; ; ; ; ; ;;**************************************************************************** ; ; ;;**************************************************************************** ; ; ;phoneInitialize proc near32 lphApp:DWORD, hInstance:DWORD, lpfnCallback:DWORD, lpsAppName:DWORD, lpdwNumDevs:DWORD ; ; ; ; ; ; ; ; ; ; ; First, check the code pointer ; ; ; ; ; ; ; push lpfnCallback ; Push the apps callback address ; ; ; call IsBadCodePtr ; Is is a valid code pointer? ; ; ; or eax, eax ; ; ; jnz BadCodePointer ; Nope. Go away. ; ; ; ; ; ; ; ; ; push CALLBACK_STRUCT_SIZE ; ; ; push 42h ;(GMEM_MOVABLE + GMEM_ZEROINIT) ; ; ; call GlobalAlloc ; Get some memory for a new node. ; ; ; push eax ; ; ; call GlobalLock ; ; ; ; ; ; or eax, eax ; Did the call fail? ; ; ; jz No_mo_mem ; Yerp - go away. ; ; ; ; ; ; push ebx ; Be polite and save this. ; ; ; ; ; ; push eax ; We'll need this later (newnode ptr) ; ; ; mov ebx, eax ; We'll also be using it very soon. ; ; ; ; ; ; push lpfnCallback ; Save 32bit address of callback ; ; ; push lp16TapiCallbackThunk ; 16bit callback gotten at init time ; ; ; call AllocSLCallback ; Get a callback ; ; ; ; ; ; ; check return code (0 = failed) ; ; ; or eax, eax ; Did we fail? ; ; ; jz No_mo_callbacks ; Yes, go away. ; ; ; ; ; ; mov [ebx+CALLBACK_CALLBACK], eax ;Save the callback address ; ; ; ; ; ; push lpdwNumDevs ; Prepare to call phoneInitialize ; ; ; push lpsAppName ; Prepare to call phoneInitialize ; ; ; push eax ; Prepare to call phoneInitialize ; ; ; push hInstance ; Prepare to call phoneInitialize ; ; ; push lphApp ; Prepare to call phoneInitialize ; ; ; call phoneInitialize16@20 ; ; ; ; ; ; or eax, eax ; Did phoneInit fail? ; ; ; jnz BadReturnCode ; Yup, go away. ; ; ; ; ; ; ; ; ; ; ; Now that everything went well, add the new node to the list. ; ; ; ; ; ; ; pop ebx ; Get the node pointer ; ; ; mov eax, PhoneCallbackList ; Get pointer to first node ; ; ; mov [ebx+CALLBACK_NEXT], eax ; Fix chain ; ; ; mov PhoneCallbackList, ebx ; Insert this new node as first ; ; ; ; ; ; mov eax, [lphApp] ; ; ; mov eax, [eax] ; ; ; mov [ebx+CALLBACK_HAPP], eax ; Save the hPhoneApp ; ; ; ; ; ; xor eax, eax ; Set return code (we got 0 from init). ; ; ; pop ebx ; Get back the saved value. ; ; ; ; ; ; ret ; ; ; ; ; ; ; ; ;BadReturnCode: ; ; ; xchg [esp], eax ; Get the struct pointer ; ; ; ; ; ; push eax ; We're gonna use this in a sec... ; ; ; ; ; ; push [eax+CALLBACK_CALLBACK] ; Push the callback address ; ; ; call FreeSLCallback ; Free the thunk callback ; ; ; ; ; ; ; ; ; ; ; We've already pushed eax (the newnode pointer) so we don't gotta do ; ; ; ; it here. ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; Free the newnode chunk of mem. ; ; ; ; ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; pop eax ; Get return code. ; ; ; pop ebx ; Get back the saved value. ; ; ; ret ; ; ; ; ; ; ; ; ;BadCodePointer: ; ; ; mov eax, 90000035h ; ; ; pop ebx ; Get back the saved value. ; ; ; ret ; ; ; ; ; ; ; ; ;No_mo_callbacks: ; ; ; pop eax ; Get out struct pointer ; ; ; ; ; ; ; Free it. ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; pop ebx ; Get back the saved value. ; ; ; ; ; ;No_mo_mem: ; ; ; mov eax, 90000044h ; ; ; ret ; ; ; ; ; ;phoneInitialize endp ; ; ; ; ; ; ; ; ;;phoneInitialize proc near32 lphApp:DWORD, hInstance:DWORD, lpfnCallback:DWORD, lpsAppName:DWORD, lpdwNumDevs:DWORD ; ; ;; ; ; ;; ; ; ; ;; ; First, check the code pointer ; ; ;; ; ; ; ;; push lpfnCallback ; ; ;; call IsBadCodePtr ; ; ;; or eax, eax ; ; ;; jz GoodPointer ; ; ;; ; ; ;; mov eax, 90000035h ; ; ;; ret ; ; ;; ; ; ;;GoodPointer: ; ; ;; push lpdwNumDevs ; ; ;; push lpsAppName ; ; ;; ; ; ;; push lpfnCallback ; ; ;; push lp16TapiCallbackThunk ; ; ;; call AllocSLCallback ; ; ;; ; ; ;; ; check return code (0 = failed) ; ; ;; or eax, eax ; ; ;; jz No_mo_callbacks ; ; ;; ; ; ;; ; ; ;; mov TapiCallbackThunk, eax ; ; ;; push eax ; ; ;; ; ; ;; push hInstance ; ; ;; push lphApp ; ; ;; ; ; ;; call phoneInitialize16@20 ; ; ;; ; ; ;; ret ; ; ;; ; ; ;; ; ; ;;No_mo_callbacks: ; ; ;; mov eax, 90000044h ; ; ;; ret ; ; ;; ; ; ;;phoneInitialize endp ; ; ; ; ; ; ; ; ;;**************************************************************************** ; ; ;;**************************************************************************** ; ; ;phoneShutdown proc near32 hApp:DWORD ; ; ; ; ; ; push ebx ; Be polite and save this. ; ; ; push ecx ; Be polite and save this. ; ; ; ; ; ; push hApp ; ; ; ; ; ; call phoneShutdown16@4 ; ; ; ; ; ; push eax ; Save the return code. ; ; ; ; ; ; ; ; ; ; ; Run the linked list of callbacks looking for this hPhoneApp ; ; ; ; ; ; ; ; ; ; mov eax, PhoneCallbackList ; Get the list anchor ; ; ; mov ebx, offset PhoneCallbackList ; The previous node. ; ; ; mov ecx, hApp ; That which we seek. ; ; ; ; ; ;LoopAMundo: ; ; ; or eax, eax ; At last entry? ; ; ; jz NotFound ; Yup. Musta not been found. ; ; ; ; ; ; cmp [eax+CALLBACK_HAPP], ecx ; Is this that which we seek? ; ; ; je GotIt ; Yep - shure is. ; ; ; ; ; ; mov ebx, eax ; Update the "previous" pointer. ; ; ; mov eax, [eax+CALLBACK_NEXT] ; Get next node in the list ; ; ; jmp LoopAMundo ; Do the walk of life. ; ; ; ; ; ;GotIt: ; ; ; push eax ; We're gonna need this in a sec... ; ; ; ; ; ; push [eax+CALLBACK_CALLBACK] ; Push the thunk thing. ; ; ; call FreeSLCallback ; Be free... ; ; ; ; ; ; ; ; ; ; ; Now take it out of the list. ; ; ; ; ; ; ; ; ; ; pop eax ; Get back the node pointer. ; ; ; ; ; ; push [eax+CALLBACK_NEXT] ; Get this "next" pointer. ; ; ; pop [ebx+CALLBACK_NEXT] ; and fix the chain. ; ; ; ; ; ; push eax ; Push the trash node pointer ; ; ; ; ; ; ; Free it. ; ; ; ; ; ; call GlobalHandle ; ; ; push eax ; ; ; push eax ; ; ; call GlobalUnlock ; ; ; call GlobalFree ; ; ; ; ; ; ; ; ;NotFound: ; ; ; ; ; ; ; ; How is this possible? ; ; ; ; Oh well. We can do nothing here. ; ; ; ; ; ; ; ; ; ; ; ; ; pop eax ; Retrieve the return code. ; ; ; ; ; ; pop ecx ; Get the saved value. ; ; ; pop ebx ; Get the saved value. ; ; ; ; ; ; ret ; ; ; ; ; ; ; ; ; ; ; ;; push hApp ; ; ;; ; ; ;; call phoneShutdown16@4 ; ; ;; ; ; ;; cmp TapiCallbackThunk, 0 ; ; ;; je Dont_free_me ; ; ;; ; ; ;; push eax ; We're gonna need this... ; ; ;; ; ; ;; push TapiCallbackThunk ; ; ;; call FreeSLCallback ; ; ;; ; ; ;; pop eax ; Get back our _real_ return code... ; ; ;; ; ; ;; mov TapiCallbackThunk, 0 ; ; ;; ; ; ;; ; ; ;;Dont_free_me: ; ; ;; ; ; ;; ret ; ; ;; ; ; ;phoneShutdown endp ; ; ; end