diff --git a/PInvoke/WinSCard/WinSCard.cs b/PInvoke/WinSCard/WinSCard.cs
index 856dea60..6e007a7c 100644
--- a/PInvoke/WinSCard/WinSCard.cs
+++ b/PInvoke/WinSCard/WinSCard.cs
@@ -2,7 +2,7 @@ global using System;
global using System.Runtime.InteropServices;
global using Vanara.InteropServices;
-global using SCARDCONTEXT = System.UIntPtr;
+global using SCARDCONTEXT = nuint;
using System.Linq;
using System.Text;
using Vanara.Extensions;
@@ -68,6 +68,8 @@ public static partial class WinSCard
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Auto)]
public delegate void LPOCNDSCPROC([In] SCARDCONTEXT hSCardContext, [In] SCARDHANDLE hcard, [In] IntPtr pvUserData);
+ private delegate SCARD_RET SCardListCardsDelegate(IntPtr mszOut, ref uint pcch);
+
///
[PInvokeData("winscard.h", MSDNShortId = "NS:winscard.__unnamed_struct_4")]
[Flags]
@@ -2246,35 +2248,37 @@ public static partial class WinSCard
/// are supplied, the cards returned will match the ATR string supplied and support the interfaces specified.
///
///
- ///
+ ///
+ ///
/// Handle that identifies the resource manager context for the query. The resource manager context can be set by a previous call to SCardEstablishContext.
///
- /// If this parameter is set to NULL, the search for cards is not limited to any context.
+ /// If this parameter is set to NULL, the search for cards is not limited to any context.
+ ///
/// Address of an ATR string to compare to known cards, or NULL if no ATR matching is to be performed.
- /// Array of identifiers (GUIDs), or NULL if no interface matching is to be performed. When an array is supplied, a card name will
- /// be returned only if all the specified identifiers are supported by the card.
- /// Multi-string that lists the smart cards found. If this value is NULL, SCardListCards ignores the buffer length supplied
+ ///
+ /// Array of identifiers (GUIDs), or NULL if no interface matching is to be performed. When an array is supplied, a card name will
+ /// be returned only if all the specified identifiers are supported by the card.
+ ///
+ ///
+ /// Multi-string that lists the smart cards found. If this value is NULL, SCardListCards ignores the buffer length supplied
/// in pcchCards, returning the length of the buffer that would have been returned if this parameter had not been NULL to
- /// pcchCards and a success code.
+ /// pcchCards and a success code.
+ ///
///
/// This function returns different values depending on whether it succeeds or fails.
///
- ///
- /// Return code
- /// Description
- ///
- /// -
- ///
- /// Success
- ///
- /// SCARD_S_SUCCESS.
- ///
- /// -
- ///
- /// Failure
- ///
- /// An error code. For more information, see Smart Card Return Values.
- ///
+ ///
+ /// Return code
+ /// Description
+ ///
+ /// -
+ /// Success
+ /// SCARD_S_SUCCESS.
+ ///
+ /// -
+ /// Failure
+ /// An error code. For more information, see Smart Card Return Values.
+ ///
///
///
///
@@ -2293,21 +2297,13 @@ public static partial class WinSCard
/// SCardBeginTransaction function.
///
///
- /// Windows Server 2008 R2 and Windows 7: Calling this function within a transaction could result in your computer becoming unresponsive.
+ /// Windows Server 2008 R2 and Windows 7: Calling this function within a transaction could result in your computer becoming unresponsive.
///
- ///
- /// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: Not applicable.
+ /// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: Not applicable.
///
[PInvokeData("winscard.h", MSDNShortId = "NF:winscard.SCardListCardsA")]
- public static SCARD_RET SCardListCards(SCARDCONTEXT hContext, [Optional] byte[]? pbAtr, [Optional] Guid[]? rgquidInterfaces, out string[] mszCards)
- {
- IntPtr ptr = default;
- uint cch = SCARD_AUTOALLOCATE;
- var ret = SCardListCards(hContext, pbAtr, rgquidInterfaces, (uint)(rgquidInterfaces?.Length ?? 0), ptr, ref cch);
- mszCards = ret.Succeeded ? ptr.ToStringEnum(CharSet.Auto, 0, cch).ToArray() : null;
- SCardFreeMemory(hContext, ptr);
- return ret;
- }
+ public static SCARD_RET SCardListCards(SCARDCONTEXT hContext, [Optional] byte[]? pbAtr, [Optional] Guid[]? rgquidInterfaces, out string[] mszCards) =>
+ ListSCardFunc((IntPtr p, ref uint c) => SCardListCards(hContext, pbAtr, rgquidInterfaces, (uint)(rgquidInterfaces?.Length ?? 0), p, ref c), out mszCards);
///
/// The SCardListInterfaces function provides a list of interfaces supplied by a given card.
@@ -2456,29 +2452,27 @@ public static partial class WinSCard
/// the card.
///
///
- /// Handle that identifies the resource manager context for the query. The resource manager context can be set by a previous call to
- /// SCardEstablishContext. This parameter cannot be NULL.
+ ///
+ /// Handle that identifies the resource manager context for the query. The resource manager context can be set by a previous call to
+ /// SCardEstablishContext. This parameter cannot be NULL.
+ ///
/// Name of the smart card already introduced to the smart card subsystem.
/// Array of interface identifiers (GUIDs) that indicate the interfaces supported by the smart card.
///
/// This function returns different values depending on whether it succeeds or fails.
///
- ///
- /// Return code
- /// Description
- ///
- /// -
- ///
- /// Success
- ///
- /// SCARD_S_SUCCESS.
- ///
- /// -
- ///
- /// Failure
- ///
- /// An error code. For more information, see Smart Card Return Values.
- ///
+ ///
+ /// Return code
+ /// Description
+ ///
+ /// -
+ /// Success
+ /// SCARD_S_SUCCESS.
+ ///
+ /// -
+ /// Failure
+ /// An error code. For more information, see Smart Card Return Values.
+ ///
///
///
///
@@ -2492,15 +2486,8 @@ public static partial class WinSCard
///
///
[PInvokeData("winscard.h", MSDNShortId = "NF:winscard.SCardListInterfacesA")]
- public static SCARD_RET SCardListInterfaces([In] SCARDCONTEXT hContext, string szCard, out Guid[] pguidInterfaces)
- {
- IntPtr ptr = default;
- uint cch = SCARD_AUTOALLOCATE;
- var ret = SCardListInterfaces(hContext, szCard, ptr, ref cch);
- pguidInterfaces = ret.Succeeded ? ptr.ToArray((int)cch) : null;
- SCardFreeMemory(hContext, ptr);
- return ret;
- }
+ public static SCARD_RET SCardListInterfaces([In] SCARDCONTEXT hContext, string szCard, out Guid[] pguidInterfaces) =>
+ ListSCardFunc((IntPtr p, ref uint c) => SCardListInterfaces(hContext, szCard, p, ref c), out pguidInterfaces);
///
/// The SCardListReaderGroups function provides the list of reader groups that have previously been introduced to the system.
@@ -2605,67 +2592,61 @@ public static partial class WinSCard
///
/// The SCardListReaderGroups function provides the list of reader groups that have previously been introduced to the system.
///
- ///
+ ///
+ ///
/// Handle that identifies the resource manager context for the query. The resource manager context can be set by a previous call to SCardEstablishContext.
///
- /// If this parameter is set to NULL, the search for reader groups is not limited to any context.
- ///
- /// String array that lists the reader groups defined to the system and available to the current user on the current terminal.
- ///
+ /// If this parameter is set to NULL, the search for reader groups is not limited to any context.
+ ///
+ ///
+ /// String array that lists the reader groups defined to the system and available to the current user on the current terminal.
///
- ///
- /// Value
- /// Meaning
- ///
- /// -
- ///
- /// SCARD_ALL_READERS TEXT("SCard$AllReaders\000")
- ///
+ ///
+ /// Value
+ /// Meaning
+ ///
+ ///
-
+ /// SCARD_ALL_READERS TEXT("SCard$AllReaders\000")
+ ///
/// Group used when no group name is provided when listing readers. Returns a list of all readers, regardless of what group or groups the
/// readers are in.
///
- ///
- /// -
- ///
- /// SCARD_DEFAULT_READERS TEXT("SCard$DefaultReaders\000")
- /// Default group to which all readers are added when introduced into the system.
- ///
- /// -
- ///
- /// SCARD_LOCAL_READERS TEXT("SCard$LocalReaders\000")
- ///
+ ///
+ /// -
+ /// SCARD_DEFAULT_READERS TEXT("SCard$DefaultReaders\000")
+ /// Default group to which all readers are added when introduced into the system.
+ ///
+ /// -
+ /// SCARD_LOCAL_READERS TEXT("SCard$LocalReaders\000")
+ ///
/// Unused legacy value. This is an internally managed group that cannot be modified by using any reader group APIs. It is intended to be
/// used for enumeration only.
///
- ///
- /// -
- ///
- /// SCARD_SYSTEM_READERS TEXT("SCard$SystemReaders\000")
- ///
+ ///
+ /// -
+ /// SCARD_SYSTEM_READERS TEXT("SCard$SystemReaders\000")
+ ///
/// Unused legacy value. This is an internally managed group that cannot be modified by using any reader group APIs. It is intended to be
/// used for enumeration only.
///
- ///
- ///
+ ///
+ ///
+ ///
///
/// This function returns different values depending on whether it succeeds or fails.
///
- ///
- /// Return code
- /// Description
- ///
- /// -
- ///
- /// Success
- ///
- /// SCARD_S_SUCCESS.
- ///
- /// -
- ///
- /// Failure
- ///
- /// An error code. For more information, see Smart Card Return Values.
- ///
+ ///
+ /// Return code
+ /// Description
+ ///
+ /// -
+ /// Success
+ /// SCARD_S_SUCCESS.
+ ///
+ /// -
+ /// Failure
+ /// An error code. For more information, see Smart Card Return Values.
+ ///
///
///
///
@@ -2679,15 +2660,8 @@ public static partial class WinSCard
///
///
[PInvokeData("winscard.h", MSDNShortId = "NF:winscard.SCardListReaderGroupsA")]
- public static SCARD_RET SCardListReaderGroups([In, Optional] SCARDCONTEXT hContext, out string[] mszGroups)
- {
- IntPtr ptr = default;
- uint cch = SCARD_AUTOALLOCATE;
- var ret = SCardListReaderGroups(hContext, ptr, ref cch);
- mszGroups = ret.Succeeded ? ptr.ToStringEnum(CharSet.Auto, 0, cch).ToArray() : null;
- SCardFreeMemory(hContext, ptr);
- return ret;
- }
+ public static SCARD_RET SCardListReaderGroups([In, Optional] SCARDCONTEXT hContext, out string[] mszGroups) =>
+ ListSCardFunc((IntPtr p, ref uint c) => SCardListReaderGroups(hContext, p, ref c), out mszGroups);
///
/// The SCardListReaders function provides the list of readers within a set of named reader groups, eliminating duplicates.
@@ -2810,78 +2784,73 @@ public static partial class WinSCard
/// ignored. This function only returns readers within the named groups that are currently attached to the system and available for use.
///
///
- ///
+ ///
+ ///
/// Handle that identifies the resource manager context for the query. The resource manager context can be set by a previous call to SCardEstablishContext.
///
- /// If this parameter is set to NULL, the search for readers is not limited to any context.
- ///
+ /// If this parameter is set to NULL, the search for readers is not limited to any context.
+ ///
+ ///
+ ///
/// Names of the reader groups defined to the system, as a string array. Use a NULL value to list all readers in the system (that
/// is, the SCard$AllReaders group).
///
///
- ///
- /// Value
- /// Meaning
- ///
- /// -
- ///
- /// SCARD_ALL_READERS TEXT("SCard$AllReaders\000")
- ///
+ ///
+ /// Value
+ /// Meaning
+ ///
+ ///
-
+ /// SCARD_ALL_READERS TEXT("SCard$AllReaders\000")
+ ///
/// Group used when no group name is provided when listing readers. Returns a list of all readers, regardless of what group or groups the
/// readers are in.
///
- ///
- /// -
- ///
- /// SCARD_DEFAULT_READERS TEXT("SCard$DefaultReaders\000")
- /// Default group to which all readers are added when introduced into the system.
- ///
- /// -
- ///
- /// SCARD_LOCAL_READERS TEXT("SCard$LocalReaders\000")
- ///
+ ///
+ /// -
+ /// SCARD_DEFAULT_READERS TEXT("SCard$DefaultReaders\000")
+ /// Default group to which all readers are added when introduced into the system.
+ ///
+ /// -
+ /// SCARD_LOCAL_READERS TEXT("SCard$LocalReaders\000")
+ ///
/// Unused legacy value. This is an internally managed group that cannot be modified by using any reader group APIs. It is intended to be
/// used for enumeration only.
///
- ///
- /// -
- ///
- /// SCARD_SYSTEM_READERS TEXT("SCard$SystemReaders\000")
- ///
+ ///
+ /// -
+ /// SCARD_SYSTEM_READERS TEXT("SCard$SystemReaders\000")
+ ///
/// Unused legacy value. This is an internally managed group that cannot be modified by using any reader group APIs. It is intended to be
/// used for enumeration only.
///
- ///
- ///
+ ///
+ ///
+ ///
/// String array that lists the card readers within the supplied reader groups.
///
/// This function returns different values depending on whether it succeeds or fails.
///
- ///
- /// Return code/value
- /// Description
- ///
- /// -
- ///
- /// Success 0 (0x0)
- /// SCARD_S_SUCCESS
- ///
- /// -
- ///
- /// Group contains no readers 2148532270 (0x8010002E)
- /// SCARD_E_NO_READERS_AVAILABLE
- ///
- /// -
- ///
- /// Specified reader is not currently available for use 2148532247 (0x80100017)
- /// SCARD_E_READER_UNAVAILABLE
- ///
- /// -
- ///
- /// Other
- ///
- /// An error code. For more information, see Smart Card Return Values.
- ///
+ ///
+ /// Return code/value
+ /// Description
+ ///
+ /// -
+ /// Success 0 (0x0)
+ /// SCARD_S_SUCCESS
+ ///
+ /// -
+ /// Group contains no readers 2148532270 (0x8010002E)
+ /// SCARD_E_NO_READERS_AVAILABLE
+ ///
+ /// -
+ /// Specified reader is not currently available for use 2148532247 (0x80100017)
+ /// SCARD_E_READER_UNAVAILABLE
+ ///
+ /// -
+ /// Other
+ /// An error code. For more information, see Smart Card Return Values.
+ ///
///
///
///
@@ -2891,15 +2860,8 @@ public static partial class WinSCard
///
///
[PInvokeData("winscard.h", MSDNShortId = "NF:winscard.SCardListReadersA")]
- public static SCARD_RET SCardListReaders([In, Optional] SCARDCONTEXT hContext, [In, Optional] string[]? mszGroups, out string[] mszReaders)
- {
- IntPtr ptr = default;
- uint cch = SCARD_AUTOALLOCATE;
- var ret = SCardListReaders(hContext, mszGroups, ptr, ref cch);
- mszReaders = ret.Succeeded ? ptr.ToStringEnum(CharSet.Auto, 0, cch).ToArray() : null;
- SCardFreeMemory(hContext, ptr);
- return ret;
- }
+ public static SCARD_RET SCardListReaders([In, Optional] SCARDCONTEXT hContext, [In, Optional] string[]? mszGroups, out string[]? mszReaders) =>
+ ListSCardFunc((IntPtr p, ref uint c) => SCardListReaders(hContext, mszGroups, p, ref c), out mszReaders);
///
/// The SCardListReadersWithDeviceInstanceId function gets the list of readers that have provided a device instance identifier.
@@ -2971,30 +2933,30 @@ public static partial class WinSCard
/// The SCardListReadersWithDeviceInstanceId function gets the list of readers that have provided a device instance identifier.
/// This function does not affect the state of the reader.
///
- /// Handle that identifies the resource manager context for the query. You can set the resource manager context by a previous call to the
- /// SCardEstablishContext function. This parameter cannot be NULL.
- /// Device instance ID of the reader. You can get this value by calling the SCardGetReaderDeviceInstanceId function with the reader name
- /// or by calling the SetupDiGetDeviceInstanceId function from the DDK.
+ ///
+ /// Handle that identifies the resource manager context for the query. You can set the resource manager context by a previous call to the
+ /// SCardEstablishContext function. This parameter cannot be NULL.
+ ///
+ ///
+ /// Device instance ID of the reader. You can get this value by calling the SCardGetReaderDeviceInstanceId function with the reader name
+ /// or by calling the SetupDiGetDeviceInstanceId function from the DDK.
+ ///
/// A string array that contain the smart card readers within the supplied device instance identifier.
///
/// This function returns different values depending on whether it succeeds or fails.
///
- ///
- /// Return code
- /// Description
- ///
- /// -
- ///
- /// Success
- ///
- /// SCARD_S_SUCCESS.
- ///
- /// -
- ///
- /// Failure
- ///
- /// An error code. For more information, see Smart Card Return Values.
- ///
+ ///
+ /// Return code
+ /// Description
+ ///
+ /// -
+ /// Success
+ /// SCARD_S_SUCCESS.
+ ///
+ /// -
+ /// Failure
+ /// An error code. For more information, see Smart Card Return Values.
+ ///
///
///
///
@@ -3004,15 +2966,8 @@ public static partial class WinSCard
///
///
[PInvokeData("winscard.h", MSDNShortId = "NF:winscard.SCardListReadersWithDeviceInstanceIdA")]
- public static SCARD_RET SCardListReadersWithDeviceInstanceId([In] SCARDCONTEXT hContext, string szDeviceInstanceId, out string[] mszReaders)
- {
- IntPtr ptr = default;
- uint cch = SCARD_AUTOALLOCATE;
- var ret = SCardListReadersWithDeviceInstanceId(hContext, szDeviceInstanceId, ptr, ref cch);
- mszReaders = ret.Succeeded ? ptr.ToStringEnum(CharSet.Auto, 0, cch).ToArray() : null;
- SCardFreeMemory(hContext, ptr);
- return ret;
- }
+ public static SCARD_RET SCardListReadersWithDeviceInstanceId([In] SCARDCONTEXT hContext, string szDeviceInstanceId, out string[] mszReaders) =>
+ ListSCardFunc((IntPtr p, ref uint c) => SCardListReadersWithDeviceInstanceId(hContext, szDeviceInstanceId, p, ref c), out mszReaders);
///
/// The SCardLocateCards function searches the readers listed in the rgReaderStates parameter for a card with an ATR string
@@ -3880,6 +3835,21 @@ public static partial class WinSCard
public static extern SCARD_RET SCardWriteCache([In] SCARDCONTEXT hContext, in Guid CardIdentifier, [In] uint FreshnessCounter,
[MarshalAs(UnmanagedType.LPTStr)] string LookupName, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)] byte[] Data, [In] uint DataLen);
+ private static SCARD_RET ListSCardFunc(SCardListCardsDelegate d, out T[] msz)
+ {
+ msz = new T[0];
+ uint cch = 0;
+ SCARD_RET ret = d(default, ref cch);
+ if (ret.Failed) return ret;
+ using SafeCoTaskMemHandle ptr = new(Len(cch));
+ ret = d(ptr, ref cch);
+ if (ret.Succeeded)
+ msz = typeof(T) == typeof(string) ? (T[])(object)ptr.ToStringEnum().ToArray() : ptr.ToArray((int)cch);
+ return ret;
+
+ static int Len(uint cch) => typeof(T) == typeof(string) ? (1 + (int)cch) * Marshal.SystemDefaultCharSize : InteropExtensions.SizeOf() * cch;
+ }
+
///
/// The OPENCARD_SEARCH_CRITERIA structure is used by the SCardUIDlgSelectCard function in order to recognize cards that meet the
/// requirements set forth by the caller. You can, however, call SCardUIDlgSelectCard without using this structure.
@@ -4505,7 +4475,7 @@ public static partial class WinSCard
/// Provides a handle to a smartcard.
[StructLayout(LayoutKind.Sequential)]
- public struct SCARDHANDLE : IHandle
+ public readonly struct SCARDHANDLE : IHandle
{
private readonly IntPtr handle;
diff --git a/UnitTests/PInvoke/WinSCard/WinSCardTests.cs b/UnitTests/PInvoke/WinSCard/WinSCardTests.cs
index dca09f14..df47d3f6 100644
--- a/UnitTests/PInvoke/WinSCard/WinSCardTests.cs
+++ b/UnitTests/PInvoke/WinSCard/WinSCardTests.cs
@@ -20,7 +20,7 @@ public class WinSCardTests
[Test]
public void ListTest()
{
- SCardEstablishContext(SCARD_SCOPE.SCARD_SCOPE_USER, phContext: out var ctx).ThrowIfFailed();
+ SCardEstablishContext(SCARD_SCOPE.SCARD_SCOPE_SYSTEM, phContext: out var ctx).ThrowIfFailed();
SCardListReaders(ctx, null, out var readers).ThrowIfFailed();
TestContext.WriteLine("Registerd Readers(s)\n======================\n" + (readers is null ? "None" : string.Join("\n", readers)));