Monitoring a Smartcard Reader

February 3, 2008

Introduction

In this followup to the article Accessing the Belgian Identity Card from VB.NET you will see how you can monitor a smartcard reader. Contained within is the necessary code to detect two simple events, namely when a card was inserted into the reader and when it was ejected.

The material in this article doesn’t build upon the source code of the previous one. If you haven’t read the first part of this series you can download the source code here. It might be good to explore it first. If you just want to find out how to monitor a smart card reader then this article alone suffices.

Unlike the first article I’ve choosen C# for this project, but you can easily convert the code back to VB.NET. Should you require assistence you can find decent convertors on the following websites:

In an attempt not to be so verbose this article will mainly consist out of source code with brief comments along the way explaining its purpose. So without further ado, let’s get started…

Table Of Contents

The Winscard Dynamic Link Library

For working with smartcard readers Microsoft already provides a rich set of API’s contained in the Winscard Dynamic Link Library. You’ll need to import four functions from this library with the Platform Invocation interoperability mechanism (PInvoke). If you want to learn more about the smartcard API you can visit the following sections on MSDN:

The following listing displays the code for importing the four necessary functions.

Listing 1 – Complete UnsafeNativeMethods.cs File

using System;
using System.Runtime.InteropServices;

namespace SmartcardLibrary
{
    internal enum ScopeOption
    {
        //User
        None = 0,
        Terminal = 1,
        System = 2
    }     

    internal sealed partial class UnsafeNativeMethods
    {
         #region WinScard.DLL Imports

         [DllImport("WINSCARD.DLL", EntryPoint = "SCardEstablishContext", CharSet = CharSet.Unicode, 
             SetLastError = true)]
         static internal extern uint EstablishContext(ScopeOption scope, IntPtr reserved1, 
             IntPtr reserved2, ref SmartcardContextSafeHandle context);
        
         [DllImport("WINSCARD.DLL", EntryPoint = "SCardReleaseContext", CharSet = CharSet.Unicode, 
             SetLastError = true)]
         static internal extern uint ReleaseContext(IntPtr context);
        
         [DllImport("WINSCARD.DLL", EntryPoint = "SCardListReaders", CharSet = CharSet.Unicode, 
             SetLastError = true)]
         static internal extern uint ListReaders(SmartcardContextSafeHandle context, string groups, 
             string readers, ref int size);
        
         [DllImport("WINSCARD.DLL", EntryPoint = "SCardGetStatusChange", CharSet = CharSet.Unicode, 
             SetLastError = true)]
         static internal extern uint GetStatusChange([In(), Out()] SmartcardContextSafeHandle context,
             [In(), Out()] int timeout, [In(), Out()] ReaderState[] states, [In(), Out()] int count);
        
         #endregion
    }
}

This sealed class UnsafeNativeMethods is simular to the one used in the first article of this series. It imports the following four functions from the Winscard.dll library:

  • SCardEstablishContext: This function establishes the resource manager context within which operations are performed. It creates a communication context to the PC/SC resource manager. This must be the first function called in a PC/SC application
  • SCardReleaseContext: This function closes the previously established context. Call this function when you are done communicating with the PC/SC resource manager.
  • SCardListReaders: Provides a list of readers while automatically eliminating duplicates.
  • SCardGetStatusChange: Blocks execution until the current availability of the cards in a specific set of readers changes.

The function EstablishContext (SCardEstablishContext) takes one parameter of the enumerated type ScopeOption which is also declared in the UnsafeNativeMethods file. This parameter specifies the scope of the resource manager context. Just pass ScopeOption.System to perform operations within the domain of the system.

You can eliminate the hardcoded WINSCARD.DLL reference by introducing a constant. In the section Invoking The Middle of the first article this was done by introducing the static holder type GlobalConstants. Have a look at that section if you want to change this. For the sake of keeping this article shorter I eliminated this here.

Top of page

SafeHandle

