Mensaje de error al insertar una tarjeta inteligente en un lector: el software del controlador de dispositivo no se instaló correctamente

En este artículo se proporciona una solución a un error que se produce al insertar una tarjeta inteligente en un lector.

Versión original del producto:   Windows 7 Service Pack 1, Windows Server 2012 R2
Número KB original:   976832

Síntomas

Al insertar una tarjeta inteligente en un lector de tarjetas inteligentes, Windows intenta descargar e instalar los minidrivers de tarjeta inteligente para la tarjeta a través de los servicios Plug and Play. Si el controlador de la tarjeta inteligente no está disponible en ninguna de las ubicaciones preconfiguradas, como Windows Update, WSUS o rutas de acceso de intranet, y un proveedor de servicios criptográficos personalizado no está ya instalado en el sistema, recibirás el siguiente mensaje de error en el área de notificación:

El software del controlador de dispositivo no se instaló correctamente

Haga clic aquí para obtener más información.

Este mensaje de error desaparece después de varios segundos.

Además, en el Administrador de dispositivos, en Otros dispositivos, el dispositivo de tarjeta inteligente tiene el estado DNF (no se encuentra el controlador).

Esto requiere con frecuencia que el usuario obtenga uno de los siguientes elementos del emisor de tarjetas inteligentes para resolver este error:

  1. Un minidriver de tarjeta inteligente registrado en Windows.
  2. Un proveedor de servicios criptográficos (CSP) personalizado para la tarjeta inteligente.
  3. Un minidriver de tarjeta inteligente sin logotipo de Windows.
  4. Otro software intermedio, como un control ActiveX, software PKCS#11 u otro software personalizado.

Sin embargo, si solo se proporciona al usuario el elemento 3 o 4 de esta lista, la tarjeta inteligente seguirá funcionando en el sistema. Sin embargo, el usuario recibirá el mensaje de error que se menciona en esta sección cada vez que inserte la tarjeta inteligente.

Este problema afecta a todas las versiones de Windows 7, Windows Server 2008 R2 y en versiones posteriores de ambos sistemas operativos.

Causa

Todas las tarjetas inteligentes requieren software adicional para funcionar en Windows a menos que haya un controlador de bandeja de entrada que permita al usuario usar la tarjeta sin instalar software adicional. Windows Smart Card Framework se mejoró en Windows 7 para habilitar la descarga automática de minidrivers de tarjetas inteligentes de Windows Update o de otras ubicaciones similares, como un servidor WSUS, cuando la tarjeta inteligente se inserta en el lector. Todas las tarjetas inteligentes que cumplen correctamente los requisitos del logotipo, según lo publicado por el Programa de logotipos de Windows, se benefician de esta característica.

Sin embargo, si el software necesario para usar una tarjeta inteligente en Windows no tiene logotipo o es de un tipo que difiere de un minidriver, como un controlador PKCS#11, un CSP personalizado, software intermedio o un control ActiveX, la opción de descarga automática falla porque Microsoft solo certifica minidrivers de tarjeta inteligente. Por lo tanto, si el usuario inserta una tarjeta para la que aún no está registrado un CSP personalizado, el usuario recibe un mensaje de error que indica que falta el software del controlador para el dispositivo de tarjeta inteligente aunque el usuario pueda usar la tarjeta inteligente a través de software adicional que se instaló en el equipo del usuario desde una instalación personalizada.

Solución

Aunque las tarjetas inteligentes siguen funcionando a pesar del mensaje de error que ve el usuario, un emisor de tarjetas inteligentes, un proveedor o un fabricante pueden usar uno de los siguientes métodos para resolver este error.

Implementar un minidriver de tarjeta inteligente

Recomendamos que los emisores, proveedores y fabricantes de tarjetas inteligentes implementen minidrivers de tarjeta inteligente y participen en el Programa de logotipos de Windows para beneficiarse de las mejoras que se introducen en la plataforma, como Plug and Play de tarjeta inteligente, Fase de dispositivo para tarjetas inteligentes, entre otras.

Implementar un controlador NULL para la tarjeta inteligente

