594 lines
17 KiB
C
594 lines
17 KiB
C
//---------------------------------------------------------------------------
|
|
//
|
|
// TrackME.C (TrackMouseEvent)
|
|
//
|
|
// Created by: Sankar on 1/24/96
|
|
//
|
|
// What:
|
|
// This emulates the TrackMouseEvent() API for the Nashville project
|
|
// in comctl32.dll
|
|
//
|
|
// How:
|
|
// This subclasses the given window to get mouse messages and uses a
|
|
// high frequency timer to learn about mouse leaves.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "ctlspriv.h"
|
|
|
|
#ifdef TrackMouseEvent
|
|
#undef TrackMouseEvent
|
|
#endif
|
|
|
|
extern const TCHAR FAR c_szTMEdata[];
|
|
|
|
extern DWORD g_dwHoverSelectTimeout;
|
|
|
|
#define ID_MOUSEHOVER 0xFFFFFFF0L
|
|
#define ID_MOUSELEAVE 0xFFFFFFF1L
|
|
|
|
#define TME_MOUSELEAVE_TIME (GetDoubleClickTime() / 5)
|
|
|
|
#define IsKeyDown(Key) (GetKeyState(Key) & 0x8000)
|
|
|
|
// This is the structure whose pointer gets added as a property of a window
|
|
// being tracked.
|
|
typedef struct tagTMEDATA {
|
|
TRACKMOUSEEVENT TrackMouseEvent;
|
|
RECT rcMouseHover; //In screen co-ordinates.
|
|
} TMEDATA, FAR *LPTMEDATA;
|
|
|
|
|
|
void NEAR TME_ResetMouseHover(LPTRACKMOUSEEVENT lpTME, LPTMEDATA lpTMEdata);
|
|
LRESULT CALLBACK TME_SubclassProc(HWND hwnd, UINT message, WPARAM wParam,
|
|
LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData);
|
|
|
|
LPTMEDATA NEAR GetTMEdata(HWND hwnd)
|
|
{
|
|
LPTMEDATA lpTMEdata;
|
|
|
|
GetWindowSubclass(hwnd, TME_SubclassProc, 0, (ULONG_PTR *)&lpTMEdata);
|
|
|
|
return lpTMEdata;
|
|
}
|
|
|
|
void NEAR TME_PostMouseLeave(HWND hwnd)
|
|
{
|
|
PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L);
|
|
}
|
|
|
|
void NEAR TME_CancelMouseLeave(LPTMEDATA lpTMEdata)
|
|
{
|
|
if(!(lpTMEdata->TrackMouseEvent.dwFlags & TME_LEAVE))
|
|
return;
|
|
|
|
// Remove the flag.
|
|
lpTMEdata->TrackMouseEvent.dwFlags &= ~(TME_LEAVE);
|
|
|
|
// We leave the timer set here since our hover implementation uses it too.
|
|
// TME_CancelTracking will kill it later.
|
|
}
|
|
|
|
void NEAR TME_CancelMouseHover(LPTMEDATA lpTMEdata)
|
|
{
|
|
if(!(lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER))
|
|
return;
|
|
|
|
lpTMEdata->TrackMouseEvent.dwFlags &= ~(TME_HOVER);
|
|
|
|
KillTimer(lpTMEdata->TrackMouseEvent.hwndTrack, ID_MOUSEHOVER);
|
|
}
|
|
|
|
void NEAR TME_CancelTracking(LPTMEDATA lpTMEdata)
|
|
{
|
|
HWND hwndTrack;
|
|
|
|
//If either MouseLeave or MouseHover is ON, don't cancel tracking.
|
|
if(lpTMEdata->TrackMouseEvent.dwFlags & (TME_HOVER | TME_LEAVE))
|
|
return;
|
|
|
|
hwndTrack = lpTMEdata->TrackMouseEvent.hwndTrack;
|
|
|
|
// Uninstall our subclass callback.
|
|
RemoveWindowSubclass(hwndTrack, TME_SubclassProc, 0);
|
|
|
|
// Kill the mouseleave timer.
|
|
KillTimer(hwndTrack, ID_MOUSELEAVE);
|
|
|
|
// Free the tracking data.
|
|
LocalFree((HANDLE)lpTMEdata);
|
|
}
|
|
|
|
void NEAR TME_RemoveAllTracking(LPTMEDATA lpTMEdata)
|
|
{
|
|
TME_CancelMouseLeave(lpTMEdata);
|
|
TME_CancelMouseHover(lpTMEdata);
|
|
TME_CancelTracking(lpTMEdata);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// TME_MouseHasLeft()
|
|
// The mouse has left the region being tracked. Send the MOUSELEAVE msg
|
|
// and then cancel all tracking.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void NEAR TME_MouseHasLeft(LPTMEDATA lpTMEdata)
|
|
{
|
|
DWORD dwFlags;
|
|
|
|
//Is WM_MOUSELEAVE notification requied?
|
|
if((dwFlags = lpTMEdata->TrackMouseEvent.dwFlags) & TME_LEAVE)
|
|
TME_PostMouseLeave(lpTMEdata->TrackMouseEvent.hwndTrack); //Then, do it!
|
|
|
|
// Cancel all the tracking since the mouse has left.
|
|
TME_RemoveAllTracking(lpTMEdata);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// TME_SubclassWndProc()
|
|
//
|
|
// The subclass proc used for TrackMouseEvent()...!
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
|
|
LRESULT CALLBACK TME_SubclassProc(HWND hwnd, UINT message, WPARAM wParam,
|
|
LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData)
|
|
{
|
|
LPTMEDATA lpTMEdata = (LPTMEDATA)dwRefData;
|
|
|
|
ASSERT(lpTMEdata);
|
|
|
|
switch(message)
|
|
{
|
|
case WM_DESTROY:
|
|
case WM_NCDESTROY:
|
|
TME_RemoveAllTracking(lpTMEdata);
|
|
break;
|
|
|
|
case WM_ENTERMENULOOP:
|
|
// If the window being tracked enters menu mode, then we need to
|
|
// act asif the mouse has left.
|
|
// NOTE: Because when we are in menu mode, the SCREEN_CAPTURE has occurred
|
|
// and we don't see any mouse moves. This is the only way out!
|
|
|
|
// Post mouse leave and cancel all tracking!
|
|
TME_MouseHasLeft(lpTMEdata);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_NCLBUTTONDOWN:
|
|
case WM_NCLBUTTONUP:
|
|
case WM_NCMBUTTONDOWN:
|
|
case WM_NCMBUTTONUP:
|
|
case WM_NCRBUTTONDOWN:
|
|
case WM_NCRBUTTONUP:
|
|
//Whenever there is a mouse click, reset mouse hover.
|
|
if(lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER)
|
|
TME_ResetMouseHover(&(lpTMEdata->TrackMouseEvent), lpTMEdata);
|
|
break;
|
|
|
|
case WM_NCMOUSEMOVE:
|
|
TME_MouseHasLeft(lpTMEdata);
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
POINT Pt;
|
|
|
|
Pt.x = GET_X_LPARAM(lParam);
|
|
Pt.y = GET_Y_LPARAM(lParam);
|
|
|
|
ClientToScreen(hwnd, &Pt);
|
|
|
|
//Check if the mouse is within the hover rect.
|
|
if((lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER) &&
|
|
!PtInRect(&(lpTMEdata->rcMouseHover), Pt))
|
|
TME_ResetMouseHover(&(lpTMEdata->TrackMouseEvent), lpTMEdata);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return DefSubclassProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// TME_CheckInWindow()
|
|
//
|
|
// This get the current cursor position and checks if it still lies in the
|
|
// "valid" area.
|
|
// Returns TRUE, if it lies in the valid area.
|
|
// FALSE, otherwise.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
|
|
BOOL NEAR TME_CheckInWindow(LPTRACKMOUSEEVENT lpTME, LPPOINT lpPt)
|
|
{
|
|
POINT pt;
|
|
HWND hwnd; // Given window.
|
|
HWND hwndPt; //Window from the given point.
|
|
HWND hwndCapture;
|
|
|
|
hwnd = lpTME->hwndTrack; //Given window handle.
|
|
|
|
//See if anyone has captured the mouse input.
|
|
if((hwndCapture = GetCapture()) && IsWindow(hwndCapture))
|
|
{
|
|
// If tracking is required for a window other than the one that
|
|
// has the capture, forget it! It is not possible!
|
|
|
|
if(hwndCapture != hwnd)
|
|
return(FALSE);
|
|
}
|
|
|
|
GetCursorPos(&pt); //Get cursor point in screen co-ordinates.
|
|
|
|
if (!hwndCapture)
|
|
{
|
|
hwndPt = WindowFromPoint(pt);
|
|
|
|
if (!hwndPt || !IsWindow(hwndPt) || (hwnd != hwndPt))
|
|
return FALSE;
|
|
|
|
if (SendMessage(hwnd, WM_NCHITTEST, 0,
|
|
MAKELPARAM((SHORT)pt.x, (SHORT)pt.y)) != HTCLIENT)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// The current point falls on the same area of the same window.
|
|
// It is a valid location.
|
|
if (lpPt)
|
|
*lpPt = pt;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// TME_MouseLeaveTimer()
|
|
//
|
|
// Timer callback for WM_MOUSELEAVE generation and cancelling HOVER!
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
VOID CALLBACK TME_MouseLeaveTimer(HWND hwnd, UINT msg, UINT_PTR id, DWORD dwTime)
|
|
{
|
|
LPTMEDATA lpTMEdata;
|
|
|
|
if(!(lpTMEdata = GetTMEdata(hwnd)))
|
|
return;
|
|
|
|
// YIELD!!!
|
|
if(TME_CheckInWindow(&(lpTMEdata->TrackMouseEvent), NULL))
|
|
return; //The mouse is still in the valid region. So, do nothing.
|
|
|
|
if (!IsWindow(hwnd))
|
|
return;
|
|
|
|
//The mouse has left the valid region. So, post mouse-leave if requested
|
|
//Because we are cancelling mouse-leave, we need to cancel mouse-hover too!
|
|
// There can be no hover tracking, if the mouse has already left!
|
|
|
|
TME_MouseHasLeft(lpTMEdata);
|
|
}
|
|
|
|
|
|
WPARAM NEAR GetMouseKeyFlags()
|
|
{
|
|
WPARAM wParam = 0;
|
|
|
|
if (IsKeyDown(VK_LBUTTON))
|
|
wParam |= MK_LBUTTON;
|
|
if (IsKeyDown(VK_RBUTTON))
|
|
wParam |= MK_RBUTTON;
|
|
if (IsKeyDown(VK_MBUTTON))
|
|
wParam |= MK_MBUTTON;
|
|
if (IsKeyDown(VK_SHIFT))
|
|
wParam |= MK_SHIFT;
|
|
if (IsKeyDown(VK_CONTROL))
|
|
wParam |= MK_CONTROL;
|
|
|
|
return wParam;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// TME_MouseHoverTimer()
|
|
//
|
|
// Timer callback for WM_MOUSEHOVER/WM_NCMOUSEHOVER generation.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
VOID CALLBACK TME_MouseHoverTimer(HWND hwnd, UINT msg, UINT_PTR id, DWORD dwTime)
|
|
{
|
|
POINT pt;
|
|
WPARAM wParam;
|
|
LPTMEDATA lpTMEdata;
|
|
|
|
if (!(lpTMEdata = GetTMEdata(hwnd)))
|
|
return;
|
|
|
|
//BOGUS: we can not detect hwndSysModal from here!
|
|
//Also, tracking is for a per-window basis now!
|
|
//
|
|
// BOGUS: We don't have to worry about JournalPlayback?
|
|
//pt = fJournalPlayback? Lpq(hwnd->hq)->ptLast : ptTrueCursor;
|
|
|
|
// YIELD!!!
|
|
if(!TME_CheckInWindow(&(lpTMEdata->TrackMouseEvent), &pt))
|
|
{
|
|
// Mouse has left the valid region of the window. So, cancel all
|
|
// the tracking.
|
|
TME_MouseHasLeft(lpTMEdata);
|
|
return;
|
|
}
|
|
|
|
if (!IsWindow(hwnd))
|
|
return;
|
|
|
|
if (!PtInRect(&(lpTMEdata->rcMouseHover), pt))
|
|
{
|
|
// Mouse has gone out of the hover rectangle. Reset the hovering.
|
|
TME_ResetMouseHover(&(lpTMEdata->TrackMouseEvent), lpTMEdata);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// set up to check the tolerance and
|
|
//
|
|
wParam = GetMouseKeyFlags();
|
|
ScreenToClient(hwnd, &pt);
|
|
|
|
//Mouse is still within the hover rectangle. Let's post hover msg
|
|
PostMessage(hwnd, WM_MOUSEHOVER, wParam, MAKELPARAM(pt.x, pt.y));
|
|
|
|
//And then cancel the hovering.
|
|
TME_CancelMouseHover(lpTMEdata);
|
|
TME_CancelTracking(lpTMEdata); //Cancel the tracking, if needed.
|
|
}
|
|
|
|
BOOL NEAR TME_SubclassWnd(LPTMEDATA lpTMEdata)
|
|
{
|
|
BOOL fResult;
|
|
|
|
fResult = SetWindowSubclass(lpTMEdata->TrackMouseEvent.hwndTrack,
|
|
TME_SubclassProc, 0, (ULONG_PTR)lpTMEdata);
|
|
|
|
ASSERT(fResult);
|
|
return fResult;
|
|
}
|
|
|
|
void NEAR TME_ResetMouseLeave(LPTRACKMOUSEEVENT lpTME, LPTMEDATA lpTMEdata)
|
|
{
|
|
//See if already MouseLeave is being tracked.
|
|
if(lpTMEdata->TrackMouseEvent.dwFlags & TME_LEAVE)
|
|
return; // Nothing else to do.
|
|
|
|
//Else, set the flag.
|
|
lpTMEdata ->TrackMouseEvent.dwFlags |= TME_LEAVE;
|
|
|
|
//Set the high frequency Timer.
|
|
SetTimer(lpTME->hwndTrack, ID_MOUSELEAVE, TME_MOUSELEAVE_TIME, TME_MouseLeaveTimer);
|
|
}
|
|
|
|
void NEAR TME_ResetMouseHover(LPTRACKMOUSEEVENT lpTME, LPTMEDATA lpTMEdata)
|
|
{
|
|
DWORD dwMouseHoverTime;
|
|
POINT pt;
|
|
|
|
// Even if the hover tracking is already happening, the caller might
|
|
// change the timer value, restart the timer or change the hover
|
|
// rectangle.
|
|
lpTMEdata->TrackMouseEvent.dwFlags |= TME_HOVER;
|
|
|
|
dwMouseHoverTime = lpTME->dwHoverTime;
|
|
if (!dwMouseHoverTime || (dwMouseHoverTime == HOVER_DEFAULT))
|
|
dwMouseHoverTime = (g_dwHoverSelectTimeout ? g_dwHoverSelectTimeout : GetDoubleClickTime()*4/5); // BUGBUG: Can't we remember this?
|
|
GetCursorPos(&pt);
|
|
|
|
//
|
|
// update the tolerance rectangle for the hover window.
|
|
//
|
|
*((POINT *)&(lpTMEdata->rcMouseHover.left)) = *((POINT *)&(lpTMEdata->rcMouseHover.right)) = pt;
|
|
|
|
//BOGUS: Can we use globals to remeber these metrics. What about NT?
|
|
InflateRect(&(lpTMEdata->rcMouseHover), g_cxDoubleClk/2, g_cyDoubleClk/2);
|
|
|
|
// We need to remember the timer interval we are setting. This value
|
|
// needs to be returned when TME_QUERY is used.
|
|
lpTME->dwHoverTime = dwMouseHoverTime;
|
|
lpTMEdata->TrackMouseEvent.dwHoverTime = dwMouseHoverTime;
|
|
SetTimer(lpTME->hwndTrack, ID_MOUSEHOVER, dwMouseHoverTime, TME_MouseHoverTimer);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// QueryTrackMouseEvent()
|
|
//
|
|
// Fills in a TRACKMOUSEEVENT structure describing current tracking state
|
|
// for a given window. The given window is in lpTME->hwndTrack.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
BOOL NEAR QueryTrackMouseEvent(LPTRACKMOUSEEVENT lpTME)
|
|
{
|
|
HWND hwndTrack;
|
|
LPTMEDATA lpTMEdata;
|
|
|
|
//
|
|
// if there isn't anything being tracked get out
|
|
//
|
|
if((!(hwndTrack = lpTME->hwndTrack)) || !IsWindow(hwndTrack))
|
|
goto Sorry;
|
|
|
|
if(!(lpTMEdata = GetTMEdata(hwndTrack)))
|
|
goto Sorry;
|
|
|
|
if(!(lpTMEdata->TrackMouseEvent.dwFlags & (TME_HOVER | TME_LEAVE)))
|
|
goto Sorry;
|
|
|
|
//
|
|
// fill in the requested information
|
|
//
|
|
lpTME->dwFlags = lpTMEdata->TrackMouseEvent.dwFlags;
|
|
|
|
if (lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER)
|
|
lpTME->dwHoverTime = lpTMEdata->TrackMouseEvent.dwHoverTime;
|
|
else
|
|
lpTME->dwHoverTime = 0;
|
|
|
|
goto Done;
|
|
|
|
Sorry:
|
|
// zero out the struct
|
|
lpTME->dwFlags = 0;
|
|
lpTME->hwndTrack = NULL;
|
|
lpTME->dwHoverTime = 0;
|
|
|
|
Done:
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// EmulateTrackMouseEvent()
|
|
//
|
|
// emulate API for requesting extended mouse notifications (hover, leave...)
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
BOOL WINAPI EmulateTrackMouseEvent(LPTRACKMOUSEEVENT lpTME)
|
|
{
|
|
HWND hwnd;
|
|
DWORD dwFlags;
|
|
LPTMEDATA lpTMEdata;
|
|
|
|
if (lpTME->dwFlags & ~TME_VALID)
|
|
return FALSE;
|
|
|
|
#ifdef TME_NONCLIENT
|
|
//
|
|
// this implementation does not handle TME_NONCLIENT (anymore)
|
|
// we agreed with the NT team to rip it out until the system uses it...
|
|
//
|
|
if (lpTME->dwFlags & TME_NONCLIENT)
|
|
return FALSE;
|
|
#endif
|
|
|
|
//
|
|
// implement queries separately
|
|
//
|
|
if (lpTME->dwFlags & TME_QUERY)
|
|
return QueryTrackMouseEvent(lpTME);
|
|
|
|
//
|
|
// Check the validity of the request.
|
|
//
|
|
hwnd = lpTME->hwndTrack;
|
|
dwFlags = lpTME->dwFlags;
|
|
|
|
if (!IsWindow(hwnd))
|
|
return FALSE;
|
|
|
|
// Check if the mouse is currently in a valid position
|
|
// Use GetCursorPos() to get the mouse position and then check if
|
|
// it lies within the client/non-client portion of the window as
|
|
// defined in this call;
|
|
|
|
// YIELD!!!
|
|
if(!TME_CheckInWindow(lpTME, NULL))
|
|
{
|
|
//If the mouse leave is requested when the mouse is already outside
|
|
// the window, then generate one mouse leave immly.
|
|
if((dwFlags & TME_LEAVE) && !(dwFlags & TME_CANCEL))
|
|
TME_PostMouseLeave(hwnd);
|
|
|
|
//Because it is an invalid request, we return immly.
|
|
return(TRUE);
|
|
}
|
|
|
|
if (!IsWindow(hwnd))
|
|
return FALSE;
|
|
|
|
//It is a valid request, either to install or remove tracking.
|
|
|
|
//See if we already have tracking for this window.
|
|
if(!(lpTMEdata = GetTMEdata(hwnd)))
|
|
{
|
|
//We are not tracking this window already.
|
|
if(dwFlags & TME_CANCEL)
|
|
return(TRUE); //There is nothing to cancel; Ignore!
|
|
|
|
//Do they want any tracking at all?
|
|
ASSERT(dwFlags & (TME_HOVER | TME_LEAVE));
|
|
|
|
//Allocate global mem to remember the tracking data
|
|
if(!(lpTMEdata = (LPTMEDATA)LocalAlloc(LPTR, sizeof(TMEDATA))))
|
|
return(FALSE);
|
|
|
|
// copy in the hwnd
|
|
lpTMEdata->TrackMouseEvent.hwndTrack = lpTME->hwndTrack;
|
|
|
|
// Make sure our subclass callback is installed.
|
|
if (!TME_SubclassWnd(lpTMEdata))
|
|
{
|
|
TME_CancelTracking(lpTMEdata);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//Else fall through!
|
|
|
|
if(dwFlags & TME_CANCEL)
|
|
{
|
|
if(dwFlags & TME_HOVER)
|
|
TME_CancelMouseHover(lpTMEdata);
|
|
|
|
if(dwFlags & TME_LEAVE)
|
|
TME_CancelMouseLeave(lpTMEdata);
|
|
|
|
// If both hover and leave are cancelled, then we don't need any
|
|
// tracking.
|
|
TME_CancelTracking(lpTMEdata);
|
|
|
|
return(TRUE); // Cancelled whatever they asked for.
|
|
}
|
|
|
|
if(dwFlags & TME_HOVER)
|
|
TME_ResetMouseHover(lpTME, lpTMEdata);
|
|
|
|
if(dwFlags & TME_LEAVE)
|
|
TME_ResetMouseLeave(lpTME, lpTMEdata);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
typedef BOOL (WINAPI* PFNTME)(LPTRACKMOUSEEVENT);
|
|
|
|
PFNTME g_pfnTME = NULL;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// _TrackMouseEvent() entrypoint
|
|
//
|
|
// calls TrackMouseEvent if present, otherwise uses EmulateTrackMouseEvent
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
BOOL WINAPI _TrackMouseEvent(LPTRACKMOUSEEVENT lpTME)
|
|
{
|
|
if (!g_pfnTME)
|
|
{
|
|
HMODULE hmod = GetModuleHandle(TEXT("USER32"));
|
|
|
|
if (hmod)
|
|
g_pfnTME = (PFNTME)GetProcAddress(hmod, "TrackMouseEvent");
|
|
|
|
if (!g_pfnTME)
|
|
g_pfnTME = EmulateTrackMouseEvent;
|
|
}
|
|
|
|
return g_pfnTME(lpTME);
|
|
}
|
|
|