├── src └── PcscDotNet │ ├── PcscDotNet.csproj │ ├── SCardReaderStatesExtensions.cs │ ├── PcscProvider.cs │ ├── PcscReaderState.cs │ ├── PcscException.cs │ ├── SCardContext.cs │ ├── SCardHandle.cs │ ├── SCardDisposition.cs │ ├── SCardErrorExtensions.cs │ ├── SCardShare.cs │ ├── SCardIORequest.cs │ ├── SCardIORequestLinux.cs │ ├── SCardScope.cs │ ├── Pcsc_1.cs │ ├── SCardReaderGroup.cs │ ├── SCardReaderStateLinux.cs │ ├── SCardReaderState.cs │ ├── SCardProtocols.cs │ ├── SCardReaderStateOSX.cs │ ├── SCardReaderGroupExtensions.cs │ ├── Pcsc.cs │ ├── SCardControlFunction.cs │ ├── IPcscProviderExtensions.cs │ ├── IPcscProvider.cs │ ├── SCardReaderStates.cs │ ├── PcscReaderStatus.cs │ ├── PcscContext.cs │ ├── WinSCard.cs │ ├── PcscConnection.cs │ └── SCardError.cs ├── tests └── PcscDotNet.ConsoleTests │ ├── PcscDotNet.ConsoleTests.csproj │ └── Program.cs ├── .vscode ├── tasks.json └── launch.json ├── LICENSE ├── .gitattributes ├── PcscDotNet.sln ├── README.md └── .gitignore /src/PcscDotNet/PcscDotNet.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | true 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardReaderStatesExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | public static class SCardReaderStatesExtensions 4 | { 5 | public static bool IsSet(this SCardReaderStates src, SCardReaderStates states) 6 | { 7 | return (src & states) == states; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/PcscDotNet.ConsoleTests/PcscDotNet.ConsoleTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp2.0 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/PcscDotNet/PcscProvider.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// Usage shared for IPcscProvider. 5 | /// 6 | public static class PcscProvider 7 | { 8 | /// 9 | /// Length designator for auto-allocation memory from the Smart Card Resource Manager. 10 | /// 11 | public const int SCardAutoAllocate = -1; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "dotnet", 4 | "isShellCommand": true, 5 | "args": [], 6 | "tasks": [ 7 | { 8 | "taskName": "build", 9 | "args": [ 10 | "${workspaceRoot}/tests/PcscDotNet.ConsoleTests/PcscDotNet.ConsoleTests.csproj" 11 | ], 12 | "isBuildCommand": true, 13 | "problemMatcher": "$msCompile" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /src/PcscDotNet/PcscReaderState.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | public sealed class PcscReaderState 4 | { 5 | public byte[] Atr { get; internal set; } 6 | 7 | public int EventNumber { get; internal set; } 8 | 9 | public string ReaderName { get; private set; } 10 | 11 | public SCardReaderStates State { get; internal set; } 12 | 13 | public PcscReaderState(string readerName) 14 | { 15 | ReaderName = readerName; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PcscDotNet/PcscException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace PcscDotNet 5 | { 6 | public delegate void PcscExceptionHandler(PcscException error); 7 | 8 | public sealed class PcscException : Win32Exception 9 | { 10 | public SCardError Error { get; private set; } 11 | 12 | public bool ThrowIt { get; set; } = true; 13 | 14 | public PcscException(SCardError error) : base((int)error) 15 | { 16 | Error = error; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardContext.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// A handle that identifies the resource manager context. 5 | /// 6 | public struct SCardContext 7 | { 8 | /// 9 | /// Default value. 10 | /// 11 | public static readonly SCardContext Default = default(SCardContext); 12 | 13 | /// 14 | /// Returns true if `Value` is not null; otherwise, false. 15 | /// 16 | public unsafe bool HasValue => Value != null; 17 | 18 | /// 19 | /// Handle value. 20 | /// 21 | public unsafe void* Value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardHandle.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// A handle that identifies the connection to the smart card in the designated reader. 5 | /// 6 | public struct SCardHandle 7 | { 8 | /// 9 | /// Default value. 10 | /// 11 | public static readonly SCardHandle Default = default(SCardHandle); 12 | 13 | /// 14 | /// Returns true if `Value` is not null; otherwise, false. 15 | /// 16 | public unsafe bool HasValue => Value != null; 17 | 18 | /// 19 | /// Handle value. 20 | /// 21 | public unsafe void* Value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardDisposition.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// Action to take on the card in the connected reader on close. 5 | /// 6 | public enum SCardDisposition 7 | { 8 | /// 9 | /// Eject the card on close. 10 | /// 11 | Eject = 3, 12 | /// 13 | /// Don't do anything special on close. 14 | /// 15 | Leave = 0, 16 | /// 17 | /// Reset the card on close (warm reset). 18 | /// 19 | Reset = 1, 20 | /// 21 | /// Power down the card on close (cold reset). 22 | /// 23 | Unpower = 2 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/PcscDotNet.ConsoleTests/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet.ConsoleTests 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | using (var context = Pcsc.EstablishContext(SCardScope.User)) 10 | { 11 | Console.WriteLine(context.IsEstablished); 12 | context.Validate(); 13 | context.Release(); 14 | Console.WriteLine(context.IsEstablished); 15 | try 16 | { 17 | context.Validate(); 18 | } 19 | catch (PcscException ex) 20 | { 21 | Console.WriteLine($"0x{ex.NativeErrorCode:X8}: {ex.Message}"); 22 | } 23 | } 24 | Console.WriteLine("Hello World!"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardErrorExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet 4 | { 5 | /// 6 | /// Extension methods for SCardError. 7 | /// 8 | public static class SCardErrorExtensions 9 | { 10 | public static void Throw(this SCardError error, PcscExceptionHandler onException = null) 11 | { 12 | var exception = new PcscException(error); 13 | if (onException != null) 14 | { 15 | onException(exception); 16 | if (!exception.ThrowIt) return; 17 | } 18 | throw exception; 19 | } 20 | 21 | public static void ThrowIfNotSuccess(this SCardError error, PcscExceptionHandler onException = null) 22 | { 23 | if (error != SCardError.Successs) 24 | { 25 | Throw(error, onException); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardShare.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// Indicates whether other applications may form connections to the card. 5 | /// 6 | public enum SCardShare 7 | { 8 | /// 9 | /// This application demands direct control of the reader, so it is not available to other applications. 10 | /// 11 | Direct = 3, 12 | /// 13 | /// This application is not willing to share this card with other applications. 14 | /// 15 | Exclusive = 1, 16 | /// 17 | /// This application is willing to share this card with other applications. 18 | /// 19 | Shared = 2, 20 | /// 21 | /// Share mode is undefined, can not be used to connect to card/reader. 22 | /// 23 | Undefined = 0 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardIORequest.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// The protocol control information structure. 5 | /// Any protocol-specific information then immediately follows this structure. 6 | /// The entire length of the structure must be aligned with the underlying hardware architecture word size. 7 | /// For example, in Win32 the length of any PCI information must be a multiple of four bytes so that it aligns on a 32-bit boundary. 8 | /// (For WinSCard in Windows and pcsc-lite in OS X.) 9 | /// 10 | public struct SCardIORequest 11 | { 12 | /// 13 | /// Protocol in use. 14 | /// 15 | public SCardProtocols Protocol; 16 | 17 | /// 18 | /// Length, in bytes, of the current structure plus any following PCI-specific information. 19 | /// 20 | public int PciLength; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardIORequestLinux.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet 4 | { 5 | /// 6 | /// The protocol control information structure. 7 | /// Any protocol-specific information then immediately follows this structure. 8 | /// The entire length of the structure must be aligned with the underlying hardware architecture word size. 9 | /// For example, in Win32 the length of any PCI information must be a multiple of four bytes so that it aligns on a 32-bit boundary. 10 | /// (For pcsc-lite in Linux.) 11 | /// 12 | public struct SCardIORequestLinux 13 | { 14 | /// 15 | /// Protocol in use. 16 | /// 17 | public IntPtr Protocol; 18 | 19 | /// 20 | /// Length, in bytes, of the current structure plus any following PCI-specific information. 21 | /// 22 | public IntPtr PciLength; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardScope.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// Scope of the Smart Card Resource Manager context. 5 | /// 6 | public enum SCardScope 7 | { 8 | /// 9 | /// The context is a user context, and any database operations are performed within the domain of the user. 10 | /// 11 | User = 0, 12 | /// 13 | /// The context is that of the current terminal, and any database operations are performed within the domain of that terminal. 14 | /// (The calling application must have appropriate access permissions for any database actions.) 15 | /// 16 | Terminal = 1, 17 | /// 18 | /// The context is the system context, and any database operations are performed within the domain of the system. 19 | /// (The calling application must have appropriate access permissions for any database actions.) 20 | /// 21 | System = 2 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Archie Yang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/PcscDotNet/Pcsc_1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PcscDotNet 5 | { 6 | public static class Pcsc where TIPcscProvider : IPcscProvider, new() 7 | { 8 | public static Pcsc Instance { get; private set; } = new Pcsc(new TIPcscProvider()); 9 | 10 | public static PcscContext CreateContext() 11 | { 12 | return Instance.CreateContext(); 13 | } 14 | 15 | public static PcscContext EstablishContext(SCardScope scope, PcscExceptionHandler onException = null) 16 | { 17 | return Instance.EstablishContext(scope, onException); 18 | } 19 | 20 | public static IEnumerable GetReaderGroupNames(PcscExceptionHandler onException = null) 21 | { 22 | return Instance.GetReaderGroupNames(onException); 23 | } 24 | 25 | public static IEnumerable GetReaderNames(SCardReaderGroup group = SCardReaderGroup.NotSpecified, PcscExceptionHandler onException = null) 26 | { 27 | return Instance.GetReaderNames(group.GetDefinedValue(), onException); 28 | } 29 | 30 | public static IEnumerable GetReaderNames(string group, PcscExceptionHandler onException = null) 31 | { 32 | return Instance.GetReaderNames(group, onException); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceRoot}/tests/PcscDotNet.ConsoleTests/bin/Debug/netcoreapp2.0/PcscDotNet.ConsoleTests.dll", 14 | "args": [], 15 | "cwd": "${workspaceRoot}/tests/PcscDotNet.ConsoleTests", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false, 19 | "internalConsoleOptions": "openOnSessionStart" 20 | }, 21 | { 22 | "name": ".NET Core Attach", 23 | "type": "coreclr", 24 | "request": "attach", 25 | "processId": "${command:pickProcess}" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /src/PcscDotNet/SCardReaderGroup.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// Smart card reader groups defined to the system. 5 | /// 6 | public enum SCardReaderGroup 7 | { 8 | /// 9 | /// Group used when no group name is provided when listing readers. 10 | /// Returns a list of all readers, regardless of what group or groups the readers are in. 11 | /// 12 | All = 0, 13 | /// 14 | /// Default group to which all readers are added when introduced into the system. 15 | /// 16 | Defualt = 1, 17 | /// 18 | /// Unused legacy value. 19 | /// This is an internally managed group that cannot be modified by using any reader group APIs. 20 | /// It is intended to be used for enumeration only. 21 | /// 22 | Local = 2, 23 | /// 24 | /// List all readers in the system. 25 | /// (Same as All.) 26 | /// 27 | NotSpecified = 3, 28 | /// 29 | /// Unused legacy value. 30 | /// This is an internally managed group that cannot be modified by using any reader group APIs. 31 | /// It is intended to be used for enumeration only. 32 | /// 33 | System = 4 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | #common settings that generally should always be used with your language specific settings 2 | 3 | # Auto detect text files and perform LF normalization 4 | # http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | 11 | # Documents 12 | *.doc diff=astextplain 13 | *.DOC diff=astextplain 14 | *.docx diff=astextplain 15 | *.DOCX diff=astextplain 16 | *.dot diff=astextplain 17 | *.DOT diff=astextplain 18 | *.pdf diff=astextplain 19 | *.PDF diff=astextplain 20 | *.rtf diff=astextplain 21 | *.RTF diff=astextplain 22 | *.md text 23 | *.adoc text 24 | *.textile text 25 | *.mustache text 26 | *.csv text 27 | *.tab text 28 | *.tsv text 29 | *.sql text 30 | 31 | # Graphics 32 | *.png binary 33 | *.jpg binary 34 | *.jpeg binary 35 | *.gif binary 36 | *.tif binary 37 | *.tiff binary 38 | *.ico binary 39 | # SVG treated as an asset (binary) by default. If you want to treat it as text, 40 | # comment-out the following line and uncomment the line after. 41 | *.svg binary 42 | #*.svg text 43 | *.eps binary 44 | 45 | # Auto detect text files and perform LF normalization 46 | # http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ 47 | # Commented because this line appears before in the file. 48 | # * text=auto 49 | 50 | *.cs diff=csharp 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardReaderStateLinux.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet 4 | { 5 | /// 6 | /// This structure is used by functions for tracking smart cards within readers. 7 | /// (For pcsc-lite in Linux.) 8 | /// 9 | public struct SCardReaderStateLinux 10 | { 11 | /// 12 | /// A pointer to the name of the reader being monitored. 13 | /// Set the value of this member to "\\\\?PnP?\\Notification" and the values of all other members to zero to be notified of the arrival of a new smart card reader. 14 | /// 15 | public unsafe void* Reader; 16 | 17 | /// 18 | /// Not used by the smart card subsystem. 19 | /// This member is used by the application. 20 | /// 21 | public unsafe void* UserData; 22 | 23 | /// 24 | /// Current state of the reader, as seen by the application, combination, as a bitmask. 25 | /// 26 | public IntPtr CurrentState; 27 | 28 | /// 29 | /// Current state of the reader, as known by the smart card resource manager, combination, as a bitmask. 30 | /// 31 | public IntPtr EventState; 32 | 33 | /// 34 | /// Number of bytes in the returned `ATR`. 35 | /// 36 | public IntPtr AtrLength; 37 | 38 | /// 39 | /// ATR of the inserted card. 40 | /// 41 | public unsafe fixed byte Atr[33]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardReaderState.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// This structure is used by functions for tracking smart cards within readers. 5 | /// (For WinSCard in Windows.) 6 | /// 7 | public struct SCardReaderState 8 | { 9 | /// 10 | /// A pointer to the name of the reader being monitored. 11 | /// Set the value of this member to "\\\\?PnP?\\Notification" and the values of all other members to zero to be notified of the arrival of a new smart card reader. 12 | /// 13 | public unsafe void* Reader; 14 | 15 | /// 16 | /// Not used by the smart card subsystem. 17 | /// This member is used by the application. 18 | /// 19 | public unsafe void* UserData; 20 | 21 | /// 22 | /// Current state of the reader, as seen by the application, combination, as a bitmask. 23 | /// 24 | public SCardReaderStates CurrentState; 25 | 26 | /// 27 | /// Current state of the reader, as known by the smart card resource manager, combination, as a bitmask. 28 | /// 29 | public SCardReaderStates EventState; 30 | 31 | /// 32 | /// Number of bytes in the returned `ATR`. 33 | /// 34 | public int AtrLength; 35 | 36 | /// 37 | /// ATR of the inserted card, with extra alignment bytes. 38 | /// 39 | public unsafe fixed byte Atr[36]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardProtocols.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet 4 | { 5 | /// 6 | /// Acceptable protocols for the connection. 7 | /// 8 | [Flags] 9 | public enum SCardProtocols : uint 10 | { 11 | /// 12 | /// Use the default transmission parameters and card clock frequency. 13 | /// (For WinSCard in Windows platform.) 14 | /// 15 | Default = 0x80000000, 16 | /// 17 | /// Raw is the active protocol. 18 | /// (For WinSCard in Windows platform.) 19 | /// 20 | Raw = 0x00010000, 21 | /// 22 | /// Raw is the active protocol, use with memory type cards. 23 | /// (For pcsc-lite in Linux and OS X platforms.) 24 | /// 25 | RawPcscLite = 0x00000004, 26 | /// 27 | /// T=0 is the active protocol. 28 | /// 29 | T0 = 0x00000001, 30 | /// 31 | /// T=1 is the active protocol. 32 | /// 33 | T1 = 0x00000002, 34 | /// 35 | /// T=15 is the active protocol. 36 | /// (For pcsc-lite in Linux and OS X platforms.) 37 | /// 38 | T15 = 0x00000008, 39 | /// 40 | /// This is the mask of ISO defined transmission protocols. 41 | /// 42 | Tx = T0 | T1, 43 | /// 44 | /// There is no active protocol. 45 | /// 46 | Undefined = 0x00000000 47 | } 48 | } -------------------------------------------------------------------------------- /src/PcscDotNet/SCardReaderStateOSX.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace PcscDotNet 5 | { 6 | /// 7 | /// This structure is used by functions for tracking smart cards within readers. 8 | /// (For pcsc-lite in OS X.) 9 | /// 10 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 11 | public struct SCardReaderStateOSX 12 | { 13 | /// 14 | /// A pointer to the name of the reader being monitored. 15 | /// Set the value of this member to "\\\\?PnP?\\Notification" and the values of all other members to zero to be notified of the arrival of a new smart card reader. 16 | /// 17 | public unsafe void* Reader; 18 | 19 | /// 20 | /// Not used by the smart card subsystem. 21 | /// This member is used by the application. 22 | /// 23 | public unsafe void* UserData; 24 | 25 | /// 26 | /// Current state of the reader, as seen by the application, combination, as a bitmask. 27 | /// 28 | public SCardReaderStates CurrentState; 29 | 30 | /// 31 | /// Current state of the reader, as known by the smart card resource manager, combination, as a bitmask. 32 | /// 33 | public SCardReaderStates EventState; 34 | 35 | /// 36 | /// Number of bytes in the returned `ATR`. 37 | /// 38 | public int AtrLength; 39 | 40 | /// 41 | /// ATR of the inserted card. 42 | /// 43 | public unsafe fixed byte Atr[33]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardReaderGroupExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// Extension methods of SCardReaderGroup. 5 | /// 6 | public static class SCardReaderGroupExtensions 7 | { 8 | /// 9 | /// Defined value of SCardReaderGroup.All. 10 | /// 11 | private const string All = "SCard$AllReaders\0"; 12 | 13 | /// 14 | /// Defined value of SCardReaderGroup.Default. 15 | /// 16 | private const string Default = "SCard$DefaultReaders\0"; 17 | 18 | /// 19 | /// Defined value of SCardReaderGroup.Local. 20 | /// 21 | private const string Local = "SCard$LocalReaders\0"; 22 | 23 | /// 24 | /// Defined value of SCardReaderGroup.System. 25 | /// 26 | private const string System = "SCard$SystemReaders\0"; 27 | 28 | /// 29 | /// Gets defined value for specific SCardReaderGroup. 30 | /// 31 | /// SCardReaderGroup value. 32 | /// Defined value. 33 | public static string GetDefinedValue(this SCardReaderGroup group) 34 | { 35 | switch (group) 36 | { 37 | case SCardReaderGroup.All: 38 | return All; 39 | case SCardReaderGroup.Defualt: 40 | return Default; 41 | case SCardReaderGroup.Local: 42 | return Local; 43 | case SCardReaderGroup.System: 44 | return System; 45 | default: 46 | return null; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/PcscDotNet/Pcsc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PcscDotNet 5 | { 6 | public class Pcsc 7 | { 8 | public IPcscProvider Provider { get; private set; } 9 | 10 | public Pcsc(IPcscProvider provider) 11 | { 12 | Provider = provider; 13 | } 14 | 15 | public PcscContext CreateContext() 16 | { 17 | return new PcscContext(Provider); 18 | } 19 | 20 | public PcscContext EstablishContext(SCardScope scope, PcscExceptionHandler onException = null) 21 | { 22 | return CreateContext().Establish(scope, onException); 23 | } 24 | 25 | public IEnumerable GetReaderGroupNames(PcscExceptionHandler onException = null) 26 | { 27 | var groupNames = Provider.GetReaderGroupNames(SCardContext.Default, onException); 28 | if (groupNames == null) yield break; 29 | for (int offset = 0, offsetNull, length = groupNames.Length; ;) 30 | { 31 | if (offset >= length || (offsetNull = groupNames.IndexOf('\0', offset)) <= offset) yield break; 32 | yield return groupNames.Substring(offset, offsetNull - offset); 33 | offset = offsetNull + 1; 34 | } 35 | } 36 | 37 | public IEnumerable GetReaderNames(SCardReaderGroup group = SCardReaderGroup.NotSpecified, PcscExceptionHandler onException = null) 38 | { 39 | return GetReaderNames(group.GetDefinedValue(), onException); 40 | } 41 | 42 | public IEnumerable GetReaderNames(string group, PcscExceptionHandler onException = null) 43 | { 44 | var readerNames = Provider.GetReaderNames(SCardContext.Default, group, onException); 45 | if (readerNames == null) yield break; 46 | for (int offset = 0, offsetNull, length = readerNames.Length; ;) 47 | { 48 | if (offset >= length || (offsetNull = readerNames.IndexOf('\0', offset)) <= offset) yield break; 49 | yield return readerNames.Substring(offset, offsetNull - offset); 50 | offset = offsetNull + 1; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardControlFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet 4 | { 5 | /// 6 | /// Reader action I/O functions. 7 | /// 8 | public enum SCardControlFunction 9 | { 10 | /// 11 | /// IOCTL_CCID_ESCAPE. 12 | /// 13 | CcidEscape = 3500, 14 | /// 15 | /// IOCTL_SMARTCARD_CONFISCATE. 16 | /// 17 | SmartCardConfiscate = 4, 18 | /// 19 | /// IOCTL_SMARTCARD_EJECT. 20 | /// 21 | SmartCardEject = 6, 22 | /// 23 | /// IOCTL_SMARTCARD_GET_ATTRIBUTE. 24 | /// 25 | SmartCardGetAttribute = 2, 26 | /// 27 | /// IOCTL_SMARTCARD_GET_FEATURE_REQUEST. 28 | /// 29 | SmartCardGetFeatureRequest = 3400, 30 | /// 31 | /// IOCTL_SMARTCARD_GET_LAST_ERROR. 32 | /// 33 | SmartCardGetLastError = 15, 34 | /// 35 | /// IOCTL_SMARTCARD_GET_PERF_CNTR. 36 | /// 37 | SmartCardGetPerfCntr = 16, 38 | /// 39 | /// IOCTL_SMARTCARD_GET_STATE. 40 | /// 41 | SmartCardGetState = 14, 42 | /// 43 | /// IOCTL_SMARTCARD_IS_ABSENT. 44 | /// 45 | SmartCardIsAbsent = 11, 46 | /// 47 | /// IOCTL_SMARTCARD_IS_PRESENT. 48 | /// 49 | SmartCardIsPresent = 10, 50 | /// 51 | /// IOCTL_SMARTCARD_POWER. 52 | /// 53 | SmartCardPower = 1, 54 | /// 55 | /// IOCTL_SMARTCARD_READ. 56 | /// 57 | [Obsolete] 58 | SmartCardRead = 8, 59 | /// 60 | /// IOCTL_SMARTCARD_SET_ATTRIBUTE. 61 | /// 62 | SmartCardSetAttribute = 3, 63 | /// 64 | /// IOCTL_SMARTCARD_SET_PROTOCOL. 65 | /// 66 | SmartCardSetProtocol = 12, 67 | /// 68 | /// IOCTL_SMARTCARD_SWALLOW. 69 | /// 70 | SmartCardSwallow = 7, 71 | /// 72 | /// IOCTL_SMARTCARD_TRANSMIT. 73 | /// 74 | SmartCardTransmit = 5, 75 | /// 76 | /// IOCTL_SMARTCARD_WRITE. 77 | /// 78 | [Obsolete] 79 | SmartCardWrite = 9 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/PcscDotNet/IPcscProviderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// Extension methods of `IPcscProvider` interface. 5 | /// 6 | public static class IPcscProviderExtensions 7 | { 8 | public unsafe static string GetReaderGroupNames(this IPcscProvider provider, SCardContext handle, PcscExceptionHandler onException) 9 | { 10 | byte* pGroupNames = null; 11 | var charCount = PcscProvider.SCardAutoAllocate; 12 | try 13 | { 14 | provider.SCardListReaderGroups(handle, &pGroupNames, &charCount).ThrowIfNotSuccess(onException); 15 | return provider.AllocateString(pGroupNames, charCount); 16 | } 17 | finally 18 | { 19 | if (pGroupNames != null) provider.SCardFreeMemory(handle, pGroupNames).ThrowIfNotSuccess(onException); 20 | } 21 | } 22 | 23 | public unsafe static string GetReaderNames(this IPcscProvider provider, SCardContext handle, string group, PcscExceptionHandler onException) 24 | { 25 | string readerNames = null; 26 | byte* pReaderNames; 27 | var charCount = PcscProvider.SCardAutoAllocate; 28 | var err = provider.SCardListReaders(handle, group, &pReaderNames, &charCount); 29 | try 30 | { 31 | switch (err) 32 | { 33 | case SCardError.NoReadersAvailable: 34 | // In Windows, it seems to still return a `NULL` character with `SCardError.Success` status even none of reader names is found. 35 | break; 36 | case SCardError.Successs: 37 | /* 38 | Providers can use ANSI (e.g., WinSCard and pcsc-lite) or Unicode (e.g., WinSCard) for the encoding of characters. 39 | In ANSI, `charCount` means the byte count of string. 40 | In Unicode, `charCount` means the number of Unicode characters of string. 41 | */ 42 | readerNames = provider.AllocateString(pReaderNames, charCount); 43 | break; 44 | default: 45 | err.Throw(onException); 46 | break; 47 | } 48 | } 49 | finally 50 | { 51 | if (pReaderNames != null) provider.SCardFreeMemory(handle, pReaderNames).ThrowIfNotSuccess(onException); 52 | } 53 | return readerNames; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/PcscDotNet/IPcscProvider.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | public interface IPcscProvider 4 | { 5 | byte[] AllocateIORequest(int informationLength); 6 | 7 | byte[] AllocateReaderStates(int count); 8 | 9 | unsafe void* AllocateString(string value); 10 | 11 | unsafe string AllocateString(void* ptr, int length); 12 | 13 | unsafe void FreeString(void* ptr); 14 | 15 | unsafe void ReadIORequest(void* pIORequest, out SCardProtocols protocol, out byte[] information); 16 | 17 | unsafe void ReadReaderState(void* pReaderStates, int index, out void* pReaderName, out SCardReaderStates currentState, out SCardReaderStates eventState, out byte[] atr); 18 | 19 | SCardError SCardBeginTransaction(SCardHandle hCard); 20 | 21 | SCardError SCardCancel(SCardContext hContext); 22 | 23 | unsafe SCardError SCardConnect(SCardContext hContext, string szReader, SCardShare dwShareMode, SCardProtocols dwPreferredProtocols, SCardHandle* phCard, SCardProtocols* pdwActiveProtocol); 24 | 25 | unsafe SCardError SCardControl(SCardHandle hCard, int dwControlCode, void* lpInBuffer, int nInBufferSize, void* lpOutBuffer, int nOutBufferSize, int* lpBytesReturned); 26 | 27 | int SCardCtlCode(SCardControlFunction function); 28 | 29 | SCardError SCardDisconnect(SCardHandle hCard, SCardDisposition dwDisposition); 30 | 31 | SCardError SCardEndTransaction(SCardHandle hCard, SCardDisposition dwDisposition); 32 | 33 | unsafe SCardError SCardEstablishContext(SCardScope dwScope, void* pvReserved1, void* pvReserved2, SCardContext* phContext); 34 | 35 | unsafe SCardError SCardFreeMemory(SCardContext hContext, void* pvMem); 36 | 37 | unsafe SCardError SCardGetStatusChange(SCardContext hContext, int dwTimeout, void* rgReaderStates, int cReaders); 38 | 39 | SCardError SCardIsValidContext(SCardContext hContext); 40 | 41 | unsafe SCardError SCardListReaderGroups(SCardContext hContext, void* mszGroups, int* pcchGroups); 42 | 43 | unsafe SCardError SCardListReaders(SCardContext hContext, string mszGroups, void* mszReaders, int* pcchReaders); 44 | 45 | unsafe SCardError SCardReconnect(SCardHandle hCard, SCardShare dwShareMode, SCardProtocols dwPreferredProtocols, SCardDisposition dwInitialization, SCardProtocols* pdwActiveProtocol); 46 | 47 | SCardError SCardReleaseContext(SCardContext hContext); 48 | 49 | unsafe SCardError SCardTransmit(SCardHandle hCard, void* pioSendPci, byte* pbSendBuffer, int cbSendLength, void* pioRecvPci, byte* pbRecvBuffer, int* pcbRecvLength); 50 | 51 | unsafe void WriteIORequest(void* pIORequest, SCardProtocols protocol, int totalLength, byte[] information); 52 | 53 | unsafe void WriteReaderState(void* pReaderStates, int index, SCardReaderStates currentState); 54 | 55 | unsafe void WriteReaderState(void* pReaderStates, int index, void* pReaderName); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardReaderStates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet 4 | { 5 | /// 6 | /// Reader states. 7 | /// 8 | [Flags] 9 | public enum SCardReaderStates 10 | { 11 | /// 12 | /// This implies that there is a card in the reader with an ATR matching one of the target cards. 13 | /// If this bit is set, `Present` will also be set. 14 | /// This bit is only returned on the `SCardLocateCard()` service. 15 | /// 16 | AtrMatch = 0x00000040, 17 | /// 18 | /// This implies that there is a difference between the state believed by the application, and the state known by the Service Manager. 19 | /// When this bit is set, the application may assume a significant state change has occurred on this reader. 20 | /// 21 | Changed = 0x00000002, 22 | /// 23 | /// This implies that there is not card in the reader. 24 | /// If this bit is set, all the following bits will be clear. 25 | /// 26 | Empty = 0x00000010, 27 | /// 28 | /// This implies that the card in the reader is allocated for exclusive use by another application. 29 | /// If this bit is set, `Present` will also be set. 30 | /// 31 | Exclusive = 0x00000080, 32 | /// 33 | /// The application requested that this reader be ignored. 34 | /// No other bits will be set. 35 | /// 36 | Ignore = 0x00000001, 37 | /// 38 | /// This implies that the card in the reader is in use by one or more other applications, but may be connected to in shared mode. 39 | /// If this bit is set, `Present` will also be set. 40 | /// 41 | InUse = 0x00000100, 42 | /// 43 | /// This implies that the card in the reader is unresponsive or not supported by the reader or software. 44 | /// 45 | Mute = 0x00000200, 46 | /// 47 | /// This implies that there is a card in the reader. 48 | /// 49 | Present = 0x00000020, 50 | /// 51 | /// This implies that the actual state of this reader is not available. 52 | /// If this bit is set, then all the following bits are clear. 53 | /// 54 | Unavailable = 0x00000008, 55 | /// 56 | /// The application is unaware of the current state, and would like to know. 57 | /// The use of this value results in an immediate return from state transition monitoring services. 58 | /// This is represented by all bits set to zero. 59 | /// 60 | Unaware = 0x00000000, 61 | /// 62 | /// This implies that the given reader name is not recognized by the Service Manager. 63 | /// If this bit is set, then `Changed` and `Ignore` will also be set. 64 | /// 65 | Unknown = 0x00000004, 66 | /// 67 | /// This implies that the card in the reader has not been powered up. 68 | /// 69 | Unpowered = 0x00000400 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/PcscDotNet/PcscReaderStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Threading; 5 | 6 | namespace PcscDotNet 7 | { 8 | public delegate void PcscReaderStatusAction(PcscReaderStatus readerStatus); 9 | 10 | public sealed class PcscReaderStatus : ReadOnlyCollection 11 | { 12 | /// 13 | /// This special reader name is used to be notified when a reader is added to or removed from the system through `SCardGetStatusChange`. 14 | /// 15 | public const string PnPReaderName = "\\\\?PnP?\\Notification"; 16 | 17 | private readonly byte[] _readerStates; 18 | 19 | public PcscContext Context { get; private set; } 20 | 21 | public IPcscProvider Provider { get; private set; } 22 | 23 | public PcscReaderStatus(PcscContext context, IEnumerable readerNames) : base(new List()) 24 | { 25 | Context = context; 26 | foreach (var readerName in readerNames) 27 | { 28 | Items.Add(new PcscReaderState(readerName)); 29 | } 30 | _readerStates = (Provider = context.Provider).AllocateReaderStates(Items.Count); 31 | } 32 | 33 | public void Cancel(PcscExceptionHandler onException = null) 34 | { 35 | Context.Cancel(onException); 36 | } 37 | 38 | public PcscReaderStatus Do(PcscReaderStatusAction action) 39 | { 40 | action(this); 41 | return this; 42 | } 43 | 44 | public unsafe PcscReaderStatus WaitForChanged(int timeout = Timeout.Infinite, PcscExceptionHandler onException = null) 45 | { 46 | if (Context.IsDisposed) throw new ObjectDisposedException(nameof(PcscContext), nameof(WaitForChanged)); 47 | fixed (byte* fReaderStates = _readerStates) 48 | { 49 | var pReaderStates = fReaderStates; 50 | try 51 | { 52 | for (var i = 0; i < Count; ++i) 53 | { 54 | Provider.WriteReaderState(pReaderStates, i, Provider.AllocateString(Items[i].ReaderName)); 55 | } 56 | Provider.SCardGetStatusChange(Context.Handle, timeout, pReaderStates, Count).ThrowIfNotSuccess(onException); 57 | } 58 | finally 59 | { 60 | for (var i = 0; i < Count; ++i) 61 | { 62 | Provider.ReadReaderState(pReaderStates, i, out void* pReaderName, out SCardReaderStates currentState, out SCardReaderStates eventState, out byte[] atr); 63 | Provider.WriteReaderState(pReaderStates, i, eventState); 64 | var readerState = Items[i]; 65 | readerState.Atr = atr; 66 | readerState.EventNumber = ((int)eventState >> 16) & 0x0000FFFF; 67 | readerState.State = eventState & (SCardReaderStates)0x0000FFFF; 68 | Provider.FreeString(pReaderName); 69 | } 70 | } 71 | } 72 | return this; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /PcscDotNet.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{01AA272B-63C6-4365-94C5-A363AC4D293A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcscDotNet", "src\PcscDotNet\PcscDotNet.csproj", "{CF60120D-776A-4E80-B9D9-9DB62E2015B8}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{3B8817AD-C3F0-4883-BE90-D3437CD3CAEA}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcscDotNet.ConsoleTests", "tests\PcscDotNet.ConsoleTests\PcscDotNet.ConsoleTests.csproj", "{49B7AD05-4C80-48D9-8150-361E1C9A05CE}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Debug|x64.ActiveCfg = Debug|x64 30 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Debug|x64.Build.0 = Debug|x64 31 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Debug|x86.ActiveCfg = Debug|x86 32 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Debug|x86.Build.0 = Debug|x86 33 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Release|x64.ActiveCfg = Release|x64 36 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Release|x64.Build.0 = Release|x64 37 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Release|x86.ActiveCfg = Release|x86 38 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8}.Release|x86.Build.0 = Release|x86 39 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Debug|x64.ActiveCfg = Debug|x64 42 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Debug|x64.Build.0 = Debug|x64 43 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Debug|x86.ActiveCfg = Debug|x86 44 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Debug|x86.Build.0 = Debug|x86 45 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Release|x64.ActiveCfg = Release|x64 48 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Release|x64.Build.0 = Release|x64 49 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Release|x86.ActiveCfg = Release|x86 50 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE}.Release|x86.Build.0 = Release|x86 51 | EndGlobalSection 52 | GlobalSection(NestedProjects) = preSolution 53 | {CF60120D-776A-4E80-B9D9-9DB62E2015B8} = {01AA272B-63C6-4365-94C5-A363AC4D293A} 54 | {49B7AD05-4C80-48D9-8150-361E1C9A05CE} = {3B8817AD-C3F0-4883-BE90-D3437CD3CAEA} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PcscDotNet 2 | 3 | .NET standard library for accessing *PC/SC* (*Personal Computer/Smart Card*) functions. 4 | 5 | > This library provides the generic way for accessing *PC/SC* functions, you can write your specific provider with [`IPcscProvider`][] interface implementation. 6 | 7 | --- 8 | 9 | ## Projects 10 | 11 | - [src/PcscDotNet](src/PcscDotNet) 12 | > *PcscDotNet* library. 13 | - [tests/PcscDotNet.ConsoleTests](tests/PcscDotNet.ConsoleTests) 14 | > Console tests. 15 | 16 | --- 17 | 18 | ## Introduction 19 | 20 | The implementations of *PC/SC* enumerations and structures and the declarations of *PC/SC* methods, both are referenced from *Windows Kit*, *MSDN* and *pcsc-lite*. 21 | 22 | The main interfaces/classes: 23 | 24 | - [`IPcscProvider`][] Interface 25 | - [`Pcsc`][] Class 26 | - [`Pcsc`][] Class 27 | - [`WinSCard`][] Class 28 | 29 | [`IPcscProvider`]: #ipcscprovider-interface 30 | [`Pcsc`]: #pcsc-class 31 | [`Pcsc`]: #pcsctipcscprovider-class 32 | [`WinSCard`]: #winscard-class 33 | 34 | ### [IPcscProvider Interface](src/PcscDotNet/IPcscProvider.cs "Go to Source") 35 | 36 | This interface declares the members that need to be implemented for accessing *PC/SC* functions. 37 | 38 | These are the methods declared with the same name of *PC/SC* functions currently: 39 | 40 | - `SCardBeginTransaction` 41 | - `SCardCancel` 42 | - `SCardConnect` 43 | - `SCardControl` 44 | - `SCardDisconnect` 45 | - `SCardEndTransaction` 46 | - `SCardEstablishContext` 47 | - `SCardFreeMemory` 48 | - `SCardGetStatusChange` 49 | - `SCardIsValidContext` 50 | - `SCardListReaderGroups` 51 | - `SCardListReaders` 52 | - `SCardReconnect` 53 | - `SCardReleaseContext` 54 | - `SCardTransmit` 55 | 56 | Other methods: 57 | 58 | - `AllocateIORequest` 59 | > Allocates managed byte array which mapped to unmanaged `SCARD_IO_REQUEST` structure. 60 | - `AllocateReaderStates` 61 | > Allocates managed byte array which mapped to unmanaged array of `SCARD_READERSTATE` structure. 62 | - `AllocateString` 63 | > Allocates string in managed or unmanaged memory. 64 | - `FreeString` 65 | > Releases the unmanaged memory which allocated by `AllocateString`. 66 | - `ReadIORequest` 67 | > Reads values from `SCARD_IO_REQUEST` which allocated by `AllocateIORequest` method. 68 | - `ReadReaderState` 69 | > Reads values from the specific index of the `SCARD_READERSTATE` array which allocated by `AllocateReaderStates` method. 70 | - `SCardCtlCode` 71 | > Gets reader action I/O control codes. 72 | - `WriteIORequest` 73 | > Writes values to `SCARD_IO_REQUEST` which allocated by `AllocateIORequest` method. 74 | - `WriteReaderState` 75 | > Writes values to the specific index of the `SCARD_READERSTATE` array which allocated by `AllocateReaderStates` method. 76 | 77 | If you want to implement your provider using `WinSCard` or `pcsc-lite`, see the table below, it shows the different definitions between platforms: 78 | 79 | | Unmanaged Defined | * | `WinSCard` (*Windows*) | `pcsc-lite` (*OS X*) | `pcsc-lite` (*Linux*) | 80 | | -------------------------- | -------- | -------------------------------------- | -------------------- | --------------------- | 81 | | `SCARD_CTL_CODE(code)` | Define | 0x00310000 \| (code << 2) | `0x42000000 + code` | `0x42000000 + code` | 82 | | *ANSI* Characters | Encoding | System Locale | *UTF-8* | *UTF-8* | 83 | | *Unicode* Characters | Encoding | *Unicode* | - | - | 84 | | `SCARD_READERSTATE` | Pack | - | `1` | - | 85 | | `DWORD` | Size | `4` | `4` | `sizeof(void*)` | 86 | | `LONG` | Size | `4` | `4` | `sizeof(void*)` | 87 | | `SCARD_READERSTATE.rgbAtr` | Size | `36` | `33` | `33` | 88 | | `SCARD_PROTOCOL_RAW` | Value | `0x00010000` | `0x04` | `0x04` | 89 | 90 | ### [Pcsc Class](src/PcscDotNet/Pcsc.cs "Go to Source") 91 | 92 | This class is the start point for accessing *PC/SC* functions. You need to specify [`IPcscProvider`][] instance to create [`Pcsc`][] instance. 93 | 94 | ### [Pcsc\ Class](src/PcscDotNet/Pcsc_1.cs "Go to Source") 95 | 96 | This class provides the static members with corresponding members in [`Pcsc`][] class, using singleton instance of `TIPcscProvider` which implements [`IPcscProvider`][] interface. 97 | 98 | > Most of time, `TIPcscProvider` can be used with singleton safely unless its members may be changed, e.g., the provider loads functions from different library dynamically. 99 | 100 | #### [WinSCard Class](src/PcscDotNet/WinSCard.cs "Go to Source") 101 | 102 | This class implements [`IPcscProvider`][] using `WinSCard.dll` of *Windows* environment (*Unicode*). 103 | 104 | --- 105 | 106 | Archie Yang 107 | -------------------------------------------------------------------------------- /src/PcscDotNet/PcscContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PcscDotNet 5 | { 6 | public class PcscContext : IDisposable 7 | { 8 | public SCardContext Handle { get; private set; } 9 | 10 | public bool IsDisposed { get; private set; } = false; 11 | 12 | public bool IsEstablished => Handle.HasValue; 13 | 14 | public IPcscProvider Provider { get; private set; } 15 | 16 | public PcscContext(IPcscProvider provider) 17 | { 18 | Provider = provider; 19 | } 20 | 21 | ~PcscContext() 22 | { 23 | Dispose(false); 24 | } 25 | 26 | public void Cancel(PcscExceptionHandler onException = null) 27 | { 28 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscContext), nameof(Cancel)); 29 | Provider.SCardCancel(Handle).ThrowIfNotSuccess(onException); 30 | } 31 | 32 | public PcscConnection Connect(string readerName, SCardShare shareMode, SCardProtocols protocols, PcscExceptionHandler onException = null) 33 | { 34 | return CreateConnection(readerName).Connect(shareMode, protocols, onException); 35 | } 36 | 37 | public PcscConnection CreateConnection(string readerName) 38 | { 39 | return new PcscConnection(this, readerName); 40 | } 41 | 42 | public void Dispose() 43 | { 44 | Dispose(true); 45 | } 46 | 47 | public unsafe PcscContext Establish(SCardScope scope, PcscExceptionHandler onException = null) 48 | { 49 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscContext), nameof(Establish)); 50 | if (IsEstablished) throw new InvalidOperationException("Context has been established."); 51 | SCardContext handle; 52 | Provider.SCardEstablishContext(scope, null, null, &handle).ThrowIfNotSuccess(onException); 53 | Handle = handle; 54 | return this; 55 | } 56 | 57 | public IEnumerable GetReaderGroupNames(PcscExceptionHandler onException = null) 58 | { 59 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscContext), nameof(GetReaderGroupNames)); 60 | var groupNames = Provider.GetReaderGroupNames(Handle, onException); 61 | if (groupNames == null) yield break; 62 | for (int offset = 0, offsetNull, length = groupNames.Length; ;) 63 | { 64 | if (offset >= length || (offsetNull = groupNames.IndexOf('\0', offset)) <= offset) yield break; 65 | yield return groupNames.Substring(offset, offsetNull - offset); 66 | offset = offsetNull + 1; 67 | } 68 | } 69 | 70 | public IEnumerable GetReaderNames(SCardReaderGroup group = SCardReaderGroup.NotSpecified, PcscExceptionHandler onException = null) 71 | { 72 | return GetReaderNames(group.GetDefinedValue(), onException); 73 | } 74 | 75 | public IEnumerable GetReaderNames(string group, PcscExceptionHandler onException = null) 76 | { 77 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscContext), nameof(GetReaderNames)); 78 | var readerNames = Provider.GetReaderNames(Handle, group, onException); 79 | if (readerNames == null) yield break; 80 | for (int offset = 0, offsetNull, length = readerNames.Length; ;) 81 | { 82 | if (offset >= length || (offsetNull = readerNames.IndexOf('\0', offset)) <= offset) yield break; 83 | yield return readerNames.Substring(offset, offsetNull - offset); 84 | offset = offsetNull + 1; 85 | } 86 | } 87 | 88 | public PcscReaderStatus GetStatus(params string[] readerNames) 89 | { 90 | return new PcscReaderStatus(this, readerNames).WaitForChanged(); 91 | } 92 | 93 | public PcscReaderStatus GetStatus(IEnumerable readerNames, PcscExceptionHandler onException = null) 94 | { 95 | return new PcscReaderStatus(this, readerNames).WaitForChanged(onException: onException); 96 | } 97 | 98 | public PcscContext Release(PcscExceptionHandler onException = null) 99 | { 100 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscContext), nameof(Release)); 101 | ReleaseInternal(onException); 102 | return this; 103 | } 104 | 105 | public PcscContext Validate(PcscExceptionHandler onException = null) 106 | { 107 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscContext), nameof(Validate)); 108 | Provider.SCardIsValidContext(Handle).ThrowIfNotSuccess(onException); 109 | return this; 110 | } 111 | 112 | private void Dispose(bool isSuppressFinalize) 113 | { 114 | if (!IsDisposed) 115 | { 116 | ReleaseInternal(); 117 | IsDisposed = true; 118 | } 119 | if (isSuppressFinalize) GC.SuppressFinalize(this); 120 | } 121 | 122 | private void ReleaseInternal(PcscExceptionHandler onException = null) 123 | { 124 | if (!IsEstablished) return; 125 | Provider.SCardReleaseContext(Handle).ThrowIfNotSuccess(onException); 126 | Handle = SCardContext.Default; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # Benchmark Results 46 | BenchmarkDotNet.Artifacts/ 47 | 48 | # .NET Core 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | **/Properties/launchSettings.json 53 | 54 | *_i.c 55 | *_p.c 56 | *_i.h 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.svclog 77 | *.scc 78 | 79 | # Chutzpah Test files 80 | _Chutzpah* 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opendb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | *.VC.db 91 | *.VC.VC.opendb 92 | 93 | # Visual Studio profiler 94 | *.psess 95 | *.vsp 96 | *.vspx 97 | *.sap 98 | 99 | # TFS 2012 Local Workspace 100 | $tf/ 101 | 102 | # Guidance Automation Toolkit 103 | *.gpState 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper*/ 107 | *.[Rr]e[Ss]harper 108 | *.DotSettings.user 109 | 110 | # JustCode is a .NET coding add-in 111 | .JustCode 112 | 113 | # TeamCity is a build add-in 114 | _TeamCity* 115 | 116 | # DotCover is a Code Coverage Tool 117 | *.dotCover 118 | 119 | # AxoCover is a Code Coverage Tool 120 | .axoCover/* 121 | !.axoCover/settings.json 122 | 123 | # Visual Studio code coverage results 124 | *.coverage 125 | *.coveragexml 126 | 127 | # NCrunch 128 | _NCrunch_* 129 | .*crunch*.local.xml 130 | nCrunchTemp_* 131 | 132 | # MightyMoose 133 | *.mm.* 134 | AutoTest.Net/ 135 | 136 | # Web workbench (sass) 137 | .sass-cache/ 138 | 139 | # Installshield output folder 140 | [Ee]xpress/ 141 | 142 | # DocProject is a documentation generator add-in 143 | DocProject/buildhelp/ 144 | DocProject/Help/*.HxT 145 | DocProject/Help/*.HxC 146 | DocProject/Help/*.hhc 147 | DocProject/Help/*.hhk 148 | DocProject/Help/*.hhp 149 | DocProject/Help/Html2 150 | DocProject/Help/html 151 | 152 | # Click-Once directory 153 | publish/ 154 | 155 | # Publish Web Output 156 | *.[Pp]ublish.xml 157 | *.azurePubxml 158 | # Note: Comment the next line if you want to checkin your web deploy settings, 159 | # but database connection strings (with potential passwords) will be unencrypted 160 | *.pubxml 161 | *.publishproj 162 | 163 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 164 | # checkin your Azure Web App publish settings, but sensitive information contained 165 | # in these scripts will be unencrypted 166 | PublishScripts/ 167 | 168 | # NuGet Packages 169 | *.nupkg 170 | # The packages folder can be ignored because of Package Restore 171 | **/packages/* 172 | # except build/, which is used as an MSBuild target. 173 | !**/packages/build/ 174 | # Uncomment if necessary however generally it will be regenerated when needed 175 | #!**/packages/repositories.config 176 | # NuGet v3's project.json files produces more ignorable files 177 | *.nuget.props 178 | *.nuget.targets 179 | 180 | # Microsoft Azure Build Output 181 | csx/ 182 | *.build.csdef 183 | 184 | # Microsoft Azure Emulator 185 | ecf/ 186 | rcf/ 187 | 188 | # Windows Store app package directories and files 189 | AppPackages/ 190 | BundleArtifacts/ 191 | Package.StoreAssociation.xml 192 | _pkginfo.txt 193 | *.appx 194 | 195 | # Visual Studio cache files 196 | # files ending in .cache can be ignored 197 | *.[Cc]ache 198 | # but keep track of directories ending in .cache 199 | !*.[Cc]ache/ 200 | 201 | # Others 202 | ClientBin/ 203 | ~$* 204 | *~ 205 | *.dbmdl 206 | *.dbproj.schemaview 207 | *.jfm 208 | *.pfx 209 | *.publishsettings 210 | orleans.codegen.cs 211 | 212 | # Since there are multiple workflows, uncomment next line to ignore bower_components 213 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 214 | #bower_components/ 215 | 216 | # RIA/Silverlight projects 217 | Generated_Code/ 218 | 219 | # Backup & report files from converting an old project file 220 | # to a newer Visual Studio version. Backup files are not needed, 221 | # because we have git ;-) 222 | _UpgradeReport_Files/ 223 | Backup*/ 224 | UpgradeLog*.XML 225 | UpgradeLog*.htm 226 | 227 | # SQL Server files 228 | *.mdf 229 | *.ldf 230 | *.ndf 231 | 232 | # Business Intelligence projects 233 | *.rdl.data 234 | *.bim.layout 235 | *.bim_*.settings 236 | 237 | # Microsoft Fakes 238 | FakesAssemblies/ 239 | 240 | # GhostDoc plugin setting file 241 | *.GhostDoc.xml 242 | 243 | # Node.js Tools for Visual Studio 244 | .ntvs_analysis.dat 245 | node_modules/ 246 | 247 | # Typescript v1 declaration files 248 | typings/ 249 | 250 | # Visual Studio 6 build log 251 | *.plg 252 | 253 | # Visual Studio 6 workspace options file 254 | *.opt 255 | 256 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 257 | *.vbw 258 | 259 | # Visual Studio LightSwitch build output 260 | **/*.HTMLClient/GeneratedArtifacts 261 | **/*.DesktopClient/GeneratedArtifacts 262 | **/*.DesktopClient/ModelManifest.xml 263 | **/*.Server/GeneratedArtifacts 264 | **/*.Server/ModelManifest.xml 265 | _Pvt_Extensions 266 | 267 | # Paket dependency manager 268 | .paket/paket.exe 269 | paket-files/ 270 | 271 | # FAKE - F# Make 272 | .fake/ 273 | 274 | # JetBrains Rider 275 | .idea/ 276 | *.sln.iml 277 | 278 | # CodeRush 279 | .cr/ 280 | 281 | # Python Tools for Visual Studio (PTVS) 282 | __pycache__/ 283 | *.pyc 284 | 285 | # Cake - Uncomment if you are using it 286 | # tools/** 287 | # !tools/packages.config 288 | 289 | # Tabs Studio 290 | *.tss 291 | 292 | # Telerik's JustMock configuration file 293 | *.jmconfig 294 | 295 | # BizTalk build output 296 | *.btp.cs 297 | *.btm.cs 298 | *.odx.cs 299 | *.xsd.cs 300 | 301 | # OpenCover UI analysis results 302 | OpenCover/ 303 | 304 | .vscode/* 305 | !.vscode/settings.json 306 | !.vscode/tasks.json 307 | !.vscode/launch.json 308 | !.vscode/extensions.json 309 | -------------------------------------------------------------------------------- /src/PcscDotNet/WinSCard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace PcscDotNet 5 | { 6 | /// 7 | /// The PC/SC provider of WinSCard in Windows. 8 | /// 9 | public class WinSCard : IPcscProvider 10 | { 11 | /// 12 | /// The name of the DLL. 13 | /// 14 | public const string DllName = "WinSCard.dll"; 15 | 16 | [DllImport(DllName)] 17 | public static extern SCardError SCardBeginTransaction(SCardHandle hCard); 18 | 19 | [DllImport(DllName)] 20 | public static extern SCardError SCardCancel(SCardContext hContext); 21 | 22 | [DllImport(DllName, CharSet = CharSet.Unicode)] 23 | public unsafe static extern SCardError SCardConnect(SCardContext hContext, string szReader, SCardShare dwShareMode, SCardProtocols dwPreferredProtocols, SCardHandle* phCard, SCardProtocols* pdwActiveProtocol); 24 | 25 | [DllImport(DllName)] 26 | public unsafe static extern SCardError SCardControl(SCardHandle hCard, int dwControlCode, void* lpInBuffer, int nInBufferSize, void* lpOutBuffer, int nOutBufferSize, int* lpBytesReturned); 27 | 28 | [DllImport(DllName)] 29 | public static extern SCardError SCardDisconnect(SCardHandle hCard, SCardDisposition dwDisposition); 30 | 31 | [DllImport(DllName)] 32 | public static extern SCardError SCardEndTransaction(SCardHandle hCard, SCardDisposition dwDisposition); 33 | 34 | [DllImport(DllName)] 35 | public unsafe static extern SCardError SCardEstablishContext(SCardScope dwScope, void* pvReserved1, void* pvReserved2, SCardContext* phContext); 36 | 37 | [DllImport(DllName)] 38 | public unsafe static extern SCardError SCardFreeMemory(SCardContext hContext, void* pvMem); 39 | 40 | [DllImport(DllName, CharSet = CharSet.Unicode)] 41 | public unsafe static extern SCardError SCardGetStatusChange(SCardContext hContext, int dwTimeout, SCardReaderState* rgReaderStates, int cReaders); 42 | 43 | [DllImport(DllName)] 44 | public static extern SCardError SCardIsValidContext(SCardContext hContext); 45 | 46 | [DllImport(DllName, CharSet = CharSet.Unicode)] 47 | public unsafe static extern SCardError SCardListReaderGroups(SCardContext hContext, void* mszGroups, int* pcchGroups); 48 | 49 | [DllImport(DllName, CharSet = CharSet.Unicode)] 50 | public unsafe static extern SCardError SCardListReaders(SCardContext hContext, string mszGroups, void* mszReaders, int* pcchReaders); 51 | 52 | [DllImport(DllName)] 53 | public unsafe static extern SCardError SCardReconnect(SCardHandle hCard, SCardShare dwShareMode, SCardProtocols dwPreferredProtocols, SCardDisposition dwInitialization, SCardProtocols* pdwActiveProtocol); 54 | 55 | [DllImport(DllName)] 56 | public static extern SCardError SCardReleaseContext(SCardContext hContext); 57 | 58 | [DllImport(DllName)] 59 | public unsafe static extern SCardError SCardTransmit(SCardHandle hCard, SCardIORequest* pioSendPci, byte* pbSendBuffer, int cbSendLength, SCardIORequest* pioRecvPci, byte* pbRecvBuffer, int* pcbRecvLength); 60 | 61 | unsafe byte[] IPcscProvider.AllocateIORequest(int informationLength) 62 | { 63 | if (informationLength <= 0) return new byte[sizeof(SCardIORequest)]; 64 | var remain = (informationLength += sizeof(SCardIORequest)) % sizeof(void*); 65 | return new byte[remain == 0 ? informationLength : informationLength + sizeof(SCardIORequest) - remain]; 66 | } 67 | 68 | unsafe byte[] IPcscProvider.AllocateReaderStates(int count) 69 | { 70 | return new byte[count * sizeof(SCardReaderState)]; 71 | } 72 | 73 | unsafe void* IPcscProvider.AllocateString(string value) 74 | { 75 | return (void*)Marshal.StringToHGlobalUni(value); 76 | } 77 | 78 | unsafe string IPcscProvider.AllocateString(void* ptr, int length) 79 | { 80 | return Marshal.PtrToStringUni((IntPtr)ptr, length); 81 | } 82 | 83 | unsafe void IPcscProvider.FreeString(void* ptr) 84 | { 85 | Marshal.FreeHGlobal((IntPtr)ptr); 86 | } 87 | 88 | unsafe void IPcscProvider.ReadIORequest(void* pIORequest, out SCardProtocols protocol, out byte[] information) 89 | { 90 | var p = (SCardIORequest*)pIORequest; 91 | protocol = p->Protocol; 92 | var length = p->PciLength - sizeof(SCardIORequest); 93 | if (length <= 0) 94 | { 95 | information = null; 96 | } 97 | else 98 | { 99 | information = new byte[length]; 100 | Marshal.Copy((IntPtr)(p + 1), information, 0, length); 101 | } 102 | } 103 | 104 | unsafe void IPcscProvider.ReadReaderState(void* pReaderStates, int index, out void* pReaderName, out SCardReaderStates currentState, out SCardReaderStates eventState, out byte[] atr) 105 | { 106 | var pReaderState = ((SCardReaderState*)pReaderStates) + index; 107 | pReaderName = pReaderState->Reader; 108 | currentState = pReaderState->CurrentState; 109 | eventState = pReaderState->EventState; 110 | var atrLength = pReaderState->AtrLength; 111 | if (atrLength <= 0) 112 | { 113 | atr = null; 114 | } 115 | else 116 | { 117 | Marshal.Copy((IntPtr)pReaderState->Atr, atr = new byte[atrLength], 0, atrLength); 118 | } 119 | } 120 | 121 | SCardError IPcscProvider.SCardBeginTransaction(SCardHandle hCard) 122 | { 123 | return SCardBeginTransaction(hCard); 124 | } 125 | 126 | SCardError IPcscProvider.SCardCancel(SCardContext hContext) 127 | { 128 | return SCardCancel(hContext); 129 | } 130 | 131 | unsafe SCardError IPcscProvider.SCardConnect(SCardContext hContext, string szReader, SCardShare dwShareMode, SCardProtocols dwPreferredProtocols, SCardHandle* phCard, SCardProtocols* pdwActiveProtocol) 132 | { 133 | return SCardConnect(hContext, szReader, dwShareMode, dwPreferredProtocols, phCard, pdwActiveProtocol); 134 | } 135 | 136 | unsafe SCardError IPcscProvider.SCardControl(SCardHandle hCard, int dwControlCode, void* lpInBuffer, int nInBufferSize, void* lpOutBuffer, int nOutBufferSize, int* lpBytesReturned) 137 | { 138 | return SCardControl(hCard, dwControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned); 139 | } 140 | 141 | int IPcscProvider.SCardCtlCode(SCardControlFunction function) 142 | { 143 | return 0x00310000 | ((int)function << 2); 144 | } 145 | 146 | SCardError IPcscProvider.SCardDisconnect(SCardHandle hCard, SCardDisposition dwDisposition) 147 | { 148 | return SCardDisconnect(hCard, dwDisposition); 149 | } 150 | 151 | SCardError IPcscProvider.SCardEndTransaction(SCardHandle hCard, SCardDisposition dwDisposition) 152 | { 153 | return SCardEndTransaction(hCard, dwDisposition); 154 | } 155 | 156 | unsafe SCardError IPcscProvider.SCardEstablishContext(SCardScope dwScope, void* pvReserved1, void* pvReserved2, SCardContext* phContext) 157 | { 158 | return SCardEstablishContext(dwScope, pvReserved1, pvReserved2, phContext); 159 | } 160 | 161 | unsafe SCardError IPcscProvider.SCardFreeMemory(SCardContext hContext, void* pvMem) 162 | { 163 | return SCardFreeMemory(hContext, pvMem); 164 | } 165 | 166 | unsafe SCardError IPcscProvider.SCardGetStatusChange(SCardContext hContext, int dwTimeout, void* rgReaderStates, int cReaders) 167 | { 168 | return SCardGetStatusChange(hContext, dwTimeout, (SCardReaderState*)rgReaderStates, cReaders); 169 | } 170 | 171 | SCardError IPcscProvider.SCardIsValidContext(SCardContext hContext) 172 | { 173 | return SCardIsValidContext(hContext); 174 | } 175 | 176 | unsafe SCardError IPcscProvider.SCardListReaderGroups(SCardContext hContext, void* mszGroups, int* pcchGroups) 177 | { 178 | return SCardListReaderGroups(hContext, mszGroups, pcchGroups); 179 | } 180 | 181 | unsafe SCardError IPcscProvider.SCardListReaders(SCardContext hContext, string mszGroups, void* mszReaders, int* pcchReaders) 182 | { 183 | return SCardListReaders(hContext, mszGroups, mszReaders, pcchReaders); 184 | } 185 | 186 | unsafe SCardError IPcscProvider.SCardReconnect(SCardHandle hCard, SCardShare dwShareMode, SCardProtocols dwPreferredProtocols, SCardDisposition dwInitialization, SCardProtocols* pdwActiveProtocol) 187 | { 188 | return SCardReconnect(hCard, dwShareMode, dwPreferredProtocols, dwInitialization, pdwActiveProtocol); 189 | } 190 | 191 | SCardError IPcscProvider.SCardReleaseContext(SCardContext hContext) 192 | { 193 | return SCardReleaseContext(hContext); 194 | } 195 | 196 | unsafe SCardError IPcscProvider.SCardTransmit(SCardHandle hCard, void* pioSendPci, byte* pbSendBuffer, int cbSendLength, void* pioRecvPci, byte* pbRecvBuffer, int* pcbRecvLength) 197 | { 198 | return SCardTransmit(hCard, (SCardIORequest*)pioSendPci, pbSendBuffer, cbSendLength, (SCardIORequest*)pioRecvPci, pbRecvBuffer, pcbRecvLength); 199 | } 200 | 201 | unsafe void IPcscProvider.WriteIORequest(void* pIORequest, SCardProtocols protocol, int totalLength, byte[] information) 202 | { 203 | var p = (SCardIORequest*)pIORequest; 204 | p->Protocol = protocol; 205 | p->PciLength = totalLength; 206 | if (information?.Length > 0) 207 | { 208 | Marshal.Copy(information, 0, (IntPtr)(p + 1), information.Length); 209 | } 210 | } 211 | 212 | unsafe void IPcscProvider.WriteReaderState(void* pReaderStates, int index, SCardReaderStates currentState) 213 | { 214 | (((SCardReaderState*)pReaderStates) + index)->CurrentState = currentState; 215 | } 216 | 217 | unsafe void IPcscProvider.WriteReaderState(void* pReaderStates, int index, void* pReaderName) 218 | { 219 | (((SCardReaderState*)pReaderStates) + index)->Reader = pReaderName; 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/PcscDotNet/PcscConnection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PcscDotNet 4 | { 5 | public class PcscConnection : IDisposable 6 | { 7 | public const int DefaultControlBufferSize = 256; 8 | 9 | public const int DefaultTransmitBufferSize = 256; 10 | 11 | public const int ExtendedTransmitBufferSize = 65536; 12 | 13 | public PcscContext Context { get; private set; } 14 | 15 | public int ControlBufferSize { get; set; } = DefaultControlBufferSize; 16 | 17 | public SCardHandle Handle { get; private set; } 18 | 19 | public bool IsConnect => Handle.HasValue; 20 | 21 | public bool IsDisposed { get; private set; } = false; 22 | 23 | public SCardProtocols Protocol { get; private set; } = SCardProtocols.Undefined; 24 | 25 | public IPcscProvider Provider { get; private set; } 26 | 27 | public string ReaderName { get; private set; } 28 | 29 | public SCardShare ShareMode { get; private set; } = SCardShare.Undefined; 30 | 31 | public int TransmitBufferSize { get; set; } = DefaultTransmitBufferSize; 32 | 33 | public PcscConnection(PcscContext context, string readerName) 34 | { 35 | Provider = (Context = context).Provider; 36 | ReaderName = readerName; 37 | } 38 | 39 | ~PcscConnection() 40 | { 41 | Dispose(false); 42 | } 43 | 44 | public PcscConnection BeginTransaction(PcscExceptionHandler onException = null) 45 | { 46 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(BeginTransaction)); 47 | Provider.SCardBeginTransaction(Handle).ThrowIfNotSuccess(onException); 48 | return this; 49 | } 50 | 51 | public unsafe PcscConnection Connect(SCardShare shareMode, SCardProtocols protocol, PcscExceptionHandler onException = null) 52 | { 53 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(Connect)); 54 | SCardHandle handle; 55 | Provider.SCardConnect(Context.Handle, ReaderName, shareMode, protocol, &handle, &protocol).ThrowIfNotSuccess(onException); 56 | Handle = handle; 57 | ShareMode = shareMode; 58 | Protocol = protocol; 59 | return this; 60 | } 61 | 62 | public byte[] Control(int code, params byte[] send) 63 | { 64 | return Control(code, send, ControlBufferSize); 65 | } 66 | 67 | public byte[] Control(int code, byte[] send, PcscExceptionHandler onException = null) 68 | { 69 | return Control(code, send, ControlBufferSize, onException); 70 | } 71 | 72 | public unsafe byte[] Control(int code, byte[] send, int bufferSize, PcscExceptionHandler onException = null) 73 | { 74 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(Control)); 75 | if (bufferSize <= 0) 76 | { 77 | if (send == null) 78 | { 79 | Provider.SCardControl(Handle, code, null, 0, null, 0, &bufferSize).ThrowIfNotSuccess(onException); 80 | } 81 | else 82 | { 83 | fixed (byte* fSend = send) 84 | { 85 | Provider.SCardControl(Handle, code, fSend, send.Length, null, 0, &bufferSize).ThrowIfNotSuccess(onException); 86 | } 87 | } 88 | return new byte[0]; 89 | } 90 | else 91 | { 92 | var recv = new byte[bufferSize]; 93 | fixed (byte* fRecv = recv) 94 | { 95 | if (send == null) 96 | { 97 | Provider.SCardControl(Handle, code, null, 0, fRecv, bufferSize, &bufferSize).ThrowIfNotSuccess(onException); 98 | } 99 | else 100 | { 101 | fixed (byte* fSend = send) 102 | { 103 | Provider.SCardControl(Handle, code, fSend, send.Length, fRecv, bufferSize, &bufferSize).ThrowIfNotSuccess(onException); 104 | } 105 | } 106 | } 107 | if (bufferSize <= 0) return new byte[0]; 108 | Array.Resize(ref recv, bufferSize); 109 | return recv; 110 | } 111 | } 112 | 113 | public byte[] Control(SCardControlFunction function, params byte[] send) 114 | { 115 | return Control(Provider.SCardCtlCode(function), send, ControlBufferSize); 116 | } 117 | 118 | public byte[] Control(SCardControlFunction function, byte[] send, PcscExceptionHandler onException = null) 119 | { 120 | return Control(Provider.SCardCtlCode(function), send, ControlBufferSize, onException); 121 | } 122 | 123 | public byte[] Control(SCardControlFunction function, byte[] send, int bufferSize, PcscExceptionHandler onException = null) 124 | { 125 | return Control(Provider.SCardCtlCode(function), send, bufferSize, onException); 126 | } 127 | 128 | public PcscConnection Disconnect(SCardDisposition disposition = SCardDisposition.Leave, PcscExceptionHandler onException = null) 129 | { 130 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(Disconnect)); 131 | DisconnectInternal(disposition, onException); 132 | return this; 133 | } 134 | 135 | public void Dispose() 136 | { 137 | Dispose(true); 138 | } 139 | 140 | public PcscConnection EndTransaction(SCardDisposition disposition = SCardDisposition.Leave, PcscExceptionHandler onException = null) 141 | { 142 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(EndTransaction)); 143 | Provider.SCardEndTransaction(Handle, disposition).ThrowIfNotSuccess(onException); 144 | return this; 145 | } 146 | 147 | public unsafe PcscConnection Reconnect(SCardShare shareMode, SCardProtocols protocol, SCardDisposition initializationDisposition = SCardDisposition.Leave, PcscExceptionHandler onException = null) 148 | { 149 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(Reconnect)); 150 | Provider.SCardReconnect(Handle, shareMode, protocol, initializationDisposition, &protocol).ThrowIfNotSuccess(onException); 151 | ShareMode = shareMode; 152 | Protocol = protocol; 153 | return this; 154 | } 155 | 156 | public unsafe byte[] Transmit(params byte[] send) 157 | { 158 | return Transmit(send, null, TransmitBufferSize); 159 | } 160 | 161 | public unsafe byte[] Transmit(byte[] send, PcscExceptionHandler onException = null) 162 | { 163 | return Transmit(send, null, TransmitBufferSize, onException); 164 | } 165 | 166 | public unsafe byte[] Transmit(byte[] send, int bufferSize, PcscExceptionHandler onException = null) 167 | { 168 | return Transmit(send, null, bufferSize, onException); 169 | } 170 | 171 | public unsafe byte[] Transmit(byte[] send, byte[] sendInformation, PcscExceptionHandler onException = null) 172 | { 173 | return Transmit(send, sendInformation, TransmitBufferSize, onException); 174 | } 175 | 176 | public unsafe byte[] Transmit(byte[] send, int recvInformationLength, out byte[] recvInformation, PcscExceptionHandler onException = null) 177 | { 178 | return Transmit(send, null, TransmitBufferSize, recvInformationLength, out recvInformation, onException); 179 | } 180 | 181 | public unsafe byte[] Transmit(byte[] send, int bufferSize, int recvInformationLength, out byte[] recvInformation, PcscExceptionHandler onException = null) 182 | { 183 | return Transmit(send, null, bufferSize, recvInformationLength, out recvInformation, onException); 184 | } 185 | 186 | public unsafe byte[] Transmit(byte[] send, byte[] sendInformation, int recvInformationLength, out byte[] recvInformation, PcscExceptionHandler onException = null) 187 | { 188 | return Transmit(send, sendInformation, TransmitBufferSize, recvInformationLength, out recvInformation, onException); 189 | } 190 | 191 | public unsafe byte[] Transmit(byte[] send, byte[] sendInformation, int bufferSize, PcscExceptionHandler onException = null) 192 | { 193 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(Transmit)); 194 | var recv = new byte[bufferSize]; 195 | var sendIORequest = Provider.AllocateIORequest(sendInformation?.Length ?? 0); 196 | fixed (byte* fSend = send, fRecv = recv, fSendIORequest = sendIORequest) 197 | { 198 | var pSendIORequest = fSendIORequest; 199 | Provider.WriteIORequest(pSendIORequest, Protocol, sendIORequest.Length, sendInformation); 200 | Provider.SCardTransmit(Handle, pSendIORequest, fSend, send.Length, null, fRecv, &bufferSize).ThrowIfNotSuccess(onException); 201 | } 202 | Array.Resize(ref recv, bufferSize); 203 | return recv; 204 | } 205 | 206 | public unsafe byte[] Transmit(byte[] send, byte[] sendInformation, int bufferSize, int recvInformationLength, out byte[] recvInformation, PcscExceptionHandler onException = null) 207 | { 208 | if (IsDisposed) throw new ObjectDisposedException(nameof(PcscConnection), nameof(Transmit)); 209 | var recv = new byte[bufferSize]; 210 | var sendIORequest = Provider.AllocateIORequest(sendInformation?.Length ?? 0); 211 | var recvIORequest = Provider.AllocateIORequest(recvInformationLength); 212 | fixed (byte* fSend = send, fRecv = recv, fSendIORequest = sendIORequest, fRecvIORequest = recvIORequest) 213 | { 214 | var pSendIORequest = fSendIORequest; 215 | var pRecvIORequest = fRecvIORequest; 216 | Provider.WriteIORequest(pSendIORequest, Protocol, sendIORequest.Length, sendInformation); 217 | Provider.WriteIORequest(pRecvIORequest, SCardProtocols.Undefined, recvIORequest.Length, null); 218 | Provider.SCardTransmit(Handle, pSendIORequest, fSend, send.Length, pRecvIORequest, fRecv, &bufferSize).ThrowIfNotSuccess(onException); 219 | SCardProtocols protocol; 220 | Provider.ReadIORequest(pRecvIORequest, out protocol, out recvInformation); 221 | Protocol = protocol; 222 | } 223 | Array.Resize(ref recv, bufferSize); 224 | return recv; 225 | } 226 | 227 | private void DisconnectInternal(SCardDisposition disposition = SCardDisposition.Leave, PcscExceptionHandler onException = null) 228 | { 229 | if (!IsConnect) return; 230 | Provider.SCardDisconnect(Handle, disposition).ThrowIfNotSuccess(onException); 231 | Handle = SCardHandle.Default; 232 | ShareMode = SCardShare.Undefined; 233 | Protocol = SCardProtocols.Undefined; 234 | } 235 | 236 | private void Dispose(bool isSuppressFinalize) 237 | { 238 | if (!IsDisposed) 239 | { 240 | DisconnectInternal(); 241 | IsDisposed = true; 242 | } 243 | if (isSuppressFinalize) GC.SuppressFinalize(this); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/PcscDotNet/SCardError.cs: -------------------------------------------------------------------------------- 1 | namespace PcscDotNet 2 | { 3 | /// 4 | /// The return values of smart card functions. 5 | /// 6 | public enum SCardError : uint 7 | { 8 | /// 9 | /// There was an error trying to set the smart card file object pointer. 10 | /// 11 | BadSeek = 0x80100029, 12 | /// 13 | /// The client attempted a smart card operation in a remote session, such as a client session running on a terminal server, and the operating system in use does not support smart card redirection. 14 | /// 15 | BrokenPipe = 0x00000109, 16 | /// 17 | /// The requested item could not be found in the cache. 18 | /// 19 | CacheItemNotFound = 0x80100070, 20 | /// 21 | /// The requested cache item is too old and was deleted from the cache. 22 | /// 23 | CacheItemStale = 0x80100071, 24 | /// 25 | /// The new cache item exceeds the maximum per-item size defined for the cache. 26 | /// 27 | CacheItemTooBig = 0x80100072, 28 | /// 29 | /// The action was cancelled by an SCardCancel request. 30 | /// 31 | Cancelled = 0x80100002, 32 | /// 33 | /// The action was cancelled by the user. 34 | /// 35 | CancelledByUser = 0x8010006E, 36 | /// 37 | /// The system could not dispose of the media in the requested manner. 38 | /// 39 | CantDispose = 0x8010000E, 40 | /// 41 | /// No PIN was presented to the smart card. 42 | /// 43 | CardNotAuthenticated = 0x8010006F, 44 | /// 45 | /// The smart card does not meet minimal requirements for support. 46 | /// 47 | CardUnsupported = 0x8010001C, 48 | /// 49 | /// The requested certificate could not be obtained. 50 | /// 51 | CertificateUnavailable = 0x8010002D, 52 | /// 53 | /// The card cannot be accessed because the maximum number of PIN entry attempts has been reached. 54 | /// 55 | ChvBlocked = 0x8010006C, 56 | /// 57 | /// A communications error with the smart card has been detected. Retry the operation. 58 | /// 59 | CommDataLost = 0x8010002F, 60 | /// 61 | /// An internal communications error has been detected. 62 | /// 63 | CommError = 0x80100013, 64 | /// 65 | /// The reader driver did not produce a unique reader name. 66 | /// 67 | DuplicateReader = 0x8010001B, 68 | /// 69 | /// The end of the smart card file has been reached. 70 | /// 71 | Eof = 0x8010006D, 72 | /// 73 | /// The identified directory does not exist in the smart card. 74 | /// 75 | DirNotFound = 0x80100023, 76 | /// 77 | /// The identified file does not exist in the smart card. 78 | /// 79 | FileNotFound = 0x80100024, 80 | /// 81 | /// The requested order of object creation is not supported. 82 | /// 83 | IccCreateOrder = 0x80100021, 84 | /// 85 | /// No Primary Provider can be found for the smart card. 86 | /// 87 | IccInstallation = 0x80100020, 88 | /// 89 | /// The data buffer to receive returned data is too small for the returned data. 90 | /// 91 | InsufficientBuffer = 0x80100008, 92 | /// 93 | /// An internal consistency check failed. 94 | /// 95 | InternalError = 0x80100001, 96 | /// 97 | /// An ATR obtained from the registry is not a valid ATR string. 98 | /// 99 | InvalidAtr = 0x80100015, 100 | /// 101 | /// The supplied PIN is incorrect. 102 | /// 103 | InvalidChv = 0x8010002A, 104 | /// 105 | /// The supplied handle was invalid. 106 | /// 107 | InvalidHandle = 0x80100003, 108 | /// 109 | /// One or more of the supplied parameters could not be properly interpreted. 110 | /// 111 | InvalidParameter = 0x80100004, 112 | /// 113 | /// Registry startup information is missing or invalid. 114 | /// 115 | InvalidTarget = 0x80100005, 116 | /// 117 | /// One or more of the supplied parameters values could not be properly interpreted. 118 | /// 119 | InvalidValue = 0x80100011, 120 | /// 121 | /// Access is denied to this file. 122 | /// 123 | NoAccess = 0x80100027, 124 | /// 125 | /// The supplied path does not represent a smart card directory. 126 | /// 127 | NoDir = 0x80100025, 128 | /// 129 | /// The supplied path does not represent a smart card file. 130 | /// 131 | NoFile = 0x80100026, 132 | /// 133 | /// The requested key container does not exist on the smart card. 134 | /// 135 | NoKeyContainer = 0x80100030, 136 | /// 137 | /// Not enough memory available to complete this command. 138 | /// 139 | NoMemory = 0x80100006, 140 | /// 141 | /// The smart card PIN cannot be cached. 142 | /// 143 | NoPinCache = 0x80100033, 144 | /// 145 | /// Cannot find a smart card reader. 146 | /// 147 | NoReadersAvailable = 0x8010002E, 148 | /// 149 | /// The Smart Card Resource Manager is not running. 150 | /// 151 | NoService = 0x8010001D, 152 | /// 153 | /// The operation requires a smart card, but no smart card is currently in the device. 154 | /// 155 | NoSmartCard = 0x8010000C, 156 | /// 157 | /// The requested certificate does not exist. 158 | /// 159 | NoSuchCertificate = 0x8010002C, 160 | /// 161 | /// The reader or smart card is not ready to accept commands. 162 | /// 163 | NotReady = 0x80100010, 164 | /// 165 | /// An attempt was made to end a non-existent transaction. 166 | /// 167 | NotTransacted = 0x80100016, 168 | /// 169 | /// The smart card PIN cache has expired. 170 | /// 171 | PinCacheExpired = 0x80100032, 172 | /// 173 | /// The PCI Receive buffer was too small. 174 | /// 175 | PciTooSmall = 0x80100019, 176 | /// 177 | /// The requested protocols are incompatible with the protocol currently in use with the smart card. 178 | /// 179 | ProtoMismatch = 0x8010000F, 180 | /// 181 | /// The specified reader is not currently available for use. 182 | /// 183 | ReaderUnavailable = 0x80100017, 184 | /// 185 | /// The reader driver does not meet minimal requirements for support. 186 | /// 187 | ReaderUnsupported = 0x8010001A, 188 | /// 189 | /// The smart card is read only and cannot be written to. 190 | /// 191 | ReadOnlyCard = 0x80100034, 192 | /// 193 | /// The smart card has been removed, so that further communication is not possible. 194 | /// 195 | RemovedCard = 0x80100069, 196 | /// 197 | /// The smart card has been reset, so any shared state information is invalid. 198 | /// 199 | ResetCard = 0x80100068, 200 | /// 201 | /// Access was denied because of a security violation. 202 | /// 203 | SecurityViolation = 0x8010006A, 204 | /// 205 | /// The Smart Card Resource Manager is too busy to complete this operation. 206 | /// 207 | ServerTooBusy = 0x80100031, 208 | /// 209 | /// The Smart Card Resource Manager has shut down. 210 | /// 211 | ServiceStopped = 0x8010001E, 212 | /// 213 | /// The smart card cannot be accessed because of other connections outstanding. 214 | /// 215 | SharingViolation = 0x8010000B, 216 | /// 217 | /// The operation has been aborted to allow the server application to exit. 218 | /// 219 | Shutdown = 0x80100018, 220 | /// 221 | /// No error was encountered. 222 | /// 223 | Successs = 0x00000000, 224 | /// 225 | /// The action was cancelled by the system, presumably to log off or shut down. 226 | /// 227 | SystemCancelled = 0x80100012, 228 | /// 229 | /// The user-specified timeout value has expired. 230 | /// 231 | Timeout = 0x8010000A, 232 | /// 233 | /// An unexpected card error has occurred. 234 | /// 235 | Unexpected = 0x8010001F, 236 | /// 237 | /// The specified smart card name is not recognized. 238 | /// 239 | UnknownCard = 0x8010000D, 240 | /// 241 | /// An internal error has been detected, but the source is unknown. 242 | /// 243 | UnknownError = 0x80100014, 244 | /// 245 | /// The specified reader name is not recognized. 246 | /// 247 | UnknownReader = 0x80100009, 248 | /// 249 | /// An unrecognized error code was returned from a layered component. 250 | /// 251 | UnknownResMng = 0x8010002B, 252 | /// 253 | /// Power has been removed from the smart card, so that further communication is not possible. 254 | /// 255 | UnpoweredCard = 0x80100067, 256 | /// 257 | /// The smart card is not responding to a reset. 258 | /// 259 | UnresponsiveCard = 0x80100066, 260 | /// 261 | /// The reader cannot communicate with the smart card, due to ATR configuration conflicts. 262 | /// 263 | UnsupportedCard = 0x80100065, 264 | /// 265 | /// This smart card does not support the requested feature. 266 | /// 267 | UnsupportedFeature = 0x80100022, 268 | /// 269 | /// An internal consistency timer has expired. 270 | /// 271 | WaitedTooLong = 0x80100007, 272 | /// 273 | /// The smart card does not have enough memory to store the information. 274 | /// 275 | WriteTooMany = 0x80100028, 276 | /// 277 | /// The card cannot be accessed because the wrong PIN was presented. 278 | /// 279 | WrongChv = 0x8010006B 280 | } 281 | } 282 | --------------------------------------------------------------------------------