Si se requiere software personalizado como un controlador PKCS#11, un control ActiveX o algún otro software intermedio para habilitar el uso de tarjetas inteligentes en Windows e implementar un minidriver de tarjeta inteligente o un CSP personalizado no es una opción práctica, recomendamos que los emisores de tarjetas, proveedores o fabricantes consideren enviar controladores NULL a Windows Update. El proceso típico para asegurarse de que un controlador NULL está disponible en Windows Update requiere un envío de dispositivo sin clasificar correctamente a través de Winqual. Si en el futuro hay un minidriver disponible para estas tarjetas, el nuevo controlador puede cargarse en Windows Update participando en el Programa de logotipos de Windows. A continuación, los usuarios finales pueden descargar manualmente los controladores NULL o pueden estar disponibles mediante actualizaciones opcionales.

A continuación se muestra una plantilla de ejemplo para un controlador NULL para una tarjeta inteligente.

;  
; Null Driver for Fabrikam Smartcard installation x86 and x64 package.  
;

[Version]  
Signature="$Windows NT$"  
Class=SmartCard  
ClassGuid={990A2BD7-E738-46c7-B26F-1CF8FB9F1391}  
Provider=%ProviderName%  
CatalogFile=delta.cat  
DriverVer=4/21/2006,1.0.0.0

[Manufacturer]  
%ProviderName%=Minidriver,NTamd64,NTamd64.6.1,NTx86,NTx86.6.1

[Minidriver.NTamd64]  
;This driver has no applicability on OS versions earlier than Windows 7

[Minidriver.NTx86]  
;This driver has no applicability on OS versions earlier than Windows 7

[Minidriver.NTamd64.6.1]  
%CardDeviceName%=Minidriver64_Install,<DEVICE_ID>  
;%CardDeviceName%=Minidriver64_Install,<DEVICE_ID2>  
;%CardDeviceName%=Minidriver64_Install,<DEVICE_ID3>  
;...

[Minidriver.NTx86.6.1]  
%CardDeviceName%=Minidriver32_Install,<DEVICE_ID>  
;%CardDeviceName%=Minidriver32_Install,<DEVICE_ID2>  
;%CardDeviceName%=Minidriver32_Install,<DEVICE_ID3>  
;...

;Leave the following sections blank  
[DefaultInstall]  
[DefaultInstall.ntamd64]  
[DefaultInstall.NTx86]  
[DefaultInstall.ntamd64.6.1]  
[DefaultInstall.NTx86.6.1]  
[Minidriver64_Install.NT]  
[Minidriver64_61_Install.NT]  
[Minidriver32_Install.NT]  
[Minidriver32_61_Install.NT]

[Minidriver64_61_Install.NT.Services]  
AddService = ,2

[Minidriver32_61_Install.NT.Services]  
AddService = ,2

; =================== Generic ==================================

[Strings]  
ProviderName ="Microsoft"  
CardDeviceName="Fabrikam Generic Smart card"

Para generar el identificador de dispositivo de hardware al que hace referencia la cadena DEVICE_ID en el ejemplo, siga las instrucciones de la especificación del minidriver de tarjeta inteligente.

Para obtener información detallada sobre cómo enviar un controlador NULL a Microsoft, póngase en contacto con el Servicio de soporte al cliente de Microsoft.

Deshabilitar la directiva de grupo Plug and Play through de tarjeta inteligente para equipos administrados

Esta opción se recomienda solo para implementaciones empresariales en las que los equipos son administrados por administradores y todo el software necesario para trabajar con las tarjetas inteligentes que se usan en la empresa se instala mediante el uso de herramientas de administración de software como SMS.

Este procedimiento no se recomienda en los siguientes entornos porque afectará a todas las tarjetas inteligentes del entorno:

  • Implementaciones comerciales destinadas a usuarios finales, como la banca en línea.
  • Entornos que incluyen tarjetas inteligentes Plug and Play y tarjetas inteligentes que no son Plug and Play y que usan la directiva de grupo para deshabilitar Plug and Play para tarjetas inteligentes.

Plug and Play de tarjeta inteligente se puede deshabilitar en empresas en las que el equipo del usuario final se administra mediante mecanismos como la directiva de grupo.

Si la implementación solo usa soluciones de tarjetas inteligentes que no sean Plug and Play, un administrador local puede deshabilitar plug and play de tarjeta inteligente en un equipo cliente. Al deshabilitar Plug and Play de tarjeta inteligente, se impide la descarga de los controladores de tarjeta inteligente, también conocidos como minidrivers de tarjeta inteligente. También impide los mensajes plug and play de tarjeta inteligente.

