combase: Move CoWaitForMultipleHandles().

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2020-08-19 11:29:12 +03:00 committed by Alexandre Julliard
parent c9586634d4
commit e56061bb4c
6 changed files with 206 additions and 183 deletions

View File

@ -25,6 +25,7 @@
#define USE_COM_CONTEXT_DEF
#include "objbase.h"
#include "oleauto.h"
#include "dde.h"
#include "winternl.h"
#include "combase_private.h"
@ -1521,3 +1522,165 @@ HRESULT WINAPI CoRevokeInitializeSpy(ULARGE_INTEGER cookie)
}
return S_OK;
}
static BOOL com_peek_message(struct apartment *apt, MSG *msg)
{
/* First try to retrieve messages for incoming COM calls to the apartment window */
return (apt->win && PeekMessageW(msg, apt->win, 0, 0, PM_REMOVE | PM_NOYIELD)) ||
/* Next retrieve other messages necessary for the app to remain responsive */
PeekMessageW(msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE | PM_NOYIELD) ||
PeekMessageW(msg, NULL, 0, 0, PM_QS_PAINT | PM_QS_SENDMESSAGE | PM_REMOVE | PM_NOYIELD);
}
/***********************************************************************
* CoWaitForMultipleHandles (combase.@)
*/
HRESULT WINAPI CoWaitForMultipleHandles(DWORD flags, DWORD timeout, ULONG handle_count, HANDLE *handles,
DWORD *index)
{
BOOL check_apc = !!(flags & COWAIT_ALERTABLE), post_quit = FALSE, message_loop;
DWORD start_time, wait_flags = 0;
struct tlsdata *tlsdata;
struct apartment *apt;
UINT exit_code;
HRESULT hr;
TRACE("%#x, %#x, %u, %p, %p\n", flags, timeout, handle_count, handles, index);
if (!index)
return E_INVALIDARG;
*index = 0;
if (!handles)
return E_INVALIDARG;
if (!handle_count)
return RPC_E_NO_SYNC;
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
return hr;
apt = com_get_current_apt();
message_loop = apt && !apt->multi_threaded;
if (flags & COWAIT_WAITALL)
wait_flags |= MWMO_WAITALL;
if (flags & COWAIT_ALERTABLE)
wait_flags |= MWMO_ALERTABLE;
start_time = GetTickCount();
while (TRUE)
{
DWORD now = GetTickCount(), res;
if (now - start_time > timeout)
{
hr = RPC_S_CALLPENDING;
break;
}
if (message_loop)
{
TRACE("waiting for rpc completion or window message\n");
res = WAIT_TIMEOUT;
if (check_apc)
{
res = WaitForMultipleObjectsEx(handle_count, handles, !!(flags & COWAIT_WAITALL), 0, TRUE);
check_apc = FALSE;
}
if (res == WAIT_TIMEOUT)
res = MsgWaitForMultipleObjectsEx(handle_count, handles,
timeout == INFINITE ? INFINITE : start_time + timeout - now,
QS_SENDMESSAGE | QS_ALLPOSTMESSAGE | QS_PAINT, wait_flags);
if (res == WAIT_OBJECT_0 + handle_count) /* messages available */
{
int msg_count = 0;
MSG msg;
/* call message filter */
if (apt->filter)
{
PENDINGTYPE pendingtype = tlsdata->pending_call_count_server ? PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL;
DWORD be_handled = IMessageFilter_MessagePending(apt->filter, 0 /* FIXME */, now - start_time, pendingtype);
TRACE("IMessageFilter_MessagePending returned %d\n", be_handled);
switch (be_handled)
{
case PENDINGMSG_CANCELCALL:
WARN("call canceled\n");
hr = RPC_E_CALL_CANCELED;
break;
case PENDINGMSG_WAITNOPROCESS:
case PENDINGMSG_WAITDEFPROCESS:
default:
/* FIXME: MSDN is very vague about the difference
* between WAITNOPROCESS and WAITDEFPROCESS - there
* appears to be none, so it is possibly a left-over
* from the 16-bit world. */
break;
}
}
if (!apt->win)
{
/* If window is NULL on apartment, peek at messages so that it will not trigger
* MsgWaitForMultipleObjects next time. */
PeekMessageW(NULL, NULL, 0, 0, PM_QS_POSTMESSAGE | PM_NOREMOVE | PM_NOYIELD);
}
/* Some apps (e.g. Visio 2010) don't handle WM_PAINT properly and loop forever,
* so after processing 100 messages we go back to checking the wait handles */
while (msg_count++ < 100 && com_peek_message(apt, &msg))
{
if (msg.message == WM_QUIT)
{
TRACE("Received WM_QUIT message\n");
post_quit = TRUE;
exit_code = msg.wParam;
}
else
{
TRACE("Received message whilst waiting for RPC: 0x%04x\n", msg.message);
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
continue;
}
}
else
{
TRACE("Waiting for rpc completion\n");
res = WaitForMultipleObjectsEx(handle_count, handles, !!(flags & COWAIT_WAITALL),
(timeout == INFINITE) ? INFINITE : start_time + timeout - now, !!(flags & COWAIT_ALERTABLE));
}
switch (res)
{
case WAIT_TIMEOUT:
hr = RPC_S_CALLPENDING;
break;
case WAIT_FAILED:
hr = HRESULT_FROM_WIN32(GetLastError());
break;
default:
*index = res;
break;
}
break;
}
if (post_quit) PostQuitMessage(exit_code);
TRACE("-- 0x%08x\n", hr);
return hr;
}

View File

@ -170,7 +170,7 @@
@ stub CoVrfCheckThreadState
@ stub CoVrfGetThreadState
@ stub CoVrfReleaseThreadState
@ stdcall CoWaitForMultipleHandles(long long long ptr ptr) ole32.CoWaitForMultipleHandles
@ stdcall CoWaitForMultipleHandles(long long long ptr ptr)
@ stub CoWaitForMultipleObjects
@ stdcall CreateErrorInfo(ptr)
@ stdcall CreateStreamOnHGlobal(ptr long ptr) ole32.CreateStreamOnHGlobal

View File

@ -15,10 +15,41 @@
*/
#include "winternl.h"
#include "wine/orpc.h"
#include "wine/list.h"
struct apartment;
struct apartment
{
struct list entry;
LONG refs; /* refcount of the apartment (LOCK) */
BOOL multi_threaded; /* multi-threaded or single-threaded apartment? (RO) */
DWORD tid; /* thread id (RO) */
OXID oxid; /* object exporter ID (RO) */
LONG ipidc; /* interface pointer ID counter, starts at 1 (LOCK) */
CRITICAL_SECTION cs; /* thread safety */
struct list proxies; /* imported objects (CS cs) */
struct list stubmgrs; /* stub managers for exported objects (CS cs) */
BOOL remunk_exported; /* has the IRemUnknown interface for this apartment been created yet? (CS cs) */
LONG remoting_started; /* has the RPC system been started for this apartment? (LOCK) */
struct list loaded_dlls; /* list of dlls loaded by this apartment (CS cs) */
DWORD host_apt_tid; /* thread ID of apartment hosting objects of differing threading model (CS cs) */
HWND host_apt_hwnd; /* handle to apartment window of host apartment (CS cs) */
struct local_server *local_server; /* A marshallable object exposing local servers (CS cs) */
BOOL being_destroyed; /* is currently being destroyed */
/* FIXME: OIDs should be given out by RPCSS */
OID oidc; /* object ID counter, starts at 1, zero is invalid OID (CS cs) */
/* STA-only fields */
HWND win; /* message window (LOCK) */
IMessageFilter *filter; /* message filter (CS cs) */
BOOL main; /* is this a main-threaded-apartment? (RO) */
/* MTA-only */
struct list usage_cookies; /* Used for refcount control with CoIncrementMTAUsage()/CoDecrementMTAUsage(). */
};
/* this is what is stored in TEB->ReservedForOle */
struct tlsdata
@ -50,3 +81,10 @@ static inline HRESULT com_get_tlsdata(struct tlsdata **data)
*data = NtCurrentTeb()->ReservedForOle;
return *data ? S_OK : InternalTlsAllocData(data);
}
static inline struct apartment* com_get_current_apt(void)
{
struct tlsdata *tlsdata = NULL;
com_get_tlsdata(&tlsdata);
return tlsdata->apt;
}

View File

@ -22,9 +22,10 @@
#define COBJMACROS
#include "objbase.h"
#include "combase_private.h"
#include "wine/debug.h"
#include "wine/heap.h"
#include "wine/orpc.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);

View File

@ -3348,185 +3348,6 @@ HRESULT WINAPI CoAllowSetForegroundWindow(IUnknown *pUnk, void *pvReserved)
return S_OK;
}
static BOOL COM_PeekMessage(struct apartment *apt, MSG *msg)
{
/* first try to retrieve messages for incoming COM calls to the apartment window */
return (apt->win && PeekMessageW(msg, apt->win, 0, 0, PM_REMOVE|PM_NOYIELD)) ||
/* next retrieve other messages necessary for the app to remain responsive */
PeekMessageW(msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE|PM_NOYIELD) ||
PeekMessageW(msg, NULL, 0, 0, PM_QS_PAINT|PM_QS_SENDMESSAGE|PM_REMOVE|PM_NOYIELD);
}
/***********************************************************************
* CoWaitForMultipleHandles [OLE32.@]
*
* Waits for one or more handles to become signaled.
*
* PARAMS
* dwFlags [I] Flags. See notes.
* dwTimeout [I] Timeout in milliseconds.
* cHandles [I] Number of handles pointed to by pHandles.
* pHandles [I] Handles to wait for.
* lpdwindex [O] Index of handle that was signaled.
*
* RETURNS
* Success: S_OK.
* Failure: RPC_S_CALLPENDING on timeout.
*
* NOTES
*
* The dwFlags parameter can be zero or more of the following:
*| COWAIT_WAITALL - Wait for all of the handles to become signaled.
*| COWAIT_ALERTABLE - Allows a queued APC to run during the wait.
*
* SEE ALSO
* MsgWaitForMultipleObjects, WaitForMultipleObjects.
*/
HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
ULONG cHandles, LPHANDLE pHandles, LPDWORD lpdwindex)
{
HRESULT hr = S_OK;
DWORD start_time = GetTickCount();
APARTMENT *apt = COM_CurrentApt();
BOOL message_loop = apt && !apt->multi_threaded;
BOOL check_apc = (dwFlags & COWAIT_ALERTABLE) != 0;
BOOL post_quit = FALSE;
UINT exit_code;
TRACE("(0x%08x, 0x%08x, %d, %p, %p)\n", dwFlags, dwTimeout, cHandles,
pHandles, lpdwindex);
if (!lpdwindex)
return E_INVALIDARG;
*lpdwindex = 0;
if (!pHandles)
return E_INVALIDARG;
if (!cHandles)
return RPC_E_NO_SYNC;
while (TRUE)
{
DWORD now = GetTickCount();
DWORD res;
if (now - start_time > dwTimeout)
{
hr = RPC_S_CALLPENDING;
break;
}
if (message_loop)
{
DWORD wait_flags = ((dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0) |
((dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0);
TRACE("waiting for rpc completion or window message\n");
res = WAIT_TIMEOUT;
if (check_apc)
{
res = WaitForMultipleObjectsEx(cHandles, pHandles,
(dwFlags & COWAIT_WAITALL) != 0, 0, TRUE);
check_apc = FALSE;
}
if (res == WAIT_TIMEOUT)
res = MsgWaitForMultipleObjectsEx(cHandles, pHandles,
(dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
QS_SENDMESSAGE | QS_ALLPOSTMESSAGE | QS_PAINT, wait_flags);
if (res == WAIT_OBJECT_0 + cHandles) /* messages available */
{
MSG msg;
int count = 0;
/* call message filter */
if (COM_CurrentApt()->filter)
{
PENDINGTYPE pendingtype =
COM_CurrentInfo()->pending_call_count_server ?
PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL;
DWORD be_handled = IMessageFilter_MessagePending(
COM_CurrentApt()->filter, 0 /* FIXME */,
now - start_time, pendingtype);
TRACE("IMessageFilter_MessagePending returned %d\n", be_handled);
switch (be_handled)
{
case PENDINGMSG_CANCELCALL:
WARN("call canceled\n");
hr = RPC_E_CALL_CANCELED;
break;
case PENDINGMSG_WAITNOPROCESS:
case PENDINGMSG_WAITDEFPROCESS:
default:
/* FIXME: MSDN is very vague about the difference
* between WAITNOPROCESS and WAITDEFPROCESS - there
* appears to be none, so it is possibly a left-over
* from the 16-bit world. */
break;
}
}
if (!apt->win)
{
/* If window is NULL on apartment, peek at messages so that it will not trigger
* MsgWaitForMultipleObjects next time. */
PeekMessageW(NULL, NULL, 0, 0, PM_QS_POSTMESSAGE | PM_NOREMOVE | PM_NOYIELD);
}
/* some apps (e.g. Visio 2010) don't handle WM_PAINT properly and loop forever,
* so after processing 100 messages we go back to checking the wait handles */
while (count++ < 100 && COM_PeekMessage(apt, &msg))
{
if (msg.message == WM_QUIT)
{
TRACE("received WM_QUIT message\n");
post_quit = TRUE;
exit_code = msg.wParam;
}
else
{
TRACE("received message whilst waiting for RPC: 0x%04x\n", msg.message);
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
continue;
}
}
else
{
TRACE("waiting for rpc completion\n");
res = WaitForMultipleObjectsEx(cHandles, pHandles, (dwFlags & COWAIT_WAITALL) != 0,
(dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
(dwFlags & COWAIT_ALERTABLE) != 0);
}
switch (res)
{
case WAIT_TIMEOUT:
hr = RPC_S_CALLPENDING;
break;
case WAIT_FAILED:
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
default:
*lpdwindex = res;
break;
}
break;
}
if (post_quit) PostQuitMessage(exit_code);
TRACE("-- 0x%08x\n", hr);
return hr;
}
/***********************************************************************
* CoGetObject [OLE32.@]
*

View File

@ -91,7 +91,7 @@
@ stub CoUnloadingWOW
@ stdcall CoUnmarshalHresult(ptr ptr) combase.CoUnmarshalHresult
@ stdcall CoUnmarshalInterface(ptr ptr ptr)
@ stdcall CoWaitForMultipleHandles(long long long ptr ptr)
@ stdcall CoWaitForMultipleHandles(long long long ptr ptr) combase.CoWaitForMultipleHandles
@ stdcall CreateAntiMoniker(ptr)
@ stdcall CreateBindCtx(long ptr)
@ stdcall CreateClassMoniker(ptr ptr)