[Bug 48919] New: Balabolka (TTS app) reports SAPI Error Code 0x80045004 since Wine 2.18 with native SAPI 5.1 via 'winetricks -q speedcsdk'

WineHQ Bugzilla wine-bugs at winehq.org
Sat Apr 11 21:49:11 CDT 2020


https://bugs.winehq.org/show_bug.cgi?id=48919

            Bug ID: 48919
           Summary: Balabolka (TTS app) reports SAPI Error Code 0x80045004
                    since Wine 2.18 with native SAPI 5.1 via 'winetricks
                    -q speedcsdk'
           Product: Wine
           Version: 5.6
          Hardware: x86-64
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: -unknown
          Assignee: wine-bugs at winehq.org
          Reporter: focht at gmx.net
      Distribution: ---

Hello folks,

a user named "HaJones" visited IRC #winehq some hours ago. He asked how to
install multiple Wine versions. He wanted old Wine 2.17 alongside with 5.x. It
turns out the real reason was this:

https://forum.winehq.org/viewtopic.php?t=30711 ("Balabolka and SAPI5 TTS
doesn't work on Wine 3.0 and 3.0.1.").

Apparently Balabolka broke with Wine 2.18

The users suspicion was that the addition of SAPI component to Wine broke it

https://www.winehq.org/announce/2.18

--- snip ---
Huw D. M. Davies (16):
      include: Add sapi.idl.
      include: Add sapiddk.idl.
      include: Add sperror.h.
      sapi: Add a stub dll.
      sapi: Register the typelib.
      sapi: Register the classes from sapiddk.idl that would otherwise not get
registered.
      sapi: Create the Voices registry key.
      sapi: Add a stub SpDataKey object implementation.
      sapi: Implement SpDataKey::SetKey().
      sapi: Add a stub SpObjectTokenEnum object implementation.
      sapi: Implement SpObjectTokenEnum::SetAttribs().
      sapi: Implement SpObjectTokenEnum::GetCount().
      sapi: Add a partial implementation of SpObjectTokenEnum::Next().
      sapi: Add a stub SpObjectTokenCategory object implementation.
      sapi: Implement SpObjectTokenCategory::SetId().
      sapi: Add a partial implementation of
SpObjectTokenCategory::EnumTokens().
--- snip ---

Native SAPI was installed via 'winetricks -q speechsdk' so the reason must be
something else unless there is some override problem or weird COM class
registry accident/mixup.

Download:

https://web.archive.org/web/20200406005249/http://balabolka.site/balabolka.zip

"test 123" is the sample text to synthesize.

--- snip ---
$ pwd
/home/focht/.wine/drive_c/Program Files (x86)/Balabolka

