/****************************************************************************** Copyright (C) Microsoft Corporation 1985-1991. All rights reserved. Title: TIME.C : MMSYSTEM TIMER API Version: 1.00 *****************************************************************************/ // // ***DANGER WARNING**** // // none of these functions in this file need the default data segment // so we undefine BUILDDLL, if you write code in this file that needs // DS == DGROUP be warned! // // NOTE: most of this code is interupt time enterable, so we don't want // it touching DGROUP anyway! // #undef BUILDDLL #include #include "mmsystem.h" #include "mmddk.h" #include "mmsysi.h" #include "drvr.h" #include "thunks.h" #define MIN_RES 1 #define MIN_DELAY 6 // // Define moveable code for timer interface. // #pragma alloc_text( RARE, timeGetDevCaps ) extern SZCODE szTimerDrv[]; // see init.c DWORD dwLastGetTime = 0; // last TimeGetTime return value can be bigger than system TimeGetTime DWORD dwRealLastGetTime = 0; // last system TimeGetTime return value DWORD pfnDelayTimeGetTime = 0; // 32-bit function that sleeps for 1ms and returns if TimeGetTime flag applied // look in timeTimeGetTime and WOWDelayTimeGetTime in wow32 // // Define the init code for this file. // #pragma alloc_text( INIT, TimeInit ) /**************************************************************************** @doc EXTERNAL @api UINT | timeGetSystemTime | This function retrieves the system time in milliseconds. The system time is the time elapsed since Windows was started. @parm LPMMTIME | lpTime | Specifies a far pointer to an data structure. @parm UINT | wSize | Specifies the size of the structure. @rdesc Returns zero. The system time is returned in the field of the structure. @comm The time is always returned in milliseconds. @xref timeGetTime ****************************************************************************/ UINT WINAPI timeGetSystemTime( LPMMTIME lpTime, UINT wSize ) { // // !!!WARNING DS is not setup right!!! see above // if (wSize < sizeof(MMTIME)) return TIMERR_STRUCT; lpTime->u.ms = timeGetTime(); lpTime->wType = TIME_MS; return TIMERR_NOERROR; } /**************************************************************************** @doc EXTERNAL @api UINT | timeSetEvent | This function sets up a timed callback event. The event can be a one-time event or a periodic event. Once activated, the event calls the specified callback function. @parm UINT | wDelay | Specifies the event period in milliseconds. If the delay is less than the minimum period supported by the timer, or greater than the maximum period supported by the timer, the function returns an error. @parm UINT | wResolution | Specifies the accuracy of the delay in milliseconds. The resolution of the timer event increases with smaller

values. To reduce system overhead, use the maximum

value appropriate for your application. @parm LPTIMECALLBACK | lpFunction | Specifies the procedure address of a callback function that is called once upon expiration of a one-shot event or periodically upon expiration of periodic events. @parm DWORD | dwUser | Contains user-supplied callback data. @parm UINT | wFlags | Specifies the type of timer event, using one of the following flags: @flag TIME_ONESHOT | Event occurs once, after

milliseconds. @flag TIME_PERIODIC | Event occurs every

milliseconds. @rdesc Returns an ID code that identifies the timer event. Returns NULL if the timer event was not created. The ID code is also passed to the callback function. @comm Using this function to generate a high-frequency periodic-delay event (with a period less than 10 milliseconds) can consume a significant portion of the system CPU bandwidth. Any call to for a periodic-delay timer must be paired with a call to . The callback function must reside in a DLL. You don't have to use to get a procedure-instance address for the callback function. @cb void CALLBACK | TimeFunc | is a placeholder for the application-supplied function name. The actual name must be exported by including it in the EXPORTS statement of the module-definition file for the DLL. @parm UINT | wID | The ID of the timer event. This is the ID returned by . @parm UINT | wMsg | Not used. @parm DWORD | dwUser | User instance data supplied to the

