//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: N C C O M . C P P // // Contents: Helper functions for doing COM things // // Notes: // //---------------------------------------------------------------------------- #include #pragma hdrstop #include "nccom.h" #include "ncbase.h" #include "trace.h" //+--------------------------------------------------------------------------- // // Function: HrMyWaitForMultipleHandles // // Purpose: Waits for specified handles to be signaled or for a specified // timeout period to elapse, pumping messages in the meantime. // For use by apartment-threaded object that have to block, // but are on a system that doesn't support // CoWaitForMultipleHandles. // // Arguments: // [in] dwFlags Wait options. Values are taken from the // COWAIT_FLAGS enumeration. // // [in] dwTimeout Timeout period, in milliseconds. // // [in] cHandles Number of elements in the pHandles array. // // [in] pHandles Array of Win32 handles. // // [out] lpdwIndex Index to the event that was signalled // // Returns: S_OK The required handle or handles were signaled. // RPC_S_CALLPENDING The timeout period elapsed before the required // handle or handles were signaled. // RPC_E_NO_SYNC No handles were specified. // // Notes: This code was basically stolen from the implementation // of CoWaitForMultipleHandles and KB article Q136885. // It is meant to look and work exactly like // CoWaitForMultipleHandles, which exists on nt5 but not // on Millennium // See KB article Q136885 for more info. // HRESULT HrMyWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout, ULONG cHandles, LPHANDLE pHandles, LPDWORD lpdwIndex) { HRESULT hr; DWORD dwSignaled; HANDLE hTimer = NULL; ULONG cNewHandles = 0; LPHANDLE pNewHandles = NULL; hr = S_OK; dwSignaled = 0; if ((pHandles == NULL) || (lpdwIndex == NULL)) { hr = E_INVALIDARG; goto Cleanup; } if ((dwFlags & ~(COWAIT_WAITALL | COWAIT_ALERTABLE)) != 0) { hr = E_INVALIDARG; goto Cleanup; } // If nothing to do, return if (0 == cHandles) { hr = RPC_E_NO_SYNC; goto Cleanup; } { if (INFINITE != dwTimeout) { // Create a waitable timer to implement the timeout. hTimer = CreateWaitableTimer(NULL, TRUE, NULL); if (NULL == hTimer) { hr = HrFromLastWin32Error(); TraceError("HrMyWaitForMultipleHandles(): " "Failed to create waitable timer", hr); goto Cleanup; } // We need to add the waitable timer to the list of things to wait // on. So we allocate a new array, copy the handles passed in into // it, and add the timer as the last handle. cNewHandles = cHandles+1; pNewHandles = new HANDLE[cNewHandles]; if (NULL == pNewHandles) { hr = E_OUTOFMEMORY; TraceError("HrMyWaitForMultipleHandles(): " "Failed to allocate new handle array", hr); goto Cleanup; } for (ULONG i = 0; i < cHandles; i++) { pNewHandles[i] = pHandles[i]; } pNewHandles[cHandles] = hTimer; } else { pNewHandles = pHandles; cNewHandles = cHandles; } DWORD dwWaitFlags; DWORD dwMessageSignal; dwWaitFlags = (dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0; dwWaitFlags |= (dwFlags & COWAIT_ALERTABLE) ? MWMO_ALERTABLE : 0; dwMessageSignal = WAIT_OBJECT_0 + cNewHandles; if (INFINITE != dwTimeout) { // About to enter the wait - set the waitable timer. Have to first // convert the timeout, given in milliseconds to 100 nanosecond // units. LARGE_INTEGER liTimeout; liTimeout.QuadPart = Int32x32To64(((LONG)dwTimeout * -1), 10000); if (!SetWaitableTimer(hTimer, &liTimeout, 0, NULL, NULL, FALSE)) { hr = HrFromLastWin32Error(); TraceError("HrMyWaitForMultipleHandles(): " "Failed to set waitable timer", hr); goto Cleanup; } } while (TRUE) { // CAUTION: the messages that MsgWaitForMultipleObjectsEx will // wake up for (the QS_* flags) MUST be a subset of the messages // that PeekMessage will process (PM_QS_*), or the CPU can be // pegged. // dwSignaled = ::MsgWaitForMultipleObjectsEx(cNewHandles, pNewHandles, INFINITE, QS_ALLINPUT, dwWaitFlags); #pragma warning(push) #pragma warning(disable:4296) if ((dwSignaled >= WAIT_OBJECT_0) && (dwSignaled < dwMessageSignal)) { // One (or all) of our handles was signalled // dwSignaled -= WAIT_OBJECT_0; if ((cNewHandles > cHandles) && (dwSignaled == cHandles) ) { // The timer was signaled - this is a timeout. Fix up // the error code. hr = RPC_S_CALLPENDING; dwSignaled = WAIT_TIMEOUT; } break; } #pragma warning(pop) else if (dwMessageSignal == dwSignaled) { MSG msg; // There is a window message available. Dispatch it. // while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } else { Assert(FImplies(WAIT_IO_COMPLETION == dwSignaled, dwFlags & COWAIT_ALERTABLE)); hr = HrFromLastWin32Error(); break; } } } Cleanup: if (hTimer) { CancelWaitableTimer(hTimer); CloseHandle(hTimer); } if (pNewHandles && (pNewHandles != pHandles)) { delete [] pNewHandles; cNewHandles = 0; } if (lpdwIndex) { *lpdwIndex = dwSignaled; } TraceError("MyWaitForMultipleHandles", hr); return hr; } //+--------------------------------------------------------------------------- // // Function: FSupportsInterface // // Purpose: Returns TRUE if the specified object implements the given // interface, FALSE otherwise. // // Arguments: // [in] punk IUnknown * of object to query for a particular // interface. // // [in] piid IID for the interface of interest // // // Returns: // TRUE The specified interface is supported // // FALSE The specified interface is not suppored // BOOL FSupportsInterface(IUnknown * punk, REFIID piid) { Assert(punk); HRESULT hr; BOOL fResult; IUnknown * punkTemp; fResult = FALSE; punkTemp = NULL; hr = punk->QueryInterface(piid, (LPVOID*)&punkTemp); if (SUCCEEDED(hr)) { Assert(punkTemp); fResult = TRUE; punkTemp->Release(); } if (hr != E_NOINTERFACE) { TraceError("FSupportsInterface, QI", hr); } return fResult; }