655 lines
17 KiB
C++
655 lines
17 KiB
C++
#include "wsdueng.h"
|
|
|
|
HINSTANCE g_hinst;
|
|
CDynamicUpdate *g_pDynamicUpdate = NULL;
|
|
DWORD WaitAndPumpMessages(DWORD nCount, LPHANDLE pHandles, DWORD dwWakeMask);
|
|
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved)
|
|
{
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
DisableThreadLibraryCalls(hInstance);
|
|
g_hinst = hInstance;
|
|
}
|
|
else if (dwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Required function to be able to link CDMLIB..
|
|
HMODULE GetModule()
|
|
{
|
|
return g_hinst;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Function Name: SetEstimatedDownloadSpeed
|
|
// Function Description: Sets the Download speed used for download time estimates
|
|
//
|
|
// Function Returns:
|
|
// Nothing
|
|
//
|
|
void WINAPI SetEstimatedDownloadSpeed(DWORD dwBytesPerSecond)
|
|
{
|
|
if (NULL != g_pDynamicUpdate)
|
|
g_pDynamicUpdate->m_dwDownloadSpeedInBytesPerSecond = dwBytesPerSecond;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Function Name: DuInitializeA
|
|
// Function Description: Initializes the DynamicUpdate class and converts the OSVERSIONINFO information into a Platform ID
|
|
//
|
|
// Function Returns:
|
|
// INVALID_HANDLE_VALUE if it fails
|
|
// HANDLE value of 1 if it succeeds
|
|
//
|
|
// NOTE: The use of a HANDLE could allow us to return the address of the DynamicUpdate Object, which was originally intended, but it seemed simpler
|
|
// to just use a global..
|
|
HANDLE WINAPI DuInitializeA(IN LPCSTR pszBasePath, IN LPCSTR pszTempPath, POSVERSIONINFOEXA posviTargetOS, IN LPCSTR pszTargetArch,
|
|
IN LCID lcidTargetLocale, IN BOOL fUnattend, IN BOOL fUpgrade, IN PWINNT32QUERY pfnWinnt32QueryCallback)
|
|
{
|
|
LOG_block("DuInitializeA in DuEng");
|
|
|
|
// parameter validation
|
|
// RogerJ, October 5th, 2000
|
|
if (!pfnWinnt32QueryCallback)
|
|
{
|
|
LOG_error("Callback function pointer invalid");
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
// DONE RogerJ
|
|
|
|
// parse the OSVERSIONINFO struct for the platform ID
|
|
int iPlatformID = 0;
|
|
// The TargetOS Platform ID is based on a couple of things.
|
|
// The Whister Platform ID is the OSVERSIONINFOEX structure with the fields dwMajorVersion and dwMinorVersion set to 5.1
|
|
// The other identifier in the platform ID is whether its i386 or ia64 (64bit) .. This is defined in the pszTargetArch String
|
|
|
|
if (5 == posviTargetOS->dwMajorVersion)
|
|
{
|
|
if (1 == posviTargetOS->dwMinorVersion)
|
|
{
|
|
// Whistler
|
|
if (NULL != StrStrI(pszTargetArch, "i386"))
|
|
{
|
|
iPlatformID = 18; // Whistler x86 (normal)
|
|
}
|
|
else if (NULL != StrStrI(pszTargetArch, "ia64"))
|
|
{
|
|
iPlatformID = 19; // Whistler ia64 (64bit)
|
|
}
|
|
}
|
|
else if (2 == posviTargetOS->dwMinorVersion)
|
|
{
|
|
// Whistler
|
|
if (NULL != StrStrI(pszTargetArch, "i386"))
|
|
{
|
|
iPlatformID = 18; // Whistler x86 (normal)
|
|
}
|
|
else if (NULL != StrStrI(pszTargetArch, "ia64"))
|
|
{
|
|
iPlatformID = 19; // Whistler ia64 (64bit)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0 == iPlatformID)
|
|
{
|
|
// No known Platform ID for DynamicUpdate was found.. Return Error
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
WORD wPlatformSKU = posviTargetOS->wSuiteMask;
|
|
|
|
if (g_pDynamicUpdate)
|
|
{
|
|
// a former call to this function has already initialized an instance of CDynamicUpdate class
|
|
delete g_pDynamicUpdate;
|
|
g_pDynamicUpdate = NULL;
|
|
}
|
|
|
|
|
|
g_pDynamicUpdate = new CDynamicUpdate(iPlatformID, lcidTargetLocale, wPlatformSKU, pszTempPath,
|
|
pszBasePath, pfnWinnt32QueryCallback, posviTargetOS);
|
|
if (NULL == g_pDynamicUpdate)
|
|
{
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return (HANDLE)1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Function Name: DuDoDetection
|
|
// Function Description: Searches the Catalogs on the WU Site to find Updates for setup
|
|
//
|
|
// Function Returns:
|
|
// FALSE if there are no items OR there is an error.. Use GetLastError() for more information.
|
|
// TRUE if it succeeds and there are items to download.
|
|
//
|
|
// Comment: If return value is FALSE and GetLastError return ERROR_NO_MORE_ITEMS there are no items to download.
|
|
//
|
|
// Modified by RogerJ October 6th, 2000
|
|
// --- Added Driver Detection
|
|
BOOL WINAPI DuDoDetection(IN HANDLE hConnection, OUT PDWORD pdwEstimatedTime, OUT PDWORD pdwEstimatedSize)
|
|
{
|
|
LOG_block("DuDoDetection in DuEng");
|
|
|
|
DWORD dwRetSetup, dwRetDriver;
|
|
dwRetSetup = dwRetDriver = 0;
|
|
|
|
if (NULL == g_pDynamicUpdate)
|
|
return FALSE;
|
|
|
|
g_pDynamicUpdate->ClearDownloadItemList();
|
|
dwRetSetup = g_pDynamicUpdate->DoSetupUpdateDetection();
|
|
if (ERROR_SUCCESS != dwRetSetup)
|
|
{
|
|
LOG_error("Failed to get setup update item! --- %d", dwRetSetup);
|
|
g_pDynamicUpdate->PingBack(DU_PINGBACK_SETUPDETECTIONFAILED, 0, NULL, FALSE);
|
|
}
|
|
|
|
// do driver detection here
|
|
if (!g_pDynamicUpdate->DoDriverDetection() ||
|
|
!g_pDynamicUpdate->DoWindowsUpdateDriverDetection())
|
|
{
|
|
LOG_error("Failed to detect driver!");
|
|
dwRetDriver = GetLastError();
|
|
g_pDynamicUpdate->PingBack(DU_PINGBACK_DRIVERDETECTIONFAILED, 0, NULL, FALSE);
|
|
}
|
|
|
|
|
|
if (dwRetSetup && dwRetDriver)
|
|
{
|
|
LOG_error("Both Setup item and Driver detection failed");
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_pDynamicUpdate->m_dwDownloadItemCount > 0)
|
|
{
|
|
g_pDynamicUpdate->UpdateDownloadItemSize();
|
|
*pdwEstimatedSize = g_pDynamicUpdate->m_dwTotalDownloadSize; // size in bytes
|
|
// Time Estimate is based on roughly how long it took us to download the data files.
|
|
if (0 == g_pDynamicUpdate->m_dwDownloadSpeedInBytesPerSecond)
|
|
g_pDynamicUpdate->m_dwDownloadSpeedInBytesPerSecond = 2048; // default to 120k per minute, (2048 bytes per second).
|
|
|
|
*pdwEstimatedTime = g_pDynamicUpdate->m_dwTotalDownloadSize / g_pDynamicUpdate->m_dwDownloadSpeedInBytesPerSecond; // number of seconds
|
|
if (*pdwEstimatedTime == 0)
|
|
*pdwEstimatedTime = 1; // at least one second
|
|
|
|
if (dwRetSetup)
|
|
SetLastError(dwRetSetup);
|
|
if (dwRetDriver)
|
|
SetLastError(dwRetDriver);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// initialize the size and time for setup
|
|
*pdwEstimatedTime = 1;
|
|
*pdwEstimatedSize = 0;
|
|
// At this point there was no error, but we have no items to download,
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
BOOL WINAPI DuBeginDownload(IN HANDLE hConnection, IN HWND hwndNotify)
|
|
{
|
|
if ((NULL == g_pDynamicUpdate) || (NULL == hwndNotify))
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == g_pDynamicUpdate->m_dwDownloadItemCount)
|
|
{
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
PostMessage(hwndNotify, WM_DYNAMIC_UPDATE_COMPLETE, (WPARAM) DU_STATUS_SUCCESS, (LPARAM) NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
g_pDynamicUpdate->SetCallbackHWND(hwndNotify);
|
|
g_pDynamicUpdate->SetAbortDownload(FALSE);
|
|
|
|
if (ERROR_SUCCESS != g_pDynamicUpdate->DownloadFilesAsync())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE; // download has been started
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
void WINAPI DuAbortDownload(IN HANDLE hConnection)
|
|
{
|
|
if (NULL == g_pDynamicUpdate)
|
|
return;
|
|
|
|
g_pDynamicUpdate->SetAbortDownload(TRUE);
|
|
return;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
void WINAPI DuUninitialize(IN HANDLE hConnection)
|
|
{
|
|
if (NULL == g_pDynamicUpdate)
|
|
return;
|
|
|
|
// We want to hold up the Uninitialize process until any other Threads
|
|
// specifically the Download Thread. We are going to wait on the DownloadThreadProc
|
|
// thread handle if it exists. Once the thread finishes, the wait proc will exit
|
|
// and we can continue.
|
|
|
|
if (NULL != g_pDynamicUpdate->m_hDownloadThreadProc)
|
|
WaitAndPumpMessages(1, &g_pDynamicUpdate->m_hDownloadThreadProc, QS_ALLINPUT);
|
|
|
|
delete g_pDynamicUpdate;
|
|
g_pDynamicUpdate = NULL;
|
|
|
|
LOG_close();
|
|
return;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
CDynamicUpdate::CDynamicUpdate(int iPlatformID, LCID lcidLocaleID, WORD wPlatformSKU, LPCSTR pszTempPath, LPCSTR pszDownloadPath, PWINNT32QUERY pfnWinnt32QueryCallback,
|
|
POSVERSIONINFOEXA pVersionInfo)
|
|
: m_iPlatformID(iPlatformID),
|
|
m_lcidLocaleID(lcidLocaleID),
|
|
m_wPlatformSKU(wPlatformSKU),
|
|
m_hwndClientNotify(NULL),
|
|
m_pDownloadItemList(NULL),
|
|
m_dwDownloadItemCount(0),
|
|
m_dwTotalDownloadSize(0),
|
|
m_dwCurrentBytesDownloaded(0),
|
|
m_hInternet(NULL),
|
|
m_hConnect(NULL),
|
|
m_hOpenRequest(NULL),
|
|
m_pV3(NULL),
|
|
m_fAbortDownload(FALSE),
|
|
m_dwLastPercentComplete(0),
|
|
m_dwDownloadSpeedInBytesPerSecond(0),
|
|
m_hDownloadThreadProc(NULL),
|
|
m_pfnWinNT32Query(pfnWinnt32QueryCallback)
|
|
{
|
|
|
|
(void)FixUpV3LocaleID(); // BUG: 435184 - Map 0c0a to 040a for V3 purposes
|
|
|
|
if (NULL != pszTempPath)
|
|
{
|
|
lstrcpy(m_szTempPath, pszTempPath);
|
|
}
|
|
if (NULL != pszDownloadPath)
|
|
{
|
|
lstrcpy(m_szDownloadPath, pszDownloadPath);
|
|
}
|
|
|
|
lstrcpy(m_szCurrentConnectedServer, ""); // initialize to null.
|
|
|
|
CopyMemory((PVOID)&m_VersionInfo, (PVOID)pVersionInfo, sizeof(OSVERSIONINFOEXA));
|
|
|
|
InitializeCriticalSection(&m_cs);
|
|
InitializeCriticalSection(&m_csDownload);
|
|
// m_hDevInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
CDynamicUpdate::~CDynamicUpdate()
|
|
{
|
|
ClearDownloadItemList(); // free up any memory in the download list
|
|
m_arrayHardwareId.RemoveAll();
|
|
if (m_pV3) delete m_pV3;
|
|
m_pV3 = NULL;
|
|
|
|
DeleteCriticalSection(&m_cs);
|
|
DeleteCriticalSection(&m_csDownload);
|
|
SafeInternetCloseHandle(m_hOpenRequest);
|
|
SafeInternetCloseHandle(m_hConnect);
|
|
SafeInternetCloseHandle(m_hInternet);
|
|
SafeCloseHandle(m_hDownloadThreadProc);
|
|
}
|
|
|
|
LPSTR CDynamicUpdate::DuUrlCombine(LPSTR pszDest, LPCSTR pszBase, LPCSTR pszAdd)
|
|
{
|
|
if ((NULL == pszDest) || (NULL == pszBase) || (NULL == pszAdd))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
lstrcpy(pszDest, pszBase);
|
|
int iLen = lstrlen(pszDest);
|
|
if ('/' == pszDest[iLen - 1])
|
|
{
|
|
// already has a trailing slash, check the 'add' string for a preceding slash
|
|
if ('/' == *pszAdd)
|
|
{
|
|
// has a preceding slash, skip it.
|
|
lstrcat(pszDest, pszAdd + 1);
|
|
}
|
|
else
|
|
{
|
|
lstrcat(pszDest, pszAdd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no trailing slash, check the add string for a preceding slash
|
|
if ('/' == *pszAdd)
|
|
{
|
|
// has a preceding slash, Add Normally
|
|
lstrcat(pszDest, pszAdd);
|
|
}
|
|
else
|
|
{
|
|
lstrcat(pszDest, "/");
|
|
lstrcat(pszDest, pszAdd);
|
|
}
|
|
}
|
|
return pszDest;
|
|
}
|
|
|
|
|
|
LPCSTR CDynamicUpdate::GetDuDownloadPath()
|
|
{
|
|
return m_szDownloadPath;
|
|
}
|
|
|
|
LPCSTR CDynamicUpdate::GetDuServerUrl()
|
|
{
|
|
return m_szServerUrl;
|
|
}
|
|
|
|
LPCSTR CDynamicUpdate::GetDuTempPath()
|
|
{
|
|
return m_szTempPath;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
DWORD CDynamicUpdate::DoSetupUpdateDetection()
|
|
{
|
|
if (NULL == m_pV3)
|
|
{
|
|
m_pV3 = new CV31Server(this);
|
|
if (NULL == m_pV3)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (!m_pV3->ReadIdentInfo())
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
if (!m_pV3->GetCatalogPUIDs())
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
if (!m_pV3->GetCatalogs())
|
|
{
|
|
// there was an error reading the catalogs
|
|
return GetLastError();
|
|
}
|
|
if (!m_pV3->ReadCatalogINI())
|
|
{
|
|
return GetLastError();
|
|
}
|
|
if (!m_pV3->UpdateDownloadItemList(m_VersionInfo))
|
|
{
|
|
// there was an error parsing the catalogs and creating the download list.
|
|
return GetLastError();
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
void CDynamicUpdate::AddDownloadItemToList(DOWNLOADITEM *pDownloadItem)
|
|
{
|
|
LOG_block("CDynamicUpdate::AddDownloadItemToList");
|
|
if (NULL == pDownloadItem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (NULL == m_pDownloadItemList) // no drivers in list yet
|
|
{
|
|
m_pDownloadItemList = pDownloadItem;
|
|
}
|
|
else
|
|
{
|
|
|
|
// add to the end of the list
|
|
DOWNLOADITEM *pCurrent = m_pDownloadItemList;
|
|
while (NULL != pCurrent->pNext)
|
|
{
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
|
|
pCurrent->pNext = pDownloadItem;
|
|
pDownloadItem->pPrev = pCurrent;
|
|
}
|
|
|
|
m_dwDownloadItemCount++;
|
|
LOG_out("Item added, %d cab(s), first cab ---\"%s\"", pDownloadItem->iNumberOfCabs, pDownloadItem->mszFileList);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
void CDynamicUpdate::RemoveDownloadItemFromList(DOWNLOADITEM *pDownloadItem)
|
|
{
|
|
if (NULL == pDownloadItem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (NULL == m_pDownloadItemList)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DOWNLOADITEM *pCurrent = m_pDownloadItemList;
|
|
|
|
while (NULL != pCurrent)
|
|
{
|
|
if (pCurrent == pDownloadItem)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
|
|
if ((NULL == pCurrent) || (pCurrent != pDownloadItem))
|
|
{
|
|
return; // unexpected
|
|
}
|
|
|
|
if (NULL == pCurrent->pPrev) // first item in list
|
|
{
|
|
if (NULL == pCurrent->pNext) // only item in list
|
|
{
|
|
m_pDownloadItemList = NULL;
|
|
m_dwDownloadItemCount = 0;
|
|
}
|
|
else
|
|
{
|
|
pCurrent->pNext->pPrev = NULL; // next job becomes first
|
|
m_pDownloadItemList = pCurrent->pNext;
|
|
m_dwDownloadItemCount--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pCurrent->pPrev->pNext = pCurrent->pNext;
|
|
if (NULL != pCurrent->pNext)
|
|
{
|
|
pCurrent->pNext->pPrev = pCurrent->pPrev;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDynamicUpdate::SetCallbackHWND(HWND hwnd)
|
|
{
|
|
m_hwndClientNotify = hwnd;
|
|
}
|
|
|
|
void CDynamicUpdate::SetAbortDownload(BOOL fAbort)
|
|
{
|
|
EnterCriticalSection(&m_cs);
|
|
m_fAbortDownload = fAbort;
|
|
LeaveCriticalSection(&m_cs);
|
|
}
|
|
|
|
void CDynamicUpdate::UpdateDownloadItemSize()
|
|
{
|
|
m_dwTotalDownloadSize = 0;
|
|
DOWNLOADITEM *pCurrent = m_pDownloadItemList;
|
|
while (pCurrent)
|
|
{
|
|
m_dwTotalDownloadSize += pCurrent->dwTotalFileSize;
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
}
|
|
|
|
void CDynamicUpdate::ClearDownloadItemList()
|
|
{
|
|
EnterCriticalSection(&m_csDownload);
|
|
DOWNLOADITEM *pCurrent = m_pDownloadItemList;
|
|
DOWNLOADITEM *pNext;
|
|
while (pCurrent)
|
|
{
|
|
pNext = pCurrent->pNext;
|
|
SafeGlobalFree(pCurrent);
|
|
pCurrent = pNext;
|
|
}
|
|
m_pDownloadItemList = NULL;
|
|
m_dwDownloadItemCount = 0;
|
|
LeaveCriticalSection(&m_csDownload);
|
|
}
|
|
|
|
void CDynamicUpdate::EnterDownloadListCriticalSection()
|
|
{
|
|
EnterCriticalSection(&m_csDownload);
|
|
}
|
|
|
|
void CDynamicUpdate::LeaveDownloadListCriticalSection()
|
|
{
|
|
LeaveCriticalSection(&m_csDownload);
|
|
}
|
|
|
|
void CDynamicUpdate::FixUpV3LocaleID()
|
|
{
|
|
// Some XP Locale ID's map to a different Locale ID in V3 Terms
|
|
// First Example was a new Spanish (Modern) Locale ID (0c0a)
|
|
// which in V3 was (040a). For the V3 period we will fix up
|
|
// any specific LCID's until IU handles this.
|
|
|
|
switch (m_lcidLocaleID)
|
|
{
|
|
case 3082: // 0c0a = Spanish (Modern)
|
|
{
|
|
m_lcidLocaleID = 1034; // 040a
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// do nothing.
|
|
}
|
|
}
|
|
|
|
return;
|
|
};
|
|
|
|
DWORD WaitAndPumpMessages(DWORD nCount, LPHANDLE pHandles, DWORD dwWakeMask)
|
|
{
|
|
DWORD dwWaitResult;
|
|
MSG msg;
|
|
|
|
while (TRUE)
|
|
{
|
|
dwWaitResult = MsgWaitForMultipleObjects(nCount, pHandles, FALSE, 1000, dwWakeMask);
|
|
if (dwWaitResult <= WAIT_OBJECT_0 + nCount - 1)
|
|
{
|
|
return dwWaitResult;
|
|
}
|
|
|
|
if (WAIT_OBJECT_0 + nCount == dwWaitResult)
|
|
{
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
return dwWaitResult;
|
|
}
|
|
|
|
|
|
|