parameter of . @parm DWORD | dw1 | Not used. @parm DWORD | dw2 | Not used. @comm Because the callback is accessed at interrupt time, it must reside in a DLL, and its code segment must be specified as FIXED in the module-definition file for the DLL. Any data that the callback accesses must be in a FIXED data segment as well. The callback may not make any system calls except for , , , , , , , and . @xref timeKillEvent timeBeginPeriod timeEndPeriod ****************************************************************************/ UINT WINAPI timeSetEvent( UINT wDelay, UINT wResolution, LPTIMECALLBACK lpFunction, DWORD dwUser, UINT wFlags ) { // // !!!WARNING DS is not setup right!!! see above // TIMEREVENT timerEvent; V_TCALLBACK(lpFunction, MMSYSERR_INVALPARAM); // // the first time this is called init the stacks // !!!this assumes the first caller will not be at interupt time!! // // if (!(WinFlags & WF_ENHANCED)) // timeStackInit(); wDelay = max( MIN_DELAY, wDelay ); wResolution = max( MIN_RES, wResolution ); timerEvent.wDelay = wDelay; timerEvent.wResolution = wResolution; timerEvent.lpFunction = lpFunction; timerEvent.dwUser = dwUser; timerEvent.wFlags = wFlags; return (UINT)timeMessage( TDD_SETTIMEREVENT, (LPARAM)(LPVOID)&timerEvent, (LPARAM)GetCurrentTask() ); } /**************************************************************************** @doc EXTERNAL @api UINT | timeGetDevCaps | This function queries the timer device to determine its capabilities. @parm LPTIMECAPS | lpTimeCaps | Specifies a far pointer to a structure. This structure is filled with information about the capabilities of the timer device. @parm UINT | wSize | Specifies the size of the structure. @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if it fails to return the timer device capabilities. ****************************************************************************/ UINT WINAPI timeGetDevCaps( LPTIMECAPS lpTimeCaps, UINT wSize ) { // // !!!WARNING DS is not setup right!!! see above // return (UINT)timeMessage( TDD_GETDEVCAPS, (LPARAM)lpTimeCaps, (LPARAM)(DWORD)wSize); } /******************************Public*Routine******************************\ * timeBeginPeriod * * @doc EXTERNAL * * @api WORD | timeBeginPeriod | This function sets the minimum (lowest * number of milliseconds) timer resolution that an application or * driver is going to use. Call this function immediately before starting * to use timer-event services, and call immediately * after finishing with the timer-event services. * * @parm WORD | wPeriod | Specifies the minimum timer-event resolution * that the application or driver will use. * * @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified *

resolution value is out of range. * * @xref timeEndPeriod timeSetEvent * * @comm For each call to , you must call * with a matching

value. * An application or driver can make multiple calls to , * as long as each call is matched with a * call. * * * * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ UINT WINAPI timeBeginPeriod( UINT uPeriod ) { uPeriod = max( MIN_RES, uPeriod ); return (UINT)timeMessage( TDD_BEGINMINPERIOD, (LPARAM)uPeriod, 0L ); } /******************************Public*Routine******************************\ * timeEndPeriod * * @doc EXTERNAL * * @api WORD | timeEndPeriod | This function clears a previously set * minimum (lowest number of milliseconds) timer resolution that an * application or driver is going to use. Call this function * immediately after using timer event services. * * @parm WORD | wPeriod | Specifies the minimum timer-event resolution * value specified in the previous call to . * * @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified *

resolution value is out of range. * * @xref timeBeginPeriod timeSetEvent * * @comm For each call to , you must call * with a matching

value. * An application or driver can make multiple calls to , * as long as each call is matched with a * call. * * * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ UINT WINAPI timeEndPeriod( UINT uPeriod ) { uPeriod = max( MIN_RES, uPeriod ); return (UINT)timeMessage( TDD_ENDMINPERIOD, (LPARAM)uPeriod, 0L ); } /******************************Public*Routine******************************\ * * timeKillEvent * * @doc EXTERNAL * * @api WORD | timeKillEvent | This functions destroys a specified timer * callback event. * * @parm WORD | wID | Identifies the event to be destroyed. * * @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the * specified timer event does not exist. * * @comm The timer event ID specified by

must be an ID * returned by . * * @xref timeSetEvent * * * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ UINT WINAPI timeKillEvent( UINT wID ) { if ( 0 == wID ) { return 0; } return (UINT)timeMessage( TDD_KILLTIMEREVENT, (LPARAM)wID, 0L ); } /******************************Public*Routine******************************\ * timeGetTime * * @doc EXTERNAL * * @api DWORD | timeGetTime | This function retrieves the system time * in milliseconds. The system time is the time elapsed since * Windows was started. * * @rdesc The return value is the system time in milliseconds. * * @comm The only difference between this function and * the function is * uses the standard multimedia time structure to return * the system time. The function has less overhead than * . * * @xref timeGetSystemTime * * * @comment: on faster machines timeGetTime can return the same value * and some apps will take diff (0) to divide and fault * to prevent that call DelayTimeGetTime which will check if it is one * of the known apps that do that and sleep if necessary * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ DWORD WINAPI timeGetTime( void ) { DWORD dwGetTime; DWORD bDelay = 0; if (pfnDelayTimeGetTime == 0) { DWORD hmodWow32; hmodWow32 = LoadLibraryEx32W("wow32.dll", 0, 0); pfnDelayTimeGetTime = GetProcAddress32W(hmodWow32, "WOWDelayTimeGetTime"); } RepeatTGT: dwGetTime = timeMessage( TDD_GETSYSTEMTIME, 0L, 0L ); // check if it wrapped around if (dwGetTime < dwRealLastGetTime) { dwLastGetTime = dwRealLastGetTime = dwGetTime; return dwGetTime; } dwRealLastGetTime = dwGetTime; if (dwGetTime == dwLastGetTime) { if (!bDelay) { bDelay = (DWORD) CallProc32W((LPVOID)pfnDelayTimeGetTime,(DWORD)0,(DWORD)0); if(bDelay) { goto RepeatTGT; } } else { dwGetTime = ++dwLastGetTime; } } dwLastGetTime = dwGetTime; return dwGetTime; } /**************************************************************************** @doc INTERNAL @api BOOL | TimeInit | This function initialises the timer services. @rdesc The return value is TRUE if the services are initialised, FALSE if an error occurs. @comm it is not a FATAL error if a timer driver is not installed, this routine will allways return TRUE ****************************************************************************/ BOOL NEAR PASCAL TimeInit(void) { OpenDriver(szTimerDrv, NULL, 0L) ; return TRUE; }