$ WINEDEBUG=+seh,+relay,+ole,+variant wine ./balabolka.exe >>log.txt 2>&1
...
0062:Ret  window proc 0x2a20d8c
(hwnd=0x1073a,msg=CB_GETLBTEXT,wp=00000000,lp=02a0b384) retval=00000028
0062:Ret  user32.SendMessageW() retval=00000028 ret=0045e740
0062:Call user32.CharUpperBuffW(02a0ee04 L"Microsoft Mary [English (United
States)]",00000028) ret=0040ef32
0062:Ret  user32.CharUpperBuffW() retval=00000028 ret=0040ef32
0062:Call user32.CharLowerBuffW(0e23d1cc L"test 123",00000008) ret=0040efc2
....
0062:Call user32.InvalidateRect(00010794,0032f7f8,00000000) ret=004aaf92
0062:Ret  user32.InvalidateRect() retval=00000001 ret=004aaf92
0062:Call oleaut32.SysAllocStringLen(0e23d1cc L"test 123",00000008)
ret=004065dc
0062:Call KERNEL32.IsBadStringPtrW(0e23d1cc,00000008) ret=100060d7
0062:Ret  KERNEL32.IsBadStringPtrW() retval=00000000 ret=100060d7
0062:trace:ole:SysAllocStringLen L"test 123"
0062:Call ucrtbase.memcpy(02b83e14,0e23d1cc,00000010) ret=10006269
0062:Ret  ucrtbase.memcpy() retval=02b83e14 ret=10006269
0062:Ret  oleaut32.SysAllocStringLen() retval=02b83e14 ret=004065dc
...
0062:Call ole32.GetErrorInfo(00000000,0032f7f4) ret=004cfe3e
0062:trace:ole:GetErrorInfo (0, 0032F7F4, 00000000)
0062:Ret  ole32.GetErrorInfo() retval=00000001 ret=004cfe3e
0062:Call
KERNEL32.FormatMessageW(00003200,00000000,80045004,00000000,0032f538,00000100,00000000)
ret=00416070
0062:Ret  KERNEL32.FormatMessageW() retval=00000000 ret=00416070
...
0062:Call KERNEL32.RaiseException(0eedfade,00000001,00000007,0032f7a8)
ret=004d2d94
0062:Call ntdll.memcpy(0032f718,0032f7a8,0000001c) ret=7b00dbc1
0062:Ret  ntdll.memcpy() retval=0032f718 ret=7b00dbc1
0062:trace:seh:raise_exception code=eedfade flags=1 addr=0x7b00dbd1 ip=7b00dbd1
tid=0062
0062:trace:seh:raise_exception  info[0]=004d2d94
0062:trace:seh:raise_exception  info[1]=0e22bd30
0062:trace:seh:raise_exception  info[2]=80045004
0062:trace:seh:raise_exception  info[3]=004d2d94
0062:trace:seh:raise_exception  info[4]=0000008b
0062:trace:seh:raise_exception  info[5]=0032f7f8
0062:trace:seh:raise_exception  info[6]=0032f7c4
0062:trace:seh:raise_exception  eax=0032f704 ebx=80045004 ecx=0032f7a8
edx=0032f704 esi=00000007 edi=0032f770
0062:trace:seh:raise_exception  ebp=0032f758 esp=0032f704 cs=320023 ds=ffff002b
es=002b fs=f7c70063 gs=006b flags=00000212
0062:trace:seh:call_stack_handlers calling handler at 0x4cfef8 code=eedfade
flags=1
0062:trace:seh:call_stack_handlers handler at 0x4cfef8 returned 1
0062:trace:seh:call_stack_handlers calling handler at 0x4d2dad code=eedfade
flags=1
...
0062:Call winex11.drv.SetWindowText(00010bee,04d02c70 L"Can not synthesize the
speech: OLE error 80045004.\r\n\r\nThe caller has specified invalid flags for
this operation.\r\n[SAPI Error Code 0x80045004]") ret=7e7ccc65 
--- snip ---

Unfortunately not much to see from trace log. Commence debugging action.

In the working case (Wine 2.17), the app calls 'ISpVoice::Speak' like this:

ISpVoice::Speak(text, 0xb, NULL);

0xb = speak flags = (SPF_DEFAULT | SPF_PURGEBEFORESPEAK | SPF_IS_XML)

In the "broken" case, that is starting with Wine 2.18, the app passes a
different speak flags value which SAPI 5.1 refuses to accept. This resulted in
SAPI error code 0x80045004.

--- snip ---
009F79D7  | mov edx,dword ptr ss:[ebp-180] | UNICODE "test 123"
009F79DD  | mov ecx,dword ptr ds:[AAEE84]  |
009F79E3  | mov ecx,dword ptr ds:[ecx]     | ECX = 0x8B = speak flags(!)
009F79E5  | mov eax,dword ptr ss:[ebp-8]   |
009F79E8  | mov eax,dword ptr ds:[eax+A94] |
009F79EE  | call balabolka.4D2D54     
--- snip ---

https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee431843(v%3Dvs.85)

0x8b = speak flags = (SPF_PARSE_SAPI | SPF_DEFAULT | SPF_PURGEBEFORESPEAK |
SPF_IS_XML)

009F79E3: ECX = ptr 0x00AB940C

--- snip ---
...
00AB93FC   012A28E4 ; ASCII "JPop"
00AB9400   0129CD9C ; ASCII "Synthpop"
00AB9404   00000000
00AB9408   FFFFFFFF 
00AB940C   0000000B ; speak flags 0xB = '1011
00AB9410   00000000
00AB9414   00000000
...
--- snip ---

Using the debugger's "find all references to address" for 0xAB940C:

--- snip ---
Address  Disassembly

00A7A867 mov dword ptr ds:[AB940C],8B
00A7A8B2 mov dword ptr ds:[AB940C],B
--- snip ---

one comes across this piece of code:

--- snip ---
00A7A81A | mov byte ptr ds:[AAD968],al   
00A7A81F | cmp byte ptr ds:[AB9435],0     
00A7A826 | je balabolka.A7A873            ; set default speak flag 0xb

00A7A828 | lea eax,dword ptr ss:[ebp-3C] 
00A7A82B | call balabolka.67AD8C         
00A7A830 | mov edx,dword ptr ss:[ebp-3C] 
00A7A833 | mov eax,balabolka.AAD96C      
00A7A838 | call balabolka.4068F0         
00A7A83D | lea eax,dword ptr ss:[ebp-40] 
00A7A840 | call balabolka.67AEF0         
00A7A845 | mov edx,dword ptr ss:[ebp-40] 
00A7A848 | mov eax,balabolka.AAD970      
00A7A84D | call balabolka.4068F0         
00A7A852 | lea eax,dword ptr ss:[ebp-44] 
00A7A855 | call balabolka.67B3DC         
00A7A85A | mov edx,dword ptr ss:[ebp-44] 
00A7A85D | mov eax,balabolka.AAD974      
00A7A862 | call balabolka.4068F0         
00A7A867 | mov dword ptr ds:[AB940C],8B  ; speak flags = questionable
00A7A871 | jmp balabolka.A7A8BC          

00A7A873 | lea eax,dword ptr ss:[ebp-48] 
00A7A876 | call balabolka.67B054         
00A7A87B | mov edx,dword ptr ss:[ebp-48] 
00A7A87E | mov eax,balabolka.AAD96C      
00A7A883 | call balabolka.4068F0         
00A7A888 | lea eax,dword ptr ss:[ebp-4C] 
00A7A88B | call balabolka.67B218         
00A7A890 | mov edx,dword ptr ss:[ebp-4C] 
00A7A893 | mov eax,balabolka.AAD970      
00A7A898 | call balabolka.4068F0         
00A7A89D | lea eax,dword ptr ss:[ebp-50] 
00A7A8A0 | call balabolka.67B598         
00A7A8A5 | mov edx,dword ptr ss:[ebp-50] 
00A7A8A8 | mov eax,balabolka.AAD974      
00A7A8AD | call balabolka.4068F0         
00A7A8B2 | mov dword ptr ds:[AB940C],B   ; speak flags = ok
--- snip ---

The decision is made from 00A7A81F, based on value of [AB9435].
Again, using the debugger's "find all references to address" for 0xAB9435:

--- snip ---
Address  Disassembly

00A7A13A setg byte ptr ds:[AB9435]     ; set internal app flag
00A7A6A5 cmp byte ptr ds:[AB9435],0
00A7A81F cmp byte ptr ds:[AB9435],0
--- snip ---

One comes across this piece of code:

--- snip ---
00A7A0F3 | mov byte ptr ds:[AB9423],al    
00A7A0F8 | mov dl,1                       
00A7A0FA | mov eax,dword ptr ds:[427088]  
00A7A0FF | call balabolka.404604          
00A7A104 | mov dword ptr ds:[AB9430],eax  
00A7A109 | mov eax,dword ptr ds:[AAE8F4]  
00A7A10E | cmp dword ptr ds:[eax],5       
00A7A111 | jne balabolka.A7A11D           
00A7A113 | mov eax,dword ptr ds:[AAF1CC]  
00A7A118 | cmp dword ptr ds:[eax],0       
00A7A11B | jg balabolka.A7A12B            
00A7A11D | mov eax,dword ptr ds:[AAE8F4]  
00A7A122 | cmp dword ptr ds:[eax],5       
00A7A125 | jg balabolka.A7A12B            
00A7A127 | xor eax,eax                    
00A7A129 | jmp balabolka.A7A12D           
00A7A12B | mov al,1                       
00A7A12D | mov byte ptr ds:[AB9434],al    
00A7A132 | mov eax,dword ptr ds:[AAE8F4]  ; ptr -> 00A7CC74
00A7A137 | cmp dword ptr ds:[eax],5       ; OS Major version 5.x ?
00A7A13A | setg byte ptr ds:[AB9435]      ; set flag if Windows Vista+ (6.x)
00A7A141 | mov eax,dword ptr ds:[AAE8F4]  
00A7A146 | cmp dword ptr ds:[eax],6       ; Windows Vista 6.0 ?
00A7A149 | jne balabolka.A7A155           
00A7A14B | mov eax,dword ptr ds:[AAF1CC] 
--- snip ---

Repeating the recipe of backwards resolving address references over and over
again. Fortunately there is no dynamic heap memory allocation involved here, so
the memory references are stable across multiple debugger sessions.

The final place (when going backwards):

--- snip ---
00A7CC64  00000000  
00A7CC68  00000000  
00A7CC6C  00000000  
00A7CC70  00000002  
00A7CC74  00000006  ; OS Major version
00A7CC78  00000001  
00A7CC7C  00001DB1  
00A7CC80  029D675C  L"Service Pack 1"
--- snip ---

--- snip ---
004176D4  | add esp,FFFFFEEC              |
004176DA  | mov dword ptr ss:[esp],114    |
004176E1  | push esp                      |
004176E2  | call balabolka.40A094         | call to jmp GetVersionExW
004176E7  | test eax,eax                  |
004176E9  | je balabolka.41773B           |
004176EB  | mov eax,dword ptr ss:[esp+10] |
004176EF  | mov dword ptr ds:[A7CC70],eax |
004176F4  | mov eax,dword ptr ss:[esp+4]  |
004176F8  | mov dword ptr ds:[A7CC74],eax | store OS Major version
004176FD  | mov eax,dword ptr ss:[esp+8]  |
00417701  | mov dword ptr ds:[A7CC78],eax |
00417706  | cmp dword ptr ds:[A7CC70],1   |
0041770D  | jne balabolka.41771F          |
--- snip ---

Trace log for reference (return address):

--- snip ---
0062:Call KERNEL32.GetVersionExW(0032fd80) ret=004176e7 
0062:Ret  KERNEL32.GetVersionExW() retval=00000001 ret=004176e7 
--- snip ---

Apparently the application detects 'Windows 7' and assumes SAPI 5.3 (Vista+) or
5.4, hence the speak flag value of 0xb8. This obviously can't work with SAPI
5.1  which was installed via winetricks.

Why did it "work" on Wine 2.17 then?

'winetricks -q speechsdk' with Wine 5.6:

--- snip ---
...
regsvr32: Successfully registered DLL 'C:\Program Files (x86)\Microsoft Speech
SDK 5.1\Bin\SREng.dll'
Executing ln -s /home/focht/.wine/dosdevices/c:/Program Files (x86)/Common
Files/Microsoft Shared/Speech/sapi.dll
/home/focht/.wine/dosdevices/c:/windows/syswow64/Speech/Common
Using native override for following DLLs: sapi
...
The operation completed successfully
Setting Windows version to default
Executing wine regedit /S C:\windows\Temp\set-winver.reg
Executing wine64 regedit /S C:\windows\Temp\set-winver.reg
...
--- snip ---

Recent 'winetricks -q speechsdk' with Wine 2.17:

--- snip ---
...
Using winetricks 20191224-next - sha256sum:
895b74ea65452b02d32fe9190a63a8cac492760834c1951607005ba32045d59e with wine-5.6
and WINEARCH=win64
...
regsvr32: Successfully registered DLL 'C:\Program Files (x86)\Microsoft Speech
SDK 5.1\Bin\SREng.dll'
Executing ln -s
/home/focht/.wine/dosdevices/z:/home/focht/%CommonProgramFiles(x86)%/Microsoft
Shared/Speech/sapi.dll
/home/focht/.wine/dosdevices/c:/windows/syswow64/Speech/Common
ln: failed to create symbolic link
'/home/focht/.wine/dosdevices/c:/windows/syswow64/Speech/Common': No such file
or directory
------------------------------------------------------
Note: command ln -s
/home/focht/.wine/dosdevices/z:/home/focht/%CommonProgramFiles(x86)%/Microsoft
Shared/Speech/sapi.dll
/home/focht/.wine/dosdevices/c:/windows/syswow64/Speech/Common returned status
1. Aborting.
------------------------------------------------------

--- snip ---

'syswow64/Speech/Common' only exists starting with Wine 2.18. The failure of
the verb causes winetricks to abort, not restoring the Windows version from
'Windows 2000' to default 'Windows 7'. The application detects WinVer < Windows
Vista (6.x) magically works (passing speak flags -> 0xb).

Additional point: Even if the an old release of 'winetricks' script restored
the Windows version (before
https://github.com/austin987/winetricks/commit/84b50038f090ec262d6cdb48ed7940033883c9a6),
many users created WINEPREFIXes with older Wine versions and WinVer is not
changed/reset by an upgrade. Last time the default WinVer was bumped with Wine
2.2 from 'Windows XP SP3' to 'Windows 7 SP1'.

Multiple options:

(1)

When installing SAPI 5.1, the Windows version must be kept at 'Windows 2000' or
'Windows XP' and not restored to default 'Windows 7'. This might impact some
TTS apps that require Windows 7 but on the other hand with Windows 7 you would
never encounter SAPI 5.1. SAPI 5.4 -> distributed as part of OS.

The app works again with WinVer set to 'Windows XP'.

(2)

Upgrade the 'speechsdk' verb to install SAPI 5.3 or later (5.4) from Windows
SDK. 

---

Anyway, this is not a Wine problem but a Winetricks one. This ticket is only
for Winetricks reference and documenation. Will create a ticket at
https://github.com/Winetricks/winetricks asap.

$ sha1sum balabolka.zip 
fb63e6232acba9bf0bfae70e8d5155d347a47292  balabolka.zip

$ du -sh balabolka.zip 
19M    balabolka.zip

$ wine --version
wine-5.6

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list