#include-once ; From Nine ; Suggestion retrieval adapted from bladem2003's approach by #AI. ;~ Opt("MustDeclareVars", True) #include #include Global Const $MSSPELLCHECKINGAPI_S_OK = 0 Global Const $MSSPELLCHECKINGAPI_S_FALSE = 1 Global Const $CLSID_SpellCheckerFactory = "{7AB36653-1796-484B-BDFA-E74F1DB7C1DC}" Global Const $IID_ISpellCheckerFactory = "{8E018A9D-2415-4677-BF08-794EA61F94BB}" Global Const $tag_ISpellCheckerFactory = _ "get_SupportedLanguages hresult(ptr*);" & _ "IsSupported hresult(wstr;bool*);" & _ "CreateSpellChecker hresult(wstr;ptr*);" Global Const $IID_ISpellChecker = "{B6FD0B71-E2BC-4653-8D05-F197E412770B}" Global Const $tag_ISpellChecker = _ "get_LanguageTag hresult(wstr*);" & _ "Check hresult(wstr;ptr*);" & _ "Suggest hresult(wstr;ptr*);" & _ "Add hresult(wstr);" & _ "Ignore hresult(wstr);" Global Const $IID_IEnumSpellingError = "{803E3BD4-2828-4410-8290-418D1D73C762}" Global Const $tag_IEnumSpellingError = _ "Next hresult(ptr*);" Global Const $IID_ISpellingError = "{B7C82D61-FBE8-4B47-9B27-6C0D2E0DE0A3}" Global Const $tag_ISpellingError = _ "get_StartIndex hresult(ulong*);" & _ "get_Length hresult(ulong*);" & _ "get_CorrectiveAction hresult(int*);" & _ "get_Replacement hresult(wstr*);" ; Request one string at a time. ptr* supplies the address of the output pointer. Global Const $MSSPELLCHECKINGAPI_IID_IEnumString = "{00000101-0000-0000-C000-000000000046}" Global Const $MSSPELLCHECKINGAPI_tag_IEnumString = "Next hresult(ulong;ptr*;ulong*);" If Not @Compiled And StringRegExp(@ScriptName, "(?i)^MSSpellCheckingAPI(?:_\d{4}-\d{2}-\d{2}_\d{4})?\.au3$") Then _MSSpellCheckingAPI_Example() EndIf ; #FUNCTION# ==================================================================================================================== ; Name ..........: _MSSpellCheckingAPI_Example ; Description ...: Demonstrates spell checking and suggestion retrieval with one reusable checker. ; Syntax ........: _MSSpellCheckingAPI_Example() ; Parameters ....: None. ; Return values .: Success - True. Per-word failures are displayed and the example continues. ; Failure - An empty string on initialization failure; @error and @extended are preserved. ; Remarks .......: Header generated by #AI ; =============================================================================================================================== Func _MSSpellCheckingAPI_Example() ; Initialize Spell Checking API Local $oSpell = _MSSpellCheckingAPI_CreateSpell("en-US") ; English setup ;~ Local $oSpell = _MSSpellCheckingAPI_CreateSpell("fr-FR") ; French setup ;~ Local $oSpell = _MSSpellCheckingAPI_CreateSpell("pl-PL") ; Polish setup If @error Then Local $iInitError = @error, $iInitExtended = @extended MsgBox($MB_ICONERROR, "Spell Checking API", "Failed to initialize the spell checker." & @CRLF & _ "@error = " & $iInitError & @CRLF & "@extended = " & $iInitExtended) Return SetError($iInitError, $iInitExtended, '') EndIf ; Words to check ;~ Local $aWord = ["Spell Checking", "Speell", "Checcking"] ; English words ;~ Local $aWord = ["Test", "Entêté", "Pasd'allure"] ; French words ;~ Local $aWord = ["Sprawdzanie pisowni", "sprawdanie", "pissowni"] ; Polish words Local $bResult = False For $sWord In $aWord $bResult = _MSSpellCheckingAPI_Check($oSpell, $sWord) If @error Then Local $iCheckError = @error, $iCheckExtended = @extended MsgBox($MB_ICONERROR, "Spell Checking API", "Spell checking failed for: " & $sWord & @CRLF & _ "@error = " & $iCheckError & @CRLF & "@extended = " & $iCheckExtended) ContinueLoop EndIf If $bResult Then MsgBox($MB_OK, "Correct", $sWord) ContinueLoop EndIf Local $aSuggestions = _MSSpellCheckingAPI_GetSuggestions($oSpell, $sWord) Local $iSuggestError = @error, $iSuggestExtended = @extended If $iSuggestError Then MsgBox($MB_ICONERROR, "Spell Checking API", "Suggestion retrieval failed for: " & $sWord & @CRLF & _ "@error = " & $iSuggestError & @CRLF & "@extended = " & $iSuggestExtended) ContinueLoop EndIf ; Correctness comes from _Check(), not from comparing suggestion strings. Local $sMessage = $sWord & @CRLF & @CRLF & "Suggestions:" If $aSuggestions[0] = 0 Then $sMessage &= @CRLF & "(no suggestions found)" Else For $i = 1 To $aSuggestions[0] $sMessage &= @CRLF & " - " & $aSuggestions[$i] Next EndIf MsgBox($MB_ICONWARNING, "Spelling error", $sMessage) Next Return SetError(0, 0, True) EndFunc ;==>_MSSpellCheckingAPI_Example ; #FUNCTION# ==================================================================================================================== ; Name ..........: _MSSpellCheckingAPI_CreateSpell ; Description ...: Creates an independent spell checker for a supported language. ; Syntax ........: _MSSpellCheckingAPI_CreateSpell([$sLanguageTag = "en-US"]) ; Parameters ....: $sLanguageTag - BCP 47 language tag supported by an installed spell-checking provider. ; Return values .: Success - An ISpellChecker object; @error = 0, @extended = 0. ; Failure - 0 and a nonzero @error: ; |1 - Factory creation failed; @extended = ObjCreateInterface @error. ; |2 - IsSupported failed; @extended = HRESULT. ; |3 - The requested language is not supported; @extended = 0. ; |4 - CreateSpellChecker failed or returned NULL; @extended = HRESULT. ; |5 - Checker wrapping failed; @extended = ObjCreateInterface @error. ; Remarks .......: Header generated by #AI ; Requires Windows 8 / Windows Server 2012 or later and a provider for the requested language. ; Retain one returned object per language. Assign 0 when an object is no longer needed. ; =============================================================================================================================== Func _MSSpellCheckingAPI_CreateSpell($sLanguageTag = "en-US") ; Create the spell checker factory Local $oFactory = ObjCreateInterface($CLSID_SpellCheckerFactory, $IID_ISpellCheckerFactory, $tag_ISpellCheckerFactory) Local $iObjectError = @error If Not IsObj($oFactory) Then If Not @Compiled Then ConsoleWrite("! Err : oFactory, ObjCreateInterface @error = " & $iObjectError & @CRLF) Return SetError(1, $iObjectError, 0) EndIf ; Verify that the requested language is supported Local $bSupported = False Local $iHRESULT = $oFactory.IsSupported($sLanguageTag, $bSupported) If $iHRESULT <> $MSSPELLCHECKINGAPI_S_OK Then If Not @Compiled Then ConsoleWrite("! Err : IsSupported HRESULT = " & $iHRESULT & @CRLF) Return SetError(2, $iHRESULT, 0) EndIf If Not $bSupported Then If Not @Compiled Then ConsoleWrite("! Err : Unsupported language tag: " & $sLanguageTag & @CRLF) Return SetError(3, 0, 0) EndIf ; Create the spell checker for the requested language Local $pSpellChecker = 0 $iHRESULT = $oFactory.CreateSpellChecker($sLanguageTag, $pSpellChecker) If $iHRESULT <> $MSSPELLCHECKINGAPI_S_OK Or $pSpellChecker = 0 Then If Not @Compiled Then ConsoleWrite("! Err : CreateSpellChecker HRESULT = " & $iHRESULT & @CRLF) Return SetError(4, $iHRESULT, 0) EndIf Local $oSpell = ObjCreateInterface($pSpellChecker, $IID_ISpellChecker, $tag_ISpellChecker) $iObjectError = @error If Not IsObj($oSpell) Then ; Do not guess whether a failed wrapper consumed the reference; retain the original ownership policy. If Not @Compiled Then ConsoleWrite("! Err : oSpell, ObjCreateInterface @error = " & $iObjectError & @CRLF) Return SetError(5, $iObjectError, 0) EndIf Return SetError(0, 0, $oSpell) EndFunc ;==>_MSSpellCheckingAPI_CreateSpell ; #FUNCTION# ==================================================================================================================== ; Name ..........: _MSSpellCheckingAPI_Check ; Description ...: Checks whether the supplied text contains any spelling errors. ; Syntax ........: _MSSpellCheckingAPI_Check(ByRef $oSpell, $sWord) ; Parameters ....: $oSpell - ISpellChecker object returned by _MSSpellCheckingAPI_CreateSpell(). ; $sWord - Nonempty text to check; may contain more than one word. ; Return values .: Success - True if no errors were found; otherwise False. @error = 0, @extended = 0. ; Failure - False and a nonzero @error: ; |1 - $oSpell is not an object; @extended = 0. ; |2 - The supplied text is empty; @extended = 0. ; |3 - Check failed; @extended = HRESULT. ; |4 - Check returned a NULL enumerator; @extended = 0. ; |5 - Enumerator wrapping failed; @extended = ObjCreateInterface @error. ; |6 - A spelling-error pointer is NULL or wrapping failed; @extended = 0 or ObjCreateInterface @error. ; |7 - IEnumSpellingError.Next returned an unexpected status; @extended = HRESULT. ; Remarks .......: Header generated by #AI ; Always inspect @error before interpreting False as a spelling mistake. ; IsObj() does not verify the interface type. Arbitrary COM objects are not supported. ; Enumeration stops after the first spelling error; positions and corrections are not returned. ; =============================================================================================================================== Func _MSSpellCheckingAPI_Check(ByRef $oSpell, $sWord) If Not IsObj($oSpell) Then Return SetError(1, 0, False) If $sWord = "" Then Return SetError(2, 0, False) ; Check the supplied text and obtain the spelling-error enumerator Local $pEnumSpellingError = 0 Local $iHRESULT = $oSpell.Check($sWord, $pEnumSpellingError) If $iHRESULT <> $MSSPELLCHECKINGAPI_S_OK Then If Not @Compiled Then ConsoleWrite("! Err : Check HRESULT = " & $iHRESULT & @CRLF) Return SetError(3, $iHRESULT, False) EndIf If $pEnumSpellingError = 0 Then If Not @Compiled Then ConsoleWrite("! Err : pEnumSpellingError" & @CRLF) Return SetError(4, 0, False) EndIf Local $oErrors = ObjCreateInterface($pEnumSpellingError, $IID_IEnumSpellingError, $tag_IEnumSpellingError) Local $iObjectError = @error If Not IsObj($oErrors) Then ; Failed-wrapper ownership must be verified before adding a manual Release(). If Not @Compiled Then ConsoleWrite("! Err : oErrors, ObjCreateInterface @error = " & $iObjectError & @CRLF) Return SetError(5, $iObjectError, False) EndIf ; S_OK means that at least one spelling error was returned. ; S_FALSE means that the enumerator contains no spelling errors. Local $pSpellingError = 0 $iHRESULT = $oErrors.Next($pSpellingError) Switch $iHRESULT Case $MSSPELLCHECKINGAPI_S_OK If $pSpellingError = 0 Then Return SetError(6, 0, False) ; Wrap the returned interface pointer so AutoIt releases its COM reference. Local $oSpellingError = ObjCreateInterface($pSpellingError, $IID_ISpellingError, $tag_ISpellingError) $iObjectError = @error If Not IsObj($oSpellingError) Then ; Failed-wrapper ownership must be verified before adding a manual Release(). If Not @Compiled Then ConsoleWrite("! Err : oSpellingError, ObjCreateInterface @error = " & $iObjectError & @CRLF) Return SetError(6, $iObjectError, False) EndIf $oSpellingError = 0 Return SetError(0, 0, False) Case $MSSPELLCHECKINGAPI_S_FALSE Return SetError(0, 0, True) Case Else If Not @Compiled Then ConsoleWrite("! Err : IEnumSpellingError.Next HRESULT = " & $iHRESULT & @CRLF) Return SetError(7, $iHRESULT, False) EndSwitch EndFunc ;==>_MSSpellCheckingAPI_Check ; #FUNCTION# ==================================================================================================================== ; Name ..........: _MSSpellCheckingAPI_CheckWord ; Description ...: Checks text using a cached Polish spell checker. ; Syntax ........: _MSSpellCheckingAPI_CheckWord($sWord) ; Parameters ....: $sWord - Nonempty text to check. ; Return values .: Success - True if no spelling errors were found; otherwise False. @error = 0. ; Failure - False; @error and @extended are propagated from initialization or checking. ; Remarks .......: Header generated by #AI ; Uses the fixed language pl-PL and retries initialization after an earlier failure. ; For another language, create a checker explicitly and call _MSSpellCheckingAPI_Check(). ; =============================================================================================================================== Func _MSSpellCheckingAPI_CheckWord($sWord) ; Reuse one Polish spell checker, but retry initialization after a previous failure. Local Static $oSpell = 0 If Not IsObj($oSpell) Then $oSpell = _MSSpellCheckingAPI_CreateSpell("pl-PL") If @error Then Return SetError(@error, @extended, False) EndIf Local $bResult = _MSSpellCheckingAPI_Check($oSpell, $sWord) Return SetError(@error, @extended, $bResult) EndFunc ;==>_MSSpellCheckingAPI_CheckWord ; #FUNCTION# ==================================================================================================================== ; Name ..........: _MSSpellCheckingAPI_GetSuggestions ; Description ...: Retrieves spelling suggestions without performing a separate preliminary spelling check. ; Syntax ........: _MSSpellCheckingAPI_GetSuggestions(ByRef $oSpell, $sWord) ; Parameters ....: $oSpell - ISpellChecker object returned by _MSSpellCheckingAPI_CreateSpell(). ; $sWord - Nonempty word or phrase for which suggestions are requested. ; Return values .: Success - A one-dimensional array; [0] contains the count and [1..count] contain suggestions. ; An empty list is a one-element array containing 0, not a failure. ; @error = 0; @extended contains the original ISpellChecker.Suggest HRESULT: ; |$MSSPELLCHECKINGAPI_S_OK (0) - The request succeeded. ; |$MSSPELLCHECKINGAPI_S_FALSE (1) - The API reports correct spelling; the list contains the input. ; Failure - False and a nonzero @error. No partial list is returned: ; |1 - $oSpell is not an object; @extended = 0. ; |2 - The supplied text is empty; @extended = 0. ; |3 - Suggest returned an unexpected status; @extended = HRESULT. ; |4 - Suggest returned a NULL enumerator; @extended = 0. ; |5 - Enumerator wrapping failed; @extended = ObjCreateInterface @error. ; |6 - IEnumString.Next returned an unexpected status; @extended = HRESULT. ; |7 - Next returned inconsistent status/count/pointer values; @extended = fetched count. ; |8 - Reading a returned string failed; @extended = _WinAPI_GetString @error. ; |9 - CoTaskMemFree could not be called; @extended = DllCall @error. ; Author ........: #AI; suggestion-enumeration approach contributed by bladem2003. ; Remarks .......: Header generated by #AI ; Capture @error and @extended immediately after calling this function. ; An S_FALSE from Next means end of enumeration, not that the input is correctly spelled. ; No global checker is created or replaced. Arbitrary COM objects are not supported. ; Recoverable failures discard the partial result after attempting string cleanup. ; Link ..........: https://learn.microsoft.com/en-us/windows/win32/api/spellcheck/nf-spellcheck-ispellchecker-suggest ; =============================================================================================================================== Func _MSSpellCheckingAPI_GetSuggestions(ByRef $oSpell, $sWord) If Not IsObj($oSpell) Then Return SetError(1, 0, False) If $sWord = "" Then Return SetError(2, 0, False) Local $pSuggestions = 0 Local $iSuggestHRESULT = $oSpell.Suggest($sWord, $pSuggestions) If $iSuggestHRESULT <> $MSSPELLCHECKINGAPI_S_OK And $iSuggestHRESULT <> $MSSPELLCHECKINGAPI_S_FALSE Then If Not @Compiled Then ConsoleWrite("! Err : Suggest HRESULT = " & $iSuggestHRESULT & @CRLF) Return SetError(3, $iSuggestHRESULT, False) EndIf If $pSuggestions = 0 Then Return SetError(4, 0, False) Local $oSuggestions = ObjCreateInterface($pSuggestions, $MSSPELLCHECKINGAPI_IID_IEnumString, $MSSPELLCHECKINGAPI_tag_IEnumString) Local $iObjectError = @error If Not IsObj($oSuggestions) Then ; Match the existing UDF: do not add an unverified Release() after a failed wrapper. If Not @Compiled Then ConsoleWrite("! Err : oSuggestions, ObjCreateInterface @error = " & $iObjectError & @CRLF) Return SetError(5, $iObjectError, False) EndIf ; Keep spare capacity while enumerating, then return an exactly sized counted array. Local $iCapacity = 8 Local $aResult[$iCapacity + 1] = [0] Local $pString = 0, $iFetched = 0, $iHRESULT = 0 Local $iError = 0, $iExtended = 0, $iReadError = 0, $iFreeError = 0 Local $sSuggestion = "" While True $pString = 0 $iFetched = 0 $iHRESULT = $oSuggestions.Next(1, $pString, $iFetched) Switch $iHRESULT Case $MSSPELLCHECKINGAPI_S_OK If $iFetched <> 1 Or $pString = 0 Then $iError = 7 $iExtended = $iFetched EndIf Case $MSSPELLCHECKINGAPI_S_FALSE ; With celt = 1, a successful end of enumeration returns no item. If $iFetched <> 0 Or $pString <> 0 Then $iError = 7 $iExtended = $iFetched EndIf Case Else $iError = 6 $iExtended = $iHRESULT EndSwitch If $iError = 0 And $iHRESULT = $MSSPELLCHECKINGAPI_S_OK Then $sSuggestion = _WinAPI_GetString($pString, True) $iReadError = @error If $iReadError Then $iError = 8 $iExtended = $iReadError EndIf EndIf ; Release the returned task-memory string even when enumeration or reading failed. ; Save the primary failure so cleanup cannot replace its diagnostic information. __MSSpellCheckingAPI_CoTaskMemFree($pString) $iFreeError = @error If $iFreeError Then If Not @Compiled Then ConsoleWrite("! Err : CoTaskMemFree DllCall @error = " & $iFreeError & @CRLF) If $iError = 0 Then $iError = 9 $iExtended = $iFreeError EndIf EndIf If $iError <> 0 Or $iHRESULT = $MSSPELLCHECKINGAPI_S_FALSE Then ExitLoop If $aResult[0] = $iCapacity Then $iCapacity *= 2 ReDim $aResult[$iCapacity + 1] EndIf $aResult[0] += 1 ; Direct assignment keeps embedded delimiters and line breaks in a single element. $aResult[$aResult[0]] = $sSuggestion WEnd $oSuggestions = 0 If $iError Then Return SetError($iError, $iExtended, False) ReDim $aResult[$aResult[0] + 1] Return SetError(0, $iSuggestHRESULT, $aResult) EndFunc ;==>_MSSpellCheckingAPI_GetSuggestions ; #INTERNAL_USE_ONLY# ============================================================================================================ ; Name ..........: __MSSpellCheckingAPI_CoTaskMemFree ; Description ...: Frees a task-memory allocation and clears its pointer after a successful call. ; Syntax ........: __MSSpellCheckingAPI_CoTaskMemFree(ByRef $pMemory) ; Parameters ....: $pMemory - Owned task-memory pointer, or 0. Never pass a COM interface pointer. ; Return values .: Success - True; @error = 0. A NULL pointer is a successful no-op. ; Failure - False; @error contains the DllCall error and the pointer remains unchanged. ; Author ........: #AI ; Remarks .......: Header generated by #AI ; CoTaskMemFree returns void; a successful DllCall has no API return value to inspect. ; =============================================================================================================================== Func __MSSpellCheckingAPI_CoTaskMemFree(ByRef $pMemory) If $pMemory = 0 Then Return SetError(0, 0, True) DllCall("ole32.dll", "none", "CoTaskMemFree", "ptr", $pMemory) Local $iDllError = @error If $iDllError Then Return SetError($iDllError, 0, False) $pMemory = 0 Return SetError(0, 0, True) EndFunc ;==>__MSSpellCheckingAPI_CoTaskMemFree