Para deshabilitar la directiva de grupo local Plug and Play de tarjeta inteligente, sigue estos pasos:

  1. Haga clic en Inicio, escriba gpedit.msc en el cuadro Buscar programas y archivos y, a continuación, presione ENTRAR.

  2. En el árbol de consola en Configuración del equipo, haga clic en Plantillas administrativas.

  3. En el panel de detalles, haga doble clic en Componentes de Windows y, a continuación, haga doble clic en Tarjeta inteligente.

  4. Haga clic con el botón secundario en Activar el servicio Plug and Play de tarjeta inteligente y, a continuación, haga clic en Editar.

  5. Haga clic en Deshabilitado y, a continuación, haga clic en Aceptar.

Cambiar el sistema del usuario final y deshabilitar Plug and Play de tarjeta inteligente para tarjetas específicas

Esta es la opción menos recomendada. Debe usar esta opción solo si las tarjetas son tarjetas heredadas y no hay planes para implementar minidrivers de tarjeta inteligente en el futuro. Esta opción requiere que el software existente que ya está instalado en el sistema notifique a Windows que hay un CSP personalizado instalado en el sistema aunque no exista dicho CSP en el sistema del usuario final. En cuanto Windows determina que ya hay un CSP personalizado instalado en el sistema, Windows no intenta descargar e instalar un controlador a través de Plug and Play de tarjeta inteligente. No se crea ningún nodo de dispositivo para el dispositivo de tarjeta inteligente que esté visible en el Administrador de dispositivos. Esta opción da como resultado los siguientes cambios en el Registro del sistema:

Subclave: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\<Smart card name>

Entradas del Registro de subclave:

  • ATR=DWORD hexadecimal: ATR delimitado por comas de la tarjeta inteligente.

  • ATRMask= DWORD hexadecimal: máscara delimitada por comas que se aplica al ATR para enmascarar bytes insignificantes en el ATR.

  • Crypto Provider=String value: cadena relevante para la tarjeta inteligente.

Por ejemplo:

Subclave: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\Fabrikam ATM card

Entradas del Registro de subclave:

  • ATR=DWORD hexadecimal: 3b,dc,13,00,40,3a,49,54,47,5f,4d,53,43,53,50,5f,56,32
  • ATRMask= DWORD hexadecimal: ff,ff,ff,ff,ff,ff,ff,ff
  • Crypto Provider=String value: Fabrikam ATM Ficticia Provider

Para los sistemas de x64 bits, deben realizarse cambios idénticos en la siguiente subclave: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\Calais\SmartCards

Te recomendamos que, en lugar de cambiar directamente el registro del sistema, usas las API de WinSCard para introducir estos cambios en el sistema. Este es un ejemplo de código de ejemplo que detecta la inserción de tarjetas inteligentes y, a continuación, deshabilita Plug and Play de tarjeta inteligente para la tarjeta en particular mediante la creación de una entrada del Registro que asocia la tarjeta con un proveedor no existente.

Microsoft proporciona ejemplos de programación con fines ilustrativos únicamente, sin ninguna garantía, ya sea expresa o implícita. Esto incluye, entre otras, las garantías implícitas de comerciabilidad e idoneidad para un fin determinado. Se considera que está familiarizado con el lenguaje de programación que se muestra y con las herramientas para crear y depurar procedimientos. Los ingenieros de soporte técnico de Microsoft pueden explicarle la funcionalidad de un determinado procedimiento. Sin embargo, no modificarán estos ejemplos para proporcionar funcionalidad adicional ni construir procedimientos para cumplir los requisitos específicos.

//==============================================================;
//
// Disable Smart card Plug and Play for specific cards
//
// Abstract:
// This is an example of how to create a new
// Smart Card Database entry when a smart card is inserted
// into the computer.
//
// This source code is only intended as a supplement to existing Microsoft
// documentation.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED. THIS INCLUDES BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//==============================================================;

// This code must be compiled with UNICODE support to work correctly
#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h>
#include <winscard.h>
#include <stdio.h>
#include <strsafe.h>
#include <rpc.h>

