/*++ * * WOW v1.0 * * Copyright (c) 1991, Microsoft Corporation * * WUSER31.C * WOW32 16-bit Win 3.1 User API support * * History: * Created 16-Mar-1992 by Chandan S. Chauhan (ChandanC) --*/ #include "precomp.h" #pragma hdrstop MODNAME(wuser31.c); ULONG FASTCALL WU32DlgDirSelectComboBoxEx(PVDMFRAME pFrame) { ULONG ul; PSZ psz2; VPVOID vp; register PDLGDIRSELECTCOMBOBOXEX16 parg16; GETARGPTR(pFrame, sizeof(DLGDIRSELECTCOMBOBOXEX16), parg16); GETVDMPTR(parg16->f2, INT32(parg16->f3), psz2); vp = parg16->f2; // note: this calls back to 16-bit code and could invalidate the flat ptrs ul = GETBOOL16(DlgDirSelectComboBoxEx( HWND32(parg16->f1), psz2, INT32(parg16->f3), WORD32(parg16->f4) // we zero-extend window IDs everywhere )); // special case to keep common dialog structs in sync (see wcommdlg.c) Check_ComDlg_pszptr(CURRENTPTD()->CommDlgTd, vp); FLUSHVDMPTR(parg16->f2, INT32(parg16->f3), psz2); FREEVDMPTR(psz2); FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32DlgDirSelectEx(PVDMFRAME pFrame) { ULONG ul; PSZ psz2; VPVOID vp; register PDLGDIRSELECTEX16 parg16; GETARGPTR(pFrame, sizeof(DLGDIRSELECTEX16), parg16); GETVDMPTR(parg16->f2, INT32(parg16->f3), psz2); vp = parg16->f2; ul = GETBOOL16(DlgDirSelectEx( HWND32(parg16->f1), psz2, INT32(parg16->f3), WORD32(parg16->f4) )); // special case to keep common dialog structs in sync (see wcommdlg.c) Check_ComDlg_pszptr(CURRENTPTD()->CommDlgTd, vp); FLUSHVDMPTR(parg16->f2, INT32(parg16->f3), psz2); FREEVDMPTR(psz2); FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32GetClipCursor(PVDMFRAME pFrame) { RECT Rect; register PGETCLIPCURSOR16 parg16; GETARGPTR(pFrame, sizeof(GETCLIPCURSOR16), parg16); GetClipCursor(&Rect); PUTRECT16(parg16->f1, &Rect); FREEARGPTR(parg16); RETURN (0); // GetClipCursor has no return value } ULONG FASTCALL WU32GetDCEx(PVDMFRAME pFrame) { ULONG ul; register PGETDCEX16 parg16; HAND16 htask16 = pFrame->wTDB; GETARGPTR(pFrame, sizeof(GETDCEX16), parg16); // This may need the same logic as WU32GetDC() and WU32GetWindowDC() to // prevent a handle leak during the life of a given task (or at least until // the app calls GetDC() or GetWindowDC() which will empty the cache): // // if (CACHENOTEMPTY()) { // ReleaseCachedDCs(htask16, parg16->f1, 0, 0, SRCHDC_TASK16_HWND16); // } // CURRENTPTD()->ulLastDesktophDC = 0; // // We may not be removing cached DC's in this case because the cache code // has no concept of "clip regions" which are associated with this API. I // kind of doubt it because the SRCHDC_TASK16_HWND16 flag specified in the // other two cases wlll cause all cached DC's to be released // indiscriminantly. ul = GETHDC16(GetDCEx(HWND32(parg16->f1), HRGN32(parg16->f2), DWORD32(parg16->f3))); if (ul) StoreDC(htask16, parg16->f1, (HAND16)ul); FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32RedrawWindow(PVDMFRAME pFrame) { ULONG ul; RECT Rect, *p2; register PREDRAWWINDOW16 parg16; GETARGPTR(pFrame, sizeof(REDRAWWINDOW16), parg16); p2 = GETRECT16 (parg16->f2, &Rect); ul = GETBOOL16(RedrawWindow(HWND32(parg16->f1), p2, HRGN32(parg16->f3), WORD32(parg16->f4))); FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32ScrollWindowEx(PVDMFRAME pFrame) { ULONG ul; register PSCROLLWINDOWEX16 parg16; RECT RectScroll, *p4; RECT RectClip, *p5; RECT RectUpdate; GETARGPTR(pFrame, sizeof(SCROLLWINDOWEX16), parg16); p4 = GETRECT16 (parg16->f4, &RectScroll); p5 = GETRECT16 (parg16->f5, &RectClip); ul = GETINT16(ScrollWindowEx(HWND32(parg16->f1), INT32(parg16->f2), INT32(parg16->f3), p4, p5, HRGN32(parg16->f6), &RectUpdate, UINT32(parg16->f8))); PUTRECT16 (parg16->f7, &RectUpdate); FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32SystemParametersInfo(PVDMFRAME pFrame) { ULONG ul = 0; register PSYSTEMPARAMETERSINFO16 parg16; UINT wParam; LONG vParam; LOGFONT lf; INT iMouse[3]; PVOID lpvParam; PWORD16 lpw; PDWORD16 lpdw; RECT rect; #ifndef _X86_ DWORD dwSize; LPBYTE lpFree = NULL; #endif GETARGPTR(pFrame, sizeof(SYSTEMPARAMETERSINFO16), parg16); // Assume these parameters fly straight through; fix them up per option // if they don't wParam = parg16->f2; lpvParam = &vParam; switch (parg16->f1) { case SPI_GETICONTITLELOGFONT: wParam = sizeof(LOGFONT); lpvParam = &lf; break; case SPI_SETICONTITLELOGFONT: GETLOGFONT16(parg16->f3, &lf); wParam = sizeof(LOGFONT); lpvParam = &lf; break; case SPI_GETMOUSE: case SPI_SETMOUSE: lpvParam = iMouse; break; case SPI_SETDESKPATTERN: // For the pattern if wParam == -1 then no string for lpvParam copy as is if (parg16->f2 == 0xFFFF) { wParam = 0xFFFFFFFF; lpvParam = (PVOID)parg16->f3; break; } // Otherwise fall through and do a string check case SPI_SETDESKWALLPAPER: // lpvParam (f3) is may be 0,-1 or a string if (parg16->f3 == 0xFFFF) { lpvParam = (PVOID)0xFFFFFFFF; break; } if (parg16->f3 == 0) { lpvParam = (PVOID)NULL; break; } // Otherwise fall through and do a string copy case SPI_LANGDRIVER: GETPSZPTR(parg16->f3, lpvParam); break; // // SPI_GET structures pointed to by pvParam, size in first dword of struct. // Note all these assume the Win16 and Win32 structures are equal. // These are all new for Win95 and thankfully that's true. However unlike // Win95 we need to ensure the buffer passed to Win32 is aligned on RISC. // To have common code to thunk all these various structures, we align to // 16 bytes. // case SPI_GETACCESSTIMEOUT: case SPI_GETANIMATION: case SPI_GETNONCLIENTMETRICS: case SPI_GETMINIMIZEDMETRICS: case SPI_GETICONMETRICS: case SPI_GETFILTERKEYS: case SPI_GETSTICKYKEYS: case SPI_GETTOGGLEKEYS: case SPI_GETMOUSEKEYS: case SPI_GETSOUNDSENTRY: #ifndef _X86_ GETMISCPTR(parg16->f3, lpdw); dwSize = *lpdw; lpFree = malloc_w(dwSize + 16); if(lpFree) { lpvParam = (LPVOID)(((DWORD)lpFree + 16) & ~(16 - 1)); *(PDWORD16)lpvParam = dwSize; break; } else { lpvParam = NULL; } #endif // otherwise fall through to simple struct case // // SPI_SET structures pointed to by pvParam, size in first dword of struct. // Note all these assume the Win16 and Win32 structures are equal. // These are all new for Win95 and thankfully that's true. However unlike // Win95 we need to ensure the buffer passed to Win32 is aligned on RISC. // To have common code to thunk all these various structures, we align to // 16 bytes. // case SPI_SETANIMATION: case SPI_SETICONMETRICS: case SPI_SETMINIMIZEDMETRICS: case SPI_SETNONCLIENTMETRICS: case SPI_SETACCESSTIMEOUT: #ifndef _X86_ GETMISCPTR(parg16->f3, lpdw); dwSize = *lpdw; lpFree = malloc_w(dwSize + 16); if(lpFree) { lpvParam = (LPVOID)(((DWORD)lpFree + 16) & ~(16 - 1)); RtlCopyMemory(lpvParam, lpdw, dwSize); break; } else { lpvParam = NULL; } #endif // otherwise fall through to simple struct case // // structures pointed to by pvParam, size in uiParam or first dword. // Note all these assume the Win16 and Win32 structures are equal. // These are all new for Win95 and thankfully that's true. // case SPI_GETHIGHCONTRAST: case SPI_GETSERIALKEYS: case SPI_SETDEFAULTINPUTLANG: case SPI_SETFILTERKEYS: case SPI_SETHIGHCONTRAST: case SPI_SETMOUSEKEYS: case SPI_SETSERIALKEYS: case SPI_SETSHOWSOUNDS: case SPI_SETSOUNDSENTRY: case SPI_SETSTICKYKEYS: case SPI_SETTOGGLEKEYS: GETMISCPTR(parg16->f3, lpvParam); break; // // pvParam points to WORD or BOOL // case SPI_GETBEEP: case SPI_GETBORDER: case SPI_GETDRAGFULLWINDOWS: case SPI_GETFASTTASKSWITCH: case SPI_GETFONTSMOOTHING: case SPI_GETGRIDGRANULARITY: case SPI_GETICONTITLEWRAP: case SPI_GETKEYBOARDSPEED: case SPI_GETKEYBOARDDELAY: case SPI_GETKEYBOARDPREF: case SPI_GETLOWPOWERACTIVE: case SPI_GETLOWPOWERTIMEOUT: case SPI_GETMENUDROPALIGNMENT: case SPI_GETMOUSETRAILS: case SPI_GETPOWEROFFACTIVE: case SPI_GETPOWEROFFTIMEOUT: case SPI_GETSCREENREADER: case SPI_GETSCREENSAVEACTIVE: case SPI_GETSCREENSAVETIMEOUT: case SPI_GETSHOWSOUNDS: case SPI_SCREENSAVERRUNNING: break; // // pvParam points to DWORD // case SPI_GETDEFAULTINPUTLANG: break; // // pvParam not used // case SPI_GETWINDOWSEXTENSION: case SPI_ICONHORIZONTALSPACING: case SPI_ICONVERTICALSPACING: case SPI_SETBEEP: case SPI_SETBORDER: case SPI_SETDOUBLECLICKTIME: case SPI_SETDOUBLECLKHEIGHT: case SPI_SETDOUBLECLKWIDTH: case SPI_SETDRAGFULLWINDOWS: case SPI_SETDRAGHEIGHT: case SPI_SETDRAGWIDTH: case SPI_SETFASTTASKSWITCH: case SPI_SETFONTSMOOTHING: case SPI_SETGRIDGRANULARITY: case SPI_SETHANDHELD: case SPI_SETICONTITLEWRAP: case SPI_SETKEYBOARDDELAY: case SPI_SETKEYBOARDPREF: case SPI_SETKEYBOARDSPEED: case SPI_SETLANGTOGGLE: case SPI_SETLOWPOWERACTIVE: case SPI_SETLOWPOWERTIMEOUT: case SPI_SETMENUDROPALIGNMENT: case SPI_SETMOUSEBUTTONSWAP: case SPI_SETMOUSETRAILS: case SPI_SETPENWINDOWS: case SPI_SETPOWEROFFACTIVE: case SPI_SETPOWEROFFTIMEOUT: case SPI_SETSCREENREADER: case SPI_SETSCREENSAVEACTIVE: case SPI_SETSCREENSAVETIMEOUT: break; // // pvParam points to a RECT // case SPI_GETWORKAREA: case SPI_SETWORKAREA: GETRECT16(parg16->f3, &rect); lpvParam = ▭ break; default: #ifdef DEBUG { DWORD dwSaveOptions = flOptions; flOptions |= OPT_DEBUG; LOGDEBUG(0, ("WARNING SystemParametersInfo case %d not pre-thunked in WOW!\n", parg16->f1)); flOptions = dwSaveOptions; } #endif break; } ul = SystemParametersInfo( UINT32(parg16->f1), wParam, lpvParam, UINT32(parg16->f4) ); switch (parg16->f1) { case SPI_GETICONTITLELOGFONT: PUTLOGFONT16(parg16->f3, sizeof(LOGFONT), lpvParam); break; case SPI_SETICONTITLELOGFONT: break; case SPI_GETMOUSE: case SPI_SETMOUSE: PUTINTARRAY16(parg16->f3, 3, lpvParam); break; case SPI_LANGDRIVER: case SPI_SETDESKWALLPAPER: FREEPSZPTR(lpvParam); break; case SPI_ICONHORIZONTALSPACING: case SPI_ICONVERTICALSPACING: // optional outee if (!parg16->f3) break; // fall through // // pvParam points to WORD or BOOL // case SPI_GETBEEP: case SPI_GETBORDER: case SPI_GETDRAGFULLWINDOWS: case SPI_GETFASTTASKSWITCH: case SPI_GETFONTSMOOTHING: case SPI_GETGRIDGRANULARITY: case SPI_GETICONTITLEWRAP: case SPI_GETKEYBOARDSPEED: case SPI_GETKEYBOARDDELAY: case SPI_GETKEYBOARDPREF: case SPI_GETLOWPOWERACTIVE: case SPI_GETLOWPOWERTIMEOUT: case SPI_GETMENUDROPALIGNMENT: case SPI_GETMOUSETRAILS: case SPI_GETPOWEROFFACTIVE: case SPI_GETPOWEROFFTIMEOUT: case SPI_GETSCREENREADER: case SPI_GETSCREENSAVEACTIVE: case SPI_GETSCREENSAVETIMEOUT: case SPI_GETSHOWSOUNDS: case SPI_SCREENSAVERRUNNING: GETVDMPTR(FETCHDWORD(parg16->f3), sizeof(*lpw), lpw); *lpw = (WORD)(*(LPLONG)lpvParam); FLUSHVDMPTR(FETCHDWORD(parg16->f3), sizeof(*lpw), lpw); FREEVDMPTR(lpw); break; // // pvParam points to DWORD // case SPI_GETDEFAULTINPUTLANG: GETVDMPTR(FETCHDWORD(parg16->f3), sizeof(*lpdw), lpdw); *lpdw = *(LPDWORD)lpvParam; FLUSHVDMPTR(FETCHDWORD(parg16->f3), sizeof(*lpdw), lpdw); FREEVDMPTR(lpdw); break; // // SPI_GET structures pointed to by pvParam, size in first dword of struct. // Note all these assume the Win16 and Win32 structures are equal. // These are all new for Win95 and thankfully that's true. However unlike // Win95 we need to ensure the buffer passed to Win32 is aligned. In order // to have common code to thunk all these various structures, we align to // 16 bytes. // case SPI_GETACCESSTIMEOUT: case SPI_GETANIMATION: case SPI_GETNONCLIENTMETRICS: case SPI_GETMINIMIZEDMETRICS: case SPI_GETICONMETRICS: case SPI_GETFILTERKEYS: case SPI_GETSTICKYKEYS: case SPI_GETTOGGLEKEYS: case SPI_GETMOUSEKEYS: case SPI_GETSOUNDSENTRY: #ifndef _X86_ RtlCopyMemory(lpdw, lpvParam, dwSize); FREEMISCPTR(lpdw); break; #endif // otherwise fall through to simple struct case // // SPI_SET structures pointed to by pvParam, size in first dword of struct. // Note all these assume the Win16 and Win32 structures are equal. // These are all new for Win95 and thankfully that's true. However unlike // Win95 we need to ensure the buffer passed to Win32 is aligned. In order // to have common code to thunk all these various structures, we align to // 16 bytes. // case SPI_SETANIMATION: case SPI_SETICONMETRICS: case SPI_SETMINIMIZEDMETRICS: case SPI_SETNONCLIENTMETRICS: case SPI_SETACCESSTIMEOUT: #ifndef _X86_ FREEMISCPTR(lpdw); break; #endif // otherwise fall through to simple struct case // // structures pointed to by pvParam, size in uiParam or first dword. // Note all these assume the Win16 and Win32 structures are equal. // These are all new for Win95 and thankfully that's true. // case SPI_GETHIGHCONTRAST: case SPI_GETSERIALKEYS: case SPI_SETDEFAULTINPUTLANG: case SPI_SETFILTERKEYS: case SPI_SETHIGHCONTRAST: case SPI_SETMOUSEKEYS: case SPI_SETSERIALKEYS: case SPI_SETSHOWSOUNDS: case SPI_SETSOUNDSENTRY: case SPI_SETSTICKYKEYS: case SPI_SETTOGGLEKEYS: FREEMISCPTR(lpvParam); break; // // pvParam not used // case SPI_GETWINDOWSEXTENSION: case SPI_SETBEEP: case SPI_SETBORDER: case SPI_SETDOUBLECLICKTIME: case SPI_SETDOUBLECLKHEIGHT: case SPI_SETDOUBLECLKWIDTH: case SPI_SETDRAGFULLWINDOWS: case SPI_SETDRAGHEIGHT: case SPI_SETDRAGWIDTH: case SPI_SETFASTTASKSWITCH: case SPI_SETFONTSMOOTHING: case SPI_SETGRIDGRANULARITY: case SPI_SETHANDHELD: case SPI_SETICONTITLEWRAP: case SPI_SETKEYBOARDDELAY: case SPI_SETKEYBOARDPREF: case SPI_SETKEYBOARDSPEED: case SPI_SETLANGTOGGLE: case SPI_SETLOWPOWERACTIVE: case SPI_SETLOWPOWERTIMEOUT: case SPI_SETMENUDROPALIGNMENT: case SPI_SETMOUSEBUTTONSWAP: case SPI_SETMOUSETRAILS: case SPI_SETPENWINDOWS: case SPI_SETPOWEROFFACTIVE: case SPI_SETPOWEROFFTIMEOUT: case SPI_SETSCREENREADER: case SPI_SETSCREENSAVEACTIVE: case SPI_SETSCREENSAVETIMEOUT: break; // // pvParam points to a RECT // case SPI_GETWORKAREA: case SPI_SETWORKAREA: PUTRECT16(parg16->f3, &rect); break; default: #ifdef DEBUG { DWORD dwSaveOptions = flOptions; flOptions |= OPT_DEBUG; LOGDEBUG(0, ("WARNING SystemParametersInfo case %d not post-thunked in WOW!\n", parg16->f1)); flOptions = dwSaveOptions; } #endif break; } #ifndef _X86_ if (lpFree) { free_w(lpFree); } #endif FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32SetWindowPlacement(PVDMFRAME pFrame) { ULONG ul = 0; register PSETWINDOWPLACEMENT16 parg16; WINDOWPLACEMENT wndpl; GETARGPTR(pFrame, sizeof(SETWINDOWPLACEMENT16), parg16); WINDOWPLACEMENT16TO32(parg16->f2, &wndpl); ul = GETBOOL16(SetWindowPlacement(HWND32(parg16->f1), &wndpl)); FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32GetWindowPlacement(PVDMFRAME pFrame) { ULONG ul = 0; register PGETWINDOWPLACEMENT16 parg16; WINDOWPLACEMENT wndpl; GETARGPTR(pFrame, sizeof(GETWINDOWPLACEMENT16), parg16); wndpl.length = sizeof(WINDOWPLACEMENT); ul = GETBOOL16(GetWindowPlacement(HWND32(parg16->f1), &wndpl)); WINDOWPLACEMENT32TO16(parg16->f2, &wndpl); FREEARGPTR(parg16); RETURN (ul); } ULONG FASTCALL WU32GetFreeSystemResources(PVDMFRAME pFrame) { ULONG ul = 90; UNREFERENCED_PARAMETER( pFrame ); RETURN (ul); } ULONG FASTCALL WU32ExitWindowsExec(PVDMFRAME pFrame) { ULONG ul = 0; register PEXITWINDOWSEXEC16 parg16; LPSTR lpstrProgName; LPSTR lpstrCmdLine; UINT lengthProgName; UINT lengthCmdLine; BYTE abT[REGISTRY_BUFFER_SIZE]; GETARGPTR(pFrame, sizeof(EXITWINDOWSEXEC16), parg16); GETPSZPTR(parg16->vpProgName, lpstrProgName); GETPSZPTR(parg16->vpCmdLine, lpstrCmdLine); lengthProgName = (lpstrProgName) ? strlen(lpstrProgName) : 0; lengthCmdLine = (lpstrCmdLine) ? strlen(lpstrCmdLine) : 0; WOW32ASSERT(sizeof(abT) > (lengthProgName+lengthCmdLine+2)); abT[0] = '\0'; // + space + NULL if(sizeof(abT) >= (lengthProgName + lengthCmdLine + 1 + 1)) { if ( lpstrProgName ) { strcpy(abT, lpstrProgName ); } if ( lpstrCmdLine ) { strcat(abT, " " ); strcat(abT, lpstrCmdLine ); } } else { return ul; } // // We write the commandline to registry "WOW/EWExecCmdLine" // If the system logs off successfully, after reboot, we read // the registry and exec the specfied app before launching any // wow app in any wow vdm. We donot launch the app before logoff // because winlogon doesn't allow any app to be execed during // the logoff process. // - nanduri // only one exitwindowsexec call at a time. // if value/key exists, return error. if (!W32EWExecData(EWEXEC_QUERY, abT, sizeof(abT))) { HANDLE hevT; // only one exitwindowsexec call at a time. // if event exits, return error. if (hevT = CreateEvent(NULL, TRUE, FALSE, WOWSZ_EWEXECEVENT)) { if (GetLastError() == 0) { // wake up any waiting threads (in w32ewexecer) SetEvent(hevT); // Write the data to the registry if (W32EWExecData(EWEXEC_SET, abT, strlen(abT)+1)) { DWORD dwlevel; DWORD dwflags; if (!GetProcessShutdownParameters(&dwlevel, &dwflags)) { dwlevel = 0x280; // default level per docs dwflags = 0; } // // 0xff = last system reserved level Logically makes this last user // process to shutdown. This takes care of Multiple WOW VDMs // SetProcessShutdownParameters(0xff, 0); // // EWX_NOTIFY private bit for WOW. Generates queue message // WM_ENDSESSION, if any process cancels logoff/shutdown. if (ExitWindowsEx(EWX_LOGOFF | EWX_NOTIFY, 0)) { MSG msg; // // PeekMessage yields to other WOW tasks. We effectively // freeze the current task by removing all input messages. // Loop terminates only if WM_ENDSESSION message has been // received. This message is posted by winsrv if any process // in the system cancels logoff. // while (TRUE) { if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { if ((msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) || (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) || (msg.message >= WM_NCMOUSEMOVE && msg.message <= WM_NCMBUTTONDBLCLK)) { // don't dispatch the message } else if (msg.message == WM_ENDSESSION) { WOW32ASSERT((msg.hwnd == 0) && (msg.wParam == 0)); break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } } } // // Here if logoff was cancelled. // Set defaults and delete the associated value from registry. // SetProcessShutdownParameters(dwlevel, dwflags); if (!W32EWExecData(EWEXEC_DEL, (LPSTR)NULL, 0)) { WOW32ASSERT(FALSE); } } } CloseHandle(hevT); } } LOGDEBUG(0,("WOW: ExitWindowsExec failed\r\n")); FREEARGPTR(parg16); return 0; } ULONG FASTCALL WU32MapWindowPoints(PVDMFRAME pFrame) { LPPOINT p3; register PMAPWINDOWPOINTS16 parg16; POINT BufferT[128]; GETARGPTR(pFrame, sizeof(MAPWINDOWPOINTS16), parg16); p3 = STACKORHEAPALLOC(parg16->f4 * sizeof(POINT), sizeof(BufferT), BufferT); if ( p3 ) { getpoint16(parg16->f3, parg16->f4, p3); MapWindowPoints( HWND32(parg16->f1), HWND32(parg16->f2), p3, INT32(parg16->f4) ); PUTPOINTARRAY16(parg16->f3, parg16->f4, p3); STACKORHEAPFREE(p3, BufferT); } else { FREEARGPTR(parg16); RETURN(0); } FREEARGPTR(parg16); RETURN(1); }