From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/async.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/windows.media.speech/async.c b/dlls/windows.media.speech/async.c index 64a095a0134..e7547225c1c 100644 --- a/dlls/windows.media.speech/async.c +++ b/dlls/windows.media.speech/async.c @@ -367,6 +367,8 @@ HRESULT async_operation_create( const GUID *iid, IInspectable *invoker, async_op { struct async_operation *impl;
+ TRACE("iid %s, invoker %p, callback %p, out %p.\n", debugstr_guid(iid), invoker, callback, out); + *out = NULL; if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; impl->IAsyncOperation_IInspectable_iface.lpVtbl = &async_operation_vtbl;
From: Bernhard Kölbl besentv@gmail.com
This one actually retrieves a handler.
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/tests/speech.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 9ef49b160cd..c7d80385b7a 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -860,7 +860,7 @@ static void test_SpeechRecognizer(void) HANDLE blocked_thread; HRESULT hr, error_code; UINT32 id; - LONG ref; + LONG ref, old_ref;
hr = RoInitialize(RO_INIT_MULTITHREADED); ok(hr == S_OK, "RoInitialize failed, hr %#lx.\n", hr); @@ -1133,14 +1133,22 @@ static void test_SpeechRecognizer(void) block_param.handler = &compilation_handler.IAsyncHandler_Compilation_iface; block_param.operation = operation; blocked_thread = CreateThread(NULL, 0, async_operation_block_thread, &block_param, 0, NULL); - ok(!WaitForSingleObject(compilation_handler.event_finished, 5000), "Wait for event_finished failed.\n"); - ok(WaitForSingleObject(blocked_thread, 100) == WAIT_TIMEOUT, "Wait for block_thread didn't time out.\n");
todo_wine ok(compilation_handler.ref == 3, "Got unexpected ref %lu.\n", compilation_handler.ref); todo_wine check_refcount(operation, 3);
+ handler = (void*)0xdeadbeef; + old_ref = compilation_handler.ref; + hr = IAsyncOperation_SpeechRecognitionCompilationResult_get_Completed(operation, &handler); + ok(hr == S_OK, "IAsyncOperation_SpeechRecognitionCompilationResult_get_Completed failed, hr %#lx.\n", hr); + todo_wine ok(handler == &compilation_handler.IAsyncHandler_Compilation_iface, "Handler was %p.\n", handler); + + ref = compilation_handler.ref - old_ref; + todo_wine ok(ref == 1, "The ref was increased by %lu.\n", ref); + if (handler) IAsyncOperationCompletedHandler_SpeechRecognitionCompilationResult_Release(handler); + hr = IAsyncOperation_SpeechRecognitionCompilationResult_QueryInterface(operation, &IID_IAsyncInfo, (void **)&info); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/async.c | 125 +++++++++++++------------ dlls/windows.media.speech/private.h | 5 +- dlls/windows.media.speech/recognizer.c | 2 +- 3 files changed, 68 insertions(+), 64 deletions(-)
diff --git a/dlls/windows.media.speech/async.c b/dlls/windows.media.speech/async.c index e7547225c1c..79510d61dc7 100644 --- a/dlls/windows.media.speech/async.c +++ b/dlls/windows.media.speech/async.c @@ -32,7 +32,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); * */
-struct async_operation +struct async_inspectable { IAsyncOperation_IInspectable IAsyncOperation_IInspectable_iface; IAsyncInfo IAsyncInfo_iface; @@ -42,7 +42,7 @@ struct async_operation IAsyncOperationCompletedHandler_IInspectable *handler; IInspectable *result;
- async_operation_callback callback; + async_operation_inspectable_callback callback; TP_WORK *async_run_work; IInspectable *invoker;
@@ -51,14 +51,14 @@ struct async_operation HRESULT hr; };
-static inline struct async_operation *impl_from_IAsyncOperation_IInspectable(IAsyncOperation_IInspectable *iface) +static inline struct async_inspectable *impl_from_IAsyncOperation_IInspectable(IAsyncOperation_IInspectable *iface) { - return CONTAINING_RECORD(iface, struct async_operation, IAsyncOperation_IInspectable_iface); + return CONTAINING_RECORD(iface, struct async_inspectable, IAsyncOperation_IInspectable_iface); }
-static HRESULT WINAPI async_operation_QueryInterface( IAsyncOperation_IInspectable *iface, REFIID iid, void **out ) +static HRESULT WINAPI async_inspectable_QueryInterface( IAsyncOperation_IInspectable *iface, REFIID iid, void **out ) { - struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable(iface);
TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
@@ -82,17 +82,17 @@ static HRESULT WINAPI async_operation_QueryInterface( IAsyncOperation_IInspectab return E_NOINTERFACE; }
-static ULONG WINAPI async_operation_AddRef( IAsyncOperation_IInspectable *iface ) +static ULONG WINAPI async_inspectable_AddRef( IAsyncOperation_IInspectable *iface ) { - struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable(iface); ULONG ref = InterlockedIncrement(&impl->ref); TRACE("iface %p, ref %lu.\n", iface, ref); return ref; }
-static ULONG WINAPI async_operation_Release( IAsyncOperation_IInspectable *iface ) +static ULONG WINAPI async_inspectable_Release( IAsyncOperation_IInspectable *iface ) { - struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable(iface);
ULONG ref = InterlockedDecrement(&impl->ref); TRACE("iface %p, ref %lu.\n", iface, ref); @@ -115,28 +115,28 @@ static ULONG WINAPI async_operation_Release( IAsyncOperation_IInspectable *iface return ref; }
-static HRESULT WINAPI async_operation_GetIids( IAsyncOperation_IInspectable *iface, ULONG *iid_count, IID **iids ) +static HRESULT WINAPI async_inspectable_GetIids( IAsyncOperation_IInspectable *iface, ULONG *iid_count, IID **iids ) { FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); return E_NOTIMPL; }
-static HRESULT WINAPI async_operation_GetRuntimeClassName( IAsyncOperation_IInspectable *iface, HSTRING *class_name ) +static HRESULT WINAPI async_inspectable_GetRuntimeClassName( IAsyncOperation_IInspectable *iface, HSTRING *class_name ) { FIXME("iface %p, class_name %p stub!\n", iface, class_name); return E_NOTIMPL; }
-static HRESULT WINAPI async_operation_GetTrustLevel( IAsyncOperation_IInspectable *iface, TrustLevel *trust_level ) +static HRESULT WINAPI async_inspectable_GetTrustLevel( IAsyncOperation_IInspectable *iface, TrustLevel *trust_level ) { FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); return E_NOTIMPL; }
-static HRESULT WINAPI async_operation_put_Completed( IAsyncOperation_IInspectable *iface, - IAsyncOperationCompletedHandler_IInspectable *handler ) +static HRESULT WINAPI async_inspectable_put_Completed( IAsyncOperation_IInspectable *iface, + IAsyncOperationCompletedHandler_IInspectable *handler ) { - struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable(iface); HRESULT hr = S_OK;
TRACE("iface %p, handler %p.\n", iface, handler); @@ -147,7 +147,7 @@ static HRESULT WINAPI async_operation_put_Completed( IAsyncOperation_IInspectabl else if (impl->handler != HANDLER_NOT_SET) hr = E_ILLEGAL_DELEGATE_ASSIGNMENT; /* - impl->handler can only be set once with async_operation_put_Completed, + impl->handler can only be set once with async_inspectable_put_Completed, so by default we set a non HANDLER_NOT_SET value, in this case handler. */ else if ((impl->handler = handler)) @@ -172,10 +172,10 @@ static HRESULT WINAPI async_operation_put_Completed( IAsyncOperation_IInspectabl return hr; }
-static HRESULT WINAPI async_operation_get_Completed( IAsyncOperation_IInspectable *iface, - IAsyncOperationCompletedHandler_IInspectable **handler ) +static HRESULT WINAPI async_inspectable_get_Completed( IAsyncOperation_IInspectable *iface, + IAsyncOperationCompletedHandler_IInspectable **handler ) { - struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable(iface); HRESULT hr = S_OK;
FIXME("iface %p, handler %p semi stub!\n", iface, handler); @@ -189,10 +189,10 @@ static HRESULT WINAPI async_operation_get_Completed( IAsyncOperation_IInspectabl return hr; }
-static HRESULT WINAPI async_operation_GetResults( IAsyncOperation_IInspectable *iface, IInspectable **results ) +static HRESULT WINAPI async_inspectable_GetResults( IAsyncOperation_IInspectable *iface, IInspectable **results ) { /* NOTE: Despite the name, this function only returns one result! */ - struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable(iface); HRESULT hr;
TRACE("iface %p, results %p.\n", iface, results); @@ -213,20 +213,20 @@ static HRESULT WINAPI async_operation_GetResults( IAsyncOperation_IInspectable * return hr; }
-static const struct IAsyncOperation_IInspectableVtbl async_operation_vtbl = +static const struct IAsyncOperation_IInspectableVtbl async_inspectable_vtbl = { /* IUnknown methods */ - async_operation_QueryInterface, - async_operation_AddRef, - async_operation_Release, + async_inspectable_QueryInterface, + async_inspectable_AddRef, + async_inspectable_Release, /* IInspectable methods */ - async_operation_GetIids, - async_operation_GetRuntimeClassName, - async_operation_GetTrustLevel, + async_inspectable_GetIids, + async_inspectable_GetRuntimeClassName, + async_inspectable_GetTrustLevel, /* IAsyncOperation<IInspectable*> */ - async_operation_put_Completed, - async_operation_get_Completed, - async_operation_GetResults + async_inspectable_put_Completed, + async_inspectable_get_Completed, + async_inspectable_GetResults };
/* @@ -235,17 +235,17 @@ static const struct IAsyncOperation_IInspectableVtbl async_operation_vtbl = * */
-DEFINE_IINSPECTABLE(async_operation_info, IAsyncInfo, struct async_operation, IAsyncOperation_IInspectable_iface) +DEFINE_IINSPECTABLE(async_inspectable_info, IAsyncInfo, struct async_inspectable, IAsyncOperation_IInspectable_iface)
-static HRESULT WINAPI async_operation_info_get_Id( IAsyncInfo *iface, UINT32 *id ) +static HRESULT WINAPI async_inspectable_info_get_Id( IAsyncInfo *iface, UINT32 *id ) { FIXME("iface %p, id %p stub!\n", iface, id); return E_NOTIMPL; }
-static HRESULT WINAPI async_operation_info_get_Status( IAsyncInfo *iface, AsyncStatus *status ) +static HRESULT WINAPI async_inspectable_info_get_Status( IAsyncInfo *iface, AsyncStatus *status ) { - struct async_operation *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p, status %p.\n", iface, status); @@ -259,9 +259,9 @@ static HRESULT WINAPI async_operation_info_get_Status( IAsyncInfo *iface, AsyncS return hr; }
-static HRESULT WINAPI async_operation_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code ) +static HRESULT WINAPI async_inspectable_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code ) { - struct async_operation *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p, error_code %p.\n", iface, error_code); @@ -276,9 +276,9 @@ static HRESULT WINAPI async_operation_info_get_ErrorCode( IAsyncInfo *iface, HRE return hr; }
-static HRESULT WINAPI async_operation_info_Cancel( IAsyncInfo *iface ) +static HRESULT WINAPI async_inspectable_info_Cancel( IAsyncInfo *iface ) { - struct async_operation *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p.\n", iface); @@ -293,9 +293,9 @@ static HRESULT WINAPI async_operation_info_Cancel( IAsyncInfo *iface ) return hr; }
-static HRESULT WINAPI async_operation_info_Close( IAsyncInfo *iface ) +static HRESULT WINAPI async_inspectable_info_Close( IAsyncInfo *iface ) { - struct async_operation *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p.\n", iface); @@ -314,29 +314,29 @@ static HRESULT WINAPI async_operation_info_Close( IAsyncInfo *iface ) return hr; }
-static const struct IAsyncInfoVtbl async_operation_info_vtbl = +static const struct IAsyncInfoVtbl async_inspectable_info_vtbl = { /* IUnknown methods */ - async_operation_info_QueryInterface, - async_operation_info_AddRef, - async_operation_info_Release, + async_inspectable_info_QueryInterface, + async_inspectable_info_AddRef, + async_inspectable_info_Release, /* IInspectable methods */ - async_operation_info_GetIids, - async_operation_info_GetRuntimeClassName, - async_operation_info_GetTrustLevel, + async_inspectable_info_GetIids, + async_inspectable_info_GetRuntimeClassName, + async_inspectable_info_GetTrustLevel, /* IAsyncInfo */ - async_operation_info_get_Id, - async_operation_info_get_Status, - async_operation_info_get_ErrorCode, - async_operation_info_Cancel, - async_operation_info_Close + async_inspectable_info_get_Id, + async_inspectable_info_get_Status, + async_inspectable_info_get_ErrorCode, + async_inspectable_info_Cancel, + async_inspectable_info_Close };
-static void CALLBACK async_run_cb(TP_CALLBACK_INSTANCE *instance, void *data, TP_WORK *work) +static void CALLBACK async_inspectable_run_cb(TP_CALLBACK_INSTANCE *instance, void *data, TP_WORK *work) { IAsyncOperation_IInspectable *operation = data; IInspectable *result = NULL; - struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(operation); + struct async_inspectable *impl = impl_from_IAsyncOperation_IInspectable(operation); HRESULT hr;
hr = impl->callback(impl->invoker, &result); @@ -363,16 +363,19 @@ static void CALLBACK async_run_cb(TP_CALLBACK_INSTANCE *instance, void *data, TP IAsyncOperation_IInspectable_Release(operation); }
-HRESULT async_operation_create( const GUID *iid, IInspectable *invoker, async_operation_callback callback, IAsyncOperation_IInspectable **out ) +HRESULT async_operation_inspectable_create( const GUID *iid, + IInspectable *invoker, + async_operation_inspectable_callback callback, + IAsyncOperation_IInspectable **out ) { - struct async_operation *impl; + struct async_inspectable *impl;
TRACE("iid %s, invoker %p, callback %p, out %p.\n", debugstr_guid(iid), invoker, callback, out);
*out = NULL; if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; - impl->IAsyncOperation_IInspectable_iface.lpVtbl = &async_operation_vtbl; - impl->IAsyncInfo_iface.lpVtbl = &async_operation_info_vtbl; + impl->IAsyncOperation_IInspectable_iface.lpVtbl = &async_inspectable_vtbl; + impl->IAsyncInfo_iface.lpVtbl = &async_inspectable_info_vtbl; impl->iid = iid; impl->ref = 1;
@@ -380,7 +383,7 @@ HRESULT async_operation_create( const GUID *iid, IInspectable *invoker, async_op impl->callback = callback; impl->status = Started;
- if (!(impl->async_run_work = CreateThreadpoolWork(async_run_cb, &impl->IAsyncOperation_IInspectable_iface, NULL))) + if (!(impl->async_run_work = CreateThreadpoolWork(async_inspectable_run_cb, &impl->IAsyncOperation_IInspectable_iface, NULL))) { free(impl); return HRESULT_FROM_WIN32(GetLastError()); diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 4b55a5cbfa7..97afa4d3499 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -69,9 +69,10 @@ struct vector_iids const GUID *view; };
-typedef HRESULT (WINAPI *async_operation_callback)( IInspectable *invoker, IInspectable **result ); +typedef HRESULT (WINAPI *async_operation_inspectable_callback)( IInspectable *invoker, IInspectable **result );
-HRESULT async_operation_create( const GUID *iid, IInspectable *invoker, async_operation_callback callback, IAsyncOperation_IInspectable **out ); +HRESULT async_operation_inspectable_create( const GUID *iid, IInspectable *invoker, async_operation_inspectable_callback callback, + IAsyncOperation_IInspectable **out );
HRESULT typed_event_handlers_append( struct list *list, ITypedEventHandler_IInspectable_IInspectable *handler, EventRegistrationToken *token ); HRESULT typed_event_handlers_remove( struct list *list, EventRegistrationToken *token ); diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 39813ef5607..c78f69b0a67 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -480,7 +480,7 @@ static HRESULT WINAPI recognizer_CompileConstraintsAsync( ISpeechRecognizer *ifa { IAsyncOperation_IInspectable **value = (IAsyncOperation_IInspectable **)operation; FIXME("iface %p, operation %p semi-stub!\n", iface, operation); - return async_operation_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, NULL, compile_callback, value); + return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, NULL, compile_callback, value); }
static HRESULT WINAPI recognizer_RecognizeAsync( ISpeechRecognizer *iface,
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/async.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/dlls/windows.media.speech/async.c b/dlls/windows.media.speech/async.c index 79510d61dc7..eceda4338b3 100644 --- a/dlls/windows.media.speech/async.c +++ b/dlls/windows.media.speech/async.c @@ -231,11 +231,12 @@ static const struct IAsyncOperation_IInspectableVtbl async_inspectable_vtbl =
/* * - * IAsyncInfo + * IAsyncInfo for IAsyncOperation<IInspectable*> * */
-DEFINE_IINSPECTABLE(async_inspectable_info, IAsyncInfo, struct async_inspectable, IAsyncOperation_IInspectable_iface) +DEFINE_IINSPECTABLE_(async_inspectable_info, IAsyncInfo, struct async_inspectable, + async_inspectable_impl_from_IAsyncInfo, IAsyncInfo_iface, &impl->IAsyncOperation_IInspectable_iface)
static HRESULT WINAPI async_inspectable_info_get_Id( IAsyncInfo *iface, UINT32 *id ) { @@ -245,7 +246,7 @@ static HRESULT WINAPI async_inspectable_info_get_Id( IAsyncInfo *iface, UINT32 *
static HRESULT WINAPI async_inspectable_info_get_Status( IAsyncInfo *iface, AsyncStatus *status ) { - struct async_inspectable *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = async_inspectable_impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p, status %p.\n", iface, status); @@ -261,7 +262,7 @@ static HRESULT WINAPI async_inspectable_info_get_Status( IAsyncInfo *iface, Asyn
static HRESULT WINAPI async_inspectable_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code ) { - struct async_inspectable *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = async_inspectable_impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p, error_code %p.\n", iface, error_code); @@ -278,7 +279,7 @@ static HRESULT WINAPI async_inspectable_info_get_ErrorCode( IAsyncInfo *iface, H
static HRESULT WINAPI async_inspectable_info_Cancel( IAsyncInfo *iface ) { - struct async_inspectable *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = async_inspectable_impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p.\n", iface); @@ -295,7 +296,7 @@ static HRESULT WINAPI async_inspectable_info_Cancel( IAsyncInfo *iface )
static HRESULT WINAPI async_inspectable_info_Close( IAsyncInfo *iface ) { - struct async_inspectable *impl = impl_from_IAsyncInfo(iface); + struct async_inspectable *impl = async_inspectable_impl_from_IAsyncInfo(iface); HRESULT hr = S_OK;
TRACE("iface %p.\n", iface);
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/tests/speech.c | 376 +++++++++++++++++++++++ 1 file changed, 376 insertions(+)
diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index c7d80385b7a..894ed5c35f8 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -100,6 +100,83 @@ static const char *debugstr_hstring(HSTRING hstr) return wine_dbgstr_wn(str, len); }
+struct async_action_handler +{ + IAsyncActionCompletedHandler IAsyncActionCompletedHandler_iface; + LONG ref; + + HANDLE event_block; + HANDLE event_finished; +}; + +static inline struct async_action_handler *impl_from_IAsyncActionCompletedHandler(IAsyncActionCompletedHandler *iface) +{ + return CONTAINING_RECORD(iface, struct async_action_handler, IAsyncActionCompletedHandler_iface); +} + +HRESULT WINAPI async_action_handler_QueryInterface( IAsyncActionCompletedHandler *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IAgileObject) || + IsEqualGUID(iid, &IID_IAsyncActionCompletedHandler)) + { + IUnknown_AddRef(iface); + *out = iface; + return S_OK; + } + + trace("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +ULONG WINAPI async_action_handler_AddRef( IAsyncActionCompletedHandler *iface ) +{ + struct async_action_handler *impl = impl_from_IAsyncActionCompletedHandler(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + return ref; +} + +ULONG WINAPI async_action_handler_Release( IAsyncActionCompletedHandler *iface ) +{ + struct async_action_handler *impl = impl_from_IAsyncActionCompletedHandler(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + return ref; +} + +HRESULT WINAPI async_action_handler_Invoke( IAsyncActionCompletedHandler *iface, IAsyncAction *sender, AsyncStatus status ) +{ + struct async_action_handler *impl = impl_from_IAsyncActionCompletedHandler(iface); + + trace("iface %p, sender %p, status %d.\n", iface, sender, status); + + /* Signal finishing of the handler. */ + if (impl->event_finished) SetEvent(impl->event_finished); + /* Block handler until event is set. */ + if (impl->event_block) WaitForSingleObject(impl->event_block, INFINITE); + + return S_OK; +} + +static const struct IAsyncActionCompletedHandlerVtbl async_action_handler_vtbl = +{ + /* IUnknown methods */ + async_action_handler_QueryInterface, + async_action_handler_AddRef, + async_action_handler_Release, + /* IAsyncActionCompletedHandler methods */ + async_action_handler_Invoke +}; + + +static HRESULT WINAPI async_action_handler_create_static( struct async_action_handler *impl ) +{ + impl->IAsyncActionCompletedHandler_iface.lpVtbl = &async_action_handler_vtbl; + impl->ref = 1; + + return S_OK; +} + struct completed_event_handler { IHandler_RecognitionCompleted IHandler_RecognitionCompleted_iface; @@ -1339,6 +1416,304 @@ done: RoUninitialize(); }
+struct async_action_block_param +{ + IAsyncActionCompletedHandler *handler; + IAsyncAction *action; +}; + +static DWORD WINAPI async_action_block_thread(void *arg) +{ + struct async_action_block_param *param = arg; + HRESULT hr; + + hr = IAsyncAction_put_Completed(param->action, param->handler); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + return 0; +} + +static void test_Recognition(void) +{ + static const WCHAR *list_constraint_name = L"Windows.Media.SpeechRecognition.SpeechRecognitionListConstraint"; + static const WCHAR *recognizer_name = L"Windows.Media.SpeechRecognition.SpeechRecognizer"; + static const WCHAR *speech_constraint_tag = L"test_message"; + static const WCHAR *speech_constraints[] = { L"This is a test.", L"Number 5!", L"What time is it?" }; + ISpeechRecognitionListConstraintFactory *listconstraint_factory = NULL; + IAsyncOperation_SpeechRecognitionCompilationResult *operation = NULL; + IVector_ISpeechRecognitionConstraint *constraints = NULL; + ISpeechContinuousRecognitionSession *session = NULL; + ISpeechRecognitionListConstraint *listconstraint = NULL; + ISpeechRecognitionConstraint *constraint = NULL; + ISpeechRecognizer *recognizer = NULL; + ISpeechRecognizer2 *recognizer2 = NULL; + IAsyncActionCompletedHandler *handler = NULL; + IAsyncAction *action = NULL, *action2 = NULL; + IInspectable *inspectable = NULL; + IAsyncInfo *info = NULL; + struct recognition_result_handler result_handler; + struct compilation_handler compilation_handler; + struct async_action_block_param block_param; + struct async_action_handler action_handler; + struct iterator_hstring iterator_hstring; + struct iterable_hstring iterable_hstring; + EventRegistrationToken token = { .value = 0 }; + HSTRING commands[3], hstr, tag; + AsyncStatus async_status; + HANDLE blocked_thread; + HRESULT hr, error_code; + LONG ref, old_ref; + UINT32 i, id; + BOOL set; + + hr = RoInitialize(RO_INIT_MULTITHREADED); + ok(hr == S_OK, "RoInitialize failed, hr %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(commands); i++) + { + hr = WindowsCreateString(speech_constraints[i], wcslen(speech_constraints[i]), &commands[i]); + ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); + } + + /* The create functions for ListConstraint are broken on Win10 1507 x32 - abort early.*/ + if (broken(is_win10_1507 && (sizeof(void*) == 4))) + { + win_skip("SpeechRecognitionListConstraint object creation broken on Win10 1507 x32!\n"); + goto done; + } + + hr = WindowsCreateString(recognizer_name, wcslen(recognizer_name), &hstr); + ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); + + hr = RoActivateInstance(hstr, &inspectable); + ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR || hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); + WindowsDeleteString(hstr); + + if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. */ + { + win_skip("SpeechRecognizer cannot be activated!\n"); + goto done; + } + + hr = WindowsCreateString(list_constraint_name, wcslen(list_constraint_name), &hstr); + ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); + + hr = RoGetActivationFactory(hstr, &IID_ISpeechRecognitionListConstraintFactory, (void **)&listconstraint_factory); + ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG), "RoGetActivationFactory failed, hr %#lx.\n", hr); + WindowsDeleteString(hstr); + + hr = WindowsCreateString(speech_constraint_tag, wcslen(speech_constraint_tag), &tag); + ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); + + iterator_hstring_create_static(&iterator_hstring, commands, ARRAY_SIZE(commands)); + iterable_hstring_create_static(&iterable_hstring, &iterator_hstring); + hr = ISpeechRecognitionListConstraintFactory_CreateWithTag(listconstraint_factory, &iterable_hstring.IIterable_HSTRING_iface, tag, &listconstraint); + ok(hr == S_OK, "ISpeechRecognitionListConstraintFactory_Create failed, hr %#lx.\n", hr); + WindowsDeleteString(tag); + + ref = ISpeechRecognitionListConstraintFactory_Release(listconstraint_factory); + ok(ref == 1, "Got unexpected ref %lu.\n", ref); + + hr = ISpeechRecognitionListConstraint_QueryInterface(listconstraint, &IID_ISpeechRecognitionConstraint, (void **)&constraint); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ISpeechRecognitionConstraint_put_IsEnabled(constraint, TRUE); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IInspectable_QueryInterface(inspectable, &IID_ISpeechRecognizer, (void **)&recognizer); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IInspectable_QueryInterface(inspectable, &IID_ISpeechRecognizer2, (void **)&recognizer2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ISpeechRecognizer2_get_ContinuousRecognitionSession(recognizer2, &session); + ok(hr == S_OK, "ISpeechRecognizer2_get_ContinuousRecognitionSession failed, hr %#lx.\n", hr); + + hr = ISpeechRecognizer_get_Constraints(recognizer, &constraints); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_get_Constraints failed, hr %#lx.\n", hr); + + hr = IVector_ISpeechRecognitionConstraint_Clear(constraints); + ok(hr == S_OK, "IVector_ISpeechRecognitionConstraint_Clear failed, hr %#lx.\n", hr); + + hr = IVector_ISpeechRecognitionConstraint_Append(constraints, constraint); + ok(hr == S_OK, "IVector_ISpeechRecognitionConstraint_Append failed, hr %#lx.\n", hr); + + ref = IVector_ISpeechRecognitionConstraint_Release(constraints); + ok(ref == 1, "Got unexpected ref %lu.\n", ref); + + ref = ISpeechRecognitionConstraint_Release(constraint); + ok(ref == 2, "Got unexpected ref %lu.\n", ref); + + ref = ISpeechRecognitionListConstraint_Release(listconstraint); + ok(ref == 1, "Got unexpected ref %lu.\n", ref); + + token.value = 0xdeadbeef; + recognition_result_handler_create_static(&result_handler); + hr = ISpeechContinuousRecognitionSession_add_ResultGenerated(session, &result_handler.IHandler_RecognitionResult_iface, &token); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_add_ResultGenerated failed, hr %#lx.\n", hr); + ok(token.value != 0xdeadbeef, "Got unexpexted token: %#I64x.\n", token.value); + + compilation_handler_create_static(&compilation_handler); + compilation_handler.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL); + compilation_handler.thread_id = GetCurrentThreadId(); + ok(!!compilation_handler.event_finished, "Finished event wasn't created.\n"); + + ISpeechRecognizer_CompileConstraintsAsync(recognizer, &operation); + ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr); + + hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, &compilation_handler.IAsyncHandler_Compilation_iface); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + ok(!WaitForSingleObject(compilation_handler.event_finished, 5000), "Wait for event_finished failed.\n"); + IAsyncOperation_SpeechRecognitionCompilationResult_Release(operation); + + hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action); + todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr); + + if (FAILED(hr)) goto skip_action; + + async_action_handler_create_static(&action_handler); + action_handler.event_block = NULL; + action_handler.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!action_handler.event_finished, "Finished event wasn't created.\n"); + + hr = IAsyncAction_put_Completed(action, &action_handler.IAsyncActionCompletedHandler_iface); + todo_wine ok(hr == S_OK, "IAsyncAction_put_Completed failed, hr %#lx.\n", hr); + todo_wine ok(!WaitForSingleObject(action_handler.event_finished , 5000), "Wait for event_finished failed.\n"); + CloseHandle(action_handler.event_finished); + + handler = (void *)0xdeadbeef; + hr = IAsyncAction_get_Completed(action, &handler); + todo_wine ok(hr == S_OK, "IAsyncAction_put_Completed failed, hr %#lx.\n", hr); + todo_wine ok(handler == NULL, "Handler was %p.\n", handler); + + hr = IAsyncAction_QueryInterface(action, &IID_IAsyncInfo, (void **)&info); + todo_wine ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr); + + id = 0xdeadbeef; + hr = IAsyncInfo_get_Id(info, &id); + todo_wine ok(hr == S_OK, "IAsyncInfo_get_Id failed, hr %#lx.\n", hr); + todo_wine ok(id != 0xdeadbeef, "Id was %#x.\n", id); + + async_status = 0xdeadbeef; + hr = IAsyncInfo_get_Status(info, &async_status); + todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); + todo_wine ok(async_status == Completed, "Status was %#x.\n", async_status); + + error_code = 0xdeadbeef; + hr = IAsyncInfo_get_ErrorCode(info, &error_code); + todo_wine ok(hr == S_OK, "IAsyncInfo_get_ErrorCode failed, hr %#lx.\n", hr); + todo_wine ok(error_code == S_OK, "ErrorCode was %#lx.\n", error_code); + + hr = IAsyncInfo_Cancel(info); + todo_wine ok(hr == S_OK, "IAsyncInfo_Cancel failed, hr %#lx.\n", hr); + + async_status = 0xdeadbeef; + hr = IAsyncInfo_get_Status(info, &async_status); + todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); + todo_wine ok(async_status == Completed, "Status was %#x.\n", async_status); + + hr = IAsyncInfo_Close(info); + todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); + + hr = IAsyncInfo_Close(info); + todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); + + hr = IAsyncInfo_Cancel(info); + todo_wine ok(hr == S_OK, "IAsyncInfo_Cancel failed, hr %#lx.\n", hr); + + async_status = 0xdeadbeef; + hr = IAsyncInfo_get_Status(info, &async_status); + todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); + todo_wine ok(async_status == AsyncStatus_Closed, "Status was %#x.\n", async_status); + + error_code = 0xdeadbeef; + hr = IAsyncInfo_get_ErrorCode(info, &error_code); + todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "IAsyncInfo_get_ErrorCode failed, hr %#lx.\n", hr); + todo_wine ok(error_code == E_ILLEGAL_METHOD_CALL, "ErrorCode was %#lx.\n", error_code); + + ref = IAsyncInfo_Release(info); + todo_wine ok(ref == 1, "Got unexpected ref %lu.\n", ref); + + /* + * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality. + */ + + hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action2); + todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); + + async_action_handler_create_static(&action_handler); + action_handler.event_block = CreateEventW(NULL, FALSE, FALSE, NULL); + action_handler.event_finished = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!action_handler.event_block, "Block event wasn't created.\n"); + ok(!!action_handler.event_finished, "Finished event wasn't created.\n"); + + /* Check if IAsyncInfo_Close is non blocking. */ + block_param.handler = &action_handler.IAsyncActionCompletedHandler_iface; + block_param.action = action2; + blocked_thread = CreateThread(NULL, 0, async_action_block_thread, &block_param, 0, NULL); + todo_wine ok(!WaitForSingleObject(action_handler.event_finished , 5000), "Wait for event_finished failed.\n"); + todo_wine ok(WaitForSingleObject(blocked_thread, 100) == WAIT_TIMEOUT, "Wait for block_thread didn't time out.\n"); + + handler = (void *)0xdeadbeef; + old_ref = action_handler.ref; + hr = IAsyncAction_get_Completed(action2, &handler); + todo_wine ok(hr == S_OK, "IAsyncAction_get_Completed failed, hr %#lx.\n", hr); + + todo_wine ok(handler == &action_handler.IAsyncActionCompletedHandler_iface || /* Broken on 1507. */ + broken(handler != NULL && handler != (void *)0xdeadbeef), "Handler was %p.\n", handler); + + ref = action_handler.ref - old_ref; + todo_wine ok(ref == 1, "The ref was increased by %lu.\n", ref); + IAsyncActionCompletedHandler_Release(handler); + + hr = IAsyncAction_QueryInterface(action2, &IID_IAsyncInfo, (void **)&info); + todo_wine ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr); + + hr = IAsyncInfo_Close(info); /* If IAsyncInfo_Close would wait for the handler to finish, the test would get stuck here. */ + todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); + + set = SetEvent(action_handler.event_block); + todo_wine ok(set == TRUE, "Event 'event_block' wasn't set.\n"); + todo_wine ok(!WaitForSingleObject(blocked_thread , 1000), "Wait for blocked_thread failed.\n"); + + ref = IAsyncInfo_Release(info); + todo_wine ok(ref == 1, "Got unexpected ref %lu.\n", ref); + + CloseHandle(action_handler.event_finished); + CloseHandle(action_handler.event_block); + + todo_wine ok(action != action2, "actions were the same!\n"); + + ref = IAsyncAction_Release(action2); + todo_wine ok(!ref, "Got unexpected ref %lu.\n", ref); + + ref = IAsyncAction_Release(action); + todo_wine ok(!ref, "Got unexpected ref %lu.\n", ref); + +skip_action: + hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr); + + ref = ISpeechContinuousRecognitionSession_Release(session); + ok(ref == 1, "Got unexpected ref %lu.\n", ref); + + ref = ISpeechRecognizer2_Release(recognizer2); + ok(ref == 2, "Got unexpected ref %lu.\n", ref); + + ref = ISpeechRecognizer_Release(recognizer); + ok(ref == 1, "Got unexpected ref %lu.\n", ref); + + ref = IInspectable_Release(inspectable); + ok(!ref, "Got unexpected ref %lu.\n", ref); + +done: + for (i = 0; i < ARRAY_SIZE(commands); i++) + WindowsDeleteString(commands[i]); + + RoUninitialize(); +} + START_TEST(speech) { test_ActivationFactory(); @@ -1346,4 +1721,5 @@ START_TEST(speech) test_VoiceInformation(); test_SpeechRecognizer(); test_SpeechRecognitionListConstraint(); + test_Recognition(); }
Rémi Bernon (@rbernon) commented about dlls/windows.media.speech/tests/speech.c:
- hr = IAsyncInfo_get_Id(info, &id);
- todo_wine ok(hr == S_OK, "IAsyncInfo_get_Id failed, hr %#lx.\n", hr);
- todo_wine ok(id != 0xdeadbeef, "Id was %#x.\n", id);
- async_status = 0xdeadbeef;
- hr = IAsyncInfo_get_Status(info, &async_status);
- todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr);
- todo_wine ok(async_status == Completed, "Status was %#x.\n", async_status);
- error_code = 0xdeadbeef;
- hr = IAsyncInfo_get_ErrorCode(info, &error_code);
- todo_wine ok(hr == S_OK, "IAsyncInfo_get_ErrorCode failed, hr %#lx.\n", hr);
- todo_wine ok(error_code == S_OK, "ErrorCode was %#lx.\n", error_code);
- hr = IAsyncInfo_Cancel(info);
- todo_wine ok(hr == S_OK, "IAsyncInfo_Cancel failed, hr %#lx.\n", hr);
I'm not sure it's really useful to check again the IAsyncInfo interface. Sure, it's from a different IAsyncAction class but imho you can expect it to work the same way as the one you tested previously, otherwise you'll just have the same tests duplicated everywhere. It also makes the test purpose a little bit more obscure (which I believe is here to test the `StartAsync` / `StopAsync` functions?)
Alternatively, though I still don't think it means you need to do it every time you create an async, I think you could have a check_async_info helper that does all the checks at once with a few parameters to tell it what to expect (I have one in `dinput/tests/force_feedback.c`), and it'll save you the test duplication.