판독기에서 스마트 카드 삽입할 때 오류 메시지: 디바이스 드라이버 소프트웨어가 성공적으로 설치되지 않았습니다.

이 문서에서는 판독기에 스마트 카드 삽입할 때 발생하는 오류에 대한 솔루션을 제공합니다.

적용 대상: Windows 7 Service Pack 1, Windows Server 2012 R2
원본 KB 번호: 976832

증상

스마트 카드 판독기에 스마트 카드 삽입하면 Windows는 플러그 앤 플레이 서비스를 통해 카드 대한 스마트 카드 미니 드라이버를 다운로드하고 설치하려고 시도합니다. 스마트 카드 대한 드라이버를 Windows 업데이트, WSUS 또는 인트라넷 경로와 같이 미리 구성된 위치에서 사용할 수 없으며 사용자 지정 Crypto 서비스 공급자가 시스템에 아직 설치되어 있지 않으면 알림 영역에서 다음 오류 메시지가 표시됩니다.

디바이스 드라이버 소프트웨어가 성공적으로 설치되지 않았습니다.

자세한 내용은 여기를 클릭하십시오.

이 오류 메시지는 몇 초 후에 사라집니다.

또한 장치 관리자 다른 장치에서 스마트 카드 디바이스에는 DNF(드라이버를 찾을 수 없음)의 상태 있습니다.

이 오류를 resolve 위해 스마트 카드 발급자에서 다음 항목 중 하나를 가져와야 하는 경우가 많습니다.

  1. Windows 로그 스마트 카드 미니드라이버.
  2. Smart 카드 대한 사용자 지정 CSP(암호화 서비스 공급자)입니다.
  3. Windows 로고가 없는 스마트 카드 미니드라이버.
  4. ActiveX 컨트롤, PKCS#11 소프트웨어 또는 기타 사용자 지정 소프트웨어와 같은 기타 미들웨어.

그러나 사용자에게 이 목록에서 항목 3 또는 4만 제공된 경우 스마트 카드 시스템에서 계속 작동합니다. 그러나 사용자는 스마트 카드 삽입할 때마다 이 섹션에 언급된 오류 메시지를 받게 됩니다.

이 문제는 Windows 7, Windows Server 2008 R2 및 이후 버전의 두 운영 체제의 모든 릴리스에 영향을 줍니다.

원인

사용자가 추가 소프트웨어를 설치하지 않고도 카드 사용할 수 있는 받은 편지함 드라이버가 없는 한 모든 스마트 카드는 Windows에서 작동하는 추가 소프트웨어가 필요합니다. Windows 스마트 카드 프레임워크는 스마트 카드 판독기에 삽입될 때 Windows 업데이트 또는 WSUS 서버와 같은 다른 유사한 위치에서 스마트 카드 미니 드라이버를 자동으로 다운로드할 수 있도록 Windows 7에서 개선되었습니다. Windows 로고 프로그램에서 게시한 로고 요구 사항을 성공적으로 통과한 모든 스마트 카드는 이 기능을 활용합니다.

그러나 Windows에서 스마트 카드 사용하는 데 필요한 소프트웨어가 로고가 지정되지 않았거나 PKCS#11 드라이버, 사용자 지정 CSP, 미들웨어 또는 ActiveX 컨트롤과 같은 미니드라이버와 다른 유형의 소프트웨어인 경우 Microsoft가 스마트 카드 미니드라이버만 인증하기 때문에 자동 다운로드 옵션이 실패합니다. 따라서 사용자 지정 CSP가 아직 등록되지 않은 카드 삽입하는 경우 사용자는 사용자 지정 설치에서 사용자 컴퓨터에 설치된 추가 소프트웨어를 통해 스마트 카드 사용할 수 있더라도 스마트 카드 디바이스에 대한 드라이버 소프트웨어가 누락되었다는 오류 메시지를 받습니다.

해결 방법

스마트 카드는 사용자에게 표시되는 오류 메시지에도 불구하고 계속 작동하지만 스마트 카드 발급자, 공급업체 또는 제조업체는 다음 방법 중 하나를 사용하여 이 오류를 resolve 수 있습니다.