// Change this prefix to specify what the beginning of the
// introduced card name in the registry will be. This is
// be prepended to a GUID value.
#define CARD_NAME_PREFIX L"MyCustomCard"

// This is the name that will be provided as the CSP for 
// the card when introduced to the system. This is provided
// in order to disable Smart Card Plug and Play for this
// card.
#define CARD_CSP L"$DisableSCPnP$"

// This special reader name is used to be notified when
// a reader is added to or removed from the system through
// SCardGetStatusChange.
#define PNP_READER_NAME L"\\\\?PnP?\\Notification"

// Maximum ATR length plus alignment bytes. This value is
// used in the SCARD_READERSTATE structure
#define MAX_ATR_LEN 36

LONG GenerateCardName(
 __deref_out LPWSTR *ppwszCardName)
{
    LONG lReturn = NO_ERROR;
    HRESULT hr = S_OK;
    DWORD cchFinalString = 0;
    WCHAR wszCardNamePrefix[] = CARD_NAME_PREFIX;
    LPWSTR pwszFinalString = NULL;
    UUID uuidCardGuid = {0};
    RPC_WSTR pwszCardGuid = NULL;
    RPC_STATUS rpcStatus = RPC_S_OK;

    // Parameter check
    if (NULL == ppwszCardName)
    {
    wprintf(L"Invalid parameter in GenerateCardName.\n");
    return ERROR_INVALID_PARAMETER;
    }

    // Generate GUID
    rpcStatus = UuidCreate(&uuidCardGuid);
    if (RPC_S_OK != rpcStatus)
    {
    wprintf(L"Failed to create new GUID with error 0x%x.\n");
    lReturn = (DWORD)rpcStatus;
    }
     else
     {
         // Convert GUID to string
         rpcStatus = UuidToString(&uuidCardGuid, &pwszCardGuid);
         if (RPC_S_OK != rpcStatus)
         {
             wprintf(L"Failed to convert new GUID to string with error 0x%x.\n", rpcStatus);
             lReturn = (DWORD)rpcStatus;
         }
         else
         {
             // Allocate memory for final string
             // Template is <prefix>-<guid>
             cchFinalString = (DWORD)(wcslen(wszCardNamePrefix) + 1 + wcslen((LPWSTR)pwszCardGuid) + 1);
             pwszFinalString = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchFinalString * sizeof(WCHAR));
             if (NULL == pwszFinalString)
             {
                 wprintf(L"Out of memory.\n");
                 lReturn = ERROR_OUTOFMEMORY;
             }
             else
             {
                 // Create final string
                 hr = StringCchPrintf(
                 pwszFinalString,
                 cchFinalString,
                 L"%s-%s",
                 wszCardNamePrefix,
                 pwszCardGuid);
                 if (FAILED(hr))
                 {
                     wprintf(L"Failed to create card name with error 0x%x.\n", hr);
                     lReturn = (DWORD)hr;
                 }
                 else
                 {
                     // Set output params
                     *ppwszCardName = pwszFinalString;
                     pwszFinalString = NULL;
                 }
             }
         }
     }

    if (NULL != pwszCardGuid)
     {
         RpcStringFree(&pwszCardGuid);
     }

    if (NULL != pwszFinalString)
     {
         HeapFree(GetProcessHeap(), 0, pwszFinalString);
     }

    return lReturn;
}

LONG IntroduceCardATR(
 __in SCARDCONTEXT hSC,
 __in LPBYTE pbAtr,
 __in DWORD cbAtr)
{
    LONG lReturn = NO_ERROR;
    LPWSTR pwszCardName = NULL;

    // Parameter checks
    if (NULL == hSC || NULL == pbAtr || 0 == cbAtr)
    {
    wprintf(L"Invalid parameter in IntroduceCardATR.\n");
    return ERROR_INVALID_PARAMETER;
    }

    // Generate a name for the card
    lReturn = GenerateCardName(&pwszCardName);
    if (NO_ERROR != lReturn)
    {
        wprintf(L"Failed to generate card name with error 0x%x.\n", lReturn);
    }
     else
     {
         // Introduce the card to the system
         lReturn = SCardIntroduceCardType(
         hSC,
         pwszCardName,
         NULL,
         NULL,
         0,
         pbAtr,
         NULL,
         cbAtr);
         if (SCARD_S_SUCCESS != lReturn)
         {
             wprintf(L"Failed to introduce card '%s' to system with error 0x%x.\n", pwszCardName, lReturn);
         }
         else
         {
             // Set the provider name
             lReturn = SCardSetCardTypeProviderName(
             hSC,
             pwszCardName,
             SCARD_PROVIDER_CSP,
             CARD_CSP);
             if (SCARD_S_SUCCESS != lReturn)
             {
                 wprintf(L"Failed to set CSP for card '%s' with error 0x%x.\n", pwszCardName, lReturn);
             }
             else
             {
                 wprintf(L"Card '%s' has been successfully introduced to the system and has had Plug and Play disabled.\n", pwszCardName);
             }
         }
     }

    if (NULL != pwszCardName)
    {
    HeapFree(GetProcessHeap(), 0, pwszCardName);
    }

    return lReturn;
}