If you were to compile now you would get a few errors because the above code references some types that you have not declared yet. The most import one is the SmartcardContextSafeHandle type.

The operating system handle (context) returned by the EstablishContext function is wrapped in a class derived from the SafeHandle class. Since this class is abstract you must implement a derived class. This class provides finalization of handle resources, preventing them from being reclaimed prematurely by garbage collection.

This was introduced in the .NET Framework 2.0. Before the release of that version all operating system handles could only be encapsulated in the IntPtr managed wrapper object.

With this SafeHandle descendant you be sure that your handle will be disposed of properly when it is no longer needed. Listing 2 Shows the entire code for this class.

Listing 2 – Complete SmartcardContextSafeHandle.cs File

using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;

namespace SmartcardLibrary
{
    internal sealed class SmartcardContextSafeHandle : SafeHandle
    {
        public SmartcardContextSafeHandle()
            : base(IntPtr.Zero, true)
        {
        }

        //The default constructor will be called by P/Invoke smart 
        //marshalling when returning MySafeHandle in a method call.
        public override bool IsInvalid
        {
            [SecurityPermission(SecurityAction.LinkDemand, 
                UnmanagedCode = true)]
            get { return (this.handle == IntPtr.Zero); }
        }

        //We should not provide a finalizer. SafeHandle's critical 
        //finalizer will call ReleaseHandle for us.
        protected override bool ReleaseHandle()
        {
            SmartcardErrorCode result = 
                (SmartcardErrorCode)UnsafeNativeMethods.ReleaseContext(handle);
            return (result == SmartcardErrorCode.None);            
        }
    }
}

Top of page

The ReaderState Structure

Another important type referenced by the UnsafeNativeMethods class is the ReaderState structure. This structure is used by functions for tracking smart cards within readers. You will need this later on when dealing with the GetStatusChange function to specifiy which card readers you want to monitor. If you want more detailled information about the ReaderState structure then have a look at the SCARD_READERSTATE page on MSDN. Listing 3 shows you the code for this structure.

Listing 3 – Complete ReaderState.cs File

using System;
using System.Runtime.InteropServices;

namespace SmartcardLibrary
{
    //Wraps the SCARD_READERSTATE structure of PC/SC.
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct ReaderState
    {
        #region Member Fields
        //Points to the name of the reader being monitored.
        [MarshalAs(UnmanagedType.LPWStr)]
        private string _reader;
        //Not used by the smart card subsystem but is used by the application.
        private IntPtr _userData;
        //Current state of reader at time of call
        private CardState _currentState;
        //State of reader after state change
        private CardState _eventState;
        //Number of bytes in the returned ATR
        [MarshalAs(UnmanagedType.U4)]
        private int _attribute;
        //ATR of inserted card, with extra alignment bytes.
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)]
        private byte[] _rgbAtr;
        #endregion

        #region Methods
        public byte[] RGBAttribute()
        {
            return this._rgbAtr;
        }
        #endregion

        #region "Properties"
        public string Reader
        {
            get { return this._reader; }
            set { this._reader = value; }
        }

        public IntPtr UserData
        {
            get { return this._userData; }
            set { this._userData = value; }
        }

        public CardState CurrentState
        {
            get { return this._currentState; }
            set { this._currentState = value; }
        }

        public CardState EventState
        {
            get { return this._eventState; }
        }
        #endregion
    }
}

The ReaderState structure in its turn references a new enumerated type which you need to declare. For the sake of being complete the code for this type is shown below in listing 4. This new type named CardState will eventually be used to determine if a card was inserted in or ejected from the card reader.

Listing 4 – Complete CardState.cs File

using System;

namespace SmartcardLibrary
{
    //CardState enumeration, used by the PC/SC function SCardGetStatusChanged.    
    internal enum CardState
    {
        //Unaware
        None = 0,
        Ignore = 1,
        Changed = 2,
        Unknown = 4,
        Unavailable = 8,
        Empty = 16,
        Present = 32,
        AttributeMatch = 64,
        Exclusive = 128,
        InUse = 256,
        Mute = 512,
        Unpowered = 1024
    }
}

