From 43af44f87fa9e0312caa48d0665751173746b664 Mon Sep 17 00:00:00 2001 From: Ryan Newington Date: Fri, 28 Aug 2026 11:22:53 +1000 Subject: [PATCH] Expand framework support and COM ABI coverage Target .NET 8, 9, 10, Framework 4.7.2, and Framework 4.8. Rename samples to stable Core and Framework names. Add raw-vtable COM tests for x64, x86, and ARM64, and require the Azure architecture matrix before packaging. --- README.md | 6 +- azure-pipelines.yml | 52 +- ....CredentialProvider.UnitTests.arm64.csproj | 22 + .../ComInterop/AbiTestCredentialProvider.cs | 52 ++ .../ComInterop/AbiTestCredentialTile2.cs | 14 + .../ComInterop/ComInterfacePointer.cs | 79 +++ .../ComInterop/ComMarshal.cs | 17 + .../ComInterop/CredentialProviderAbi.cs | 59 +++ .../ComInterop/CredentialProviderAbiTests.cs | 450 ++++++++++++++++++ .../ComInterop/GetCredentialAtDelegate.cs | 8 + .../ComInterop/GetCredentialCountDelegate.cs | 8 + .../GetFieldDescriptorAtDelegate.cs | 8 + .../GetFieldDescriptorCountDelegate.cs | 8 + .../ComInterop/GetFieldStateDelegate.cs | 8 + .../ComInterop/GetStringValueDelegate.cs | 8 + .../ComInterop/GetUserCountDelegate.cs | 8 + .../ComInterop/GetUserSidDelegate.cs | 8 + .../ITestCredentialProviderUserArray.cs | 27 ++ ...NativeCredentialProviderFieldDescriptor.cs | 17 + .../ComInterop/ProcessArchitectureTests.cs | 27 ++ .../ComInterop/SetDeselectedDelegate.cs | 8 + .../ComInterop/SetSelectedDelegate.cs | 8 + .../ComInterop/SetUsageScenarioDelegate.cs | 8 + .../ComInterop/SetUserArrayDelegate.cs | 8 + .../TestCredentialProviderUserArray.cs | 51 ++ ...et.CredentialProvider.UnitTests.x64.csproj | 9 +- ...et.CredentialProvider.UnitTests.x86.csproj | 8 +- src/Lithnet.CredentialProvider.sln | 14 +- .../Lithnet.CredentialProvider.csproj | 5 +- .../Installing the sample.md | 8 +- ....CredentialProvider.Sample.Core.x64.csproj | 30 ++ .../Resources/TileIcon.png | Bin .../TestCredentialProviderCoreX64.cs} | 34 +- .../Installing the sample.md | 8 +- ....CredentialProvider.Sample.Core.x86.csproj | 30 ++ .../Resources/TileIcon.png | Bin .../TestCredentialProviderCoreX86.cs} | 8 +- .../ControlKeys.cs | 0 .../Installing the sample.md | 14 +- .../InternalLogger.cs | 0 ...ntialProvider.Sample.Framework.x64.csproj} | 3 +- .../Resources/TileIcon.png | Bin .../TestCredentialProviderFrameworkX64.cs} | 6 +- .../TestCredentialProviderTile.cs | 4 +- .../Installing the sample.md | 8 +- ...ntialProvider.Sample.Framework.x86.csproj} | 8 +- .../Resources/TileIcon.png | Bin .../TestCredentialProviderFrameworkX86.cs} | 8 +- ...redentialProvider.Sample.net6.0.x64.csproj | 32 -- ...redentialProvider.Sample.net6.0.x86.csproj | 32 -- ...hnet.CredentialProvider.TestApp.x64.csproj | 2 - ...hnet.CredentialProvider.TestApp.x86.csproj | 4 +- 52 files changed, 1116 insertions(+), 128 deletions(-) create mode 100644 src/Lithnet.CredentialProvider.UnitTests.arm64/Lithnet.CredentialProvider.UnitTests.arm64.csproj create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialProvider.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialTile2.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComInterfacePointer.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComMarshal.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbi.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbiTests.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialAtDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialCountDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorAtDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorCountDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldStateDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetStringValueDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserCountDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserSidDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ITestCredentialProviderUserArray.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/NativeCredentialProviderFieldDescriptor.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ProcessArchitectureTests.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetDeselectedDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetSelectedDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUsageScenarioDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUserArrayDelegate.cs create mode 100644 src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/TestCredentialProviderUserArray.cs rename src/samples/{Lithnet.CredentialProvider.Sample.net6.0.x64 => Lithnet.CredentialProvider.Sample.Core.x64}/Installing the sample.md (85%) create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Lithnet.CredentialProvider.Sample.Core.x64.csproj rename src/samples/{Lithnet.CredentialProvider.Sample.net6.0.x64 => Lithnet.CredentialProvider.Sample.Core.x64}/Resources/TileIcon.png (100%) rename src/samples/{Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs => Lithnet.CredentialProvider.Sample.Core.x64/TestCredentialProviderCoreX64.cs} (74%) rename src/samples/{Lithnet.CredentialProvider.Sample.net6.0.x86 => Lithnet.CredentialProvider.Sample.Core.x86}/Installing the sample.md (85%) create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Lithnet.CredentialProvider.Sample.Core.x86.csproj rename src/samples/{Lithnet.CredentialProvider.Sample.net6.0.x86 => Lithnet.CredentialProvider.Sample.Core.x86}/Resources/TileIcon.png (100%) rename src/samples/{Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs => Lithnet.CredentialProvider.Sample.Core.x86/TestCredentialProviderCoreX86.cs} (95%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x64 => Lithnet.CredentialProvider.Sample.Framework.x64}/ControlKeys.cs (100%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x64 => Lithnet.CredentialProvider.Sample.Framework.x64}/Installing the sample.md (84%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x64 => Lithnet.CredentialProvider.Sample.Framework.x64}/InternalLogger.cs (100%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj => Lithnet.CredentialProvider.Sample.Framework.x64/Lithnet.CredentialProvider.Sample.Framework.x64.csproj} (83%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x64 => Lithnet.CredentialProvider.Sample.Framework.x64}/Resources/TileIcon.png (100%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs => Lithnet.CredentialProvider.Sample.Framework.x64/TestCredentialProviderFrameworkX64.cs} (95%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x64 => Lithnet.CredentialProvider.Sample.Framework.x64}/TestCredentialProviderTile.cs (99%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x86 => Lithnet.CredentialProvider.Sample.Framework.x86}/Installing the sample.md (89%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj => Lithnet.CredentialProvider.Sample.Framework.x86/Lithnet.CredentialProvider.Sample.Framework.x86.csproj} (53%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x86 => Lithnet.CredentialProvider.Sample.Framework.x86}/Resources/TileIcon.png (100%) rename src/samples/{Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs => Lithnet.CredentialProvider.Sample.Framework.x86/TestCredentialProviderFrameworkX86.cs} (95%) delete mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj delete mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj diff --git a/README.md b/README.md index 7cbfe37..d9e9a13 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A library for creating secure Windows Credential Providers in .NET, without the The Lithnet Credential Provider for Windows provides an easy way to create a credential provider, without having to implement the COM components. The COM components are still there, but abstracted away into a fully managed implementation. ## Getting started -* Create a new Class Library project. You can use .NET Framework 4.6.1 or higher, or you can use .NET 6.0 or higher) to create your provider. You must build as either an x64 or x86 binary. You cannot use AnyCPU. +* Create a new Class Library project. You can use .NET Framework 4.7.2 or later, or .NET 8.0, 9.0, or 10.0. You must build an x64 or x86 binary. You cannot use AnyCPU. * Install the package from nuget `Install-Package Lithnet.CredentialProvider` * Modify the `csproj` file and set `RegisterForComInterop` to `false` @@ -21,11 +21,11 @@ The Lithnet Credential Provider for Windows provides an easy way to create a cre ``` -* If you are using .NET 6 or higher, you must also set `EnableComHosting` to `true` +* If you are using .NET 8.0, 9.0, or 10.0, you must also set `EnableComHosting` to `true`. ```xml - net6.0-windows + net8.0-windows false x64 true diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d59539c..011a4a5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -26,9 +26,59 @@ name: $(build.version.major).$(build.version.minor).$(build.version.revision)$(b trigger: none stages: +- stage: test_provider + displayName: Test credential provider + dependsOn: [] + jobs: + - job: test_windows + displayName: Test Windows + strategy: + matrix: + x64: + testArchitecture: x64 + testImage: windows-latest + testProject: src/Lithnet.CredentialProvider.UnitTests.x64/Lithnet.CredentialProvider.UnitTests.x64.csproj + x86: + testArchitecture: x86 + testImage: windows-latest + testProject: src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj + arm64: + testArchitecture: ARM64 + testImage: windows-11-vs2026-arm + testProject: src/Lithnet.CredentialProvider.UnitTests.arm64/Lithnet.CredentialProvider.UnitTests.arm64.csproj + pool: + vmImage: $(testImage) + steps: + - task: UseDotNet@2 + displayName: Install .NET 8 SDK + inputs: + packageType: sdk + version: 8.0.x + + - task: UseDotNet@2 + displayName: Install .NET 9 SDK + inputs: + packageType: sdk + version: 9.0.x + + - task: UseDotNet@2 + displayName: Install .NET 10 SDK + inputs: + packageType: sdk + version: 10.0.x + + - task: DotNetCoreCLI@2 + displayName: Test $(testArchitecture) + inputs: + command: test + projects: $(testProject) + arguments: '--configuration $(buildConfiguration)' + publishTestResults: true + testRunTitle: Credential Provider $(testArchitecture) + - stage: build_provider displayName: Build credential provider - dependsOn: [] + dependsOn: test_provider jobs: - job: "build_provider_job" steps: diff --git a/src/Lithnet.CredentialProvider.UnitTests.arm64/Lithnet.CredentialProvider.UnitTests.arm64.csproj b/src/Lithnet.CredentialProvider.UnitTests.arm64/Lithnet.CredentialProvider.UnitTests.arm64.csproj new file mode 100644 index 0000000..a564069 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.arm64/Lithnet.CredentialProvider.UnitTests.arm64.csproj @@ -0,0 +1,22 @@ + + + + net8.0-windows;net9.0-windows;net10.0-windows;net472;net48 + false + ARM64 + $(DefineConstants);TEST_ARM64 + + + + + + + + + + + + + + + diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialProvider.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialProvider.cs new file mode 100644 index 0000000..3912406 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialProvider.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + [Guid("2A83F3F8-A46C-4104-8FFB-BD9979279450")] + internal sealed class AbiTestCredentialProvider : CredentialProviderBase + { + public AbiTestCredentialProvider() + { + this.Field = new SmallLabelControl("message", "COM ABI test"); + } + + public SmallLabelControl Field { get; } + + public AbiTestCredentialTile2 Tile { get; private set; } + + public override bool IsUsageScenarioSupported(UsageScenario cpus, CredUIWinFlags dwFlags) + { + return cpus == UsageScenario.CredUI; + } + + public override IEnumerable GetControls(UsageScenario cpus) + { + return new ControlBase[] { this.Field }; + } + + public override bool ShouldIncludeUserTile(CredentialProviderUser user) + { + return false; + } + + public override bool ShouldIncludeGenericTile() + { + return true; + } + + public override CredentialTile CreateGenericTile() + { + this.Tile = new AbiTestCredentialTile2(this); + return this.Tile; + } + + public override CredentialTile2 CreateUserTile(CredentialProviderUser user) + { + return null; + } + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialTile2.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialTile2.cs new file mode 100644 index 0000000..be6b6d5 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/AbiTestCredentialTile2.cs @@ -0,0 +1,14 @@ +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + internal sealed class AbiTestCredentialTile2 : CredentialTile2 + { + public AbiTestCredentialTile2(CredentialProviderBase credentialProvider) : base(credentialProvider) + { + } + + protected override CredentialResponseBase GetCredentials() + { + return null; + } + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComInterfacePointer.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComInterfacePointer.cs new file mode 100644 index 0000000..3eae0d7 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComInterfacePointer.cs @@ -0,0 +1,79 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + internal sealed class ComInterfacePointer : IDisposable + { + private IntPtr value; + + private ComInterfacePointer(IntPtr value) + { + this.value = value; + } + + public IntPtr Value + { + get + { + if (this.value == IntPtr.Zero) + { + throw new ObjectDisposedException(nameof(ComInterfacePointer)); + } + + return this.value; + } + } + + public static ComInterfacePointer Create(object instance, Guid interfaceId) + { + IntPtr unknown = Marshal.GetIUnknownForObject(instance); + + try + { + int hresult = ComMarshal.QueryInterface(unknown, interfaceId, out IntPtr interfacePointer); + if (hresult != CredentialProviderAbi.S_OK) + { + if (interfacePointer != IntPtr.Zero) + { + Marshal.Release(interfacePointer); + } + + throw new COMException("The COM interface was not available", hresult); + } + + return new ComInterfacePointer(interfacePointer); + } + finally + { + Marshal.Release(unknown); + } + } + + public static ComInterfacePointer TakeOwnership(IntPtr value) + { + if (value == IntPtr.Zero) + { + throw new ArgumentException("The COM interface pointer cannot be zero", nameof(value)); + } + + return new ComInterfacePointer(value); + } + + public TDelegate GetMethod(int slot) where TDelegate : class + { + IntPtr vtable = Marshal.ReadIntPtr(this.Value); + IntPtr method = Marshal.ReadIntPtr(vtable, checked(slot * IntPtr.Size)); + return (TDelegate)(object)Marshal.GetDelegateForFunctionPointer(method, typeof(TDelegate)); + } + + public void Dispose() + { + if (this.value != IntPtr.Zero) + { + Marshal.Release(this.value); + this.value = IntPtr.Zero; + } + } + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComMarshal.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComMarshal.cs new file mode 100644 index 0000000..51ec0fe --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ComMarshal.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + internal static class ComMarshal + { + public static int QueryInterface(IntPtr unknown, Guid interfaceId, out IntPtr interfacePointer) + { +#if NET9_0_OR_GREATER + return Marshal.QueryInterface(unknown, in interfaceId, out interfacePointer); +#else + return Marshal.QueryInterface(unknown, ref interfaceId, out interfacePointer); +#endif + } + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbi.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbi.cs new file mode 100644 index 0000000..61e3cf2 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbi.cs @@ -0,0 +1,59 @@ +using System; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + internal static class CredentialProviderAbi + { + public static readonly Guid ICredentialProvider = new Guid("D27C3481-5A1C-45B2-8AAA-C20EBBE8229E"); + + public static readonly Guid ICredentialProviderCredential = new Guid("63913A93-40C1-481A-818D-4072FF8C70CC"); + + public static readonly Guid ICredentialProviderCredential2 = new Guid("FD672C54-40EA-4D6E-9B49-CFB1A7507BD7"); + + public static readonly Guid ICredentialProviderSetUserArray = new Guid("095C1484-1C0C-4388-9C6D-500E61BF84BD"); + + public static readonly Guid ICredentialProviderUserArray = new Guid("90C119AE-0F18-4520-A1F1-114366A40FE8"); + + public const int SetUsageScenarioSlot = 3; + + public const int GetFieldDescriptorCountSlot = 7; + + public const int GetFieldDescriptorAtSlot = 8; + + public const int GetCredentialCountSlot = 9; + + public const int GetCredentialAtSlot = 10; + + public const int SetUserArraySlot = 3; + + public const int UserArrayGetCountSlot = 5; + + public const int SetSelectedSlot = 5; + + public const int SetDeselectedSlot = 6; + + public const int GetFieldStateSlot = 7; + + public const int GetStringValueSlot = 8; + + public const int GetUserSidSlot = 20; + + public const int SmallTextFieldType = 2; + + public const int DisplayInSelectedTileFieldState = 1; + + public const int NoInteractiveFieldState = 0; + + public const uint NoDefaultCredential = 0xFFFFFFFF; + + public const int S_OK = 0; + + public const int S_FALSE = 1; + + public const int E_FAIL = unchecked((int)0x80004005); + + public const int E_INVALIDARG = unchecked((int)0x80070057); + + public const int E_NOTIMPL = unchecked((int)0x80004001); + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbiTests.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbiTests.cs new file mode 100644 index 0000000..d66bcaa --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/CredentialProviderAbiTests.cs @@ -0,0 +1,450 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using NUnit.Framework; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [TestFixture] + [NonParallelizable] + [Apartment(ApartmentState.STA)] + public class CredentialProviderAbiTests + { + [Test] + public void CredentialProviderInterfaceCanBeQueried() + { + var provider = new AbiTestCredentialProvider(); + IntPtr unknown = Marshal.GetIUnknownForObject(provider); + IntPtr providerInterface = IntPtr.Zero; + + try + { + int hresult = ComMarshal.QueryInterface(unknown, CredentialProviderAbi.ICredentialProvider, out providerInterface); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(providerInterface, Is.Not.EqualTo(IntPtr.Zero)); + } + finally + { + if (providerInterface != IntPtr.Zero) + { + Marshal.Release(providerInterface); + } + + Marshal.Release(unknown); + GC.KeepAlive(provider); + } + } + + [Test] + public void SetUsageScenarioPreservesHResultAndArguments() + { + var provider = new AbiTestCredentialProvider(); + + using (ComInterfacePointer providerInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProvider)) + { + SetUsageScenarioDelegate setUsageScenario = providerInterface.GetMethod(CredentialProviderAbi.SetUsageScenarioSlot); + + int hresult = setUsageScenario(providerInterface.Value, (int)UsageScenario.CredUI, (uint)CredUIWinFlags.CREDUIWIN_SECURE_PROMPT); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(provider.UsageScenario, Is.EqualTo(UsageScenario.CredUI)); + Assert.That(provider.CredUIFlags, Is.EqualTo(CredUIWinFlags.CREDUIWIN_SECURE_PROMPT)); + + hresult = setUsageScenario(providerInterface.Value, (int)UsageScenario.Logon, 0); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.E_NOTIMPL)); + Assert.That(provider.UsageScenario, Is.EqualTo(UsageScenario.Logon)); + Assert.That(provider.CredUIFlags, Is.EqualTo((CredUIWinFlags)0)); + } + + GC.KeepAlive(provider); + } + + [Test] + public void GetFieldDescriptorCountReturnsProviderControlCount() + { + var provider = new AbiTestCredentialProvider(); + + using (ComInterfacePointer providerInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProvider)) + { + GetFieldDescriptorCountDelegate getFieldDescriptorCount = providerInterface.GetMethod(CredentialProviderAbi.GetFieldDescriptorCountSlot); + + int hresult = getFieldDescriptorCount(providerInterface.Value, out uint count); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(count, Is.EqualTo(1)); + } + + GC.KeepAlive(provider); + } + + [Test] + public void GetFieldDescriptorAtReturnsWindowsSdkLayout() + { + var provider = new AbiTestCredentialProvider(); + + using (ComInterfacePointer providerInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProvider)) + { + GetFieldDescriptorCountDelegate getFieldDescriptorCount = providerInterface.GetMethod(CredentialProviderAbi.GetFieldDescriptorCountSlot); + GetFieldDescriptorAtDelegate getFieldDescriptorAt = providerInterface.GetMethod(CredentialProviderAbi.GetFieldDescriptorAtSlot); + + Assert.That(getFieldDescriptorCount(providerInterface.Value, out uint count), Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(count, Is.EqualTo(1)); + + IntPtr descriptorPointer = IntPtr.Zero; + IntPtr labelPointer = IntPtr.Zero; + + try + { + int hresult = getFieldDescriptorAt(providerInterface.Value, 0, out descriptorPointer); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(descriptorPointer, Is.Not.EqualTo(IntPtr.Zero)); + + NativeCredentialProviderFieldDescriptor descriptor = Marshal.PtrToStructure(descriptorPointer); + labelPointer = descriptor.Label; + + Assert.That(descriptor.FieldId, Is.EqualTo(provider.Field.Id)); + Assert.That(descriptor.FieldType, Is.EqualTo(CredentialProviderAbi.SmallTextFieldType)); + Assert.That(Marshal.PtrToStringUni(labelPointer), Is.EqualTo("COM ABI test")); + Assert.That(descriptor.FieldTypeGuid, Is.EqualTo(Guid.Empty)); + } + finally + { + if (labelPointer != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(labelPointer); + } + + if (descriptorPointer != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(descriptorPointer); + } + } + } + + GC.KeepAlive(provider); + } + + [Test] + public void GetFieldDescriptorAtRejectsInvalidIndex() + { + var provider = new AbiTestCredentialProvider(); + + using (ComInterfacePointer providerInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProvider)) + { + GetFieldDescriptorCountDelegate getFieldDescriptorCount = providerInterface.GetMethod(CredentialProviderAbi.GetFieldDescriptorCountSlot); + GetFieldDescriptorAtDelegate getFieldDescriptorAt = providerInterface.GetMethod(CredentialProviderAbi.GetFieldDescriptorAtSlot); + + Assert.That(getFieldDescriptorCount(providerInterface.Value, out uint count), Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(count, Is.EqualTo(1)); + + int hresult = getFieldDescriptorAt(providerInterface.Value, count, out IntPtr descriptorPointer); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.E_INVALIDARG)); + Assert.That(descriptorPointer, Is.EqualTo(IntPtr.Zero)); + } + + GC.KeepAlive(provider); + } + + [Test] + public void CredentialProviderSetUserArrayInterfaceCanBeQueried() + { + var provider = new AbiTestCredentialProvider(); + IntPtr unknown = Marshal.GetIUnknownForObject(provider); + IntPtr setUserArrayInterface = IntPtr.Zero; + + try + { + int hresult = ComMarshal.QueryInterface(unknown, CredentialProviderAbi.ICredentialProviderSetUserArray, out setUserArrayInterface); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(setUserArrayInterface, Is.Not.EqualTo(IntPtr.Zero)); + } + finally + { + if (setUserArrayInterface != IntPtr.Zero) + { + Marshal.Release(setUserArrayInterface); + } + + Marshal.Release(unknown); + GC.KeepAlive(provider); + } + } + + [Test] + public void TestUserArrayUsesWindowsSdkGetCountSlot() + { + var users = new TestCredentialProviderUserArray(); + + using (ComInterfacePointer userArrayInterface = ComInterfacePointer.Create(users, CredentialProviderAbi.ICredentialProviderUserArray)) + { + GetUserCountDelegate getCount = userArrayInterface.GetMethod(CredentialProviderAbi.UserArrayGetCountSlot); + + int hresult = getCount(userArrayInterface.Value, out uint count); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(count, Is.EqualTo(0)); + Assert.That(users.GetCountCallCount, Is.EqualTo(1)); + } + + GC.KeepAlive(users); + } + + [Test] + public void SetUserArrayInvokesWindowsSdkUserArrayGetCount() + { + var provider = new AbiTestCredentialProvider(); + var users = new TestCredentialProviderUserArray(); + + SetEmptyUserArray(provider, users); + + Assert.That(users.GetCountCallCount, Is.EqualTo(1)); + + GC.KeepAlive(users); + GC.KeepAlive(provider); + } + + [Test] + public void GetCredentialCountReturnsGenericTileWithoutDefault() + { + var provider = new AbiTestCredentialProvider(); + var users = new TestCredentialProviderUserArray(); + + SetEmptyUserArray(provider, users); + + using (ComInterfacePointer providerInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProvider)) + { + GetCredentialCountDelegate getCredentialCount = providerInterface.GetMethod(CredentialProviderAbi.GetCredentialCountSlot); + + int hresult = getCredentialCount(providerInterface.Value, out uint count, out uint defaultCredential, out int autoLogonWithDefault); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(count, Is.EqualTo(1)); + Assert.That(defaultCredential, Is.EqualTo(CredentialProviderAbi.NoDefaultCredential)); + Assert.That(autoLogonWithDefault, Is.EqualTo(0)); + Assert.That(users.GetCountCallCount, Is.EqualTo(2)); + Assert.That(provider.Tile, Is.Not.Null); + } + + GC.KeepAlive(users); + GC.KeepAlive(provider); + } + + [Test] + public void GetCredentialAtRejectsInvalidIndex() + { + var provider = new AbiTestCredentialProvider(); + var users = new TestCredentialProviderUserArray(); + + SetEmptyUserArray(provider, users); + + using (ComInterfacePointer providerInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProvider)) + { + GetCredentialCountDelegate getCredentialCount = providerInterface.GetMethod(CredentialProviderAbi.GetCredentialCountSlot); + GetCredentialAtDelegate getCredentialAt = providerInterface.GetMethod(CredentialProviderAbi.GetCredentialAtSlot); + + Assert.That(getCredentialCount(providerInterface.Value, out uint count, out uint defaultCredential, out int autoLogonWithDefault), Is.EqualTo(CredentialProviderAbi.S_OK)); + + int hresult = getCredentialAt(providerInterface.Value, count, out IntPtr credential); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.E_FAIL)); + Assert.That(credential, Is.EqualTo(IntPtr.Zero)); + } + + GC.KeepAlive(users); + GC.KeepAlive(provider); + } + + [Test] + public void CredentialV1SelectionMethodsPreserveStateAndHResults() + { + var provider = new AbiTestCredentialProvider(); + var users = new TestCredentialProviderUserArray(); + + using (ComInterfacePointer credential = CreateCredentialInterface(provider, users)) + { + SetSelectedDelegate setSelected = credential.GetMethod(CredentialProviderAbi.SetSelectedSlot); + SetDeselectedDelegate setDeselected = credential.GetMethod(CredentialProviderAbi.SetDeselectedSlot); + + int hresult = setSelected(credential.Value, out int autoLogon); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(autoLogon, Is.EqualTo(0)); + Assert.That(provider.Tile.IsSelected, Is.True); + + hresult = setDeselected(credential.Value); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(provider.Tile.IsSelected, Is.False); + } + + GC.KeepAlive(users); + GC.KeepAlive(provider); + } + + [Test] + public void CredentialV1GetFieldStateUsesWindowsSdkEnumValues() + { + var provider = new AbiTestCredentialProvider(); + var users = new TestCredentialProviderUserArray(); + + using (ComInterfacePointer credential = CreateCredentialInterface(provider, users)) + { + GetFieldStateDelegate getFieldState = credential.GetMethod(CredentialProviderAbi.GetFieldStateSlot); + + int hresult = getFieldState(credential.Value, provider.Field.Id, out int fieldState, out int interactiveState); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(fieldState, Is.EqualTo(CredentialProviderAbi.DisplayInSelectedTileFieldState)); + Assert.That(interactiveState, Is.EqualTo(CredentialProviderAbi.NoInteractiveFieldState)); + } + + GC.KeepAlive(users); + GC.KeepAlive(provider); + } + + [Test] + public void CredentialV1GetStringValueReturnsComTaskMemory() + { + var provider = new AbiTestCredentialProvider(); + var users = new TestCredentialProviderUserArray(); + + using (ComInterfacePointer credential = CreateCredentialInterface(provider, users)) + { + GetStringValueDelegate getStringValue = credential.GetMethod(CredentialProviderAbi.GetStringValueSlot); + IntPtr value = IntPtr.Zero; + + try + { + int hresult = getStringValue(credential.Value, provider.Field.Id, out value); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(value, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(Marshal.PtrToStringUni(value), Is.EqualTo("COM ABI test")); + } + finally + { + if (value != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(value); + } + } + } + + GC.KeepAlive(users); + GC.KeepAlive(provider); + } + + [Test] + public void CredentialV2GetUserSidUsesInheritedVtableOrder() + { + var provider = new AbiTestCredentialProvider(); + var users = new TestCredentialProviderUserArray(); + + using (ComInterfacePointer credential = CreateCredentialInterface(provider, users)) + { + IntPtr credential2Pointer = IntPtr.Zero; + + try + { + int hresult = ComMarshal.QueryInterface(credential.Value, CredentialProviderAbi.ICredentialProviderCredential2, out credential2Pointer); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(credential2Pointer, Is.Not.EqualTo(IntPtr.Zero)); + + using (ComInterfacePointer credential2 = ComInterfacePointer.TakeOwnership(credential2Pointer)) + { + credential2Pointer = IntPtr.Zero; + GetUserSidDelegate getUserSid = credential2.GetMethod(CredentialProviderAbi.GetUserSidSlot); + IntPtr sid = IntPtr.Zero; + + try + { + hresult = getUserSid(credential2.Value, out sid); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_FALSE)); + Assert.That(sid, Is.EqualTo(IntPtr.Zero)); + } + finally + { + if (sid != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(sid); + } + } + } + } + finally + { + if (credential2Pointer != IntPtr.Zero) + { + Marshal.Release(credential2Pointer); + } + } + } + + GC.KeepAlive(users); + GC.KeepAlive(provider); + } + + [Test] + public void FieldDescriptorDeclarationMatchesWindowsSdkSize() + { + int expectedSize = IntPtr.Size == 4 ? 28 : 32; + + Assert.That(Marshal.SizeOf(), Is.EqualTo(expectedSize)); + } + + private static void SetEmptyUserArray(AbiTestCredentialProvider provider, TestCredentialProviderUserArray users) + { + using (ComInterfacePointer setUserArrayInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProviderSetUserArray)) + using (ComInterfacePointer userArrayInterface = ComInterfacePointer.Create(users, CredentialProviderAbi.ICredentialProviderUserArray)) + { + SetUserArrayDelegate setUserArray = setUserArrayInterface.GetMethod(CredentialProviderAbi.SetUserArraySlot); + + int hresult = setUserArray(setUserArrayInterface.Value, userArrayInterface.Value); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + } + } + + private static ComInterfacePointer CreateCredentialInterface(AbiTestCredentialProvider provider, TestCredentialProviderUserArray users) + { + SetEmptyUserArray(provider, users); + + using (ComInterfacePointer providerInterface = ComInterfacePointer.Create(provider, CredentialProviderAbi.ICredentialProvider)) + { + GetCredentialCountDelegate getCredentialCount = providerInterface.GetMethod(CredentialProviderAbi.GetCredentialCountSlot); + GetCredentialAtDelegate getCredentialAt = providerInterface.GetMethod(CredentialProviderAbi.GetCredentialAtSlot); + + Assert.That(getCredentialCount(providerInterface.Value, out uint count, out uint defaultCredential, out int autoLogonWithDefault), Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(count, Is.EqualTo(1)); + + IntPtr credential = IntPtr.Zero; + + try + { + int hresult = getCredentialAt(providerInterface.Value, 0, out credential); + + Assert.That(hresult, Is.EqualTo(CredentialProviderAbi.S_OK)); + Assert.That(credential, Is.Not.EqualTo(IntPtr.Zero)); + + ComInterfacePointer result = ComInterfacePointer.TakeOwnership(credential); + credential = IntPtr.Zero; + return result; + } + finally + { + if (credential != IntPtr.Zero) + { + Marshal.Release(credential); + } + } + } + } + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialAtDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialAtDelegate.cs new file mode 100644 index 0000000..62c0555 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialAtDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetCredentialAtDelegate(IntPtr instance, uint index, out IntPtr credential); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialCountDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialCountDelegate.cs new file mode 100644 index 0000000..e4a8601 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetCredentialCountDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetCredentialCountDelegate(IntPtr instance, out uint count, out uint defaultCredential, out int autoLogonWithDefault); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorAtDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorAtDelegate.cs new file mode 100644 index 0000000..fd9098b --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorAtDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetFieldDescriptorAtDelegate(IntPtr instance, uint index, out IntPtr descriptor); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorCountDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorCountDelegate.cs new file mode 100644 index 0000000..1432e9b --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldDescriptorCountDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetFieldDescriptorCountDelegate(IntPtr instance, out uint count); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldStateDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldStateDelegate.cs new file mode 100644 index 0000000..a677e77 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetFieldStateDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetFieldStateDelegate(IntPtr instance, uint fieldId, out int fieldState, out int interactiveState); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetStringValueDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetStringValueDelegate.cs new file mode 100644 index 0000000..e4239e3 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetStringValueDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetStringValueDelegate(IntPtr instance, uint fieldId, out IntPtr value); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserCountDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserCountDelegate.cs new file mode 100644 index 0000000..d9480df --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserCountDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetUserCountDelegate(IntPtr instance, out uint count); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserSidDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserSidDelegate.cs new file mode 100644 index 0000000..bf67eda --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/GetUserSidDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetUserSidDelegate(IntPtr instance, out IntPtr sid); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ITestCredentialProviderUserArray.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ITestCredentialProviderUserArray.cs new file mode 100644 index 0000000..3c8c053 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ITestCredentialProviderUserArray.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [ComVisible(true)] + [Guid("90C119AE-0F18-4520-A1F1-114366A40FE8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ITestCredentialProviderUserArray + { + [PreserveSig] + [return: MarshalAs(UnmanagedType.Error)] + int SetProviderFilter(ref Guid providerToFilterTo); + + [PreserveSig] + [return: MarshalAs(UnmanagedType.Error)] + int GetAccountOptions(out int accountOptions); + + [PreserveSig] + [return: MarshalAs(UnmanagedType.Error)] + int GetCount(out uint userCount); + + [PreserveSig] + [return: MarshalAs(UnmanagedType.Error)] + int GetAt(uint userIndex, out IntPtr user); + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/NativeCredentialProviderFieldDescriptor.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/NativeCredentialProviderFieldDescriptor.cs new file mode 100644 index 0000000..6909fef --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/NativeCredentialProviderFieldDescriptor.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [StructLayout(LayoutKind.Sequential)] + internal struct NativeCredentialProviderFieldDescriptor + { + public uint FieldId; + + public int FieldType; + + public IntPtr Label; + + public Guid FieldTypeGuid; + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ProcessArchitectureTests.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ProcessArchitectureTests.cs new file mode 100644 index 0000000..71a1e7f --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/ProcessArchitectureTests.cs @@ -0,0 +1,27 @@ +using System; +using NUnit.Framework; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [TestFixture] + public class ProcessArchitectureTests + { + [Test] + public void TestHostUsesRequestedArchitecture() + { +#if TEST_X86 + const string expectedArchitecture = "x86"; +#elif TEST_X64 + const string expectedArchitecture = "AMD64"; +#elif TEST_ARM64 + const string expectedArchitecture = "ARM64"; +#else +#error A test process architecture must be defined by the project. +#endif + + string actualArchitecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); + + Assert.That(actualArchitecture, Is.EqualTo(expectedArchitecture).IgnoreCase); + } + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetDeselectedDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetDeselectedDelegate.cs new file mode 100644 index 0000000..485f87a --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetDeselectedDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int SetDeselectedDelegate(IntPtr instance); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetSelectedDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetSelectedDelegate.cs new file mode 100644 index 0000000..81f9d26 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetSelectedDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int SetSelectedDelegate(IntPtr instance, out int autoLogon); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUsageScenarioDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUsageScenarioDelegate.cs new file mode 100644 index 0000000..64b1332 --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUsageScenarioDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int SetUsageScenarioDelegate(IntPtr instance, int usageScenario, uint flags); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUserArrayDelegate.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUserArrayDelegate.cs new file mode 100644 index 0000000..13e3bde --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/SetUserArrayDelegate.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int SetUserArrayDelegate(IntPtr instance, IntPtr users); +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/TestCredentialProviderUserArray.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/TestCredentialProviderUserArray.cs new file mode 100644 index 0000000..579d95a --- /dev/null +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/ComInterop/TestCredentialProviderUserArray.cs @@ -0,0 +1,51 @@ +using System; +using System.Runtime.InteropServices; +using Lithnet.CredentialProvider.Interop; + +namespace Lithnet.CredentialProvider.UnitTests.ComInterop +{ + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + // The in-process call also requires the callback to implement the parameter's managed interface type. + // ITestCredentialProviderUserArray keeps the COM contract under test independent of that adapter. + internal sealed class TestCredentialProviderUserArray : ITestCredentialProviderUserArray, ICredentialProviderUserArray + { + public int GetCountCallCount { get; private set; } + + public int SetProviderFilter(ref Guid providerToFilterTo) + { + return CredentialProviderAbi.E_NOTIMPL; + } + + public int GetAccountOptions(out int accountOptions) + { + accountOptions = 0; + return CredentialProviderAbi.S_OK; + } + + public int GetCount(out uint userCount) + { + this.GetCountCallCount++; + userCount = 0; + return CredentialProviderAbi.S_OK; + } + + public int GetAt(uint userIndex, out IntPtr user) + { + user = IntPtr.Zero; + return CredentialProviderAbi.E_INVALIDARG; + } + + int ICredentialProviderUserArray.GetAccountOptions(out AccountOptions accountOptions) + { + accountOptions = AccountOptions.None; + return CredentialProviderAbi.S_OK; + } + + int ICredentialProviderUserArray.GetAt(uint userIndex, out ICredentialProviderUser user) + { + user = null; + return CredentialProviderAbi.E_INVALIDARG; + } + } +} diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/Lithnet.CredentialProvider.UnitTests.x64.csproj b/src/Lithnet.CredentialProvider.UnitTests.x64/Lithnet.CredentialProvider.UnitTests.x64.csproj index 86fc565..aac65f3 100644 --- a/src/Lithnet.CredentialProvider.UnitTests.x64/Lithnet.CredentialProvider.UnitTests.x64.csproj +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/Lithnet.CredentialProvider.UnitTests.x64.csproj @@ -1,15 +1,16 @@  - net6.0-windows;net7.0-windows;net8.0-windows;net461;net48 + net8.0-windows;net9.0-windows;net10.0-windows;net472;net48 false - x64 + x64 + $(DefineConstants);TEST_X64 - - + + diff --git a/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj b/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj index 70b55c6..9ad8c73 100644 --- a/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj +++ b/src/Lithnet.CredentialProvider.UnitTests.x86/Lithnet.CredentialProvider.UnitTests.x86.csproj @@ -1,20 +1,22 @@ - net6.0-windows;net7.0-windows;net8.0-windows;net461;net48 + net8.0-windows;net9.0-windows;net10.0-windows;net472;net48 false x86 + $(DefineConstants);TEST_X86 - - + + + diff --git a/src/Lithnet.CredentialProvider.sln b/src/Lithnet.CredentialProvider.sln index 4460844..0005fb0 100644 --- a/src/Lithnet.CredentialProvider.sln +++ b/src/Lithnet.CredentialProvider.sln @@ -11,13 +11,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\azure-pipelines.yml = ..\azure-pipelines.yml EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net6.0.x64", "samples\Lithnet.CredentialProvider.Sample.net6.0.x64\Lithnet.CredentialProvider.Sample.net6.0.x64.csproj", "{163E16D0-9FF3-40D3-AE96-3F221C922AA3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.Core.x64", "samples\Lithnet.CredentialProvider.Sample.Core.x64\Lithnet.CredentialProvider.Sample.Core.x64.csproj", "{163E16D0-9FF3-40D3-AE96-3F221C922AA3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net6.0.x86", "samples\Lithnet.CredentialProvider.Sample.net6.0.x86\Lithnet.CredentialProvider.Sample.net6.0.x86.csproj", "{7A8A10F9-7D79-4755-BBDD-77C80FDC58ED}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.Core.x86", "samples\Lithnet.CredentialProvider.Sample.Core.x86\Lithnet.CredentialProvider.Sample.Core.x86.csproj", "{7A8A10F9-7D79-4755-BBDD-77C80FDC58ED}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net472.x64", "samples\Lithnet.CredentialProvider.Sample.net472.x64\Lithnet.CredentialProvider.Sample.net472.x64.csproj", "{F60AC10A-337C-46D9-A2DE-1ED48B4AB301}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.Framework.x64", "samples\Lithnet.CredentialProvider.Sample.Framework.x64\Lithnet.CredentialProvider.Sample.Framework.x64.csproj", "{F60AC10A-337C-46D9-A2DE-1ED48B4AB301}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net472.x86", "samples\Lithnet.CredentialProvider.Sample.net472.x86\Lithnet.CredentialProvider.Sample.net472.x86.csproj", "{57F2780E-58F1-4A7B-BCB4-A218733273EA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.Framework.x86", "samples\Lithnet.CredentialProvider.Sample.Framework.x86\Lithnet.CredentialProvider.Sample.Framework.x86.csproj", "{57F2780E-58F1-4A7B-BCB4-A218733273EA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.TestApp.x64", "samples\Lithnet.CredentialProvider.TestApp.x64\Lithnet.CredentialProvider.TestApp.x64.csproj", "{5019CCEB-AD78-4688-8C11-89A85C4289CD}" EndProject @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lithnet.CredentialProvider. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lithnet.CredentialProvider.UnitTests.x86", "Lithnet.CredentialProvider.UnitTests.x86\Lithnet.CredentialProvider.UnitTests.x86.csproj", "{DA23151B-B7B6-4942-B7A4-48E1918EF145}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lithnet.CredentialProvider.UnitTests.arm64", "Lithnet.CredentialProvider.UnitTests.arm64\Lithnet.CredentialProvider.UnitTests.arm64.csproj", "{07D19CE5-8419-4E06-B39D-CC90EAA3FF9A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,6 +71,10 @@ Global {DA23151B-B7B6-4942-B7A4-48E1918EF145}.Debug|Any CPU.Build.0 = Debug|Any CPU {DA23151B-B7B6-4942-B7A4-48E1918EF145}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA23151B-B7B6-4942-B7A4-48E1918EF145}.Release|Any CPU.Build.0 = Release|Any CPU + {07D19CE5-8419-4E06-B39D-CC90EAA3FF9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07D19CE5-8419-4E06-B39D-CC90EAA3FF9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07D19CE5-8419-4E06-B39D-CC90EAA3FF9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07D19CE5-8419-4E06-B39D-CC90EAA3FF9A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj b/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj index 7dfebbb..a3a8312 100644 --- a/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj +++ b/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj @@ -1,6 +1,6 @@  - net6.0-windows;net7.0-windows;net8.0-windows;net461 + net8.0-windows;net9.0-windows;net10.0-windows;net472;net48 false Library true @@ -45,6 +45,9 @@ <_Parameter1>Lithnet.CredentialProvider.UnitTests.x86 + + <_Parameter1>Lithnet.CredentialProvider.UnitTests.arm64 + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Installing the sample.md similarity index 85% rename from src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Installing the sample.md rename to src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Installing the sample.md index 0930d98..d686da8 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Installing the sample.md +++ b/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Installing the sample.md @@ -7,10 +7,10 @@ In order to install and run the sample app, you have to register the COM compone Build the EXE, and from an elevated command prompt, change to the bin folder, and run the following commands ``` -regsvr32 "Lithnet.CredentialProvider.Sample.net6.0.x64.comhost.dll" +regsvr32 "Lithnet.CredentialProvider.Sample.Core.x64.comhost.dll" REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /f -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net6.0.x64" +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.Core.x64" ``` ## Disable the sample @@ -31,6 +31,6 @@ REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credentia To remove the credential provider, run the following command. ``` -regsvr32 /u "Lithnet.CredentialProvider.Sample.net6.0.x64.comhost.dll" +regsvr32 /u "Lithnet.CredentialProvider.Sample.Core.x64.comhost.dll" REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /f -``` \ No newline at end of file +``` diff --git a/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Lithnet.CredentialProvider.Sample.Core.x64.csproj b/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Lithnet.CredentialProvider.Sample.Core.x64.csproj new file mode 100644 index 0000000..0dad286 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Lithnet.CredentialProvider.Sample.Core.x64.csproj @@ -0,0 +1,30 @@ + + + + net8.0-windows + false + x64 + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Resources/TileIcon.png similarity index 100% rename from src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Resources/TileIcon.png rename to src/samples/Lithnet.CredentialProvider.Sample.Core.x64/Resources/TileIcon.png diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs b/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/TestCredentialProviderCoreX64.cs similarity index 74% rename from src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs rename to src/samples/Lithnet.CredentialProvider.Sample.Core.x64/TestCredentialProviderCoreX64.cs index 1ef2d92..cd155ec 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.Core.x64/TestCredentialProviderCoreX64.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; using System.Reflection; using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; @@ -9,11 +11,11 @@ namespace Lithnet.CredentialProvider.Samples { [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] - [ProgId("Lithnet.CredentialProvider.Sample.net6.0.x64")] + [ProgId("Lithnet.CredentialProvider.Sample.Core.x64")] [Guid("4cd12d80-9259-4f38-94dc-1828080ad9ff")] - public class TestCredentialProviderNet60x64 : CredentialProviderBase + public class TestCredentialProviderCoreX64 : CredentialProviderBase { - private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger(); + private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger(); protected override ICredentialProviderLoggerFactory GetLoggerFactory() { @@ -36,10 +38,11 @@ namespace Lithnet.CredentialProvider.Samples { yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); - var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.net6.0.x64.Resources.TileIcon.png")); + var providerLogo = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.Core.x64.Resources.TileIcon.png")); + var transparentUserTile = CreateTransparentUserTile(); - yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", image); - yield return new CredentialProviderLogoControl(ControlKeys.ImageUserTile, "User tile image", image); + yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", providerLogo); + yield return new UserTileControl(ControlKeys.ImageUserTile, "Transparent user tile image", transparentUserTile); yield return new LargeLabelControl(ControlKeys.LabelLargeHeading, "The is our showcase credential provider"); yield return new SmallLabelControl(ControlKeys.LabelSmallHeading, "Let's see what we can do"); @@ -95,5 +98,24 @@ namespace Lithnet.CredentialProvider.Samples { return new TestCredentialProviderTile(this, user); } + + private static Bitmap CreateTransparentUserTile() + { + Bitmap image = new Bitmap(128, 128, PixelFormat.Format32bppArgb); + + using (Graphics graphics = Graphics.FromImage(image)) + using (SolidBrush shadow = new SolidBrush(Color.FromArgb(96, 0, 0, 0))) + using (SolidBrush foreground = new SolidBrush(Color.FromArgb(255, 38, 132, 255))) + using (Pen outline = new Pen(Color.White, 5)) + { + graphics.SmoothingMode = SmoothingMode.AntiAlias; + graphics.Clear(Color.Transparent); + graphics.FillEllipse(shadow, 28, 30, 88, 88); + graphics.FillEllipse(foreground, 12, 12, 88, 88); + graphics.DrawEllipse(outline, 12, 12, 88, 88); + } + + return image; + } } } diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Installing the sample.md similarity index 85% rename from src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Installing the sample.md rename to src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Installing the sample.md index 5e2ab19..d2192d2 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Installing the sample.md +++ b/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Installing the sample.md @@ -7,10 +7,10 @@ In order to install and run the sample app, you have to register the COM compone Build the EXE, and from an elevated command prompt, change to the bin folder, and run the following commands ``` -regsvr32 "Lithnet.CredentialProvider.Sample.net6.0.x86.comhost.dll" +regsvr32 "Lithnet.CredentialProvider.Sample.Core.x86.comhost.dll" REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /f -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net6.0.x86" +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.Core.x86" ``` ## Disable the sample @@ -31,6 +31,6 @@ REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credentia To remove the credential provider, run the following command. ``` -regsvr32 /u "Lithnet.CredentialProvider.Sample.net6.0.x86.comhost.dll" +regsvr32 /u "Lithnet.CredentialProvider.Sample.Core.x86.comhost.dll" REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /f -``` \ No newline at end of file +``` diff --git a/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Lithnet.CredentialProvider.Sample.Core.x86.csproj b/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Lithnet.CredentialProvider.Sample.Core.x86.csproj new file mode 100644 index 0000000..f94dc39 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Lithnet.CredentialProvider.Sample.Core.x86.csproj @@ -0,0 +1,30 @@ + + + + net8.0-windows + false + x86 + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Resources/TileIcon.png similarity index 100% rename from src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Resources/TileIcon.png rename to src/samples/Lithnet.CredentialProvider.Sample.Core.x86/Resources/TileIcon.png diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs b/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/TestCredentialProviderCoreX86.cs similarity index 95% rename from src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs rename to src/samples/Lithnet.CredentialProvider.Sample.Core.x86/TestCredentialProviderCoreX86.cs index 8b4d847..de40289 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.Core.x86/TestCredentialProviderCoreX86.cs @@ -9,11 +9,11 @@ namespace Lithnet.CredentialProvider.Samples { [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] - [ProgId("Lithnet.CredentialProvider.Sample.net6.0.x86")] + [ProgId("Lithnet.CredentialProvider.Sample.Core.x86")] [Guid("90592593-f4d3-4f62-aa83-9cf1f7b590e0")] - public class TestCredentialProviderNet60x86 : CredentialProviderBase + public class TestCredentialProviderCoreX86 : CredentialProviderBase { - private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger(); + private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger(); protected override ICredentialProviderLoggerFactory GetLoggerFactory() { @@ -34,7 +34,7 @@ namespace Lithnet.CredentialProvider.Samples } else { - var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.net6.0.x86.Resources.TileIcon.png")); + var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.Core.x86.Resources.TileIcon.png")); yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", image); diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/ControlKeys.cs b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/ControlKeys.cs similarity index 100% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x64/ControlKeys.cs rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/ControlKeys.cs diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Installing the sample.md similarity index 84% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Installing the sample.md rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Installing the sample.md index dd4a3e1..3f2162c 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Installing the sample.md +++ b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Installing the sample.md @@ -9,8 +9,8 @@ Build the EXE, and from an elevated command prompt, change to the bin folder, an ``` SETLOCAL SET CLSID={4EB911FA-CA18-40EA-86DF-19AFF5D1DA58} -SET BinaryPath=D:\dev\git\lithnet\windows-credential-provider\src\samples\Lithnet.CredentialProvider.Sample.net472.x64\bin\Debug\net472\Lithnet.CredentialProvider.Sample.net472.x64.dll -REM %windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /codebase "Lithnet.CredentialProvider.Sample.net472.x64.dll" +SET BinaryPath=D:\dev\git\lithnet\windows-credential-provider\src\samples\Lithnet.CredentialProvider.Sample.Framework.x64\bin\Debug\net472\Lithnet.CredentialProvider.Sample.Framework.x64.dll +REM %windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /codebase "Lithnet.CredentialProvider.Sample.Framework.x64.dll" REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Samples.TestCredentialProvider" REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" @@ -20,11 +20,11 @@ REM REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /v "Class" /t R REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /v "RuntimeVersion" /t REG_SZ /f /d "v4.0.30319" REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /v "CodeBase" /t REG_SZ /f /d "%BinaryPath%" -REG ADD "HKLM\SOFTWARE\Classes\Lithnet.CredentialProvider.Sample.net472.x64" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Samples.TestCredentialProvider" -REG ADD "HKLM\SOFTWARE\Classes\Lithnet.CredentialProvider.Sample.net472.x64\CLSID" /ve /t REG_SZ /f /d "%CLSID%" +REG ADD "HKLM\SOFTWARE\Classes\Lithnet.CredentialProvider.Sample.Framework.x64" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Samples.TestCredentialProvider" +REG ADD "HKLM\SOFTWARE\Classes\Lithnet.CredentialProvider.Sample.Framework.x64\CLSID" /ve /t REG_SZ /f /d "%CLSID%" -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\%CLSID%" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net472.x64" +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\%CLSID%" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.Framework.x64" ``` ## Disable the sample @@ -45,6 +45,6 @@ REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credentia To remove the credential provider, run the following command. ``` -%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /u "Lithnet.CredentialProvider.Sample.net472.x64.dll" +%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /u "Lithnet.CredentialProvider.Sample.Framework.x64.dll" REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4eb911fa-ca18-40ea-86df-19aff5d1da58"}" /f -``` \ No newline at end of file +``` diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/InternalLogger.cs b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/InternalLogger.cs similarity index 100% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x64/InternalLogger.cs rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/InternalLogger.cs diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Lithnet.CredentialProvider.Sample.Framework.x64.csproj similarity index 83% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Lithnet.CredentialProvider.Sample.Framework.x64.csproj index 479b909..fb5d04b 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj +++ b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Lithnet.CredentialProvider.Sample.Framework.x64.csproj @@ -15,7 +15,8 @@ - + + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Resources/TileIcon.png similarity index 100% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources/TileIcon.png rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/Resources/TileIcon.png diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/TestCredentialProviderFrameworkX64.cs similarity index 95% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/TestCredentialProviderFrameworkX64.cs index 8346cfb..e3a4b7f 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/TestCredentialProviderFrameworkX64.cs @@ -10,9 +10,9 @@ namespace Lithnet.CredentialProvider.Samples { [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] - [ProgId("Lithnet.CredentialProvider.Sample.net472.x64")] + [ProgId("Lithnet.CredentialProvider.Sample.Framework.x64")] [Guid("4eb911fa-ca18-40ea-86df-19aff5d1da58")] - public class TestCredentialProviderNet472x64 : CredentialProviderBase + public class TestCredentialProviderFrameworkX64 : CredentialProviderBase { protected override ICredentialProviderLoggerFactory GetLoggerFactory() { @@ -33,7 +33,7 @@ namespace Lithnet.CredentialProvider.Samples } else { - var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.net472.x64.Resources.TileIcon.png")); + var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.Framework.x64.Resources.TileIcon.png")); yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", image); diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderTile.cs b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/TestCredentialProviderTile.cs similarity index 99% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderTile.cs rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/TestCredentialProviderTile.cs index adb26e8..204d0c1 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderTile.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x64/TestCredentialProviderTile.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; namespace Lithnet.CredentialProvider.Samples { - public class TestCredentialProviderTile : CredentialTile2 + public class TestCredentialProviderTile : CredentialTile3 { private TextboxControl UsernameControl; private SecurePasswordTextboxControl PasswordControl; @@ -189,4 +189,4 @@ namespace Lithnet.CredentialProvider.Samples return plainTextPassword; } } -} \ No newline at end of file +} diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Installing the sample.md similarity index 89% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Installing the sample.md rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Installing the sample.md index ae0b944..c9ea520 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Installing the sample.md +++ b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Installing the sample.md @@ -7,10 +7,10 @@ In order to install and run the sample app, you have to register the COM compone Build the EXE, and from an elevated command prompt, change to the bin folder, and run the following commands ``` -%windir%\Microsoft.NET\Framework\v4.0.30319\regasm /codebase "Lithnet.CredentialProvider.Sample.net472.x86.dll" +%windir%\Microsoft.NET\Framework\v4.0.30319\regasm /codebase "Lithnet.CredentialProvider.Sample.Framework.x86.dll" REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /f -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net472.x86" +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.Framework.x86" ``` ## Disable the sample @@ -31,6 +31,6 @@ REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credentia To remove the credential provider, run the following command. ``` -%windir%\Microsoft.NET\Framework\v4.0.30319\regasm /u "Lithnet.CredentialProvider.Sample.net472.x86.dll" +%windir%\Microsoft.NET\Framework\v4.0.30319\regasm /u "Lithnet.CredentialProvider.Sample.Framework.x86.dll" REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /f -``` \ No newline at end of file +``` diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Lithnet.CredentialProvider.Sample.Framework.x86.csproj similarity index 53% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Lithnet.CredentialProvider.Sample.Framework.x86.csproj index b455ab7..8c529c9 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj +++ b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Lithnet.CredentialProvider.Sample.Framework.x86.csproj @@ -11,9 +11,9 @@ - - - + + + @@ -21,7 +21,7 @@ - + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Resources/TileIcon.png similarity index 100% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources/TileIcon.png rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/Resources/TileIcon.png diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/TestCredentialProviderFrameworkX86.cs similarity index 95% rename from src/samples/Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs rename to src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/TestCredentialProviderFrameworkX86.cs index 9bf6ad8..0d2d088 100644 --- a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.Framework.x86/TestCredentialProviderFrameworkX86.cs @@ -9,11 +9,11 @@ namespace Lithnet.CredentialProvider.Samples { [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] - [ProgId("Lithnet.CredentialProvider.Sample.net472.x86")] + [ProgId("Lithnet.CredentialProvider.Sample.Framework.x86")] [Guid("c9055c88-03f9-4a12-8e33-1ee75826a4a6")] - public class TestCredentialProviderNet472x86 : CredentialProviderBase + public class TestCredentialProviderFrameworkX86 : CredentialProviderBase { - private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger(); + private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger(); protected override ICredentialProviderLoggerFactory GetLoggerFactory() { @@ -34,7 +34,7 @@ namespace Lithnet.CredentialProvider.Samples } else { - var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.net472.x86.Resources.TileIcon.png")); + var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.Framework.x86.Resources.TileIcon.png")); yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", image); diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj deleted file mode 100644 index 707cdbd..0000000 --- a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - net6.0-windows - false - x64 - true - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj deleted file mode 100644 index 2b4cf5a..0000000 --- a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - net6.0-windows - false - x86 - true - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj b/src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj index 6628cf4..2ce78dc 100644 --- a/src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj +++ b/src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj @@ -8,8 +8,6 @@ - - diff --git a/src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj b/src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj index 84ddc34..34ee0a4 100644 --- a/src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj +++ b/src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj @@ -2,14 +2,12 @@ Exe - net6.0-windows + net8.0-windows false x86 - -