diff --git a/Directory.Build.props b/Directory.Build.props
index 68f9340b..55853567 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -32,7 +32,7 @@
-
+
diff --git a/Microsoft.Windows.CsWin32.sln b/Microsoft.Windows.CsWin32.sln
index 0637d145..971d3b6b 100644
--- a/Microsoft.Windows.CsWin32.sln
+++ b/Microsoft.Windows.CsWin32.sln
@@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Win32MetaGeneration", "src\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenerationSandbox.Tests", "test\GenerationSandbox.Tests\GenerationSandbox.Tests.csproj", "{7E8A5179-F94C-410F-8BBE-FDAAA95A19C3}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpellChecker", "test\SpellChecker\SpellChecker.csproj", "{744BE74F-8C4A-49E8-9683-52D987224285}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -65,6 +67,10 @@ Global
{7E8A5179-F94C-410F-8BBE-FDAAA95A19C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E8A5179-F94C-410F-8BBE-FDAAA95A19C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E8A5179-F94C-410F-8BBE-FDAAA95A19C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {744BE74F-8C4A-49E8-9683-52D987224285}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {744BE74F-8C4A-49E8-9683-52D987224285}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {744BE74F-8C4A-49E8-9683-52D987224285}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {744BE74F-8C4A-49E8-9683-52D987224285}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -75,6 +81,7 @@ Global
{EB7D0834-4236-408F-B172-64FB45FF643A} = {9E154A29-1796-4B85-BD81-B6A385D8FF71}
{6638957D-09ED-47C1-86B9-5D2DFD0FE625} = {9E154A29-1796-4B85-BD81-B6A385D8FF71}
{7E8A5179-F94C-410F-8BBE-FDAAA95A19C3} = {36CCE840-6FE5-4DB9-A8D5-8CF3CB6D342A}
+ {744BE74F-8C4A-49E8-9683-52D987224285} = {36CCE840-6FE5-4DB9-A8D5-8CF3CB6D342A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E3944F6A-384B-4B0F-B93F-3BD513DC57BD}
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index dc7836c7..179b9901 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -2,7 +2,6 @@
- net5.0;netcoreapp3.1;net472
false
true
diff --git a/test/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj b/test/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj
index 6f7d75c0..ad0476c2 100644
--- a/test/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj
+++ b/test/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj
@@ -2,6 +2,7 @@
+ net5.0;netcoreapp3.1;net472
diff --git a/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj b/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj
index 3c70e2c8..a1525483 100644
--- a/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj
+++ b/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj
@@ -1,6 +1,7 @@
+ net5.0;netcoreapp3.1;net472
diff --git a/test/SpellChecker/NativeMethods.txt b/test/SpellChecker/NativeMethods.txt
new file mode 100644
index 00000000..0afd0e45
--- /dev/null
+++ b/test/SpellChecker/NativeMethods.txt
@@ -0,0 +1,7 @@
+CoCreateInstance
+CoTaskMemFree
+SpellCheckerFactory
+ISpellCheckerFactory
+ISpellChecker
+CLSCTX
+S_FALSE
\ No newline at end of file
diff --git a/test/SpellChecker/PInvoke.cs b/test/SpellChecker/PInvoke.cs
new file mode 100644
index 00000000..ff5fd0f1
--- /dev/null
+++ b/test/SpellChecker/PInvoke.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.Windows.Sdk
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+ internal partial class PInvoke
+ {
+ ///
+ ///
+ internal static unsafe HRESULT CoCreateInstance(in System.Guid rclsid, IUnknown* pUnkOuter, uint dwClsContext, in System.Guid riid, out T* ppv)
+ where T : unmanaged
+ {
+ HRESULT hr = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, out void* o);
+ ppv = (T*)o;
+ return hr;
+ }
+
+ ///
+ /// The value from if it does not reflect an error.
+ ///
+ ///
+ internal static HRESULT ThrowOnFailure(this HRESULT errorCode, IntPtr errorInfo = default)
+ {
+ Marshal.ThrowExceptionForHR(errorCode, errorInfo);
+ return errorCode;
+ }
+ }
+}
diff --git a/test/SpellChecker/Program.cs b/test/SpellChecker/Program.cs
new file mode 100644
index 00000000..a443483e
--- /dev/null
+++ b/test/SpellChecker/Program.cs
@@ -0,0 +1,98 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Windows.Sdk;
+using static Microsoft.Windows.Sdk.Constants;
+using static Microsoft.Windows.Sdk.PInvoke;
+
+unsafe
+{
+ CoCreateInstance(
+ typeof(SpellCheckerFactory).GUID,
+ null,
+ (uint)CLSCTX.CLSCTX_INPROC_SERVER, // https://github.com/microsoft/win32metadata/issues/185
+ typeof(ISpellCheckerFactory).GUID,
+ out ISpellCheckerFactory* spellCheckerFactory).ThrowOnFailure();
+
+ spellCheckerFactory->IsSupported(
+ "en-US",
+ out bool supported).ThrowOnFailure();
+
+ if (!supported)
+ {
+ return;
+ }
+
+ spellCheckerFactory->CreateSpellChecker(
+ "en-US",
+ out ISpellChecker* spellChecker).ThrowOnFailure();
+
+ var text = @"""Cann I I haev some?""";
+
+ Console.WriteLine(@"Check {0}", text);
+
+ spellChecker->Check(
+ text,
+ out IEnumSpellingError* errors).ThrowOnFailure();
+
+ while (true)
+ {
+ if (errors->Next(out ISpellingError* error).ThrowOnFailure() == S_FALSE)
+ {
+ break;
+ }
+
+ error->get_StartIndex(out uint startIndex).ThrowOnFailure();
+ error->get_Length(out uint length).ThrowOnFailure();
+
+ var word = text.Substring((int)startIndex, (int)length);
+
+ error->get_CorrectiveAction(out CORRECTIVE_ACTION action).ThrowOnFailure();
+
+ switch (action)
+ {
+ case CORRECTIVE_ACTION.CORRECTIVE_ACTION_DELETE:
+ Console.WriteLine(@"Delete ""{0}""", word);
+ break;
+ case CORRECTIVE_ACTION.CORRECTIVE_ACTION_REPLACE:
+ // KNOWN ISSUE: ushort will be changed to string (https://github.com/microsoft/CsWin32/issues/121)
+ error->get_Replacement(out ushort* replacement).ThrowOnFailure();
+ Console.WriteLine(@"Replace ""{0}"" with ""{1}""", word, new string((char*)replacement));
+ CoTaskMemFree(replacement);
+ break;
+ case CORRECTIVE_ACTION.CORRECTIVE_ACTION_GET_SUGGESTIONS:
+ var l = new List();
+ spellChecker->Suggest(word, out IEnumString* suggestions).ThrowOnFailure();
+ while (true)
+ {
+ // KNOWN ISSUE: ushort will be changed to string (https://github.com/microsoft/CsWin32/issues/121)
+ ushort* suggestion;
+ if (suggestions->Next(1, &suggestion, null).ThrowOnFailure() != 0)
+ {
+ break;
+ }
+
+ l.Add(new string((char*)suggestion));
+ CoTaskMemFree(suggestion);
+ }
+
+ suggestions->Release();
+ Console.WriteLine(@"Suggest replacing ""{0}"" with:", word);
+ foreach (var s in l)
+ {
+ Console.WriteLine("\t{0}", s);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ error->Release();
+ }
+
+ errors->Release();
+ spellChecker->Release();
+}
diff --git a/test/SpellChecker/SpellChecker.csproj b/test/SpellChecker/SpellChecker.csproj
new file mode 100644
index 00000000..2a9c5b01
--- /dev/null
+++ b/test/SpellChecker/SpellChecker.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+ Exe
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+