스마트 카드 미니 드라이버 구현

카드 발급자, 공급업체 및 제조업체는 스마트 카드 미니드라이버를 구현하고 Windows 로고 프로그램에 참여하여 스마트 카드 플러그 앤 플레이, 스마트 카드용 디바이스 단계 등과 같은 플랫폼에 도입된 향상된 기능을 활용할 것을 권장합니다.

스마트 카드 대한 NULL 드라이버 구현

Windows에서 스마트 카드 사용하도록 설정하기 위해 PKCS#11 드라이버, ActiveX 컨트롤 또는 기타 미들웨어와 같은 사용자 지정 소프트웨어가 필요하고 스마트 카드 미니드라이버 또는 사용자 지정 CSP를 구현하는 것이 실용적인 옵션이 아닌 경우 카드 발급자, 공급업체 또는 제조업체가 NULL 드라이버를 Windows 업데이트 제출하는 것을 고려하는 것이 좋습니다. Windows 업데이트 NULL 드라이버를 사용할 수 있도록 하는 일반적인 프로세스에서는 Winqual을 통해 성공적인 미분류 디바이스 제출이 필요합니다. 나중에 이러한 카드에 사용할 수 있는 미니 드라이버가 있는 경우 Windows 로고 프로그램에 참여하여 새 드라이버를 Windows 업데이트 업로드할 수 있습니다. 그런 다음 NULL 드라이버는 최종 사용자가 수동으로 다운로드하거나 선택적 업데이트를 사용하여 사용할 수 있습니다.

다음은 스마트 카드 NULL 드라이버에 대한 샘플 템플릿입니다.

;  
; 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"

샘플에서 DEVICE_ID 문자열에서 참조하는 하드웨어 디바이스 ID를 생성하려면 스마트 카드 미니 드라이버 사양의 지침을 따릅니다.

NULL 드라이버를 Microsoft에 제출하는 방법에 대한 자세한 내용은 Microsoft 고객 지원 서비스에 문의하세요.

관리되는 컴퓨터에 대한 그룹 정책 통해 스마트 카드 플러그 앤 플레이 사용 안 함

이 옵션은 관리자가 컴퓨터를 관리하는 엔터프라이즈 배포에만 권장되며, 엔터프라이즈에서 사용되는 스마트 카드로 작업하는 데 필요한 모든 소프트웨어는 SMS와 같은 소프트웨어 관리 도구를 사용하여 설치됩니다.

이 절차는 사용자 환경의 모든 스마트 카드에 영향을 주므로 다음 환경에서는 권장되지 않습니다.

  • 온라인 뱅킹과 같은 최종 사용자를 대상으로 하는 상용 배포.
  • 스마트 카드에 대한 플러그 앤 플레이 사용하지 않도록 설정하는 그룹 정책 사용하는 플러그 앤 플레이 스마트 카드와 플러그 앤 플레이 아닌 스마트 카드가 모두 포함된 환경입니다.

최종 사용자의 컴퓨터가 그룹 정책 같은 메커니즘으로 관리되는 기업에서는 스마트 카드 플러그 앤 플레이 사용하지 않도록 설정할 수 있습니다.

배포에서 플러그 앤 플레이 아닌 스마트 카드 솔루션만 사용하는 경우 클라이언트 컴퓨터의 로컬 관리자가 스마트 카드 플러그 앤 플레이 사용하지 않도록 설정할 수 있습니다. 스마트 카드 플러그 앤 플레이 사용하지 않도록 설정하면 스마트 카드 미니드라이버라고도 하는 스마트 카드 드라이버가 다운로드되지 않습니다. 또한 스마트 카드 플러그 앤 플레이 프롬프트를 방지합니다.