LONG ProcessCard(
 __in SCARDCONTEXT hSC,
 __in LPSCARD_READERSTATE pRdr)
{
    LONG lReturn = NO_ERROR;
    DWORD dwActiveProtocol = 0;
    DWORD cbAtr = MAX_ATR_LEN;
    DWORD dwIndex = 0;
    DWORD cchCards = SCARD_AUTOALLOCATE;
    LPWSTR pmszCards = NULL;
    BYTE rgbAtr[MAX_ATR_LEN] = {0};
    SCARDHANDLE hSCard = NULL;

    // Parameter checks
    if (NULL == hSC || NULL == pRdr)
    {
        wprintf(L"Invalid parameter in ProcessCard.\n");
    return ERROR_INVALID_PARAMETER;
     }

    // Connect to the card in the provided reader in shared mode
    lReturn = SCardConnect(
    hSC,
    pRdr->szReader,
    SCARD_SHARE_SHARED,
    SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
    &hSCard,
    &dwActiveProtocol);
     if (SCARD_S_SUCCESS != lReturn)
     {
         wprintf(L"Failed to connect to card in reader '%s' with error 0x%x.\n", pRdr->szReader, lReturn);
     }
     else
     {
         wprintf(L"Connected to card in reader '%s'.\n", pRdr->szReader);

        /*
         * In this spot, put any necessary calls needed to identify that this
         * is the type of card you are looking for. Usually this is done via
         * SCardTransmit calls. For this example, we will grab the ATR of every
         * inserted card.
         */
    
        // Obtain the ATR of the inserted card
        lReturn = SCardGetAttrib(
        hSCard,
        SCARD_ATTR_ATR_STRING,
        rgbAtr,
        &cbAtr);
         if (SCARD_S_SUCCESS != lReturn)
         {
             wprintf(L"Failed to obtain ATR of card in reader '%s' with error 0x%x.\n", pRdr->szReader, lReturn);
         }
         else
         {
             // Output the ATR
             wprintf(L"ATR of card in reader '%s':", pRdr->szReader);
             for (dwIndex = 0; dwIndex < cbAtr; dwIndex++)
             {
                 wprintf(L" %02x", rgbAtr[dwIndex]);
             }
             wprintf(L"\n");

            // Determine if the ATR is already in the Smart Card Database
             lReturn = SCardListCards(
             hSC,
             rgbAtr,
             NULL,
             0,
             (LPWSTR)&pmszCards,
             &cchCards);
             if (SCARD_S_SUCCESS != lReturn)
             {
                 wprintf(L"Failed to determine if card in reader '%s' is currently recognized by the system with error 0x%x. Skipping.\n", pRdr->szReader, lReturn);
             }
             else if (NULL == pmszCards || 0 == *pmszCards)
             {
                 // Card not found. We need to add it.
                 wprintf(L"Card in reader '%s' is not currently recognized by the system. Adding ATR.\n", pRdr->szReader);
                 lReturn = IntroduceCardATR(
                 hSC,
                 rgbAtr,
                 cbAtr);

                 // If an error occurs here, we will continue so we can try the next time
                 // the card is inserted as well as examine other readers.
             }
            else
            {
                wprintf(L"Card in reader '%s' is already known by the system. Not adding ATR.\n", pRdr->szReader);
            }
         }
     }

    // Disconnect from the card. We do not need to reset it.
    if (NULL != hSCard)
    {
    SCardDisconnect(hSCard, SCARD_LEAVE_CARD);
    }

    // Free resources
    if (NULL != pmszCards)
    {
    SCardFreeMemory(hSC, pmszCards);
    }

    return lReturn;
}