Top of page

Error Handling

If you take a look at the UnSafeNativeMethods class you’ll see that all of its methods return an 32-bit unsigned integer value. All of the functions contained within the Winscard.dll library return this type of return value. If the function succeeds it return zero (SCARD_S_SUCCESS), if not then it returns an error code. For more information see Smart Card Return Values or Smart Card Error Values.

There are alot of possible error codes. I’ve declared all of these in an enumerated type appropriatly called SmartcardErrorCode. Listing 5 shows you the entire code for this type.

Listing 5 – Complete SmartcardErrorCode.cs File

using System;
using System.ComponentModel;

namespace SmartcardLibrary
{        
    internal enum SmartcardErrorCode : uint
    {
        [Description("Function succeeded")]
        None = 0,
        [Description("An internal consistency check failed.")]
        InternalError = 2148532225,        
        [Description("The action was canceled by a SCardCancel request.")]
        Canceled = 2148532226,        
        [Description("The supplied handle was invalid.")]
        InvalidHandle = 2148532227,        
        [Description("One or more of the supplied parameters could not be properly interpreted.")]
        InvalidParameter = 2148532228,        
        [Description("Registry startup information is missing or invalid.")]
        InvalidTarget = 2148532229,        
        [Description("Not enough memory available to complete this command.")]
        NoMemory = 2148532230,        
        [Description("An internal consistency timer has expired.")]
        WaitedTooLong = 2148532231,        
        [Description("The data buffer to receive returned data is too small for the returned data.")]
        InsufficientBuffer = 2148532232,        
        [Description("The specified reader name is not recognized.")]
        UnknownReader = 2148532233,        
        [Description("The user-specified timeout value has expired.")]
        Timeout = 2148532234,        
        [Description("The smart card cannot be accessed because of other connections outstanding.")]
        SharingViolation = 2148532235,        
        [Description("The operation requires a smart card, but not smard card is currently in the device.")]
        NoSmartcard = 2148532236,        
        [Description("The specified smart card name is not recognized.")]
        UnknownCard = 2148532237,        
        [Description("The system could not dispose of the media in the requested manner.")]
        CannotDispose = 2148532238,        
        [Description("The requested protocols are incompatible with the protocol currently in use with the smart card.")]
        ProtocolMismatch = 2148532239,        
        [Description("The reader or smart card is not ready to accept commands.")]
        NotReady = 2148532240,        
        [Description("One or more of the supplied parameters values could not be properly interpreted.")]
        InvalidValue = 2148532241,        
        [Description("The action was canceled by the system, presumably to log off or shut down.")]
        SystemCanceled = 2148532242,        
        [Description("An internal communications error has been detected.")]
        CommunicationError = 2148532243,        
        [Description("An internal error has been detected, but the source is unknown.")]
        UnknownError = 2148532244,        
        [Description("An ATR obtained from the registry is not a valid ATR string.")]
        InvalidAttribute = 2148532245,        
        [Description("An attempt was made to end a non-existent transaction.")]
        NotTransacted = 2148532246,        
        [Description("The specified reader is not currently available for use.")]
        ReaderUnavailable = 2148532248,        
        [Description("The operation has been aborted to allow the server application to exit.")]
        Shutdown = 2148532248,        
        [Description("The PCI Receive buffer was too small.")]
        PCITooSmall = 2148532249,        
        [Description("The reader driver does not meet minimal requirements for support.")]
        ReaderUnsupported = 2148532250,        
        [Description("The reader driver did not produce a unique reader name.")]
        DuplicateReader = 2148532251,        
        [Description("The smart card does not meet minimal requirements for support.")]
        CardUnsupported = 2148532252,        
        [Description("The Smart Card Resource Manager is not running.")]
        NoService = 2148532253,        
        [Description("The Smart Card Resource Manager has shut down.")]
        ServiceStopped = 2148532254,        
        [Description("An unexpected card error has occured.")]
        Unexpected = 2148532255,        
        [Description("No primary provider can be found for the smart card.")]
        ICCInstallation = 2148532256,        
        [Description("The requested order of object creation is not supported.")]
        ICCCreationOrder = 2148532257,        
        [Description("This smart card does not support the requested feature.")]
        UnsupportedFeature = 2148532258,        
        [Description("The identified directory does not exist in the smart card.")]
        DirectoryNotFound = 2148532259,        
        [Description("The identified file does not exist in the smart card.")]
        FileNotFound = 2148532260,        
        [Description("The supplied path does not represent a smart card directory.")]
        NoDirectory = 2148532261,        
        [Description("The supplied path does not represent a smart card file.")]
        NoFile = 2148532262,        
        [Description("Access is denied to this file.")]
        NoAccess = 2148532263,        
        [Description("The smart card does not have enough memory to store the information.")]
        WriteTooMany = 2148532264,        
        [Description("There was an error trying to set the smart card file object pointer.")]
        BadSeek = 2148532265,        
        [Description("The supplied PIN is incorrect.")]
        InvalidPin = 2148532266,        
        [Description("An unrecognized error code was returned from a layered component.")]
        UnknownResourceManagement = 2148532267,        
        [Description("The requested certificate does not exist.")]
        NoSuchCertificate = 2148532268,        
        [Description("The requested certificate could not be obtained.")]
        CertificateUnavailable = 2148532269,        
        [Description("Cannot find a smart card reader.")]
        NoReadersAvailable = 2148532270,        
        [Description("A communications error with the smart card has been detected. Retry the operation.")]
        CommunicationDataLast = 2148532271,        
        [Description("The requested key container does not exist on the smart card.")]
        NoKeyContainer = 2148532272,        
        [Description("The Smart Card Resource Manager is too busy to complete this operation.")]
        ServerTooBusy = 2148532273,        
        [Description("The reader cannot communiate with the card, due to ATR string configuration conflicts.")]
        UnsupportedCard = 2148532325,        
        [Description("The smart card is not responding to a reset.")]
        UnresponsiveCard = 2148532326,        
        [Description("Power has been removed from the smart card, so that further communication is not possible.")]
        UnpoweredCard = 2148532327,        
        [Description("The msart card has been reset, so any shared state information is invalid.")]
        ResetCard = 2148532328,        
        [Description("The smart card has been removed, so further communication is not possible.")]
        RemovedCard = 2148532329,        
        [Description("Access was denied because of a security violation.")]
        SecurityViolation = 2148532330,        
        [Description("The card cannot be accessed because th wrong PIN was presented.")]
        WrongPin = 2148532331,        
        [Description("The card cannot be accessed because the maximum number of PIN entry attempts has been reached.")]
        PinBlocked = 2148532332,        
        [Description("The end of the smart card file has been reached.")]
        EndOfFile = 2148532333,        
        [Description("The action was canceled by the user.")]
        CanceledByUser = 2148532334,        
        [Description("No PIN was presented to the smart card.")]
        CardNotAuthenticated = 2148532335        
    }    
}

