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.
This commit is contained in:
Ryan Newington
2026-08-28 11:22:53 +10:00
parent be65cbaa93
commit 43af44f87f
52 changed files with 1116 additions and 128 deletions
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0-windows;net9.0-windows;net10.0-windows;net472;net48</TargetFrameworks>
<IsPackable>false</IsPackable>
<PlatformTarget>ARM64</PlatformTarget>
<DefineConstants>$(DefineConstants);TEST_ARM64</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lithnet.CredentialProvider\Lithnet.CredentialProvider.csproj" />
<Compile Include="..\Lithnet.CredentialProvider.UnitTests.x64\BitmapControlTests.cs" Link="BitmapControlTests.cs" />
<Compile Include="..\Lithnet.CredentialProvider.UnitTests.x64\ComInterop\**\*.cs" Link="ComInterop\%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
</Project>
@@ -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<ControlBase> 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;
}
}
}
@@ -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;
}
}
}
@@ -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<TDelegate>(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;
}
}
}
}
@@ -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
}
}
}
@@ -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);
}
}
@@ -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<SetUsageScenarioDelegate>(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<GetFieldDescriptorCountDelegate>(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<GetFieldDescriptorCountDelegate>(CredentialProviderAbi.GetFieldDescriptorCountSlot);
GetFieldDescriptorAtDelegate getFieldDescriptorAt = providerInterface.GetMethod<GetFieldDescriptorAtDelegate>(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<NativeCredentialProviderFieldDescriptor>(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<GetFieldDescriptorCountDelegate>(CredentialProviderAbi.GetFieldDescriptorCountSlot);
GetFieldDescriptorAtDelegate getFieldDescriptorAt = providerInterface.GetMethod<GetFieldDescriptorAtDelegate>(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<GetUserCountDelegate>(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<GetCredentialCountDelegate>(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<GetCredentialCountDelegate>(CredentialProviderAbi.GetCredentialCountSlot);
GetCredentialAtDelegate getCredentialAt = providerInterface.GetMethod<GetCredentialAtDelegate>(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<SetSelectedDelegate>(CredentialProviderAbi.SetSelectedSlot);
SetDeselectedDelegate setDeselected = credential.GetMethod<SetDeselectedDelegate>(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<GetFieldStateDelegate>(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<GetStringValueDelegate>(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<GetUserSidDelegate>(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<NativeCredentialProviderFieldDescriptor>(), 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<SetUserArrayDelegate>(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<GetCredentialCountDelegate>(CredentialProviderAbi.GetCredentialCountSlot);
GetCredentialAtDelegate getCredentialAt = providerInterface.GetMethod<GetCredentialAtDelegate>(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);
}
}
}
}
}
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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);
}
}
}
@@ -0,0 +1,8 @@
using System;
using System.Runtime.InteropServices;
namespace Lithnet.CredentialProvider.UnitTests.ComInterop
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate int SetDeselectedDelegate(IntPtr instance);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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;
}
}
}
@@ -1,15 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net7.0-windows;net8.0-windows;net461;net48</TargetFrameworks>
<TargetFrameworks>net8.0-windows;net9.0-windows;net10.0-windows;net472;net48</TargetFrameworks>
<IsPackable>false</IsPackable>
<PlatformTargets>x64</PlatformTargets>
<PlatformTarget>x64</PlatformTarget>
<DefineConstants>$(DefineConstants);TEST_X64</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
</ItemGroup>
<ItemGroup>
@@ -1,20 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net7.0-windows;net8.0-windows;net461;net48</TargetFrameworks>
<TargetFrameworks>net8.0-windows;net9.0-windows;net10.0-windows;net472;net48</TargetFrameworks>
<IsPackable>false</IsPackable>
<PlatformTarget>x86</PlatformTarget>
<DefineConstants>$(DefineConstants);TEST_X86</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lithnet.CredentialProvider\Lithnet.CredentialProvider.csproj" />
<Compile Include="..\Lithnet.CredentialProvider.UnitTests.x64\BitmapControlTests.cs" Link="BitmapControlTests.cs" />
<Compile Include="..\Lithnet.CredentialProvider.UnitTests.x64\ComInterop\**\*.cs" Link="ComInterop\%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
+10 -4
View File
@@ -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
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net7.0-windows;net8.0-windows;net461</TargetFrameworks>
<TargetFrameworks>net8.0-windows;net9.0-windows;net10.0-windows;net472;net48</TargetFrameworks>
<RegisterForComInterop>false</RegisterForComInterop>
<OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -45,6 +45,9 @@
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>Lithnet.CredentialProvider.UnitTests.x86</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>Lithnet.CredentialProvider.UnitTests.arm64</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
@@ -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
```
```
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<RegisterForComInterop>false</RegisterForComInterop>
<Platform>x64</Platform>
<EnableComHosting>true</EnableComHosting>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\InternalLogger.cs" Link="InternalLogger.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\ControlKeys.cs" Link="ControlKeys.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\TestCredentialProviderTile.cs" Link="TestCredentialProviderTile.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Lithnet.CredentialProvider\Lithnet.CredentialProvider.csproj" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
</ItemGroup>
</Project>
@@ -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<TestCredentialProviderNet60x64>();
private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger<TestCredentialProviderCoreX64>();
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;
}
}
}
@@ -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
```
```
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<RegisterForComInterop>false</RegisterForComInterop>
<Platform>x86</Platform>
<EnableComHosting>true</EnableComHosting>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\InternalLogger.cs" Link="InternalLogger.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\ControlKeys.cs" Link="ControlKeys.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\TestCredentialProviderTile.cs" Link="TestCredentialProviderTile.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Lithnet.CredentialProvider\Lithnet.CredentialProvider.csproj" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
</ItemGroup>
</Project>
@@ -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<TestCredentialProviderNet60x86>();
private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger<TestCredentialProviderCoreX86>();
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);
@@ -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
```
```
@@ -15,7 +15,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lithnet.CredentialProvider" Version="1.0.*-beta1*" />
<ProjectReference Include="..\..\Lithnet.CredentialProvider\Lithnet.CredentialProvider.csproj" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
</ItemGroup>
@@ -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);
@@ -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;
}
}
}
}
@@ -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
```
```
@@ -11,9 +11,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\InternalLogger.cs" Link="InternalLogger.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\ControlKeys.cs" Link="ControlKeys.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\TestCredentialProviderTile.cs" Link="TestCredentialProviderTile.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\InternalLogger.cs" Link="InternalLogger.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\ControlKeys.cs" Link="ControlKeys.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.Framework.x64\TestCredentialProviderTile.cs" Link="TestCredentialProviderTile.cs" />
</ItemGroup>
<ItemGroup>
@@ -21,7 +21,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lithnet.CredentialProvider" Version="1.0.*-beta1*" />
<ProjectReference Include="..\..\Lithnet.CredentialProvider\Lithnet.CredentialProvider.csproj" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
@@ -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<TestCredentialProviderNet472x86>();
private static readonly ICredentialProviderLogger logger = InternalLoggerFactory.Instance.CreateLogger<TestCredentialProviderFrameworkX86>();
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);
@@ -1,32 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<RegisterForComInterop>false</RegisterForComInterop>
<Platform>x64</Platform>
<EnableComHosting>true</EnableComHosting>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\InternalLogger.cs" Link="InternalLogger.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\ControlKeys.cs" Link="ControlKeys.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\TestCredentialProviderTile.cs" Link="TestCredentialProviderTile.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lithnet.CredentialProvider" Version="1.0.*-beta1*" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
</ItemGroup>
</Project>
@@ -1,32 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<RegisterForComInterop>false</RegisterForComInterop>
<Platform>x86</Platform>
<EnableComHosting>true</EnableComHosting>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\InternalLogger.cs" Link="InternalLogger.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\ControlKeys.cs" Link="ControlKeys.cs" />
<Compile Include="..\Lithnet.CredentialProvider.Sample.net472.x64\TestCredentialProviderTile.cs" Link="TestCredentialProviderTile.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\TileIcon.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lithnet.CredentialProvider" Version="1.0.*-beta1*" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
</ItemGroup>
</Project>
@@ -8,8 +8,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
@@ -2,14 +2,12 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<RegisterForComInterop>false</RegisterForComInterop>
<Platform>x86</Platform>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="NLog" Version="5.1.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
</ItemGroup>