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:
@@ -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);
|
||||
}
|
||||
}
|
||||
+450
@@ -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);
|
||||
}
|
||||
+8
@@ -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);
|
||||
}
|
||||
+8
@@ -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);
|
||||
}
|
||||
+27
@@ -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);
|
||||
}
|
||||
}
|
||||
+17
@@ -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);
|
||||
}
|
||||
+51
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user