If the returned value equals SmartcardErrorCode.None then no error occured. This enumerated type like most of the other types in the SmartcardLibrary namespace is declared as internal. It is not exposed to other assemblies, if you wish to do so be sure to also change its type from uint to long for example. The uint type is not CLS compliant.

Top of page

SmartcardManager Singleton

Voila, now you are finally ready to start monitoring the smartcard readers connected to your system. Now you only need to write some code to do this. That’s where the SmartcardManager class comes into play. This class implements the Singleton pattern so you can only initiate one instance of this type of object. This is done by calling the static method GetManager().

When an instance of the SmartcardManager class is created a list of the smartcard readers connected to your system is composed. The list is retrieved in the ListReaders() method which internally uses the ListReaders() method of the UnsafeNativeMethods class. Finally the constructor creates a new backgroundworker (thread) and uses the WaitChangeStatus() method as its DoWork event handler.

It is in this WaitChangeStatus() method that the list of smartcard readers is passed to the GetStatusChange() method of the UnsafeNativeMethod class. This causes the backgroundworker’s thread execution to be blocked until a change occurs in the current availability of the cards in the list of monitored readers. A timeout can be set and I’ve choosen 1000 milliseconds as the default interval. When the execution of the thread resumes the status of readers is checked and if a card was inserted or ejected a message will be shown.