LONG MonitorReaders(
 __in SCARDCONTEXT hSC)
{
    LPWSTR pwszReaders = NULL;
    LPWSTR pwszOldReaders = NULL;
    LPWSTR pwszRdr = NULL;
    DWORD dwRet = ERROR_SUCCESS;
    DWORD cchReaders = SCARD_AUTOALLOCATE;
    DWORD dwRdrCount = 0;
    DWORD dwOldRdrCount = 0;
    DWORD dwIndex = 0;
    LONG lReturn = NO_ERROR;
    BOOL fDone = FALSE;
    SCARD_READERSTATE rgscState[MAXIMUM_SMARTCARD_READERS+1] = {0};
    SCARD_READERSTATE rgscOldState[MAXIMUM_SMARTCARD_READERS+1] = {0};
    LPSCARD_READERSTATE pRdr = NULL;

    // Parameter check
    if (NULL == hSC)
    {
    wprintf(L"Invalid parameter in MonitorReaders.\n");
    return ERROR_INVALID_PARAMETER;
    }

    // One of the entries for monitoring will be to detect new readers
    // The first time through the loop will be to detect whether
    // the system has any readers.
    rgscState[0].szReader = PNP_READER_NAME;
    rgscState[0].dwCurrentState = SCARD_STATE_UNAWARE;
    dwRdrCount = 1;

    while (!fDone)
    {
         while (!fDone)
         {
             // Wait for status changes to occur
             wprintf(L"Monitoring for changes.\n");
             lReturn = SCardGetStatusChange(
             hSC,
             INFINITE,
             rgscState,
             dwRdrCount);
             switch (lReturn)
             {
                 case SCARD_S_SUCCESS:
                 // Success
                 break;
                 case SCARD_E_CANCELLED:
                 // Monitoring is being cancelled
                 wprintf(L"Monitoring cancelled. Exiting.\n");
                 fDone = TRUE;
                 break;
                 default:
                 // Error occurred
                 wprintf(L"Error 0x%x occurred while monitoring reader states.\n", lReturn);
                 fDone = TRUE;
                 break;
             }

            if (!fDone)
             {
                 // Examine the status change for each reader, skipping the PnP notification reader
                 for (dwIndex = 1; dwIndex < dwRdrCount; dwIndex++)
                 {
                     pRdr = &rgscState[dwIndex];

                    // Determine if a card is now present in the reader and
                    // it can be communicated with.
                     if ((pRdr->dwCurrentState & SCARD_STATE_EMPTY ||
                     SCARD_STATE_UNAWARE == pRdr->dwCurrentState) &&
                     pRdr->dwEventState & SCARD_STATE_PRESENT &&
                     !(pRdr->dwEventState & SCARD_STATE_MUTE))
                     {
                         // A card has been inserted and is available.
                         // Grab its ATR for addition to the database.
                         wprintf(L"A card has been inserted into reader '%s'. Grabbing its ATR.\n", pRdr->szReader);
                         lReturn = ProcessCard(hSC, pRdr);

                        // If an error occurs here, we will continue so we can try the next time
                        // the card is inserted as well as examine other readers.
                     }

                    // Save off the new state of the reader
                    pRdr->dwCurrentState = pRdr->dwEventState;
                 }

                // Now see if the number of readers in the system has changed.
                // Save its new state as the current state for the next loop.
                pRdr = &rgscState[0];
                pRdr->dwCurrentState = pRdr->dwEventState;
                if (pRdr->dwEventState & SCARD_STATE_CHANGED)
                {
                    wprintf(L"Reader change detected.\n");
                    break;
                }
            }  
         }

     if (!fDone)
     {
         // Clean up previous loop
         if (NULL != pwszOldReaders)
         {
         SCardFreeMemory(hSC, pwszOldReaders);
         pwszOldReaders = NULL;
         }
         pwszReaders = NULL;
         cchReaders = SCARD_AUTOALLOCATE;

        // Save off PnP notification reader state and and list of readers previously found in the system
         memcpy_s(&rgscOldState[0], sizeof(SCARD_READERSTATE), &rgscState[0], sizeof(SCARD_READERSTATE));
         memset(rgscState, 0, sizeof(rgscState));
         dwOldRdrCount = dwRdrCount;
         pwszOldReaders = pwszReaders;

        // Obtain a list of all readers in the system
         wprintf(L"Building reader list.\n");
         lReturn = SCardListReaders(
         hSC,
         NULL,
         (LPWSTR)&pwszReaders,
         &cchReaders);
         switch (lReturn)
         {
             case SCARD_S_SUCCESS:
             // Success
             break;
             case SCARD_E_NO_READERS_AVAILABLE:
             // No readers in the system. This is OK.
             lReturn = SCARD_S_SUCCESS;
             break;
             default:
             // Error occurred
             wprintf(L"Failed to obtain list of readers with error 0x%x.\n", lReturn);
             fDone = TRUE;
             break;
         }

         // Build the reader list for monitoring - NULL indicates end-of-list
         // First entry is the PnP Notification entry.
         pRdr = rgscState;
         memcpy_s(&rgscState[0], sizeof(SCARD_READERSTATE), &rgscOldState[0], sizeof(SCARD_READERSTATE));
         pRdr++;
         pwszRdr = pwszReaders;
         while ((NULL != pwszRdr) && (0 != *pwszRdr))
         {
             BOOL fFound = FALSE;
             dwRdrCount++;

            // Look for an existing reader state from a previous loop
             for (dwIndex = 1; dwIndex < dwOldRdrCount; dwIndex++)
             {
                 if ((lstrlen(pwszRdr) == lstrlen(rgscOldState[dwIndex].szReader)) &&
                 (0 == lstrcmpi(pwszRdr, rgscOldState[dwIndex].szReader)))
                 {
                     // Found a match. Copy it.
                     memcpy_s(pRdr, sizeof(SCARD_READERSTATE), &rgscOldState[dwIndex], sizeof(SCARD_READERSTATE));
                     fFound = TRUE;
                     break;
                 }
             }

            if (!fFound)
                {
                    // New reader
                    pRdr->szReader = pwszRdr;
                    pRdr->dwCurrentState = SCARD_STATE_UNAWARE;
                }

            // Increment reader indices
            pRdr++;
            pwszRdr += lstrlen(pwszRdr)+1;
         }
     }
}

    // Clean up resources
     if (NULL != pwszReaders)
     {
         SCardFreeMemory(hSC, pwszReaders);
     }

    if (NULL != pwszOldReaders)
     {
         SCardFreeMemory(hSC, pwszOldReaders);
     }

    return lReturn;
}

