[PATCH v2] sapi: Improve Get/SetDefaultTokenId stubs.

Myah Caron qsniyg at protonmail.com
Mon Aug 3 16:55:42 CDT 2020


Signed-off-by: Myah Caron <qsniyg at protonmail.com>
---
Currently the new tests are skipped under wine due to missing registry keys. They can be added by running winetricks speechsdk.

 dlls/sapi/tests/token.c | 313 ++++++++++++++++++++++++++++++++++++++++
 dlls/sapi/token.c       | 140 +++++++++++++++++-
 2 files changed, 449 insertions(+), 4 deletions(-)

diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c
index 9f6689b83f..35e030cc87 100644
--- a/dlls/sapi/tests/token.c
+++ b/dlls/sapi/tests/token.c
@@ -83,6 +83,309 @@ static void test_token_category(void)
     ISpObjectTokenCategory_Release( cat );
 }

+static void backup_speech(HKEY root)
+{
+    LONG res;
+    HKEY key;
+
+    res = RegDeleteTreeW( root, L"SOFTWARE\\Microsoft\\Speech_winetest" );
+    ok( res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND || res == ERROR_ACCESS_DENIED, "got %08x\n", res );
+
+    res = RegCreateKeyW( root, L"SOFTWARE\\Microsoft\\Speech_winetest", &key );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+    RegCopyTreeW( root, L"SOFTWARE\\Microsoft\\Speech", key );
+    ok( res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND, "got %08x\n", res );
+
+    res = RegCloseKey(key);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+}
+
+static BOOL restore_speech(HKEY root, BOOL delete_backup)
+{
+    LONG res;
+    HKEY key;
+    BOOL returnvalue = FALSE;
+
+    res = RegDeleteTreeW( root, L"SOFTWARE\\Microsoft\\Speech" );
+    ok( res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND || res == ERROR_ACCESS_DENIED, "got %08x\n", res );
+    if (res == ERROR_ACCESS_DENIED)
+        returnvalue = TRUE;
+
+    res = RegCreateKeyW( root, L"SOFTWARE\\Microsoft\\Speech", &key );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+    RegCopyTreeW( root, L"SOFTWARE\\Microsoft\\Speech_winetest", key );
+    ok( res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND, "got %08x\n", res );
+
+    RegCloseKey(key);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+    if (delete_backup) {
+        res = RegDeleteTreeW( root, L"SOFTWARE\\Microsoft\\Speech_winetest" );
+        ok( res == ERROR_SUCCESS || res == ERROR_ACCESS_DENIED, "got %08x\n", res );
+    }
+
+    return returnvalue;
+}
+
+static void test_token_default_id(LPCWSTR cat_name)
+{
+    ISpObjectTokenCategory *cat;
+    HRESULT hr;
+    LONG res;
+    HKEY key;
+    LPWSTR token_id = NULL;
+    WCHAR regvalue[512];
+    WCHAR regvalue2[512];
+    DWORD regvalue_size = sizeof( regvalue );
+    WCHAR regcat[256];
+    WCHAR cat_id[256];
+
+    wcscpy(regcat, L"SOFTWARE\\Microsoft\\Speech\\");
+    wcscat(regcat, cat_name);
+
+    wcscpy(cat_id, L"HKEY_LOCAL_MACHINE\\");
+    wcscat(cat_id, regcat);
+
+    res = RegOpenKeyExW( HKEY_LOCAL_MACHINE, regcat, 0, KEY_READ, &key );
+    if (res == ERROR_FILE_NOT_FOUND) {
+        skip( "category %s not found in registry\n", wine_dbgstr_w(cat_name) );
+        return;
+    }
+    RegCloseKey(key);
+
+    hr = CoCreateInstance( &CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER,
+                           &IID_ISpObjectTokenCategory, (void **)&cat );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    token_id = (LPWSTR)0xdeadbeef;
+    hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, &token_id );
+    ok( hr == SPERR_UNINITIALIZED, "got %08x\n", hr );
+    ok( token_id == (LPWSTR)0xdeadbeef, "got %p\n", token_id );
+
+    hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, NULL );
+    ok( hr == SPERR_UNINITIALIZED, "got %08x\n", hr );
+
+    /* if missing, Get/SetDefaultTokenId should initialize HKEY_LOCAL_USER's
+       SOFTWARE\Microsoft\Speech\[cat_name] */
+    res = RegDeleteTreeW( HKEY_CURRENT_USER, regcat );
+    ok( res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND, "got %08x\n", res );
+
+    hr = ISpObjectTokenCategory_SetId( cat, cat_id, FALSE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    res = RegOpenKeyExW( HKEY_CURRENT_USER, regcat, 0, KEY_ALL_ACCESS, &key );
+    ok( res == ERROR_FILE_NOT_FOUND, "got %08x\n", res );
+
+    hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, NULL );
+    ok( hr == E_POINTER, "got %08x\n", hr );
+
+    res = RegOpenKeyExW( HKEY_CURRENT_USER, regcat, 0, KEY_ALL_ACCESS, &key );
+    ok( res == ERROR_FILE_NOT_FOUND, "got %08x\n", res );
+
+    token_id = (LPWSTR)0xdeadbeef;
+    hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, &token_id );
+
+    /* AudioOutput under windows server returns this error */
+    if (hr == SPERR_NOT_FOUND) {
+        /* also happens if TokenEnums/Tokens is empty or doesn't exist */
+        skip( "category %s not found\n", wine_dbgstr_w(cat_name) );
+        return;
+    }
+
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( token_id != (LPWSTR)0xdeadbeef && token_id != NULL, "got %p\n", token_id );
+
+    regvalue_size = sizeof( regvalue );
+    res = RegGetValueW( HKEY_CURRENT_USER, regcat, L"DefaultTokenId", RRF_RT_REG_SZ, NULL,
+                        (LPVOID)&regvalue, &regvalue_size);
+    if (res == ERROR_FILE_NOT_FOUND) {
+        skip( "DefaultTokenId not found for category %s (%s)\n", wine_dbgstr_w(cat_name), wine_dbgstr_w(token_id));
+    } else {
+        ok( res == ERROR_SUCCESS, "got %08x\n", res );
+        ok( !wcscmp(regvalue, token_id),
+            "GetDefaultTokenId (%s) should be equal to the DefaultTokenId key (%s)\n",
+            wine_dbgstr_w(token_id), wine_dbgstr_w(regvalue) );
+    }
+
+    CoTaskMemFree(token_id);
+
+    regvalue_size = sizeof( regvalue2 );
+    regvalue2[0] = 0;
+    res = RegGetValueW( HKEY_LOCAL_MACHINE, regcat, L"DefaultdefaultTokenId", RRF_RT_REG_SZ, NULL,
+                        (LPVOID)&regvalue2, &regvalue_size);
+    if (res == ERROR_FILE_NOT_FOUND) {
+        skip( "DefaultdefaultTokenId not found for category %s (%s)\n", wine_dbgstr_w(cat_name), wine_dbgstr_w(regvalue) );
+    } else {
+        regvalue_size = sizeof( regvalue );
+        regvalue[0] = 0;
+        res = RegGetValueW( HKEY_CURRENT_USER, regcat, L"DefaultTokenId", RRF_RT_REG_SZ, NULL,
+                            (LPVOID)&regvalue, &regvalue_size);
+        ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+        ok( res == ERROR_SUCCESS, "got %08x\n", res );
+        ok( !wcscmp(regvalue, regvalue2),
+            "DefaultTokenId (%s) should be equal to the DefaultdefaultTokenId key (%s)\n",
+            wine_dbgstr_w(regvalue), wine_dbgstr_w(regvalue2) );
+    }
+
+    /* todo: test subkeys */
+
+    res = RegOpenKeyExW( HKEY_CURRENT_USER, regcat, 0, KEY_ALL_ACCESS, &key );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    wcscpy(regvalue, L"bogus");
+    regvalue_size = (wcslen(regvalue) + 1) * sizeof( WCHAR );
+    res = RegSetValueExW( key, L"DefaultTokenId", 0, REG_SZ, (const BYTE*)regvalue, regvalue_size);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    res = RegCloseKey(key);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+    token_id = (LPWSTR)0xdeadbeef;
+    hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, &token_id );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( token_id != (LPWSTR)0xdeadbeef && token_id != NULL, "got %p\n", token_id );
+    todo_wine ok( wcscmp(regvalue, token_id),
+        "GetDefaultTokenId (%s) should not be equal to the bogus DefaultTokenId key (%s)\n",
+        wine_dbgstr_w(token_id), wine_dbgstr_w(regvalue) );
+    CoTaskMemFree(token_id);
+
+    /* todo: add more tests for the resulting token_id */
+
+    res = restore_speech( HKEY_LOCAL_MACHINE, FALSE );
+    if (res == TRUE) {
+        skip( "Unable to delete HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Speech\n" );
+    } else {
+        token_id = (LPWSTR)0xdeadbeef;
+        hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, &token_id );
+        todo_wine ok( hr == 0x800703fa, "got %08x\n", hr );
+        todo_wine ok( token_id == (LPWSTR)0xdeadbeef, "got %p\n", token_id );
+    }
+
+    ISpObjectTokenCategory_Release( cat );
+
+    hr = CoCreateInstance( &CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER,
+                           &IID_ISpObjectTokenCategory, (void **)&cat );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    res = RegDeleteTreeW( HKEY_CURRENT_USER, regcat );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    res = RegDeleteTreeW( HKEY_LOCAL_MACHINE, regcat );
+    if (res == ERROR_ACCESS_DENIED) {
+        skip( "Unable to delete HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Speech\n" );
+
+        restore_speech( HKEY_CURRENT_USER, FALSE );
+        restore_speech( HKEY_LOCAL_MACHINE, FALSE );
+    } else {
+        ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+        hr = ISpObjectTokenCategory_SetId( cat, cat_id, FALSE );
+        todo_wine ok( hr == SPERR_NOT_FOUND, "got %08x\n", hr );
+
+        restore_speech( HKEY_CURRENT_USER, FALSE );
+
+        hr = ISpObjectTokenCategory_SetId( cat, cat_id, FALSE );
+        todo_wine ok( hr == SPERR_NOT_FOUND, "got %08x\n", hr );
+
+        restore_speech( HKEY_LOCAL_MACHINE, FALSE );
+    }
+
+    res = RegDeleteTreeW( HKEY_CURRENT_USER, regcat );
+    ok( res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND, "got %08x\n", res );
+    res = RegOpenKeyExW( HKEY_LOCAL_MACHINE, regcat, 0, KEY_WRITE, &key );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    wcscpy(regvalue, L"bogus");
+    regvalue_size = (wcslen(regvalue) + 1) * sizeof( WCHAR );
+    res = RegSetValueExW( key, L"DefaultdefaultTokenId", 0, REG_SZ, (const BYTE*)regvalue, regvalue_size);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    RegCloseKey(key);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+    hr = ISpObjectTokenCategory_SetId( cat, cat_id, FALSE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    token_id = (LPWSTR)0xdeadbeef;
+    hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, &token_id );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( token_id != (LPWSTR)0xdeadbeef && token_id != NULL, "got %p\n", token_id );
+    todo_wine ok( wcscmp(regvalue, token_id),
+        "GetDefaultTokenId (%s) should not be equal to the bogus DefaultdefaultTokenId key (%s)\n",
+        wine_dbgstr_w(token_id), wine_dbgstr_w(regvalue) );
+    CoTaskMemFree(token_id);
+
+    /* todo: test valid DefaultdefaultTokenId values
+       observation: if it doesn't begin with HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\
+       it will return E_INVALIDARG */
+
+    ISpObjectTokenCategory_Release( cat );
+
+    res = RegDeleteTreeW( HKEY_CURRENT_USER, regcat );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    res = RegOpenKeyExW( HKEY_LOCAL_MACHINE, regcat, 0, KEY_ALL_ACCESS, &key );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    res = RegDeleteValueW( key, L"DefaultdefaultTokenId" );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    RegCloseKey(key);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+
+    hr = CoCreateInstance( &CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER,
+                           &IID_ISpObjectTokenCategory, (void **)&cat );
+    ok( hr == S_OK, "got %08x\n", hr );
+    hr = ISpObjectTokenCategory_SetId( cat, cat_id, FALSE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    token_id = (LPWSTR)0xdeadbeef;
+    hr = ISpObjectTokenCategory_GetDefaultTokenId( cat, &token_id );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( token_id != (LPWSTR)0xdeadbeef && token_id != NULL, "got %p\n", token_id );
+    if (token_id != (LPWSTR)0xdeadbeef)
+        CoTaskMemFree(token_id);
+
+    ISpObjectTokenCategory_Release( cat );
+    restore_speech( HKEY_LOCAL_MACHINE, FALSE );
+    hr = CoCreateInstance( &CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER,
+                           &IID_ISpObjectTokenCategory, (void **)&cat );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = ISpObjectTokenCategory_SetDefaultTokenId( cat, NULL );
+    ok( hr == SPERR_UNINITIALIZED, "got %08x\n", hr );
+
+    hr = ISpObjectTokenCategory_SetId( cat, cat_id, FALSE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = ISpObjectTokenCategory_SetDefaultTokenId( cat, NULL );
+    ok( hr == E_INVALIDARG, "got %08x\n", hr );
+
+    wcscpy(regvalue, L"deadbeef");
+    hr = ISpObjectTokenCategory_SetDefaultTokenId( cat, regvalue );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    regvalue_size = sizeof( regvalue );
+    regvalue[0] = 0;
+    res = RegGetValueW( HKEY_CURRENT_USER, regcat, L"DefaultTokenId", RRF_RT_REG_SZ, NULL,
+                        (LPVOID)&regvalue, &regvalue_size);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    ok( !wcscmp(regvalue, L"deadbeef"),
+        "DefaultTokenId in registry (%s) should be equal to the set default token id (%s)\n",
+        wine_dbgstr_w(regvalue), wine_dbgstr_w(L"deadbeef") );
+
+    res = RegDeleteTreeW( HKEY_CURRENT_USER, regcat );
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    wcscpy(regvalue, L"deadbeef");
+    hr = ISpObjectTokenCategory_SetDefaultTokenId( cat, regvalue );
+    ok( hr == S_OK, "got %08x\n", hr );
+    regvalue_size = sizeof( regvalue );
+    regvalue[0] = 0;
+    res = RegGetValueW( HKEY_CURRENT_USER, regcat, L"DefaultTokenId", RRF_RT_REG_SZ, NULL,
+                        (LPVOID)&regvalue, &regvalue_size);
+    ok( res == ERROR_SUCCESS, "got %08x\n", res );
+    ok( !wcscmp(regvalue, L"deadbeef"),
+        "GetDefaultTokenId (%s) should be equal to the set DefaultTokenId key (%s)\n",
+        wine_dbgstr_w(token_id), wine_dbgstr_w(L"deadbeef") );
+
+    ISpObjectTokenCategory_Release( cat );
+}
+
 static void test_token_enum(void)
 {
     ISpObjectTokenEnumBuilder *token_enum;
@@ -121,6 +424,16 @@ START_TEST(token)
     CoInitialize( NULL );
     test_data_key();
     test_token_category();
+    backup_speech( HKEY_LOCAL_MACHINE );
+    backup_speech( HKEY_CURRENT_USER );
+    test_token_default_id( L"AppLexicons" );
+    test_token_default_id( L"AudioInput" );
+    test_token_default_id( L"AudioOutput" );
+    test_token_default_id( L"PhoneConverters" );
+    test_token_default_id( L"Recognizers" );
+    test_token_default_id( L"Voices" );
+    restore_speech( HKEY_CURRENT_USER, TRUE );
+    restore_speech( HKEY_LOCAL_MACHINE, TRUE );
     test_token_enum();
     CoUninitialize();
 }
diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c
index d2b70c95cf..ff96796dd2 100644
--- a/dlls/sapi/token.c
+++ b/dlls/sapi/token.c
@@ -232,6 +232,7 @@ struct token_category
     LONG ref;

     ISpRegDataKey *data_key;
+    WCHAR *subkey;
 };

 static struct token_category *impl_from_ISpObjectTokenCategory( ISpObjectTokenCategory *iface )