So that basically describes the purpose of the SmartcardManager class. You can find the entire code below in Listing 6. This code just shows you one possible way to monitor the smartcard readers. Feel free to improve on this trivial piece of code.

Listing 6 – Complete SmartcardManager.cs File

using System;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace SmartcardLibrary
{    
    internal enum SmartcardState
    {
        None = 0,
        Inserted = 1,
        Ejected = 2
    }

    public class SmartcardManager : IDisposable 
    {
        #region Member Fields

        //Shared members are lazily initialized.
        //.NET guarantees thread safety for shared initialization.
        private static readonly SmartcardManager _instance = new SmartcardManager();

        private SmartcardContextSafeHandle _context;
        private SmartcardErrorCode _lastErrorCode;
        private bool _disposed = false;
        private ReaderState[] _states;                
        //A thread that watches for new smart cards.
        private BackgroundWorker _worker;                

        #endregion

        #region Methods

        //Make the constructor private to hide it. This class adheres to the singleton pattern.
        private SmartcardManager()
        {
            //Create a new SafeHandle to store the smartcard context.
            this._context = new SmartcardContextSafeHandle();
            //Establish a context with the PC/SC resource manager.
            this.EstablishContext();
            
            //Compose a list of the card readers which are connected to the
            //system and which will be monitored.
            ArrayList availableReaders = this.ListReaders();            
            this._states = new ReaderState[availableReaders.Count];
            for (int i = 0; i <= availableReaders.Count - 1; i++)
            {
                this._states[i].Reader = availableReaders[i].ToString();
            }

            //Start a background worker thread which monitors the specified
            //card readers.
            if ((availableReaders.Count > 0))
            {
                this._worker = new BackgroundWorker();
                this._worker.WorkerSupportsCancellation = true;
                this._worker.DoWork += WaitChangeStatus;
                this._worker.RunWorkerAsync();
            }
        }

        public static SmartcardManager GetManager() 
        {            
            return _instance;
        }

        private bool EstablishContext()
        {
            if ((this.HasContext))
            {
                return true;
            }
            this._lastErrorCode =
                (SmartcardErrorCode)UnsafeNativeMethods.EstablishContext(ScopeOption.System,
                IntPtr.Zero, IntPtr.Zero, ref this._context);
            return (this._lastErrorCode == SmartcardErrorCode.None);
        }        

        private void WaitChangeStatus(object sender, DoWorkEventArgs e)
        {
            while (!e.Cancel) 
            {
                SmartcardErrorCode result;
                
                //Obtain a lock when we use the context pointer, 
                //which may be modified in the Dispose() method.
                lock (this) 
                {
                    if (!this.HasContext) 
                    {
                        return;
                    }
                    
                    //This thread will be executed every 1000ms. 
                    //The thread also blocks for 1000ms, meaning 
                    //that the application may keep on running for 
                    //one extra second after the user has closed 
                    //the Main Form.
                    result = 
                        (SmartcardErrorCode)UnsafeNativeMethods.GetStatusChange(
                        this._context, 1000, this._states, this._states.Length);
                }
                
                if ((result == SmartcardErrorCode.Timeout)) 
                {
                    // Time out has passed, but there is no new info. Just go on with the loop
                    continue;
                }
                
                for (int i = 0; i <= this._states.Length - 1; i++) 
                {
                    //Check if the state changed from the last time.
                    if ((this._states[i].EventState & CardState.Changed) == CardState.Changed)
                    {
                        //Check what changed.
                        SmartcardState state = SmartcardState.None;
                        if ((this._states[i].EventState & CardState.Present) == CardState.Present 
                            && (this._states[i].CurrentState & CardState.Present) != CardState.Present)
                        {
                            //The card was inserted.                            
                            state = SmartcardState.Inserted;
                        }
                        else if ((this._states[i].EventState & CardState.Empty) == CardState.Empty 
                            && (this._states[i].CurrentState & CardState.Empty) != CardState.Empty)
                        {
                            //The card was ejected.
                            state = SmartcardState.Ejected;
                        }
                        if (state != SmartcardState.None && this._states[i].CurrentState != CardState.None)
                        {
                            switch(state)
                            {
                                case SmartcardState.Inserted:
                                {
                                    MessageBox.Show("Card inserted");
                                    break;
                                }
                                case SmartcardState.Ejected:
                                {
                                    MessageBox.Show("Card ejected");
                                    break;
                                }
                                default:
                                {
                                    MessageBox.Show("Some other state...");
                                    break;
                                }
                            }
                        }
                        //Update the current state for the next time they are checked.
                        this._states[i].CurrentState = this._states[i].EventState;
                    }
                }
            }
        }

        private int GetReaderListBufferSize()
        {
            if ((this._context.IsInvalid)) 
            {
                return 0;
            }
            int result = 0;
            this._lastErrorCode = 
                (SmartcardErrorCode)UnsafeNativeMethods.ListReaders(
                this._context, null, null, ref result);
            return result;
        }

        public ArrayList ListReaders()
        {
            ArrayList result = new ArrayList();
            
            //Make sure a context has been established before 
            //retrieving the list of smartcard readers.
            if (this.EstablishContext())
            {
                //Ask for the size of the buffer first.
                int size = this.GetReaderListBufferSize();

                //Allocate a string of the proper size in which 
                //to store the list of smartcard readers.
                string readerList = new string('', size);
                //Retrieve the list of smartcard readers.
                this._lastErrorCode =
                    (SmartcardErrorCode)UnsafeNativeMethods.ListReaders(this._context, 
                    null, readerList, ref size);
                if ((this._lastErrorCode == SmartcardErrorCode.None))
                {
                    //Extract each reader from the returned list.
                    //The readerList string will contain a multi-string of 
                    //the reader names, i.e. they are seperated by 0x00 
                    //characters.
                    string readerName = string.Empty;
                    for (int i = 0; i <= readerList.Length - 1; i++)
                    {
                        if ((readerList[i] == ''))
                        {
                            if ((readerName.Length > 0))
                            {
                                //We have a smartcard reader's name.
                                result.Add(readerName);
                                readerName = string.Empty;
                            }
                        }
                        else
                        {
                            //Append the found character.
                            readerName += new string(readerList[i], 1);
                        }
                    }
                }
            }
            return result;
        }        

        #endregion

        #region IDisposable Support

        //IDisposable
        private void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    // Free other state (managed objects).            
                }

                //Free your own state (unmanaged objects).
                //Set large fields to null.
                this._states = null;
                this._worker.CancelAsync();
                this._worker.Dispose();
                this._context.Dispose();
            }
            this._disposed = true;
        }        

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Properties

        private bool HasContext
        {            
            get { return (!this._context.IsInvalid); }
        }        

        #endregion
    }    
}

