Page 1 of 2 12 LastLast
Results 1 to 40 of 61

Thread: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Question Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Hello everyone,

    I'm dealing with a compatibility issue between VB6 and the Speech Application Programming Interface (SAPI), specifically regarding the 32-bit and 64-bit versions of SAPI.

    Here's the scenario:

    SAPI Versions: There are two versions of SAPI, one for 32-bit applications and another for 64-bit applications.

    Compatibility Constraints:

    1. A 32-bit executable (exe) can only interact with the 32-bit version of SAPI.
    2. A 64-bit executable is required to interact with the 64-bit version of SAPI.


    As a voice vendor, I create voices using a C++ COM DLL that interfaces with SAPI. Within this voice DLL, I incorporate MKLML alongside ONNX Runtime to enhance performance. Unfortunately, MKLML is not available in a 32-bit version, limiting me to producing only the 64-bit variant of my voice.dll.

    Problem: Since SAPI 32-bit can only interact with 32-bit DLLs, my VB6 application, which is inherently 32-bit, cannot use the 64-bit voice.dll.

    Question: Is there still no way for a VB6 application to interact with the 64-bit version of SAPI?

    From what I understand, there isn't a method to bridge this gap directly due to the architectural differences between 32-bit and 64-bit applications and their respective DLLs. However, I wanted to ask here in case anyone has come across a workaround or solution that I might have missed.

    Thank you for your insights!
    Last edited by tmighty2; Aug 2nd, 2024 at 05:07 PM.

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,430

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    I'd probably write a small C++ stub program that actually made the API calls into your Voice.DLL. And this C++ program would work like a no-user-interface server. Then, you'd need to establish a way you could do inter-process communications with this C++ program. I'd probably use subclassed hidden (always loaded) windows on both sides, and then use SendMessageTimeout to send messages between those two windows.

    Done that way, you could set up a protocol where VB6 could "tell" the C++ program to make API calls. And, when those API calls returned, the C++ program could send the results back to your VB6 program.

    I'll let you sort out the subclassing, but here's a piece of code I'd use to send the messages from the VB6 side. It'd be the same on the C++ side, just written in C++.

    Code:
    
    Option Explicit
    '
    Private Type COPYDATASTRUCT
        dwData  As Long
        cbData  As Long
        lpData  As Long
    End Type
    '
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
    Private Declare Function SendMessageTimeout Lib "user32" Alias "SendMessageTimeoutA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any, ByVal fuFlags As Long, ByVal uTimeout As Long, lpdwResult As Long) As Long
    '
    
    
    Public Sub SendStringToAnotherWindow(hWndSender As Long, hWndTarget As Long, sMsg As String)
        ' This can be used to send a message (string) to another window, possibly in another VB6 program.
        ' The other VB6 program MUST be expecting the message.  And it will need to be subclassed (i.e., hooked).
        ' See the StringMessageHook, StringMessageUnhook, and StringMessageWindowProc for details on how
        ' the receiving program must be set up.
        '
        Dim cds As COPYDATASTRUCT
        Dim lpdwResult As Long
        Dim Buf() As Byte
        Const WM_COPYDATA = &H4A
        '
        If hWndTarget Then
            ReDim Buf(1 To Len(sMsg) + 1)
            Call CopyMemory(Buf(1), ByVal sMsg, Len(sMsg)) ' Copy the string into a byte array, converting it to ASCII.
            cds.dwData = 3
            cds.cbData = Len(sMsg) + 1
            cds.lpData = VarPtr(Buf(1))
            'Call SendMessage(hWndTarget, WM_COPYDATA, Me.hwnd, cds)
            SendMessageTimeout hWndTarget, WM_COPYDATA, hWndSender, cds, 0, 5000, lpdwResult ' Return after 5 seconds even if receiver didn't acknowledge.
        End If
    End Sub
    
    
    
    Upon loading things up, you'd also probably have to do some FindWindow work, putting titles in these hidden windows and then each side "finding" the other side's hidden window's handle.

    But, all said and done, that shouldn't be too difficult. It's basically pretty close to creating your own home-grown "automation".
    Last edited by Elroy; Aug 3rd, 2024 at 07:01 AM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  3. #3
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,157

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    If only there was a way to compile vb6 code to 64bit instead of messing w/ c++...

    Easier to use tB for the stub, if it cant handle the full app yet, good chance it can tho.

    ps-- iirc theres some unusual ****ery with 64bit sapi defs... different guids and such. i recommend windevlib.

  4. #4

  5. #5

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Elroy, thank you, that is really a possible solution.

  6. #6
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,430

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Quote Originally Posted by tmighty2 View Post
    Elroy, thank you, that is really a possible solution.
    There'd still be lots of little details to consider, for instance:

    • I'd only want one copy of this VB6 program running at any specific time, so check App.PrevInstance.
    • VB6 would probably start up the C++ "bridge" program. So, it'd need to check if there's already a copy of it running and just use that copy if it's already loaded. If not, then the VB6 program would need to load the C++ program (probably a simple Shell command).
    • Is it ok if the C++ program just runs all the time? If not, you might want some kind of timer in it to occasionally check and make sure the VB6 program is still running ... and, if not, terminate.

    Once you get into it, there will almost certainly be other minor details. But this is certainly doable.



    Regarding using twinBasic for the "bridge" program, I'm not sure what it'd take (in terms of licensing twinBasic to get a compiled "bridge" program). But, if that's an option, and you'd rather keep it all written in a VB-ish language, that does sound like a good option. However, I'll leave it to you to look into that.

    Truth be told, any language that'll compile to 64-bit code could be used for your "bridge" program. You'd certainly want it compiled to machine language though so your "bridge" program didn't cause any slowdown.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  7. #7
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    770

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    VB6 could talk to a 64-Bit COM DLL, but not standard 64-Bit DLL. The API version of CreateObject() allows creating out-of-process objects using DLLHOST.exe. You can talk to 32 or 64 Bit COM DLL's that way. The OS takes care of moving data back and forth between VB6 and the 64-Bit COM DLL. Please see CreateObject64() in post #6 in this thread and my subsequent posts.

    https://www.vbforums.com/showthread....createobject64

    In my case, I made a 64-Bit COM DLL using C++(Not SAPI related), and talked to it from VB6. Windows runs the 64-Bit version of DLLHOST.exe to host the 64-Bit DLL.

    Another option for you is to turn your DLL into 64-Bit COM EXE(Same as ActiveX EXE). This doesn't require adding registry entries like the 64-Bit DLL option.

  8. #8
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,157

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Quote Originally Posted by Elroy View Post
    Regarding using twinBasic for the "bridge" program, I'm not sure what it'd take (in terms of licensing twinBasic to get a compiled "bridge" program). But, if that's an option, and you'd rather keep it all written in a VB-ish language, that does sound like a good option. However, I'll leave it to you to look into that.

    Truth be told, any language that'll compile to 64-bit code could be used for your "bridge" program. You'd certainly want it compiled to machine language though so your "bridge" program didn't cause any slowdown.
    Community edition is free but puts a splash screen on initial loads of 64bit exes/dlls, however no restrictions on language features or project types besides llvm-optimized binaries (partially complete). 'VB-ish language' makes it sound like b4a or powerbasic or some other incompatiible dialect; to be clear, its the VB6 language exactly as a compatibility base; it's like VB1-5 vs 6; not another language vs vb6. New syntax and language features are opt in. It runs existing VB6 code without modification in most cases (minus a few bugs, minor unimplemented features, and vb6 internals hacks easily replaced with simpler techniques). For 64bit it uses the MS Office VBA7 syntax with LongPtr/PtrSafe.

  9. #9
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,430

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Quote Originally Posted by fafalone View Post
    'VB-ish language' makes it sound,,,
    Didn't mean to imply anything negative. All I was trying to convey is that twinBasic isn't VB. But, it's certainly VB-ish.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  10. #10

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Thank you for the recommendation to explore DLL Surrogates as a solution for accessing 64-bit functionalities in my VB6 application. I am attempting to implement this solution to access 64-bit SAPI voices and would appreciate your detailed feedback on my current approach and setup.

    Background and Current Implementation:

    I have developed a VB6 application (MyApp.exe) that needs to utilize the advanced features available in the 64-bit version of the Speech API (sapi.dll).
    Based on the suggestion, I've encapsulated the SAPI functionalities within an ActiveX DLL, hoping to leverage it as a surrogate for bridging the 32-bit and 64-bit divide.

    1. ActiveX DLL Creation (SpVoiceAsActiveXDLL.dll):


    I've created an ActiveX DLL that hosts the SpVoice object from the SpeechLib library.
    This DLL is designed to function as a surrogate, aiming to offload the speech processing tasks to the 64-bit sapi.dll.
    Code:
    ' Class within the ActiveX DLL
    Class cSpVoiceInActiveXDLL
    Private WithEvents m_Voice As SpeechLib.SpVoice
    
    Public Sub Speak(ByVal uText As String)
        Dim lStream&
        lStream = m_Voice.Speak(uText, SVSFlagsAsync Or SVSFIsXML)
    End Sub
    1. Integration in VB6 Application (MyApp.exe):

    In the main VB6 application, I replaced direct instances of SpVoice with instances of the class from the ActiveX DLL.

    Code:
    ' Usage in MyApp.exe
    Private WithEvents MyVoice As New cSpVoiceInActiveXDLL
    MyVoice.Speak("Hi!")
    Points Needing Clarification:
    • A. Correct Implementation of DLL Surrogates:

    • Is my approach in structuring the ActiveX DLL as a surrogate correct for accessing 64-bit features from a 32-bit application?
    • Does this setup align with the recommended use of DLL Surrogates to handle cross-bit operations effectively?

    • B. Registry and Configuration Requirements:


    • What specific entries or modifications are necessary in the Windows Registry to ensure that my ActiveX DLL functions as a proper DLL Surrogate?
    • Are there additional configuration steps required to fully enable the interaction between the 32-bit VB6 environment and the 64-bit DLL?

    [*]C. Potential Misunderstandings and Corrections:


    If there are any misconceptions in my current understanding or implementation, what corrections would you suggest?
    I am committed to making this integration work and would immensely value any step-by-step guidance, insights, or illustrative examples you could provide to help refine my implementation.

    Thank you for your continued support and assistance!
    Last edited by tmighty2; Aug 4th, 2024 at 07:32 PM.

  11. #11

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Can somebody please confirm that I do have to compile the component which interacts with sapi.dll as a 64bit version?

    Also, I need to receive the events from the surrogate in VB6. Would that be possible?

  12. #12

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    This is such a mess. I could even write a 64bit VB.NET app for this which can see the 64bit voices and call them, and I would use a textbox in both the VB.NET 64 bit app and in my VB6 32 bit app and a timer, and each app watches the other app's textbox for a new message.
    LOL. I guess that is about the same what pipes would be able to do for me, right?

  13. #13
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    770

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    ActiveX is a marketing term that Microsoft no longer use, they simply renamed it to COM. It's the same thing. So I use them interchangeable.

    Making a VB6 ActiveX/COM DLL doesn't work, because it's always 32-Bit.

    There are 2 general options:

    1 - Preferred: Make a 64-Bit ActiveX/COM EXE. This would host SAPI and any other 64-Bit tech, and talk to it from your standard VB6 EXE, and yes, you can use WithEvents. If using C++(without .Net), the wizard can create a COM project for you, you just have to copy-paste the functions you need. I think you don't need my CreateObject64 with this, but I am not sure.

    2 - Make a 64-Bit ActiveX/COM DLL. This doesn't make sense in most cases, but I had a rare use for it. This 64-Bit DLL doesn't run on its own, it requires a 64-Bit EXE, which is DLLHOST.exe. It requires adding registry entries, and using my CreateObject64. Please use option 1 above. I mentioned this option for completeness sake.

  14. #14

  15. #15
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    770

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Quote Originally Posted by VanGoghGaming View Post
    Doesn't an ActiveX EXE also require registration?
    Yes, but I am talking about extra registry entries needed to run the DLL under DLLHOST.exe as explained here:

    https://learn.microsoft.com/en-us/wi...ate-activation

    These are not needed for ActiveX/COM EXE.

  16. #16

  17. #17
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,401

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Btw, I just failed using PutObject from linked thread above in an x64 target in TB and then GetObject from VB6 which is weird. Same code with x86 target was working fine in VB6 as expected. Both processes were running elevated which might have been a problem with x64 server in TB.

  18. #18

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Can you give me really quick guide how to make such a "64 bit ActiveX/COM EXE" using VS2022 / C++?

    I am asking because I don't understand how that would be different to a ATL COM DLL.

  19. #19

  20. #20
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    770

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Quote Originally Posted by tmighty2 View Post
    Can you give me really quick guide how to make such a "64 bit ActiveX/COM EXE" using VS2022 / C++?

    I am asking because I don't understand how that would be different to a ATL COM DLL.
    ATL = Active Template Library, which is the same as ActiveX, and probably how MS got the ActiveX name.

    However, The trick is suggesting a 3rd option. Use 64-Bit SAPI COM "directly" by using CreateObject64 to create SAPI objects, they would be hosted by the surrogate process(DLLHOST). There is no need to create a separate 64-Bit DLL or EXE in this case.

    If you have anything extra you want to talk to that is 64-Bit and doesn't have COM interface, then you have to create 64-Bit COM EXE to host it.

    Edit: I didn't consider what The trick suggested because you also mentioned MKLML and ONNX. I don't know what they are, or if they have COM interface.
    Last edited by qvb6; Aug 5th, 2024 at 04:38 PM.

  21. #21

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Can you please confirm that this is how I am supposed to do it?

    Code:
    Option Explicit
    
    Private WithEvents m_Voice As SpeechLib.SpVoice
    
    Private Sub Form_Load()
    
        Dim bTry64Bit As Boolean
        bTry64Bit = True
        
        If bTry64Bit Then
            Set m_Voice = CreateObject64("SAPI.SpVoice")
    
            'this shows the error message "CLSIDFromString failed, ret = 800401F3, LastDllError = 14007"
        Else
            Set m_Voice = CreateObject("SAPI.SpVoice")
       End If
    When I set the CLSID manually....

    Code:
    Public Function CreateObject64() As Object
        Dim ret As Long
        Dim pCLSID As TGUID
        Dim IIDispatch As TGUID
        Dim ppv As Long
    
        ' Convert IID_IDispatch to a TGUID
        ret = CLSIDFromString(StrPtr(IID_IDispatch), IIDispatch)
        If ret <> 0 Then
            MsgBox "CLSIDFromString for IID_IDispatch failed, ret = " & Hex(ret)
            Exit Function
        End If
    
        ' Manually specify CLSID for SAPI.SpVoice
        ret = CLSIDFromString(StrPtr("{96749377-3391-11D2-9EE3-00C04F797396}"), pCLSID)
        If ret = 0 Then
            ' Create Object
            ret = CoCreateInstance(VarPtr(pCLSID), 0, CLSCTX_LOCAL_SERVER, VarPtr(IIDispatch), ppv)
            If ppv <> 0 Then
                Set CreateObject64 = ObjFromPtr(ppv)
            Else
                Debug.Print "CoCreateInstance failed, ppv is zero. ret was: " & ret
                'CoCreateInstance failed, ppv is zero. ret was: -2147221164 'REGDB_E_CLASSNOTREG
            End If
        Else
            MsgBox "CLSIDFromString for SAPI.SpVoice failed, ret = " & Hex(ret)
        End If
    End Function
    ... then CoCreateInstance fails with -2147221164 (REGDB_E_CLASSNOTREG, I think)
    Last edited by tmighty2; Aug 5th, 2024 at 05:25 PM.

  22. #22
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    770

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    I had problems with the declaration of CLSIDFromProgID(), that's why it's not finding the key. Fixed below, but CoCreateInstance() instance is failing, possibly because I didn't add this registry entry under AppID:

    DllSurrogate=""

    As explained here:

    https://learn.microsoft.com/en-us/wi...m/dllsurrogate

    I am leaving so I hope that someone else will get it working.



    Code:
    Option Explicit
    
    Public Type TGUID
       Data1 As Long
       Data2 As Integer
       Data3 As Integer
       Data4(7) As Byte
    End Type
     
     
     Private Enum CLSCTX
      CLSCTX_INPROC_SERVER = &H1
      CLSCTX_INPROC_HANDLER = &H2
      CLSCTX_LOCAL_SERVER = &H4
      CLSCTX_INPROC_SERVER16 = &H8
      CLSCTX_REMOTE_SERVER = &H10
      CLSCTX_INPROC_HANDLER16 = &H20
      CLSCTX_RESERVED1 = &H40
      CLSCTX_RESERVED2 = &H80
      CLSCTX_RESERVED3 = &H100
      CLSCTX_RESERVED4 = &H200
      CLSCTX_NO_CODE_DOWNLOAD = &H400
      CLSCTX_RESERVED5 = &H800
      CLSCTX_NO_CUSTOM_MARSHAL = &H1000
      CLSCTX_ENABLE_CODE_DOWNLOAD = &H2000
      CLSCTX_NO_FAILURE_LOG = &H4000
      CLSCTX_DISABLE_AAA = &H8000
      CLSCTX_ENABLE_AAA = &H10000
      CLSCTX_FROM_DEFAULT_CONTEXT = &H20000
      CLSCTX_ACTIVATE_X86_SERVER = &H40000
      CLSCTX_ACTIVATE_32_BIT_SERVER = &H80000
      CLSCTX_ACTIVATE_64_BIT_SERVER = &H100000
      CLSCTX_ENABLE_CLOAKING = &H200000
      CLSCTX_APPCONTAINER = &H400000
      CLSCTX_ACTIVATE_AAA_AS_IU = &H800000
      CLSCTX_RESERVED6 = &H1000000
      CLSCTX_ACTIVATE_ARM32_SERVER = &H2000000
      CLSCTX_PS_DLL = &H4000000
    End Enum
    'Public Const CLSCTX_INPROC_SERVER As Long = 1
    'Public Const CLSCTX_INPROC_HANDLER As Long = 2
    'Public Const CLSCTX_LOCAL_SERVER As Long = 4
    'Public Const CLSCTX_ENABLE_AAA As Long = &H10000
    'Public Const CLSCTX_REMOTE_SERVER As Long = 16
    'Public Const CLSCTX_SERVER As Long = (CLSCTX_INPROC_SERVER Or CLSCTX_LOCAL_SERVER)
    'Public Const CLSCTX_ALL As Long = (CLSCTX_INPROC_SERVER Or CLSCTX_INPROC_HANDLER Or CLSCTX_LOCAL_SERVER)
    Public Const IID_IUnknown As String = "{00000000-0000-0000-C000-000000000046}"
    Public Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
     
    Public Declare Function CoCreateInstance Lib "OLE32.DLL" (ByVal rclsid As Long, ByVal punkOuter As Long, ByVal dwClsContext As Long, ByVal riid As Long, ByRef ppv As Any) As Long
    Public Declare Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As Long, ByRef pCLSID As TGUID) As Long
    Public Declare Function CLSIDFromProgID Lib "OLE32.DLL" (ByVal TSzProgID As Long, ByRef pCLSID As TGUID) As Long
    Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
     
    Public Function CreateObject64(ByRef sObjectName As String) As Object
        Dim ret As Long
        Dim pCLSID As TGUID
        Dim IIDispatch As TGUID
        Dim ppv As Long
     
        ret = CLSIDFromString(StrPtr(IID_IDispatch), IIDispatch)
        ' Get object by GUID
        'ret = CLSIDFromString(StrPtr("{11111111-2222-3333-4444-555555555555}"), pCLSID)
        'ret = CLSIDFromString(StrPtr("{96749377-3391-11D2-9EE3-00C04F797396}"), pCLSID)
        ' Get object by ProgID
        ret = CLSIDFromProgID(StrPtr(sObjectName), pCLSID)
        
        If ret = 0 Then
            ' Success
            
            ' Create Object
            ret = CoCreateInstance(VarPtr(pCLSID), 0, CLSCTX_LOCAL_SERVER Or CLSCTX_ACTIVATE_64_BIT_SERVER, VarPtr(IIDispatch), ppv)
            
            Debug.Print "CoCreateInstance returned = " & Hex(ret) & ", ppv = " & Hex(ppv) & ", LastDllError = " & Err.LastDllError
            
            If ppv <> 0 Then
                Set CreateObject64 = ObjFromPtr(ppv)
            End If
        Else
            Debug.Print "CLSIDFromString/CLSIDFromProgID failed, ret = " & Hex(ret) & ", LastDllError = " & Err.LastDllError
        End If
     
    End Function
     
    ' Returns an object given its pointer
    ' This function reverses the effect of the ObjPtr function
    Public Function ObjFromPtr(ByVal pObj As Long) As Object
        Dim obj As Object
        ' force the value of the pointer into the temporary object variable
        CopyMemory obj, pObj, 4
        ' assign to the result (this increments the ref counter)
        Set ObjFromPtr = obj
        ' manually destroy the temporary object variable
        ' (if you omit this step you'll get a GPF!)
        CopyMemory obj, 0&, 4
    End Function
    
    Public Sub Main()
        Dim o As Object
        
        Set o = CreateObject64("SAPI.SpVoice")
        
        If o Is Nothing Then
            Debug.Print "CreateObject64 Failed."
        Else
            Debug.Print "CreateObject64 Succeeded."
        End If
        
        Set o = Nothing
        
    End Sub

  23. #23

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    I was unable to make my 32 bit exe use the 64bit version of sapi.dll

    Question 1:

    Is the following correct?

    Windows Registry Editor Version 5.00

    [HKEY_CLASSES_ROOT\AppID\My32Bit.exe]
    "AppID"="{99a99999-2584-44b9-a6a8-9dd7022fb85e}"
    "DllSurrogate"="%SystemRoot%\\System32\\dllhost.exe"

    I have also tried twinbasic. I have created a 64bit activex DLL and tried to use it from within My32Bit.exe, but I get "Class not registered" error.

  24. #24
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,756

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    For example. If you want to have SAPI.SpVoice class created in the 64bit system surrogate process you should do the following steps:

    Get the CLSID of SAPI.SpVoice class that is {96749377-3391-11D2-9EE3-00C04F797396}:



    Create AppID entry with the empty DllSurrogate value:



    Add the AppID value under corresponding CLSID entry:



    Specify CLSCTX_LOCAL_SERVER flag (and optionally CLSCTX_ACTIVATE_64_BIT_SERVER if you have 2 versions of surrogates) and create object:

    Code:
    Option Explicit
    
    Private Sub Form_Load()
        Dim tCLSID  As UUID
        Dim tIID    As UUID
        Dim cObj    As ISpeechVoice
        Dim hr      As Long
        
        CLSIDFromString "{96749377-3391-11D2-9EE3-00C04F797396}", tCLSID    ' // CLSID_SPVoice
        CLSIDFromString "{269316D8-57BD-11D2-9EEE-00C04F797396}", tIID      ' // IID_ISpeechVoice
        
        hr = CoCreateInstance(tCLSID, Nothing, CLSCTX_LOCAL_SERVER, tIID, cObj)
        
        If (hr < 0) Then
            MsgBox "error " & hr
            Exit Sub
        End If
        
        cObj.volume = 5
        
    End Sub
    The 64 bit COM server is loaded to external 64 bit system surrogate process (DllHost.exe):



    In order to verify you can check the list of modules:


  25. #25
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,401

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Btw, PutObject/GetObject file monikers approach works using x64 TwinBASIC stub -- no registry tweaking required, works with every ProgID you need x64 instance of.

    Here is TB source of the x64 proxy exe
    Code:
    '--- Form1
    [Description("")]
    [FormDesignerId("161BDD2F-DFC8-42D6-9BCD-4097FE740594")]
    [PredeclaredId]
    Class Form1
    
    Option Explicit
    
    #If Win64 Then
    Private Declare PtrSafe Function CreateFileMoniker Lib "ole32" (ByVal lpszPathName As LongPtr, pResult As IUnknown) As Long
    Private Declare PtrSafe Function GetRunningObjectTable Lib "ole32" (ByVal dwReserved As Long, pResult As IUnknown) As Long
    Private Declare PtrSafe Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As LongPtr, ByVal oVft As Long, ByVal lCc As Long, ByVal vtReturn As VbVarType, ByVal cActuals As Long, prgVt As Any, prgpVarg As Any, pvargResult As Variant) As Long
    
    Private Const PTR_SIZE As Long = 8
    #Else
    Private Enum LongPtr
        [_]
    End Enum
    Private Declare Function CreateFileMoniker Lib "ole32" (ByVal lpszPathName As LongPtr, pResult As IUnknown) As Long
    Private Declare Function GetRunningObjectTable Lib "ole32" (ByVal dwReserved As Long, pResult As IUnknown) As Long
    Private Declare Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As LongPtr, ByVal oVft As Long, ByVal lCc As Long, ByVal vtReturn As VbVarType, ByVal cActuals As Long, prgVt As Any, prgpVarg As Any, pvargResult As Variant) As Long
    
    Private Const PTR_SIZE As Long = 4
    #End If
    
    Private m_lCookie     As Long
    
    Public Function CreateObject(sProgID As String) As Object
        Set CreateObject = VBA.CreateObject(sProgID)
    End Function
    
    Private Sub Form_Load()
        m_lCookie = PutObject(Me, "MyApp.MyProxy")
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
        RevokeObject m_lCookie
    End Sub
    
    Private Function PutObject(oObj As Object, sPathName As String) As Long
        Const ROTFLAGS_REGISTRATIONKEEPSALIVE As Long = 1
        Const IDX_REGISTER  As Long = 3
        Dim pROT            As IUnknown
        Dim pMoniker        As IUnknown
        
        Call GetRunningObjectTable(0, pROT)
        Call CreateFileMoniker(StrPtr(sPathName), pMoniker)
        DispCallByVtbl pROT, IDX_REGISTER, ROTFLAGS_REGISTRATIONKEEPSALIVE, ObjPtr(oObj), ObjPtr(pMoniker), VarPtr(PutObject)
    End Function
     
    Private Sub RevokeObject(ByVal lCookie As Long)
        Const IDX_REVOKE    As Long = 4
        Dim pROT            As IUnknown
        
        Call GetRunningObjectTable(0, pROT)
        DispCallByVtbl pROT, IDX_REVOKE, lCookie
    End Sub
     
    Private Function DispCallByVtbl(pUnk As IUnknown, ByVal lIndex As Long, ParamArray A() As Variant) As Variant
        Const CC_STDCALL    As Long = 4
        Dim lIdx            As Long
        Dim vParam()        As Variant
        Dim vType(0 To 63)  As Integer
        Dim vPtr(0 To 63)   As LongPtr
        Dim hResult         As Long
        
        vParam = A
        For lIdx = 0 To UBound(vParam)
            vType(lIdx) = VarType(vParam(lIdx))
            vPtr(lIdx) = VarPtr(vParam(lIdx))
        Next
        hResult = DispCallFunc(ObjPtr(pUnk), lIndex * PTR_SIZE, CC_STDCALL, vbLong, lIdx, vType(0), vPtr(0), DispCallByVtbl)
        If hResult < 0 Then
            Err.Raise hResult
        End If
    End Function
    
    End Class
    Here is VB6 code

    Code:
    '--- Form1
    Option Explicit
    
    Private m_oProxy64 As Object
    
    Private Sub Form_Load()
        Set m_oProxy64 = GetObject("MyApp.MyProxy")
    End Sub
    
    Private Sub Form_Click()
        Dim oSapi As Object
        
        On Error GoTo EH
        Set oSapi = m_oProxy64.CreateObject("SAPI.SpVoice")
        MsgBox TypeName(oSapi)
        Exit Sub
    EH:
        MsgBox Err.Description, vbCritical
    End Sub
    Of course TB's source can be cleaned up using regular interfaces (which the language now supports) and then can be implemented to parse command line for moniker name, etc.

    cheers,
    </wqw>

  26. #26

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    @TheTrick I get permission denied error trying to create AppID in Computer\HKEY_CLASSES_ROOT\CLSID\{96749377-3391-11D2-9EE3-00C04F797396}.
    I am going to test how to resolve this.

  27. #27
    Frenzied Member VanGoghGaming's Avatar
    Join Date
    Jan 2020
    Location
    Eve Online - Mining, Missions & Market Trading!
    Posts
    1,863

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    You need administrator rights if you want to write to "HKEY_CLASSES_ROOT" or "HKEY_LOCAL_MACHINE" from your VB6 program.

  28. #28

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    I did try it with admin rights of course after normal did not work.

  29. #29

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Thank you. I have chosen the "Standard EXE" in tb.
    For project name I used "tbTry3", Option Explicit on, Visual Styles on, Include common controls on, dpi awareness System_DPI_Aware.
    Then I clicked "Form1.twin.
    It says:
    Code:
    [Description("")]
    [FormDesignerId("257B7201-9C32-487E-93BF-C2B3B97AFBFF")]
    [PredeclaredId]
    Class Form1
    
        Sub New()
        End Sub
        
    End Class
    I noticed my FormDesignerId is different than yours.

    I replaced the code with your code.
    Trying to compile, I get:

    [DEBUGGER] global variables have been cleared
    [BUILD] Starting...
    [LINKER] compilation (codegen) error detected in 'Form1.{default_constructor}' at line #1
    [TYPELIB] failed to create typeinfo for coclass 'Form1'. Class contains compilation errors.
    [LINKER] FAILED to create type library
    [BUILD] failed

    I replace your FormDesignerId with the one that was present in my version.

    Now it compiles saying

    [BUILD] Starting...
    02:48:52.638
    [LINKER] SUCCESS created output file 'C:\Users\MyUser\Desktop\tbTry3proj\Build\tbTry3_win64.exe'
    02:48:52.638
    [LINKER]
    Launch EXE
    Open Folder

    I guess I have to start this file to be able to use it, so I double-click it, and it shows the tb splash screen.

    I click the form you provided me with, and it works.

    Can you tell me how to use the SPVoice withevents?

    I have modified your VB6 Form code like that, expecting events to occur, but they do not:

    Code:
    '--- Form1
    Option Explicit
    
    Private m_oProxy64 As Object
    Private WithEvents m_Voice As SpeechLib.SpVoice
    
    Private Sub Form_Load()
    Set m_oProxy64 = GetObject("MyApp.MyProxy")
    End Sub
    
    Private Sub Form_Click()
        Dim oSapi As Object
        
        On Error GoTo EH
        
        Set m_Voice = m_oProxy64.CreateObject("SAPI.SpVoice")
    
        Dim lFlags&
        lFlags = 1 'async
        
        Dim lStream&
        lStream = m_Voice.Speak("this is a test", lFlags)
        
    
        Exit Sub
    EH:
        MsgBox Err.Description, vbCritical
    End Sub
    
    Private Sub m_voice_StartStream(ByVal StreamNumber As Long, ByVal StreamPosition As Variant)
    
        Debug.Print "start " & StreamNumber
        
    End Sub
    
    Private Sub m_voice_Word(ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
    
        Debug.Print "word streamnumber: " & StreamNumber
        
    End Sub
    Edit: I am using polling with a Timer now. It does work fine. However, if there should be any way to get events the "usual way" I would appreciate knowing about it.

    Thank you so much! I have working solution.
    Last edited by tmighty2; Aug 6th, 2024 at 09:21 PM.

  30. #30

  31. #31

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    I noticed that the events are difficult to handle as I required multiple instance of SPVoice at the same time.

    Doing this in TB would not get me the events in TB:

    Code:
    Public Function CreateObject(sProgID As String) As Object
        Set CreateObject = VBA.CreateObject(sProgID)
    End Function
    Instead, I would (IMO) have to do this in TB:

    Code:
    Private WithEvents m_Voice As SpVoice
    
    Public Function CreateSAPIVoice() As SpeechLib.SpVoice
    
        Set m_Voice = New SpVoice
    
        Set CreateSAPIVoice = m_Voice
        
    End Function
    This way, the TB form will receive the SPVoice events, and I can use a Timer to poll the TB.

    However, I need multiple instances of SPVoice at the same time.
    I store them in a collection.

    This is how I tried to do it in TB:
    Code:
    Private WithEvents m_Voices() As SpVoice
    Public Function GetAnotherSAPIVoice() As SpeechLib.SpVoice
        
        Dim nVoice As SpVoice
        Set nVoice = New SpVoice
        
        Dim lUB As Integer = UBound(m_Voices)
        ReDim Preserve m_Voices(0 To lUB)
        
        Set m_Voices(lUB) = nVoice
    
        Set GetAnotherSAPIVoice = nVoice
        
    End Function
    This works, but the events appear as if it was just one instance:

    Code:
    Private WithEvents m_Voices() As SpVoice
    Private WithEvents m_Voice As SpVoice
    
    Private Sub m_Voice_Word(ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
    
    End Sub
    Private Sub m_Voices_Word(ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
        
    End Sub
    Even though one event is for a single instance and the other event is for an array of instances, they look perfectly the same. I think an index is missing to be able to determine which one of the instances raised this event.
    There is no "Index As Integer" in the sub, so when an event occurs, I can not determine which one of the m_Voices() raised this event.

    I would appreciate help with the events very much.

    Thank you.

  32. #32
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,401

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    > I would appreciate help with the events very much.

    There is no reason for events not to work with this proxy setup per se. Here is my test VB6 code

    Code:
    '--- Form1
    Option Explicit
    
    Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
    
    Private m_oProxy64 As Object
    Private WithEvents m_Voice As SpeechLib.SpVoice
    Private WithEvents m_oFont As StdFont
    
    Private Sub Command1_Click()
        On Error GoTo EH
        If m_oProxy64 Is Nothing Then
            Set m_oProxy64 = GetObject("MyApp.MyProxy")
        End If
        If m_oFont Is Nothing Then
            Set m_oFont = m_oProxy64.CreateObject("StdFont")
        End If
        m_oFont.Size = m_oFont.Size + 2
        Print "m_oFont.Size " & m_oFont.Size, GetCurrentThreadId
        Exit Sub
    EH:
        MsgBox Err.Description, vbCritical
        Set m_oFont = Nothing
        Set m_oProxy64 = Nothing
    End Sub
    
    Private Sub m_oFont_FontChanged(ByVal PropertyName As String)
        Print "m_oFont_FontChanged " & PropertyName, GetCurrentThreadId
    End Sub
    This works for StdFont instances and FontChanged event is received from x64 instance.

    You don't have to use New to receive events i.e. CreateObject works fine as long as your variables is declared WithEvents ... As Strong.Type for early-bound access.

    FYI, this is early-bound:

    Dim oFont As StdFont
    Set oFont = CreateObject("StdFont")

    This is late-bound:

    Dim oFont As Object
    Set oFont = New StdFont

    This is early-bound too:

    Dim oFont As StdFont
    Set oFont = New StdFont

    . . . so it does not matter if instance is created with New or CreateObject, it only matter how the variable is dimensioned i.e. As Strong.Type vs As Object. This is has always been the case, it is not something in connection with x64 vs x86 instancing we are discussing here.

    Anyway, obviously there is some incompatibility with SAPI.SpVoice in the way events are fired. Probably connected with threading/async implementation which boggles cross-process bridge OS is providing.

    Just tested x86 build of the proxy project -- events don't fire cross-process too.

    About Private WithEvents m_Voices() As SpVoice syntax -- obviously arrays are not supported in VB6 like this so this is a nice extension to the language in TB which is not completely implemented i.e. events are not massaged to have index property. Apparently ETA on this feature getting fixed can only be shared by Wayne.

    One way going forward from here is to implement event stubs in TB and forward these as function calls to a callback object in VB6, something like this

    Code:
    Private WithEvents m_oVoice As SpeechLib.SpVoice
    Private m_oCallback As Object
    
    Public Function CreateSpVoice(oCallback As Object) As Object
        Set m_oVoice = New SpeechLib.SpVoice
        Set m_oCallback = oCallback
        Set CreateSpVoice = m_oVoice '--- fixed: setting retval was missing
    End Function
    
    Private Sub m_oVoice_StartStream(ByVal StreamNumber As Long, ByVal StreamPosition As Variant)
        If Not m_oCallback Is Nothing Then
            m_oCallback.SpVoice_StartStream StreamNumber, StreamPosition
        End If
    End Sub
    
    Private Sub m_oVoice_Word(ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
        If Not m_oCallback Is Nothing Then
            m_oCallback.SpVoice_Word StreamNumber, StreamPosition, CharacterPosition, Length
        End If
    End Sub
    cheers,
    </wqw>

  33. #33

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    In the tb form, would it be an array of callback objects and voices or just 1?

    I am still confused about the callbacks. Could you perhaps tell me what it would look like on the VB6 side?
    Last edited by tmighty2; Aug 7th, 2024 at 07:20 PM.

  34. #34
    Frenzied Member VanGoghGaming's Avatar
    Join Date
    Jan 2020
    Location
    Eve Online - Mining, Missions & Market Trading!
    Posts
    1,863

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    You can't use "WithEvents" with an array of objects so just one.

  35. #35

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Edit: I got it!!

    Code:
    Option Explicit
    
    Private m_oProxy64 As Object
    Private WithEvents m_Voice As SpVoice
    
    Private Sub Form_Load()
    
        Set m_oProxy64 = GetObject("MyApp.MyProxy")
        Set m_Voice = m_oProxy64.CreateSPVoice(Me)
          
        m_Voice.Speak "hi!"
        
    End Sub
    Public Sub SpVoice_Word(ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
        Stop
    End Sub

  36. #36
    Frenzied Member VanGoghGaming's Avatar
    Join Date
    Jan 2020
    Location
    Eve Online - Mining, Missions & Market Trading!
    Posts
    1,863

    Wink Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    The Voice object can raise events only on the TB side and you forward them to the VB6 side via callbacks. You need to declare a Public Sub on the VB6 side with the name of each event you want to forward.

    Yep, I see you've already figured it out!

  37. #37

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    I am stuck:
    I get the error "A call for a property or method must not contain a reference to a private object - neither as an argument nor as a return value." in my scenario.

    I guess I have to use "Implements" in VB6, but I can an error saying that I must implement "SpVoice_Word" which is the method that TB requires to send the class messages. When I did implement it, the error wouldn't go away, so I guess there is something else...

    Here is my scenario:

    I have a Form1:

    Code:
    Option Explicit
    
    Private WithEvents m_Voices As cSPVoices
    Private WithEvents m_Voice As SpVoice
    
    Private m_oProxy64 As Object 'Currently not used
    
    Private m_sName$
    Private m_lCount&
    
    Private Sub btnYetAnotherVoiceVoice_Click()
    
        m_lCount = m_lCount + 1
        
        If m_lCount = 1 Then
            m_sName = "david"
        ElseIf m_lCount = 2 Then
            m_sName = "andreas"
        ElseIf m_lCount = 3 Then
            m_sName = "hedda"
        End If
    
        Dim nYetAnotherVoice As cSpVoice
        Set nYetAnotherVoice = New cSpVoice
        nYetAnotherVoice.CreateVoiceAndSelectSpeaker m_sName
        nYetAnotherVoice.Speak "Hi!"
        
        m_Voices.AddAVoiceToMyCollection_And_Set_Me_As_Callback nYetAnotherVoice
    
    End Sub
    Private Sub Form_Load()
    
        Set m_Voices = New cSPVoices
        
    End Sub
    Code:
    class cSpVoices:
    
    Option Explicit
    
    Implements cSpVoice
    
    Public Event Word(ByVal CharacterPosition As Long, ByVal Length As Long)
    
    Private m_MyVoices() As New cSpVoice
    
    Public Sub AddAVoiceToMyCollection_And_Set_Me_As_Callback(ByRef u As cSpVoice)
    
        Dim lUB&
        lUB = UBound(m_MyVoices) + 1
        ReDim Preserve m_MyVoices(0 To lUB)
        Set m_MyVoices(lUB) = u
        Set m_MyVoices(lUB).Callback(lUB) = Me
    
    End Sub
    
    Private Sub Class_Initialize()
    
        ReDim m_MyVoices(0)
    
    End Sub
    Public Sub WordSpokenEvent(ByVal Index As Long, ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
    
        RaiseEvent Word(CharacterPosition, Length)
    
    End Sub
    
    
    
    Private Sub cSpVoice_CreateVoiceAndSelectSpeaker(ByVal uVoiceName As String)
    
    End Sub
    
    Private Sub cSpVoice_SpVoice_Word(ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
    
    End Sub
    
    Private Sub cSpVoice_Speak(ByVal u As String)
    
    End Sub
    class cSpVoice:

    Code:
    Option Explicit
    
    Private m_oProxy64 As Object
    Private WithEvents m_Voice As SpVoice
    
    Private m_Callback As cSPVoices
    Private m_Index    As Long
    
    Public Sub CreateVoiceAndSelectSpeaker(ByVal uVoiceName As String)
    
        Set m_oProxy64 = GetObject("MyApp.MyProxy")
        Set m_Voice = m_oProxy64.CreateSPVoice(Me)
          
        pSetSpeaker uVoiceName
    
    End Sub
    
    Friend Property Get Callback(Optional ByVal Index As Long) As cSpVoice
        Set Callback = m_Callback
    End Property
    
    Friend Property Set Callback(ByVal Index As Long, ByRef RHS As cSPVoices)
        m_Index = Index
        Set m_Callback = RHS
    End Property
    
    Public Sub SpVoice_Word(ByVal StreamNumber As Long, ByVal StreamPosition As Variant, ByVal CharacterPosition As Long, ByVal Length As Long)
    
        If m_Callback Is Nothing Then
            Exit Sub
        End If
        
        m_Callback.WordSpokenEvent m_Index, StreamNumber, StreamPosition, CharacterPosition, Length
    
    End Sub
    
    Public Sub Speak(ByVal u As String)
    
        m_Voice.Speak u
    
    End Sub
    Private Function pSetSpeaker(ByVal uVoiceNameAsGUID As String) As Boolean
    On Error GoTo ErrHandler
    
        Dim Token As SpeechLib.SpObjectToken
        
        Set m_Voice = New SpVoice
        
        For Each Token In m_Voice.GetVoices
        
            Dim sName$
            sName = Token.GetAttribute("Name")
    
            Dim l&
            l = InStr(1, sName, uVoiceNameAsGUID, vbTextCompare)
            If l > 0 Then
    
                Dim sConcName$
                sConcName = "Name=" & sName
    
                Dim sConcLanguage$
                sConcLanguage = "Language=" & Token.GetAttribute("Language")
    
                Set m_Voice.Voice = m_Voice.GetVoices(sConcName, sConcLanguage).Item(0)
    
                Exit Function
            End If
    
        Next Token
    
    Exit Function
    ErrHandler:
    Debug.Print Err.Description
    Debug.Assert False
    End Function
    I have attached my project.
    Attached Files Attached Files
    Last edited by tmighty2; Aug 7th, 2024 at 08:54 PM.

  38. #38

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    Name:  finding1.jpg
Views: 261
Size:  79.8 KB

  39. #39
    Frenzied Member VanGoghGaming's Avatar
    Join Date
    Jan 2020
    Location
    Eve Online - Mining, Missions & Market Trading!
    Posts
    1,863

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    You need to do it in a form (not in a class), like you did in post #35.

  40. #40

    Thread Starter
    Hyperactive Member
    Join Date
    Jul 2017
    Posts
    459

    Re: Compatibility of 32-bit VB6 Applications with 64-bit SAPI

    I used the callback mechanism in a class like this in the past without problems.
    I don't see the difference between a class and a form.
    You say that I should make a new instance of a form for each voice, right? Isn't that just like a class?

Page 1 of 2 12 LastLast

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width