@@ -421,6 +422,8 @@ static HRESULT WINAPI token_category_SetId( ISpObjectTokenCategory *iface,
     res = RegOpenKeyExW( root, subkey, 0, KEY_ALL_ACCESS, &key );
     if (res) return SPERR_INVALID_REGISTRY_KEY;

+    This->subkey = _wcsdup(subkey);
+
     hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_ALL,
                            &IID_ISpRegDataKey, (void **)&This->data_key );
     if (FAILED(hr)) goto fail;
@@ -479,18 +482,147 @@ fail:
     return hr;
 }

+static HRESULT get_user_speech_key( struct token_category *This, HKEY* key )
+{
+    LONG res;
+    WCHAR regvalue[512];
+    DWORD regvalue_size = sizeof( regvalue );
+    struct data_key *this_data_key = impl_from_ISpRegDataKey( This->data_key );
+
+    res = RegOpenKeyExW( HKEY_CURRENT_USER, This->subkey, 0, KEY_ALL_ACCESS, key );
+    if (res == ERROR_SUCCESS) {
+        return S_OK;
+    }
+
+    FIXME( "(%p): semi-stub\n", This );
+
+    res = RegCreateKeyW( HKEY_CURRENT_USER, This->subkey, key );
+    if (res != ERROR_SUCCESS) {
+        /* probably not the correct return value */
+        FIXME( "returning %08x\n", res );
+        return res;
+    }
+
+    res = RegGetValueW( this_data_key->key, NULL, L"DefaultdefaultTokenId",
+                        RRF_RT_REG_SZ, NULL, (LPVOID)&regvalue, &regvalue_size);
+    if (res == ERROR_SUCCESS) {
+        RegSetValueExW( *key, L"DefaultTokenId", 0, REG_SZ, (const BYTE*)regvalue, regvalue_size);
+    } else if (res != ERROR_FILE_NOT_FOUND) {
+        FIXME( "returning %08x\n", res );
+        return res;
+    }
+
+    return S_OK;
+}
+
 static HRESULT WINAPI token_category_SetDefaultTokenId( ISpObjectTokenCategory *iface,
                                                         LPCWSTR id )
 {
-    FIXME( "stub\n" );
-    return E_NOTIMPL;
+    struct token_category *This = impl_from_ISpObjectTokenCategory( iface );
+    HRESULT hr;
+    LONG res;
+    HKEY key;
+
+    TRACE( "(%p)->(%s)\n", iface, debugstr_w(id) );
+
+    if (!This->data_key)
+        return SPERR_UNINITIALIZED;
+
+    if (!id)
+        return E_INVALIDARG;
+
+    hr = get_user_speech_key( This, &key );
+    if (FAILED(hr)) return hr;
+
+    res = RegSetValueExW( key, L"DefaultTokenId", 0, REG_SZ, (const BYTE*)id,
+                          wcslen( id) * sizeof( WCHAR ));
+    if (res != ERROR_SUCCESS) {
+        /* probably not the correct return value */
+        FIXME( "unable to set DefaultTokenId, returning S_FALSE\n" );
+        RegCloseKey( key );
+        return S_FALSE;
+    }
+
+    RegCloseKey( key );
+
+    return S_OK;
+}
+
+static BOOL get_default_token_from_tokens( struct token_category *This,
+                                           LPWSTR token )
+{
+    WCHAR subkey_name[256];
+    WCHAR subkey_name1[256];
+    HKEY key;
+    LONG res;
+    struct data_key *this_data_key = impl_from_ISpRegDataKey( This->data_key );
+
+    res = RegEnumKeyW( this_data_key->key, 0, &subkey_name, sizeof( subkey_name ) );
+    if (res != ERROR_SUCCESS)
+        return FALSE;
+
+    RegOpenKeyExW( this_data_key->key, subkey_name, 0, KEY_ALL_ACCESS, &key );
+    if (res != ERROR_SUCCESS) {
+        WARN( "unable to open subkey %s (%08x)\n", debugstr_w( subkey_name ), res );
+        return FALSE;
+    }
+
+    res = RegEnumKeyW( key, 0, &subkey_name1, sizeof( subkey_name1 ) );
+    if (res != ERROR_SUCCESS)
+        return FALSE;
+
+    RegCloseKey( key );
+
+    wcscpy( token, L"HKEY_LOCAL_MACHINE\\" );
+    wcscat( token, This->subkey );
+    wcscat( token, L"\\" );
+    wcscat( token, subkey_name );
+    wcscat( token, L"\\" );
+    wcscat( token, subkey_name1 );
+
+    return TRUE;
 }

 static HRESULT WINAPI token_category_GetDefaultTokenId( ISpObjectTokenCategory *iface,
                                                         LPWSTR *id )
 {
-    FIXME( "stub\n" );
-    return E_NOTIMPL;
+    struct token_category *This = impl_from_ISpObjectTokenCategory( iface );
+    HRESULT hr;
+    LONG res;
+    HKEY key;
+    WCHAR regvalue[512];
+    DWORD regvalue_size = sizeof( regvalue );
+
+    FIXME( "(%p)->(%p): semi-stub\n", iface, id );
+
+    if (!This->data_key)
+        return SPERR_UNINITIALIZED;
+
+    if (!id)
+        return E_POINTER;
+
+    hr = get_user_speech_key( This, &key );
+    if (FAILED(hr)) return hr;
+
+    res = RegGetValueW( key, NULL, L"DefaultTokenId", RRF_RT_REG_SZ, NULL,
+                        &regvalue, &regvalue_size);
+    RegCloseKey( key );
+    if (res == ERROR_FILE_NOT_FOUND) {
+        res = get_default_token_from_tokens( This, &regvalue );
+
+        if (res == FALSE) {
+            return SPERR_NOT_FOUND;
+        }
+    } else if (res != ERROR_SUCCESS) {
+        /* probably not the correct return value */
+        FIXME( "returning %08x\n", res );
+        return res;
+    }
+
+    *id = CoTaskMemAlloc( regvalue_size );
+    wcscpy( *id, regvalue );
+
+    return S_OK;
 }

 const struct ISpObjectTokenCategoryVtbl token_category_vtbl =
--
2.27.0





More information about the wine-devel mailing list