Top of page

Implementing a Test Application

You can test all of this by just creating a new Windows Forms application project and adding one line of code. Listing 7 shows you the code.

Listing 7 – Complete Form1.cs File

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SmartcardLibrary;

namespace SmartcardApplication
{
    public partial class Form1 : Form
    {
        private SmartcardManager manager = SmartcardManager.GetManager(); 

        public Form1()
        {
            InitializeComponent();
        }   
    }
}

Be sure to add a reference to the assembly which contains the SmartcardManager class. When you start this application you will receive the message “Card inserted” each time you insert a card in the reader and the message “Card ejected” when you remove the card from the reader.

Top of page

Summary

That concludes the second part in the series of working with the Belgian Identity card. In this article you have seen how you can monitor the status of a smartcard reader. You can now detect when a card is inserted or ejected from a reader. This ofcourse is usefull for any application dealing with smartcard readers, not just for reading identity cards. In the next and final part I’ll whip the two articles together to create a component for working with smartcard readers.

If you have any problems, suggestions or comments be sure to let me know.

Top of page

Download

You can find the source code for this article on the Download page of this blog.

Top of page

Advertisements

38 Responses to “Monitoring a Smartcard Reader”

  1. Gleb Says:

    Great! Just what I am looking for. I have to develop some secured web-service client for windows. And I have to read some public data from Estonian ID card fast. So your articles are very interesting for me.
    btw,
    private SmartcardManager manager = SmartcardManager.GetManager();
    is wrong, GetManaget is a property, not method 🙂
    Thank you very much. I started from this article, but now I’ll convert first article to c# and play with it)

  2. Christophe Says:

    Indeed. It has been corrected.

  3. Gal Says:

    a very interesting article
    can it be used with Visual Studio 2005

  4. Anish Says:

    Hi,

    Excellent article! Thanks a lot.

    I am having two queries:

    1. I am not able to get the message of card inserted and card ejected event.

    2. Other thing is can you please provide the code for transmitting command to card?

    Looking forward to your reply.

    Thanks,
    Anish

  5. Anish Says:

    Hi,

    Sorry, I got it working 🙂 It was my mistake. Only thing for which i need help is transmitting data to the card using SCardTransmit. I am trying from my side. If I my successful will let you know.

    Thanks,
    Anish

  6. sepideh Says:

    Hi
    I need Soft Reader Smart Card With C#
    Thank`s a lot

  7. Azlan Says:

    Great article, but download not working, can you fix it. want to test it.

  8. Christophe Says:

    The downloads are fixed.

  9. Akhilesh Says:

    i want to read the value of smart card by smart card reader but i am not able to indetify the correct code in c# . which one given here , not working correctely

  10. TKID Says:

    How to import certificates into smartcard?

  11. Sreedevi J Says:

    Hi, thanks for the great article, but the code is not really working. I try a ListReaders() call and the search turns up empty.

    I have a reader connected to the serial port and turned on, but it is not detected. Am I doing something wrong? Forgive me, I’m a nooB 🙂

  12. jaya Says:

    I am not able to get the message of card inserted and card ejected event

    thanks in advance

  13. hasaam Says:

    why smart cart return 64F5 ?
    Always return 64F5 when verify PIN with SCard,y is that?
    Please Help

  14. Islam Says:

    Am very Pleased to lookover you Code, am wondering how to move to the next step.

    I want to “connect to the card” to begin read and write, Any refrence pleas

    Thanks,

  15. Amit Sinha Says:

    How to read smart card data? i am able to find inser/eject status. but not able to find user information .

    Please guide me , this is urgent .

    can you guide me?

  16. Steven Luck Says:

    Is this post still active? 😛
    I’ve tried it, and it worked great! However, is it able to detect if card is already inserted or not? Not the state of changes, but rather if the card is already inserted or not.

    I tried to work around, but haven’t got the way to check if the card is already inserted.

    Thank you.
    Regards,

    Steven

  17. Manoj Patil Says:

    It is working for Getting List of Reader connected to our machine. Can you please provide me “How to send the Data to Smart Card and How can we read the data from smart card.

    I know the APPUD commands get used for this. but how?

  18. elizabethbabyk Says:

    hey guys, i am working on a smart card problem in which i have to record time of insertion and time of ejection of card and display that info as well. the card is not inserted , instead it is swiped in the system.So the first swipe implies inserttion and second swipe implies ejections…can some one help 😦

  19. Rory Benzo Says:

    Hi all,

    I just upgraded from Adobe 8 to Adobe X and in bought version, I’m unable to digitally sign documents with my smart card. I keep getting the following error code.

    Error encountered while signing:

    The Windows Cryptographic Service provider reported error:
    The action was cancelled by user.
    Error Code:2148532334

    • Christophe Says:

      This has little (or nothing) to do with this post. I think you are best of contacting Adobe support.

  20. Sobha Says:

    Can you please provide me “How to send the Data to Smart Card and How can we read the data from smart card.

  21. Sobha Says:

    I am currently checking Daniel Müller’s pcsc-sharp SmartCardTest sample.But getting ‘overflow exception’ in this statement [phContext = (long)ctx; ]while callling SCardEstablishContext function .I am trying to resolve this.But the context is getting fine from the SmartCardMonitor program.So i wish to do reading of card from this sample.

  22. Sobha Says:

    Hi ,
    I think Daniel Müller’s pcsc-sharp SmartCardTest sample is for reading SDCard.I actually want to read NFC tags/messages from ACR122 reader which will be received by sending NDEF messages from my NFC phone.

  23. Sobha Says:

    I have added prototypes of other functions needed for data exchange btwn ACR122 in UnsafeNativeMethods.cs and called
    SCardConnect function.But the cardhandle is not proper.Could you please provide the prototype for SCardConnect?

  24. Sobha Says:

    Thanks Charles ! I do also prefer Christophe’s code, since it is the only one smart card application that works fine in my machine. All other applications failed to get the ‘context’ may be because of interoperability issues. Please note that I’m working in windows 7 64 bit machine.


  25. i already done with read and write smard card, the problem is in application i can detect when card is insert the data show and when remove the data not show, now i develop the system in web-based using asp.net, when i place the the card, it didn’t show the data .
    how can i detect smard card insert/remove in web browser

    thank you
    *sorry if my english is bad

  26. Steve Cox Says:

    My compiler (VS2010) has a problem with the following lines in SmartcardManager.cs:
    string readerList = new string(”, size);
    if ((readerList[i] == ”))

    Changing ” to char.MinValue seems to have corrected it.

    Thanks for the good post, it was very helpful.

  27. Akos Says:

    Hi Christophe,

    In my enviroment (Win 8 64 bit, VS Ultimate 2012, ACR 122U reader, Mifare Classic 1k ) I had to change your code to get it work.
    In SmartcardManager.cs:

    string readerList = new string(”, size); -> string readerList = new string(”, size);

    if ((readerList[i] == ”)) -> if ((readerList[i] == ”))

    After this changes the code works perfect!
    Thx for your work!

  28. Akos Says:

    Hi Christophe,

    In my enviroment (Win 8 64 bit, VS Ultimate 2012, ACR 122U reader, Mifare Classic 1k ) I had to change your code to get it work.
    In SmartcardManager.cs:

    string readerList = new string(”, size); -> string readerList = new string(‘\’, size);

    if ((readerList[i] == ”)) -> if ((readerList[i] == ‘\’))

    After this changes the code works perfect!
    Thx for your work!

  29. Akos Says:

    Hi Christophe,

    In my enviroment (Win 8 64 bit, VS Ultimate 2012, ACR 122U reader, Mifare Classic 1k ) I had to change your code to get it work.
    In SmartcardManager.cs:

    string readerList = new string(”, size); -> string readerList = new string(‘\ 0’, size);

    if ((readerList[i] == ”)) -> if ((readerList[i] == ‘\ 0’))

    After this changes the code works perfect!
    Thx for your work!

  30. Cleber Says:

    Hi
    Is there any way to get the pin attempts remaining?

    []’s

    Cleber

  31. mkysoft Says:

    We must use this:
    result = new ArrayList(readerList.Split(”));

    for this:
    string readerName = string.Empty;
    for (int i = 0; i 0))
    {
    //We have a smartcard reader’s name.
    result.Add(readerName);
    readerName = string.Empty;
    }
    }
    else
    {
    //Append the found character.
    readerName += new string(readerList[i], 1);
    }
    }


Comments are closed.

%d bloggers like this: