├── 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 |
--------------------------------------------------------------------------------