diff --git a/dlls/wbemprox/services.c b/dlls/wbemprox/services.c index fc964ac7f99..10036ae2381 100644 --- a/dlls/wbemprox/services.c +++ b/dlls/wbemprox/services.c @@ -135,11 +135,68 @@ static const IClientSecurityVtbl client_security_vtbl = IClientSecurity client_security = { &client_security_vtbl }; +struct async_header +{ + IWbemObjectSink *sink; + void (*proc)( struct async_header * ); + HANDLE cancel; + HANDLE wait; +}; + +struct async_query +{ + struct async_header hdr; + WCHAR *str; +}; + +static void free_async( struct async_header *async ) +{ + if (async->sink) IWbemObjectSink_Release( async->sink ); + CloseHandle( async->cancel ); + CloseHandle( async->wait ); + heap_free( async ); +} + +static BOOL init_async( struct async_header *async, IWbemObjectSink *sink, + void (*proc)(struct async_header *) ) +{ + if (!(async->wait = CreateEventW( NULL, FALSE, FALSE, NULL ))) return FALSE; + if (!(async->cancel = CreateEventW( NULL, FALSE, FALSE, NULL ))) + { + CloseHandle( async->wait ); + return FALSE; + } + async->proc = proc; + async->sink = sink; + IWbemObjectSink_AddRef( sink ); + return TRUE; +} + +static DWORD CALLBACK async_proc( LPVOID param ) +{ + struct async_header *async = param; + HANDLE wait = async->wait; + + async->proc( async ); + + WaitForSingleObject( async->cancel, INFINITE ); + SetEvent( wait ); + return ERROR_SUCCESS; +} + +static HRESULT queue_async( struct async_header *async ) +{ + if (QueueUserWorkItem( async_proc, async, WT_EXECUTELONGFUNCTION )) return S_OK; + return HRESULT_FROM_WIN32( GetLastError() ); +} + struct wbem_services { IWbemServices IWbemServices_iface; LONG refs; + CRITICAL_SECTION cs; WCHAR *namespace; + struct async_header *async; }; static inline struct wbem_services *impl_from_IWbemServices( IWbemServices *iface ) @@ -162,6 +219,17 @@ static ULONG WINAPI wbem_services_Release( if (!refs) { TRACE("destroying %p\n", ws); + + EnterCriticalSection( &ws->cs ); + if (ws->async) SetEvent( ws->async->cancel ); + LeaveCriticalSection( &ws->cs ); + if (ws->async) + { + WaitForSingleObject( ws->async->wait, INFINITE ); + free_async( ws->async ); + } + ws->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &ws->cs ); heap_free( ws->namespace ); heap_free( ws ); } @@ -221,9 +289,27 @@ static HRESULT WINAPI wbem_services_CancelAsyncCall( IWbemServices *iface, IWbemObjectSink *pSink ) { - FIXME("%p, %p\n", iface, pSink); + struct wbem_services *services = impl_from_IWbemServices( iface ); + struct async_header *async; - IWbemObjectSink_Release( pSink ); + TRACE("%p, %p\n", iface, pSink); + + if (!pSink) return WBEM_E_INVALID_PARAMETER; + + EnterCriticalSection( &services->cs ); + + if (!(async = services->async)) + { + LeaveCriticalSection( &services->cs ); + return WBEM_E_INVALID_PARAMETER; + } + services->async = NULL; + SetEvent( async->cancel ); + + LeaveCriticalSection( &services->cs ); + + WaitForSingleObject( async->wait, INFINITE ); + free_async( async ); return S_OK; } @@ -532,6 +618,30 @@ static HRESULT WINAPI wbem_services_ExecQuery( return exec_query( strQuery, ppEnum ); } +static void async_exec_query( struct async_header *hdr ) +{ + struct async_query *query = (struct async_query *)hdr; + IEnumWbemClassObject *result; + IWbemClassObject *obj; + ULONG count; + HRESULT hr; + + hr = exec_query( query->str, &result ); + if (hr == S_OK) + { + for (;;) + { + IEnumWbemClassObject_Next( result, WBEM_INFINITE, 1, &obj, &count ); + if (!count) break; + IWbemObjectSink_Indicate( query->hdr.sink, 1, &obj ); + IWbemClassObject_Release( obj ); + } + IEnumWbemClassObject_Release( result ); + } + IWbemObjectSink_SetStatus( query->hdr.sink, WBEM_STATUS_COMPLETE, hr, NULL, NULL ); + heap_free( query->str ); +} + static HRESULT WINAPI wbem_services_ExecQueryAsync( IWbemServices *iface, const BSTR strQueryLanguage, @@ -540,8 +650,53 @@ static HRESULT WINAPI wbem_services_ExecQueryAsync( IWbemContext *pCtx, IWbemObjectSink *pResponseHandler ) { - FIXME("\n"); - return WBEM_E_FAILED; + struct wbem_services *services = impl_from_IWbemServices( iface ); + IWbemObjectSink *sink; + HRESULT hr = E_OUTOFMEMORY; + struct async_header *async; + struct async_query *query; + + TRACE("%p, %s, %s, 0x%08x, %p, %p\n", iface, debugstr_w(strQueryLanguage), debugstr_w(strQuery), + lFlags, pCtx, pResponseHandler); + + if (!pResponseHandler) return WBEM_E_INVALID_PARAMETER; + + hr = IWbemObjectSink_QueryInterface( pResponseHandler, &IID_IWbemObjectSink, (void **)&sink ); + if (FAILED(hr)) return hr; + + EnterCriticalSection( &services->cs ); + + if (services->async) + { + FIXME("handle more than one pending async\n"); + hr = WBEM_E_FAILED; + goto done; + } + if (!(query = heap_alloc_zero( sizeof(*query) ))) goto done; + async = (struct async_header *)query; + + if (!(init_async( async, sink, async_exec_query ))) + { + free_async( async ); + goto done; + } + if (!(query->str = heap_strdupW( strQuery ))) + { + free_async( async ); + goto done; + } + hr = queue_async( async ); + if (hr == S_OK) services->async = async; + else + { + heap_free( query->str ); + free_async( async ); + } + +done: + LeaveCriticalSection( &services->cs ); + IWbemObjectSink_Release( sink ); + return hr; } static HRESULT WINAPI wbem_services_ExecNotificationQuery( @@ -564,11 +719,53 @@ static HRESULT WINAPI wbem_services_ExecNotificationQueryAsync( IWbemContext *pCtx, IWbemObjectSink *pResponseHandler ) { - FIXME("%p, %s, %s, 0x%08x, %p, %p\n", iface, debugstr_w(strQueryLanguage), debugstr_w(strQuery), + struct wbem_services *services = impl_from_IWbemServices( iface ); + IWbemObjectSink *sink; + HRESULT hr = E_OUTOFMEMORY; + struct async_header *async; + struct async_query *query; + + TRACE("%p, %s, %s, 0x%08x, %p, %p\n", iface, debugstr_w(strQueryLanguage), debugstr_w(strQuery), lFlags, pCtx, pResponseHandler); - IWbemObjectSink_AddRef( pResponseHandler ); - return S_OK; + if (!pResponseHandler) return WBEM_E_INVALID_PARAMETER; + + hr = IWbemObjectSink_QueryInterface( pResponseHandler, &IID_IWbemObjectSink, (void **)&sink ); + if (FAILED(hr)) return hr; + + EnterCriticalSection( &services->cs ); + + if (services->async) + { + FIXME("handle more than one pending async\n"); + hr = WBEM_E_FAILED; + goto done; + } + if (!(query = heap_alloc_zero( sizeof(*query) ))) goto done; + async = (struct async_header *)query; + + if (!(init_async( async, sink, async_exec_query ))) + { + free_async( async ); + goto done; + } + if (!(query->str = heap_strdupW( strQuery ))) + { + free_async( async ); + goto done; + } + hr = queue_async( async ); + if (hr == S_OK) services->async = async; + else + { + heap_free( query->str ); + free_async( async ); + } + +done: + LeaveCriticalSection( &services->cs ); + IWbemObjectSink_Release( sink ); + return hr; } static HRESULT WINAPI wbem_services_ExecMethod( @@ -670,8 +867,11 @@ HRESULT WbemServices_create( IUnknown *pUnkOuter, const WCHAR *namespace, LPVOID if (!ws) return E_OUTOFMEMORY; ws->IWbemServices_iface.lpVtbl = &wbem_services_vtbl; - ws->refs = 1; + ws->refs = 1; ws->namespace = heap_strdupW( namespace ); + ws->async = NULL; + InitializeCriticalSection( &ws->cs ); + ws->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": wbemprox_services.cs"); *ppObj = &ws->IWbemServices_iface; diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c index 933f3150070..15fe6f8a271 100644 --- a/dlls/wbemprox/tests/query.c +++ b/dlls/wbemprox/tests/query.c @@ -546,14 +546,14 @@ static ULONG WINAPI sink_Release( static HRESULT WINAPI sink_Indicate( IWbemObjectSink *iface, LONG count, IWbemClassObject **objects ) { - trace("%d, %p\n", count, objects); + trace("Indicate: %d, %p\n", count, objects); return S_OK; } static HRESULT WINAPI sink_SetStatus( IWbemObjectSink *iface, LONG flags, HRESULT hresult, BSTR str_param, IWbemClassObject *obj_param ) { - trace("%08x, %08x, %s, %p\n", flags, hresult, wine_dbgstr_w(str_param), obj_param); + trace("SetStatus: %08x, %08x, %s, %p\n", flags, hresult, wine_dbgstr_w(str_param), obj_param); return S_OK; } @@ -574,11 +574,40 @@ static void test_notification_query_async( IWbemServices *services ) {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','W','i','n','3','2','_', 'D','e','v','i','c','e','C','h','a','n','g','e','E','v','e','n','t',0}; BSTR wql = SysAllocString( wqlW ), query = SysAllocString( queryW ); + ULONG prev_sink_refs; HRESULT hr; + hr = IWbemServices_ExecNotificationQueryAsync( services, wql, query, 0, NULL, NULL ); + ok( hr == WBEM_E_INVALID_PARAMETER, "got %08x\n", hr ); + + prev_sink_refs = sink_refs; hr = IWbemServices_ExecNotificationQueryAsync( services, wql, query, 0, NULL, &sink ); ok( hr == S_OK || broken(hr == WBEM_E_NOT_FOUND), "got %08x\n", hr ); - ok( sink_refs, "got %u\n", sink_refs ); + ok( sink_refs > prev_sink_refs, "got %u refs\n", sink_refs ); + + hr = IWbemServices_CancelAsyncCall( services, &sink ); + ok( hr == S_OK, "got %08x\n", hr ); + + SysFreeString( wql ); + SysFreeString( query ); +} + +static void test_query_async( IWbemServices *services ) +{ + static const WCHAR queryW[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','W','i','n','3','2','_', + 'P','r','o','c','e','s','s',0}; + BSTR wql = SysAllocString( wqlW ), query = SysAllocString( queryW ); + HRESULT hr; + + hr = IWbemServices_ExecQueryAsync( services, wql, query, 0, NULL, NULL ); + ok( hr == WBEM_E_INVALID_PARAMETER, "got %08x\n", hr ); + + hr = IWbemServices_ExecQueryAsync( services, wql, query, 0, NULL, &sink ); + ok( hr == S_OK || broken(hr == WBEM_E_NOT_FOUND), "got %08x\n", hr ); + + hr = IWbemServices_CancelAsyncCall( services, NULL ); + ok( hr == WBEM_E_INVALID_PARAMETER, "got %08x\n", hr ); hr = IWbemServices_CancelAsyncCall( services, &sink ); ok( hr == S_OK, "got %08x\n", hr ); @@ -617,6 +646,7 @@ START_TEST(query) test_Win32_Service( services ); test_StdRegProv( services ); test_notification_query_async( services ); + test_query_async( services ); SysFreeString( path ); IWbemServices_Release( services ); diff --git a/include/wbemcli.idl b/include/wbemcli.idl index 0390e1ea697..7a309e8d3a3 100644 --- a/include/wbemcli.idl +++ b/include/wbemcli.idl @@ -176,6 +176,13 @@ typedef [v1_enum] enum tag_WBEMSTATUS WBEM_E_PROVIDER_DISABLED = 0x8004108a } WBEMSTATUS; +typedef [v1_enum] enum tag_WBEM_STATUS_TYPE +{ + WBEM_STATUS_COMPLETE = 0, + WBEM_STATUS_REQUIREMENTS = 1, + WBEM_STATUS_PROGRESS = 2 +} WBEM_STATUS_TYPE; + typedef [v1_enum] enum tag_WBEM_TIMEOUT_TYPE { WBEM_NO_WAIT = 0,