Spell check on Windows 10/11 with Declares

Hello everyone,

I am trying to spell check a String on Windows 10/11 using declares in Xojo. Windows 10/11 now comes with a built-in spell checker. Unfortunately my approach fails when initializing the COM object and I don’t see where the error could be. Any thoughts on this?

Sample call

CheckSpelling("Hallo Weld, wie gehd es Dir heude? Alles gute zun geburtsdag. Mir geht es gud.")

CheckSpelling

Public Sub CheckSpelling(txt As String)
  #If TargetWindows Then
    
    Soft Declare Function CoCreateInstance Lib "ole32.dll" ( _
         ByRef rclsid As CString, _
         ByVal pUnkOuter As Ptr, _
         ByVal dwClsContext As UInt32, _
         ByRef riid As CString, _
         ByRef ppv As Ptr _
    ) As Int32
    
    Soft Declare Function CoInitializeEx Lib "ole32.dll" ( _
         ByVal pvReserved As Ptr, _
         ByVal dwCoInit As UInt32 _
    ) As Int32
    
    Soft Declare Function CoUninitialize Lib "ole32.dll" () As Int32
    
    Const CLSCTX_INPROC_SERVER As UInt32 = &h1
    Const COINIT_APARTMENTTHREADED As UInt32 = &h2
    
    // GUIDs for the ISpellCheckerFactory and ISpellChecker interfaces as strings
    Var CLSID_SpellCheckerFactory As CString = "{7AB36653-1796-484B-BDFA-E74F1DB7C1DC}"
    Var IID_ISpellCheckerFactory As CString = "{8E018A9D-2415-4677-BF08-794EA61F94BB}"
    Var IID_ISpellChecker As CString = "{B6FD0B71-98C5-403A-BF8B-80EE0C7DFCAA}"
    Var IID_IEnumSpellingError As CString = "{803E3BD4-2828-4410-8290-418D1D73C762}"
    
    // Initialize COM
    Var hr As Int32 = CoInitializeEx(Nil, COINIT_APARTMENTTHREADED)
    If hr <> 0 Then
      MessageBox("Error during COM initialization: " + hr.ToString)
      Return
    End If
    
    // Create ISpellCheckerFactory
    Var pSpellCheckerFactory As Ptr
    hr = CoCreateInstance(CLSID_SpellCheckerFactory, Nil, CLSCTX_INPROC_SERVER, IID_ISpellCheckerFactory, pSpellCheckerFactory)
    If hr <> 0 Then
      MessageBox("Error when creating the ISpellCheckerFactory: " + hr.ToString)
      Call CoUninitialize
      Return
    End If
    
    // Declares for the methods of ISpellCheckerFactory and ISpellChecker
    Soft Declare Function GetSpellChecker Lib "spellcheck.dll" ( _
         ByVal pFactory As Ptr, _
         ByVal lang As WString, _
         ByRef ppChecker As Ptr _
    ) As Int32
    
    Soft Declare Function Check Lib "spellcheck.dll" ( _
         ByVal pChecker As Ptr, _
         ByVal text As WString, _
         ByRef ppErrors As Ptr _
    ) As Int32
    
    Soft Declare Function NextError Lib "spellcheck.dll" ( _
         ByVal pEnum As Ptr, _
         ByRef pError As Ptr _
    ) As Int32
    
    // Retrieve ISpellChecker for the language
    Var pSpellChecker As Ptr
    hr = GetSpellChecker(pSpellCheckerFactory, "de-DE", pSpellChecker)
    If hr <> 0 Then
      MessageBox("Error when retrieving the ISpellChecker: " + hr.ToString)
      pSpellCheckerFactory = Nil
      Call CoUninitialize
      Return
    End If
    
    // Check text for spelling mistakes
    Var pErrors As Ptr
    hr = Check(pSpellChecker, txt, pErrors)
    If hr <> 0 Then
      MessageBox("Spell check error: " + hr.ToString)
      pSpellChecker = Nil
      pSpellCheckerFactory = Nil
      Call CoUninitialize
      Return
    End If
    
    // Process errors
    If pErrors <> Nil Then
      
      ' errorList is a DesktopListbox on our window
      errorList.HasHeader = True
      errorList.HeaderAt(0) = "Incorrect words"
      
      Var pError As Ptr
      
      While NextError(pErrors, pError) = 0 And pError <> Nil
        // Declares for ISpellingError
        Soft Declare Function GetStart Lib "spellcheck.dll" (ByVal pError As Ptr) As UInt32
        Soft Declare Function GetLength Lib "spellcheck.dll" (ByVal pError As Ptr) As UInt32
        Soft Declare Function GetCorrectiveAction Lib "spellcheck.dll" (ByVal pError As Ptr) As UInt32
        Soft Declare Function GetReplacement Lib "spellcheck.dll" (ByVal pError As Ptr, ByRef pReplacement As WString) As Int32
        
        Var start As UInt32 = GetStart(pError)
        Var length As UInt32 = GetLength(pError)
        Var correctiveAction As UInt32 = GetCorrectiveAction(pError)
        
        Var incorrectWord As String = txt.Middle(start, length)
        Var replacement As WString
        
        Call GetReplacement(pError, replacement)
        
        Var correctiveActionStr As String
        
        Select Case correctiveAction
        Case 0
          correctiveActionStr = "No action"
        Case 1
          correctiveActionStr = "Suggestion"
        Case 2
          correctiveActionStr = "Replace"
        Case 3
          correctiveActionStr = "Ignore"
        Else
          correctiveActionStr = "Unknown"
        End Select
        
        errorList.AddRow(incorrectWord + " (Index: " + start.ToString + ", Length: " + length.ToString + ", Action: " + correctiveActionStr + ", Suggestion: " + replacement + ")")
        
      Wend
      
      If errorList.RowCount > 0 Then
        MessageBox("Spelling mistake found!")
      Else
        MessageBox("No spelling mistakes found.")
      End If
    Else
      MessageBox("No spelling mistakes found.")
    End If
    
    // Release COM
    pSpellChecker = Nil
    pSpellCheckerFactory = Nil
    Call CoUninitialize
  #Else
    #Pragma Unused txt
  #EndIf
End Sub

Martin, you don’t like my classes?

Thanks Christian, I know your classes, but I’m looking for a way to handle this without plugin dependence in pure Xojo code.

So CoCreateInstance fails? or GetSpellChecker?
With an error code?

CoInitializeEx(Nil, COINIT_APARTMENTTHREADED) fails and returns 1.

Have you tried to just ignore it?
Xojo already initialized it.

You mean to comment out these lines?

 If hr <> 0 Then
  MessageBox("Error during COM initialization: " + hr.ToString)
  Return
End If

EDIT: No success with this.