로컬 그룹 정책 스마트 카드 플러그 앤 플레이 사용하지 않도록 설정하려면 다음 단계를 수행합니다.

  1. 시작을 클릭하고 프로그램 및 파일 검색 상자에 gpedit.msc를 입력한 다음 Enter 키를 누릅니다.

  2. 컴퓨터 구성 아래의 콘솔 트리에서 관리 템플릿을 클릭합니다.

  3. 세부 정보 창에서 Windows 구성 요소를 두 번 클릭한 다음 스마트 카드를 두 번 클릭합니다.

  4. 스마트 카드 플러그 앤 플레이 서비스 켜기를 마우스 오른쪽 단추로 클릭한 다음 편집을 클릭합니다.

  5. 사용 안 함을 클릭한 다음 확인을 클릭합니다.

최종 사용자의 시스템을 변경하고 특정 카드에 대한 스마트 카드 플러그 앤 플레이 사용하지 않도록 설정

가장 권장되지 않은 옵션입니다. 카드가 레거시 카드이고 향후 스마트 카드 미니드라이버를 구현할 계획이 없는 경우에만 이 옵션을 사용해야 합니다. 이 옵션을 사용하려면 시스템에 이미 설치된 기존 소프트웨어가 최종 사용자 시스템에 이러한 CSP가 없더라도 시스템에 사용자 지정 CSP가 설치되어 있음을 Windows에 알려야 합니다. Windows에서 시스템에 사용자 지정 CSP가 이미 설치되어 있다고 판단되는 즉시 Windows는 스마트 카드 플러그 앤 플레이 통해 드라이버를 다운로드하고 설치하지 않습니다. 장치 관리자 표시되는 스마트 카드 디바이스에 대한 디바이스 노드가 만들어지지 않습니다. 이 옵션을 사용하면 시스템 레지스트리가 다음과 같이 변경됩니다.

하위 키: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\<Smart card name>

하위 키 레지스트리 항목:

  • ATR=16진수 DWORD: 스마트 카드 쉼표로 구분된 ATR입니다.

  • ATRMask= 16진수 DWORD: ATR에 적용할 쉼표로 구분된 마스크로 ATR에서 중요하지 않은 바이트를 마스킹합니다.

  • Crypto Provider=String 값: 스마트 카드 관련된 일부 문자열입니다.

예를 들면

하위 키: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\Fabrikam ATM card

하위 키 레지스트리 항목:

  • ATR=16진수 DWORD: 3b,dc,13,00,40,3a,49,54,47,5f,4d,53,43,53,50,5f,56,32
  • ATRMask= 16진수 DWORD: ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff
  • Crypto Provider=String 값: Fabrikam ATM 더미 공급자

x64비트 시스템의 경우 다음 하위 키에서 동일한 변경이 이루어져야 합니다. HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\Calais\SmartCards

시스템 레지스트리를 직접 변경하는 대신 WinSCard API를 사용하여 이러한 변경 내용을 시스템에 도입하는 것이 좋습니다. 다음은 스마트 카드 삽입을 감지한 다음, 카드 기존 공급자와 연결하는 레지스트리 항목을 만들어 특정 카드 대한 스마트 카드 플러그 앤 플레이 사용하지 않도록 설정하는 샘플 코드 예제입니다.

Microsoft에서 제공하는 프로그래밍 예제는 예시를 위한 것일 뿐이며 이와 관련하여 명시적이거나 묵시적인 어떠한 보증도 하지 않습니다. 이는 상품성이나 특정 목적에 대한 적합성의 묵시적인 보증을 포함하며 이에 제한되지 않습니다. 이 문서에서는 예제에 사용되고 있는 프로그래밍 언어와 프로시저를 만들고 디버깅하는 데 사용되는 도구를 사용자가 잘 알고 있는 것으로 가정합니다. Microsoft 지원 엔지니어가 특정 프로시저의 기능을 설명하여 도움을 줄 수 있습니다. 사용자의 특정 요구 사항에 맞도록 예제를 수정하여 추가 기능을 제공하거나 프로시저를 구성하지는 않습니다.

//==============================================================;
//
// 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;
}

참조

스마트 카드 플러그 앤 플레이 문제 해결에 대한 자세한 내용은 스마트 카드 문제 해결 가이드를 참조하세요.

데이터 수집

Microsoft 지원의 지원이 필요한 경우 배포 관련 문제에 TSS를 사용하여 정보 수집에 설명된 단계에 따라 정보를 수집하는 것이 좋습니다.