LONG __cdecl main(
 VOID)
{
     DWORD dwRet = ERROR_SUCCESS;
     SCARDCONTEXT hSC = NULL;
     LONG lReturn = NO_ERROR;
     HANDLE hStartedEvent = NULL;

    // Get handle to event that will be signaled when the Smart Card Service is available
     hStartedEvent = SCardAccessStartedEvent();

    // Wait for the Smart Card Service to become available
     dwRet = WaitForSingleObject(hStartedEvent, INFINITE);
     if (WAIT_OBJECT_0 != dwRet)
     {
         wprintf(L"Wait for Smart Card Service failed with error 0x%x.\n", dwRet);
         lReturn = dwRet;
     }
     else
     {
         // Establish a system-level context with the Smart Card Service
         lReturn = SCardEstablishContext(
         SCARD_SCOPE_SYSTEM,
         NULL,
         NULL,
         &hSC);
         if (SCARD_S_SUCCESS != lReturn)
         {
         wprintf(L"Failed to establish context with the Smart Card Service with error 0x%x.\n", lReturn);
         }
         else
         {
             // Begin monitoring the readers in the system
             // This routine could be done in a separate thread so it can be cancelled via SCardCancel().
             lReturn = MonitorReaders(hSC);
         }
     }

    // Cleanup resources
     if (NULL != hSC)
     {
        SCardReleaseContext(hSC);
     }

    if (NULL != hStartedEvent)
     {
        SCardReleaseStartedEvent();
     }

    wprintf(L"Done.\n");

    return lReturn;
}

Referencias

Para obtener más información acerca de la solución de problemas plug and play de tarjeta inteligente, consulta la Guía para la solución de problemas de tarjetas inteligentes.