From b230f2033fea0d8c9ed0bbb7dd78ef3307cfbf0f Mon Sep 17 00:00:00 2001 From: David Hall Date: Sun, 28 May 2023 14:45:29 -0600 Subject: [PATCH] Fixed #402 --- PInvoke/WinSCard/WinSCard.cs | 388 +++++++++++++--------------- UnitTests/PInvoke/WinSCard/WinSCardTests.cs | 2 +- 2 files changed, 180 insertions(+), 210 deletions(-) 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)));