You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1418 lines
36 KiB
1418 lines
36 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
ScSearch
|
|
|
|
Abstract:
|
|
|
|
This file contains the outline implementation of
|
|
miscellaneous smart card search and check functions
|
|
for the Microsoft Smart Card Common Dialog
|
|
|
|
Author:
|
|
|
|
Amanda Matlosz 5/7/98
|
|
|
|
Environment:
|
|
|
|
Win32, C++ w/Exceptions, MFC
|
|
|
|
Revision History:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Includes
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
// #include <atlconv.cpp>
|
|
#include <winscard.h>
|
|
#include "ScSearch.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Helpers
|
|
//
|
|
|
|
LPSTR GetCardNameA(SCARDCONTEXT hSCardContext, LPBYTE pbAtr)
|
|
{
|
|
LPSTR szCard = NULL;
|
|
DWORD dwNameLength = SCARD_AUTOALLOCATE;
|
|
|
|
LONG lReturn = SCardListCardsA(
|
|
hSCardContext,
|
|
pbAtr,
|
|
NULL,
|
|
0,
|
|
(LPSTR)&szCard,
|
|
&dwNameLength);
|
|
|
|
if (SCARD_S_SUCCESS != lReturn)
|
|
{
|
|
if (NULL != szCard)
|
|
{
|
|
SCardFreeMemory(hSCardContext, (PVOID)szCard);
|
|
szCard = NULL;
|
|
}
|
|
}
|
|
|
|
return szCard;
|
|
}
|
|
|
|
|
|
LPWSTR GetCardNameW(SCARDCONTEXT hSCardContext, LPBYTE pbAtr)
|
|
{
|
|
LPWSTR szCard = NULL;
|
|
DWORD dwNameLength = SCARD_AUTOALLOCATE;
|
|
|
|
LONG lReturn = SCardListCardsW(
|
|
hSCardContext,
|
|
pbAtr,
|
|
NULL,
|
|
0,
|
|
(LPWSTR)&szCard,
|
|
&dwNameLength);
|
|
|
|
if (SCARD_S_SUCCESS != lReturn)
|
|
{
|
|
if (NULL != szCard)
|
|
{
|
|
SCardFreeMemory(hSCardContext, (PVOID)szCard);
|
|
szCard = NULL;
|
|
}
|
|
}
|
|
|
|
return szCard;
|
|
}
|
|
|
|
|
|
DWORD AnsiMStringCount(LPCSTR msz)
|
|
{
|
|
DWORD dwRet = 0;
|
|
|
|
while (NULL != msz)
|
|
{
|
|
if (NULL == *msz)
|
|
{
|
|
msz = NULL;
|
|
}
|
|
else
|
|
{
|
|
DWORD cchLen = strlen(msz);
|
|
msz = msz+(sizeof(CHAR)*(cchLen+1));
|
|
dwRet++;
|
|
}
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
void
|
|
MatchInterfacesW(
|
|
SCARDCONTEXT hSCardContext,
|
|
LPCGUID pGUIDInterfaces,
|
|
DWORD cGUIDInterfaces,
|
|
CTextMultistring& mstrAllCards)
|
|
{
|
|
//
|
|
// Append all cards that support the requested guidInterfaces
|
|
//
|
|
|
|
if (NULL != pGUIDInterfaces && 0 < cGUIDInterfaces)
|
|
{
|
|
LONG lResult = SCARD_S_SUCCESS;
|
|
LPWSTR szListCards = NULL;
|
|
DWORD dwCards = SCARD_AUTOALLOCATE;
|
|
|
|
lResult = SCardListCardsW(
|
|
hSCardContext,
|
|
NULL,
|
|
pGUIDInterfaces,
|
|
cGUIDInterfaces,
|
|
(LPWSTR) &szListCards,
|
|
&dwCards);
|
|
|
|
|
|
if (SCARD_S_SUCCESS == lResult)
|
|
{
|
|
// append them to the list of all possible card names
|
|
mstrAllCards += szListCards;
|
|
}
|
|
|
|
if (NULL != szListCards)
|
|
{
|
|
SCardFreeMemory(hSCardContext, (PVOID)szListCards);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Methods
|
|
//
|
|
|
|
__inline BOOL CheckProtocols(
|
|
IN DWORD dwProtocols)
|
|
{
|
|
return
|
|
SCARD_PROTOCOL_T0 == (SCARD_PROTOCOL_T0 & dwProtocols) ||
|
|
SCARD_PROTOCOL_T1 == (SCARD_PROTOCOL_T1 & dwProtocols) ||
|
|
(SCARD_PROTOCOL_Tx) == ((SCARD_PROTOCOL_Tx) & dwProtocols) ||
|
|
SCARD_PROTOCOL_RAW == (SCARD_PROTOCOL_RAW & dwProtocols) ||
|
|
SCARD_PROTOCOL_DEFAULT == (SCARD_PROTOCOL_DEFAULT & dwProtocols) ||
|
|
SCARD_PROTOCOL_OPTIMAL == (SCARD_PROTOCOL_OPTIMAL & dwProtocols);
|
|
}
|
|
|
|
/*++
|
|
|
|
BOOL CheckOCN:
|
|
|
|
Routine performs simple parameter checks on OPENCARDNAME and
|
|
OPENCARD_SEARCH_CRITERIA structs
|
|
|
|
Return Value:
|
|
|
|
FALSE if invalid parameter, otherwise TRUE.
|
|
|
|
Author:
|
|
|
|
Amanda Matlosz 09/16/1998
|
|
|
|
--*/
|
|
BOOL CheckOCN(LPOPENCARDNAMEA_EX pOCNA)
|
|
{
|
|
if (NULL == pOCNA)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (pOCNA->dwStructSize != sizeof(OPENCARDNAMEA_EX))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (NULL == pOCNA->hSCardContext)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == pOCNA->nMaxRdr || NULL == pOCNA->lpstrRdr)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == pOCNA->nMaxCard || NULL == pOCNA->lpstrCard)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL fOneFlagOnly = FALSE;
|
|
if (0 != (pOCNA->dwFlags & SC_DLG_MINIMAL_UI))
|
|
{
|
|
fOneFlagOnly = TRUE;
|
|
}
|
|
if (0 != (pOCNA->dwFlags & SC_DLG_FORCE_UI))
|
|
{
|
|
if (fOneFlagOnly)
|
|
{
|
|
return FALSE;
|
|
}
|
|
fOneFlagOnly = TRUE;
|
|
}
|
|
if (0 != (pOCNA->dwFlags & SC_DLG_NO_UI))
|
|
{
|
|
if (fOneFlagOnly)
|
|
{
|
|
return FALSE;
|
|
}
|
|
fOneFlagOnly = TRUE;
|
|
}
|
|
|
|
// Now check POPENCARD_SEARCH_CRITERIAA, if applicable
|
|
if (NULL != pOCNA->pOpenCardSearchCriteria)
|
|
{
|
|
DWORD dwShareMode = pOCNA->pOpenCardSearchCriteria->dwShareMode;
|
|
DWORD dwPreferredProtocols = pOCNA->pOpenCardSearchCriteria->dwPreferredProtocols;
|
|
|
|
if (NULL != pOCNA->pOpenCardSearchCriteria->lpfnCheck)
|
|
{
|
|
// either lpfnConnect and lpfnDisconnect must be set
|
|
if ( (NULL != pOCNA->pOpenCardSearchCriteria->lpfnConnect) &&
|
|
(NULL != pOCNA->pOpenCardSearchCriteria->lpfnDisconnect) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if ( ( SCARD_SHARE_EXCLUSIVE == dwShareMode ||
|
|
SCARD_SHARE_SHARED == dwShareMode ||
|
|
SCARD_SHARE_DIRECT == dwShareMode ) &&
|
|
CheckProtocols(dwPreferredProtocols)
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// if all the tests have passed, it must be OK.
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL CheckOCN(LPOPENCARDNAMEW_EX pOCNW)
|
|
{
|
|
if (NULL == pOCNW)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (pOCNW->dwStructSize != sizeof(OPENCARDNAMEW_EX))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (NULL == pOCNW->hSCardContext)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == pOCNW->nMaxRdr || NULL == pOCNW->lpstrRdr)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 == pOCNW->nMaxCard || NULL == pOCNW->lpstrCard)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL fOneFlagOnly = FALSE;
|
|
if (0 != (pOCNW->dwFlags & SC_DLG_MINIMAL_UI))
|
|
{
|
|
fOneFlagOnly = TRUE;
|
|
}
|
|
if (0 != (pOCNW->dwFlags & SC_DLG_FORCE_UI))
|
|
{
|
|
if (fOneFlagOnly)
|
|
{
|
|
return FALSE;
|
|
}
|
|
fOneFlagOnly = TRUE;
|
|
}
|
|
if (0 != (pOCNW->dwFlags & SC_DLG_NO_UI))
|
|
{
|
|
if (fOneFlagOnly)
|
|
{
|
|
return FALSE;
|
|
}
|
|
fOneFlagOnly = TRUE;
|
|
}
|
|
|
|
// Now check POPENCARD_SEARCH_CRITERIAW, if applicable
|
|
if (NULL != pOCNW->pOpenCardSearchCriteria)
|
|
{
|
|
DWORD dwShareMode = pOCNW->pOpenCardSearchCriteria->dwShareMode;
|
|
DWORD dwPreferredProtocols = pOCNW->pOpenCardSearchCriteria->dwPreferredProtocols;
|
|
|
|
if (NULL != pOCNW->pOpenCardSearchCriteria->lpfnCheck)
|
|
{
|
|
// either lpfnConnect and lpfnDisconnect must be set
|
|
if ( (NULL != pOCNW->pOpenCardSearchCriteria->lpfnConnect) &&
|
|
(NULL != pOCNW->pOpenCardSearchCriteria->lpfnDisconnect) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if ( ( SCARD_SHARE_EXCLUSIVE == dwShareMode ||
|
|
SCARD_SHARE_SHARED == dwShareMode ||
|
|
SCARD_SHARE_DIRECT == dwShareMode ) &&
|
|
CheckProtocols(dwPreferredProtocols)
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// if all the tests have passed, it must be OK.
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
void ListAllOKCardNames:
|
|
|
|
Routine creates a multistring list of card names that match the
|
|
search criteria for both ATR (as determined by the list of card
|
|
names) and supported interfaces. This list of card names is not
|
|
displayed to the user, but is used internally.
|
|
|
|
This is a complete list of possible cards. Note that the list of
|
|
supported interfaces is an _additive_ criteria, not a restrictive
|
|
one.
|
|
|
|
TODO: ?? recheck that assumption re: additive vs. restrictive. ??
|
|
|
|
Arguments:
|
|
|
|
pOCSC - POPENCARD_SEARCH_CRITERIAA
|
|
|
|
mstrAllCards - referece to a CTextMultistring to take list of all OK cards
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Author:
|
|
|
|
Amanda Matlosz 09/16/1998
|
|
|
|
--*/
|
|
void ListAllOKCardNames(LPOPENCARDNAMEA_EX pOCNA, CTextMultistring& mstrAllCards) // ANSI
|
|
{
|
|
POPENCARD_SEARCH_CRITERIAA pOCSC = pOCNA->pOpenCardSearchCriteria;
|
|
|
|
if ((NULL == pOCSC) || (NULL == pOCSC->lpstrCardNames))
|
|
{
|
|
// No cards specified
|
|
return;
|
|
}
|
|
mstrAllCards = pOCSC->lpstrCardNames;
|
|
|
|
//
|
|
// List all cards that support the requested guidInterfaces
|
|
//
|
|
|
|
MatchInterfacesW(
|
|
pOCNA->hSCardContext,
|
|
pOCSC->rgguidInterfaces,
|
|
pOCSC->cguidInterfaces,
|
|
mstrAllCards);
|
|
|
|
}
|
|
|
|
|
|
void ListAllOKCardNames(LPOPENCARDNAMEW_EX pOCNW, CTextMultistring& mstrAllCards) // UNICODE
|
|
{
|
|
POPENCARD_SEARCH_CRITERIAW pOCSC = pOCNW->pOpenCardSearchCriteria;
|
|
|
|
if ((NULL == pOCSC) || (NULL == pOCSC->lpstrCardNames))
|
|
{
|
|
// No cards specified
|
|
return;
|
|
}
|
|
mstrAllCards = pOCSC->lpstrCardNames;
|
|
|
|
//
|
|
// List all cards that support the requested guidInterfaces
|
|
//
|
|
|
|
MatchInterfacesW(
|
|
pOCNW->hSCardContext,
|
|
pOCSC->rgguidInterfaces,
|
|
pOCSC->cguidInterfaces,
|
|
mstrAllCards);
|
|
}
|
|
|
|
|
|
// pdwOKCards is used so the caller can decide what additional actions to take
|
|
// based on how many suitable cards were found
|
|
LONG NoUISearch(OPENCARDNAMEA_EX* pOCN, DWORD* pdwOKCards, LPCSTR mszCards) // ansi-only
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
_ASSERTE(pOCN != NULL);
|
|
|
|
*pdwOKCards = 0;
|
|
|
|
LONG lReturn = SCARD_S_SUCCESS;
|
|
|
|
SCARD_READERSTATEA* pReaderStatus = NULL;
|
|
DWORD dwReaders = 0;
|
|
|
|
const DWORD dwMeetsCriteria = 1;
|
|
LPSTR szGroupNames = NULL;
|
|
LPSTR szReaderNames = NULL;
|
|
DWORD dw=0;
|
|
DWORD dwNameLength = SCARD_AUTOALLOCATE;
|
|
|
|
//
|
|
// get list of readers we'll consider
|
|
//
|
|
|
|
if (NULL != pOCN->pOpenCardSearchCriteria &&
|
|
NULL != pOCN->pOpenCardSearchCriteria->lpstrGroupNames)
|
|
{
|
|
szGroupNames = pOCN->pOpenCardSearchCriteria->lpstrGroupNames;
|
|
}
|
|
else
|
|
{
|
|
szGroupNames = W2A(SCARD_DEFAULT_READERS);
|
|
}
|
|
|
|
lReturn = SCardListReadersA(pOCN->hSCardContext,
|
|
szGroupNames,
|
|
(LPSTR)&szReaderNames,
|
|
&dwNameLength);
|
|
|
|
if(SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
//
|
|
// use the list of readers to build a readerstate array
|
|
//
|
|
dwReaders = AnsiMStringCount(szReaderNames);
|
|
_ASSERTE(0 != dwReaders);
|
|
pReaderStatus = new SCARD_READERSTATEA[dwReaders];
|
|
if (NULL != pReaderStatus)
|
|
{
|
|
LPCSTR pchReader = szReaderNames;
|
|
int nIndex = 0;
|
|
|
|
memset(pReaderStatus, 0, sizeof(SCARD_READERSTATEA) * dwReaders);
|
|
|
|
while(0 != *pchReader)
|
|
{
|
|
pReaderStatus[nIndex].szReader = pchReader;
|
|
pReaderStatus[nIndex].dwCurrentState = SCARD_STATE_UNAWARE;
|
|
pchReader += strlen(pchReader)+1;
|
|
nIndex++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lReturn = SCARD_E_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there are no readers, there's no point to go on.
|
|
//
|
|
|
|
if (0 == dwReaders || SCARD_S_SUCCESS != lReturn)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Search for cards: use SCardLocateCards to find cards that match
|
|
// ATR & interfaces supported if mszCards is not empty,
|
|
// otherwise use SCardGetStatusChange() to look for any card
|
|
//
|
|
|
|
if (0 < AnsiMStringCount(mszCards))
|
|
{
|
|
lReturn = SCardLocateCardsA(pOCN->hSCardContext,
|
|
mszCards,
|
|
pReaderStatus,
|
|
dwReaders);
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
// for any that have SCARD_STATE_ATRMATCH set & don't have SCARD_STATE_EXCLUSIVE,
|
|
// set pvUserData to dwMeetsCriteria
|
|
|
|
for (dw=0; dw<dwReaders; dw++)
|
|
{
|
|
if ((pReaderStatus[dw].dwEventState & SCARD_STATE_ATRMATCH) &&
|
|
!(pReaderStatus[dw].dwEventState & SCARD_STATE_EXCLUSIVE))
|
|
{
|
|
pReaderStatus[dw].pvUserData = ULongToHandle(dwMeetsCriteria);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lReturn = SCardGetStatusChangeA(
|
|
pOCN->hSCardContext,
|
|
0,
|
|
pReaderStatus,
|
|
dwReaders);
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
// for any that have SCARD_STATE_PRESENT & don't have SCARD_STATE_EXCLUSIVE,
|
|
// set pvUserData to dwMeetsCriteria
|
|
|
|
for (dw=0; dw<dwReaders; dw++)
|
|
{
|
|
if ((pReaderStatus[dw].dwEventState & SCARD_STATE_PRESENT) &&
|
|
!(pReaderStatus[dw].dwEventState & SCARD_STATE_EXCLUSIVE))
|
|
{
|
|
pReaderStatus[dw].pvUserData = ULongToHandle(dwMeetsCriteria);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// check each card to see if it's OK (meets callback criteria)
|
|
//
|
|
|
|
for (dw=0; dw<dwReaders; dw++)
|
|
{
|
|
if (dwMeetsCriteria == (DWORD)((DWORD_PTR)pReaderStatus[dw].pvUserData))
|
|
{
|
|
pReaderStatus[dw].pvUserData = NULL;
|
|
|
|
// get the card name for CheckCardCallback;
|
|
// if there's no name, don't accept it
|
|
|
|
LPSTR szCard = NULL;
|
|
szCard = GetCardNameA(pOCN->hSCardContext, pReaderStatus[dw].rgbAtr);
|
|
|
|
if (NULL != szCard && NULL != *szCard)
|
|
{
|
|
if (CheckCardCallback((LPSTR)pReaderStatus[dw].szReader, szCard, pOCN))
|
|
{
|
|
pReaderStatus[dw].pvUserData = ULongToHandle(dwMeetsCriteria);
|
|
(*pdwOKCards)++;
|
|
}
|
|
}
|
|
|
|
if (NULL != szCard)
|
|
{
|
|
SCardFreeMemory(pOCN->hSCardContext, (PVOID)szCard);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if SC_DLG_MIN_UI and only found one OK card, connect to that one
|
|
// if SC_DLG_NO_UI and found one or more OK card, connect to the first
|
|
//
|
|
|
|
if ((0 != (pOCN->dwFlags & SC_DLG_MINIMAL_UI) && 1 == *pdwOKCards) ||
|
|
(0 != (pOCN->dwFlags & SC_DLG_NO_UI) && 1 <= *pdwOKCards))
|
|
{
|
|
DWORD dwSel = 0;
|
|
while (dwSel < dwReaders)
|
|
{
|
|
if (dwMeetsCriteria == (DWORD)((DWORD_PTR)pReaderStatus[dwSel].pvUserData))
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwSel++;
|
|
}
|
|
|
|
_ASSERTE(dwSel<dwReaders); // Why didn't it find any OK cards?
|
|
|
|
// get the card name for SetFinalCardSelection; can be NULL
|
|
LPSTR szCard = NULL;
|
|
szCard = GetCardNameA(pOCN->hSCardContext, pReaderStatus[dwSel].rgbAtr);
|
|
|
|
lReturn = SetFinalCardSelection((LPSTR)(pReaderStatus[dwSel].szReader), szCard, pOCN);
|
|
|
|
// let go of CardName
|
|
if (NULL != szCard)
|
|
{
|
|
SCardFreeMemory(pOCN->hSCardContext, (PVOID)szCard);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
lReturn = SCARD_E_CANCELLED; // any non-SCARD_S_SUCCESS return val will do
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
|
|
if (NULL != pReaderStatus)
|
|
{
|
|
delete [] pReaderStatus;
|
|
}
|
|
if (NULL != szReaderNames)
|
|
{
|
|
SCardFreeMemory(pOCN->hSCardContext, (PVOID)szReaderNames);
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
|
|
LONG NoUISearch(OPENCARDNAMEW_EX* pOCN, DWORD* pdwOKCards, LPCWSTR mszCards) // UNICODE
|
|
{
|
|
_ASSERTE(pOCN != NULL);
|
|
|
|
*pdwOKCards = 0;
|
|
|
|
LONG lReturn = SCARD_S_SUCCESS;
|
|
|
|
SCARD_READERSTATEW* pReaderStatus = NULL;
|
|
DWORD dwReaders = 0;
|
|
|
|
const DWORD dwMeetsCriteria = 1;
|
|
LPWSTR szGroupNames = NULL;
|
|
LPWSTR szReaderNames = NULL;
|
|
DWORD dw=0;
|
|
DWORD dwNameLength = SCARD_AUTOALLOCATE;
|
|
|
|
//
|
|
// get list of readers we'll consider
|
|
//
|
|
|
|
if (NULL != pOCN->pOpenCardSearchCriteria &&
|
|
NULL != pOCN->pOpenCardSearchCriteria->lpstrGroupNames)
|
|
{
|
|
szGroupNames = pOCN->pOpenCardSearchCriteria->lpstrGroupNames;
|
|
}
|
|
else
|
|
{
|
|
szGroupNames = SCARD_DEFAULT_READERS;
|
|
}
|
|
|
|
lReturn = SCardListReadersW(pOCN->hSCardContext,
|
|
szGroupNames,
|
|
(LPWSTR)&szReaderNames,
|
|
&dwNameLength);
|
|
|
|
if(SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
//
|
|
// use the list of readers to build a readerstate array
|
|
//
|
|
dwReaders = MStringCount(szReaderNames);
|
|
_ASSERTE(0 != dwReaders);
|
|
pReaderStatus = new SCARD_READERSTATEW[dwReaders];
|
|
if (NULL != pReaderStatus)
|
|
{
|
|
LPCWSTR pchReader = szReaderNames;
|
|
int nIndex = 0;
|
|
|
|
memset(pReaderStatus, 0, sizeof(SCARD_READERSTATEW) * dwReaders);
|
|
|
|
while(0 != *pchReader)
|
|
{
|
|
pReaderStatus[nIndex].szReader = pchReader;
|
|
pReaderStatus[nIndex].dwCurrentState = SCARD_STATE_UNAWARE;
|
|
pchReader += lstrlen(pchReader)+1;
|
|
nIndex++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lReturn = SCARD_E_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there are no readers, there's no point to go on.
|
|
//
|
|
|
|
if (0 == dwReaders)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Search for cards: use SCardLocateCards to find cards that match
|
|
// ATR & interfaces supported if mszCards is not empty,
|
|
// otherwise use SCardGetStatusChange() to look for any card
|
|
//
|
|
|
|
if (0 < MStringCount(mszCards))
|
|
{
|
|
lReturn = SCardLocateCardsW(pOCN->hSCardContext,
|
|
mszCards,
|
|
pReaderStatus,
|
|
dwReaders);
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
// for any that have SCARD_STATE_ATRMATCH set & don't have SCARD_STATE_EXCLUSIVE,
|
|
// set pvUserData to dwMeetsCriteria
|
|
|
|
for (dw=0; dw<dwReaders; dw++)
|
|
{
|
|
if ((pReaderStatus[dw].dwEventState & SCARD_STATE_ATRMATCH) &&
|
|
!(pReaderStatus[dw].dwEventState & SCARD_STATE_EXCLUSIVE))
|
|
{
|
|
pReaderStatus[dw].pvUserData = ULongToHandle(dwMeetsCriteria);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lReturn = SCardGetStatusChangeW(
|
|
pOCN->hSCardContext,
|
|
0,
|
|
pReaderStatus,
|
|
dwReaders);
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
// for any that have SCARD_STATE_PRESENT & don't have SCARD_STATE_EXCLUSIVE,
|
|
// set pvUserData to dwMeetsCriteria
|
|
|
|
for (dw=0; dw<dwReaders; dw++)
|
|
{
|
|
if ((pReaderStatus[dw].dwEventState & SCARD_STATE_PRESENT) &&
|
|
!(pReaderStatus[dw].dwEventState & SCARD_STATE_EXCLUSIVE))
|
|
{
|
|
pReaderStatus[dw].pvUserData = ULongToHandle(dwMeetsCriteria);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// check each card to see if it's OK (meets callback criteria)
|
|
//
|
|
|
|
for (dw=0; dw<dwReaders; dw++)
|
|
{
|
|
if (dwMeetsCriteria == (DWORD)((DWORD_PTR)pReaderStatus[dw].pvUserData))
|
|
{
|
|
pReaderStatus[dw].pvUserData = NULL;
|
|
|
|
// get the card name for CheckCardCallback;
|
|
// if there's no name, don't accept it
|
|
|
|
LPWSTR szCard = NULL;
|
|
szCard = GetCardNameW(pOCN->hSCardContext, pReaderStatus[dw].rgbAtr);
|
|
|
|
if (NULL != szCard && 0 != lstrlen(szCard))
|
|
{
|
|
if (CheckCardCallback((LPWSTR)pReaderStatus[dw].szReader, szCard, pOCN))
|
|
{
|
|
pReaderStatus[dw].pvUserData = ULongToHandle(dwMeetsCriteria);
|
|
(*pdwOKCards)++;
|
|
}
|
|
}
|
|
|
|
// let go of CardName
|
|
if (NULL != szCard)
|
|
{
|
|
SCardFreeMemory(pOCN->hSCardContext, (PVOID)szCard);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if SC_DLG_MIN_UI and only found one OK card, connect to that one
|
|
// if SC_DLG_NO_UI and found one or more OK card, connect to the first
|
|
//
|
|
|
|
if ((0 != (pOCN->dwFlags & SC_DLG_MINIMAL_UI) && 1 == *pdwOKCards) ||
|
|
(0 != (pOCN->dwFlags & SC_DLG_NO_UI) && 1 <= *pdwOKCards))
|
|
{
|
|
DWORD dwSel = 0;
|
|
while (dwSel < dwReaders)
|
|
{
|
|
if (dwMeetsCriteria == (DWORD)((DWORD_PTR)pReaderStatus[dwSel].pvUserData))
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwSel++;
|
|
}
|
|
|
|
_ASSERTE(dwSel<dwReaders); // Why didn't it find any OK cards?
|
|
|
|
// get the card name for SetFinalCardSelection; can be NULL
|
|
LPWSTR szCard = NULL;
|
|
szCard = GetCardNameW(pOCN->hSCardContext, pReaderStatus[dwSel].rgbAtr);
|
|
|
|
lReturn = SetFinalCardSelection((LPWSTR)(pReaderStatus[dwSel].szReader), szCard, pOCN);
|
|
|
|
// let go of CardName
|
|
if (NULL != szCard)
|
|
{
|
|
SCardFreeMemory(pOCN->hSCardContext, (PVOID)szCard);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
lReturn = SCARD_E_CANCELLED; // any non-SCARD_S_SUCCESS return val will do
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
|
|
if (NULL != pReaderStatus)
|
|
{
|
|
delete [] pReaderStatus;
|
|
}
|
|
if (NULL != szReaderNames)
|
|
{
|
|
SCardFreeMemory(pOCN->hSCardContext, (PVOID)szReaderNames);
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
BOOL CheckCardCallback:
|
|
|
|
Routine connects to the indicated card/reader, calls user-
|
|
supplied "check" function, and disconnects from card
|
|
according to search criteria members.
|
|
|
|
Arguments:
|
|
|
|
szReader - indicated reader.
|
|
|
|
szCard - indicated card name.
|
|
|
|
pOCN - OPENCARDNAME_EX struct containing search criteria
|
|
|
|
Return Value:
|
|
|
|
TRUE if connection, check, and disconnection succeeds, otherwise
|
|
FALSE.
|
|
|
|
Author:
|
|
|
|
Amanda Matlosz 07/09/1998
|
|
|
|
--*/
|
|
BOOL CheckCardCallback(LPSTR szReader, LPSTR szCard, OPENCARDNAMEA_EX* pOCN)
|
|
{
|
|
BOOL fReturn = FALSE;
|
|
|
|
//
|
|
// Check parameters
|
|
//
|
|
|
|
// if no check callback, succeed by default
|
|
if (!(NULL != pOCN->pOpenCardSearchCriteria &&
|
|
NULL != pOCN->pOpenCardSearchCriteria->lpfnCheck))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// in order to connect, we either need to have dwShareMode set to non-0
|
|
// or both the conenct and disconnect callbacks have to be valid.
|
|
// This should have been caught in SetOCN()!
|
|
if (0 == pOCN->pOpenCardSearchCriteria->dwShareMode &&
|
|
(NULL == pOCN->pOpenCardSearchCriteria->lpfnConnect
|
|
|| NULL == pOCN->pOpenCardSearchCriteria->lpfnDisconnect))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LPOCNCONNPROCA lpfnConnect = pOCN->pOpenCardSearchCriteria->lpfnConnect;
|
|
LPOCNCHKPROC lpfnCheck = pOCN->pOpenCardSearchCriteria->lpfnCheck;
|
|
LPOCNDSCPROC lpfnDisconnect = pOCN->pOpenCardSearchCriteria->lpfnDisconnect;
|
|
PVOID pvUserData = pOCN->pOpenCardSearchCriteria->pvUserData;
|
|
|
|
//
|
|
// Connect, preferably through the callback
|
|
//
|
|
|
|
SCARDHANDLE hCard = NULL;
|
|
|
|
if (NULL != lpfnConnect)
|
|
{
|
|
hCard = lpfnConnect(pOCN->hSCardContext,
|
|
szReader,
|
|
szCard,
|
|
pvUserData);
|
|
}
|
|
else
|
|
{
|
|
DWORD dw = 0; // Don't need to know the active protocol
|
|
|
|
LONG lReturn = SCardConnectA(pOCN->hSCardContext,
|
|
(LPCSTR)szReader,
|
|
pOCN->pOpenCardSearchCriteria->dwShareMode,
|
|
pOCN->pOpenCardSearchCriteria->dwPreferredProtocols,
|
|
&hCard,
|
|
&dw);
|
|
|
|
// TODO: ?? maybe want to trace failure of lReturn... ??
|
|
}
|
|
|
|
// if the connect failed, there's no way we can check the card!
|
|
if (NULL == hCard)
|
|
{
|
|
return fReturn;
|
|
}
|
|
|
|
//
|
|
// Check the card
|
|
//
|
|
|
|
fReturn = lpfnCheck(pOCN->hSCardContext,
|
|
hCard,
|
|
pvUserData);
|
|
|
|
//
|
|
// Disconnect from the card and clean up.
|
|
//
|
|
|
|
if (NULL != lpfnDisconnect)
|
|
{
|
|
lpfnDisconnect(pOCN->hSCardContext,
|
|
hCard,
|
|
pvUserData);
|
|
}
|
|
else
|
|
{
|
|
SCardDisconnect(hCard, SCARD_UNPOWER_CARD);
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
|
|
BOOL CheckCardCallback(LPWSTR szReader, LPWSTR szCard, OPENCARDNAMEW_EX* pOCN)
|
|
{
|
|
BOOL fReturn = FALSE;
|
|
|
|
//
|
|
// Check parameters
|
|
//
|
|
|
|
// if no check callback, succeed by default
|
|
if (!(NULL != pOCN->pOpenCardSearchCriteria &&
|
|
NULL != pOCN->pOpenCardSearchCriteria->lpfnCheck))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// in order to connect, we either need to have dwShareMode set to non-0
|
|
// or both the conenct and disconnect callbacks have to be valid.
|
|
// This should have been caught in SetOCN()!
|
|
if (0 == pOCN->pOpenCardSearchCriteria->dwShareMode &&
|
|
(NULL == pOCN->pOpenCardSearchCriteria->lpfnConnect
|
|
|| NULL == pOCN->pOpenCardSearchCriteria->lpfnDisconnect))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LPOCNCONNPROCW lpfnConnect = pOCN->pOpenCardSearchCriteria->lpfnConnect;
|
|
LPOCNCHKPROC lpfnCheck = pOCN->pOpenCardSearchCriteria->lpfnCheck;
|
|
LPOCNDSCPROC lpfnDisconnect = pOCN->pOpenCardSearchCriteria->lpfnDisconnect;
|
|
PVOID pvUserData = pOCN->pOpenCardSearchCriteria->pvUserData;
|
|
|
|
//
|
|
// Connect, preferably through the callback
|
|
//
|
|
|
|
SCARDHANDLE hCard = NULL;
|
|
|
|
if (NULL != lpfnConnect)
|
|
{
|
|
hCard = lpfnConnect(pOCN->hSCardContext,
|
|
szReader,
|
|
szCard,
|
|
pvUserData);
|
|
}
|
|
else
|
|
{
|
|
DWORD dw = 0; // Don't need to know the active protocol
|
|
|
|
LONG lReturn = SCardConnectW(pOCN->hSCardContext,
|
|
(LPCWSTR)szReader,
|
|
pOCN->pOpenCardSearchCriteria->dwShareMode,
|
|
pOCN->pOpenCardSearchCriteria->dwPreferredProtocols,
|
|
&hCard,
|
|
&dw);
|
|
|
|
// TODO: ?? maybe want to trace failure of lReturn... ??
|
|
}
|
|
|
|
// if the connect failed, there's no way we can check the card!
|
|
if (NULL == hCard)
|
|
{
|
|
return fReturn;
|
|
}
|
|
|
|
//
|
|
// Check the card
|
|
//
|
|
|
|
fReturn = lpfnCheck(pOCN->hSCardContext,
|
|
hCard,
|
|
pvUserData);
|
|
|
|
//
|
|
// Disconnect from the card and clean up.
|
|
//
|
|
|
|
if (NULL != lpfnDisconnect)
|
|
{
|
|
lpfnDisconnect(pOCN->hSCardContext,
|
|
hCard,
|
|
pvUserData);
|
|
}
|
|
else
|
|
{
|
|
SCardDisconnect(hCard, SCARD_UNPOWER_CARD);
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
BOOL CheckCardAll:
|
|
|
|
Routine checks to see whether or not the indicated card meets
|
|
the search criteria. ATR, supported interfaces, and callbacks
|
|
are all checked.
|
|
|
|
Arguments:
|
|
|
|
pReader - Reader containing card to check acceptability of
|
|
|
|
pOCN - OPENCARDNAME[A|W]_EX struct containing search criteria, etc.
|
|
|
|
mszCards - a multistring containing names of ALL acceptable cards
|
|
|
|
Return Value:
|
|
|
|
A BOOL value indicating the acceptibility of the given card.
|
|
|
|
Author:
|
|
|
|
Amanda Matlosz 07/09/1998
|
|
|
|
--*/
|
|
BOOL CheckCardAll(CSCardReaderState* pReader, OPENCARDNAMEA_EX* pOCN, LPCWSTR mszCards) // ANSI
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
BOOL fReturn = FALSE; // assume no match
|
|
|
|
_ASSERTE(NULL != pReader && NULL != pOCN);
|
|
if (NULL == pReader || NULL == pOCN)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check Name & interfaces
|
|
|
|
if (!pReader->strCard.IsEmpty())
|
|
{
|
|
LPCWSTR msz = mszCards;
|
|
if (0 < MStringCount(msz))
|
|
{
|
|
msz = FirstString(mszCards);
|
|
while (!fReturn && msz != NULL)
|
|
{
|
|
// compare if OK fReturn = TRUE;
|
|
if (0 == pReader->strCard.Compare(msz) || 0==lstrlen(msz))
|
|
{
|
|
fReturn = TRUE;
|
|
}
|
|
else
|
|
{
|
|
msz = NextString(msz);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fReturn = TRUE;
|
|
}
|
|
}
|
|
|
|
// Check Callback
|
|
|
|
if (fReturn)
|
|
{
|
|
// turn CStrings into LPSTRs
|
|
LPSTR szReader = W2A(pReader->strReader);
|
|
LPSTR szCard = W2A(pReader->strCard);
|
|
|
|
fReturn = CheckCardCallback(szReader, szCard, pOCN);
|
|
}
|
|
|
|
// note in readerstate whether or not the card passed all tests
|
|
pReader->fOK = fReturn;
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
|
|
BOOL CheckCardAll(CSCardReaderState* pReader, OPENCARDNAMEW_EX* pOCN, LPCWSTR mszCards) // UNICODE
|
|
{
|
|
BOOL fReturn = FALSE; // assume no match
|
|
|
|
_ASSERTE(NULL != pReader && NULL != pOCN);
|
|
if (NULL == pReader || NULL == pOCN)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check Name & interfaces
|
|
|
|
if (!pReader->strCard.IsEmpty())
|
|
{
|
|
LPCWSTR msz = mszCards;
|
|
if (0 < MStringCount(msz))
|
|
{
|
|
msz = FirstString(mszCards);
|
|
while (!fReturn && msz != NULL)
|
|
{
|
|
// compare if OK fReturn = TRUE;
|
|
if (0 == pReader->strCard.Compare(msz) || 0==lstrlen(msz))
|
|
{
|
|
fReturn = TRUE;
|
|
}
|
|
else
|
|
{
|
|
msz = NextString(msz);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fReturn = TRUE;
|
|
}
|
|
}
|
|
|
|
// Check Callback
|
|
|
|
if (fReturn)
|
|
{
|
|
// turn CStrings into LPSTRs
|
|
LPWSTR szReader = pReader->strReader.GetBuffer(1);
|
|
LPWSTR szCard = pReader->strCard.GetBuffer(1);
|
|
|
|
fReturn = CheckCardCallback(szReader, szCard, pOCN);
|
|
|
|
pReader->strReader.ReleaseBuffer();
|
|
pReader->strCard.ReleaseBuffer();
|
|
}
|
|
|
|
// note in readerstate whether or not the card passed all tests
|
|
pReader->fOK = fReturn;
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
LONG SetFinalCardSelection:
|
|
|
|
Routine connects to a selected reader, and sets the user-provided structs
|
|
to contain the reader&cardname. Returns an error if the user-provided struct's
|
|
buffers aren't long enough.
|
|
|
|
Arguments:
|
|
|
|
dwSelectedReader - index used to select which reader to connect to.
|
|
|
|
Return Value:
|
|
|
|
A LONG value indicating the status of the requested action.
|
|
See the Smartcard header files for additional information.
|
|
|
|
Author:
|
|
|
|
Amanda Matlosz 07/09/1998
|
|
|
|
--*/
|
|
LONG SetFinalCardSelection(LPSTR szReader, LPSTR szCard, OPENCARDNAMEA_EX* pOCN) // ANSI
|
|
{
|
|
_ASSERTE(NULL != pOCN);
|
|
|
|
pOCN->hCardHandle = NULL;
|
|
LONG lReturn = SCARD_S_SUCCESS;
|
|
|
|
//
|
|
// Set return values in OCN
|
|
//
|
|
|
|
if (NULL == szReader)
|
|
{
|
|
lReturn = SCARD_F_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if (pOCN->nMaxRdr >= strlen(szReader)+1)
|
|
{
|
|
::CopyMemory( (LPVOID)pOCN->lpstrRdr,
|
|
(CONST LPVOID)szReader,
|
|
strlen(szReader)+1);
|
|
}
|
|
else
|
|
{
|
|
pOCN->nMaxRdr = strlen(szReader)+1;
|
|
lReturn = SCARD_E_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
if (NULL == szCard)
|
|
{
|
|
lReturn = SCARD_F_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if (pOCN->nMaxCard >= strlen(szCard)+1)
|
|
{
|
|
::CopyMemory( (LPVOID)pOCN->lpstrCard,
|
|
(CONST LPVOID)szCard,
|
|
strlen(szCard)+1);
|
|
}
|
|
else
|
|
{
|
|
pOCN->nMaxCard = strlen(szCard)+1;
|
|
lReturn = SCARD_E_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Connect to card only if we're still in a successful state,
|
|
//
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
if(NULL != pOCN->lpfnConnect)
|
|
{
|
|
pOCN->hCardHandle = pOCN->lpfnConnect(
|
|
pOCN->hSCardContext,
|
|
szReader,
|
|
szCard,
|
|
pOCN->pvUserData);
|
|
}
|
|
else if (0 != pOCN->dwShareMode)
|
|
{
|
|
lReturn = SCardConnectA(pOCN->hSCardContext,
|
|
(LPCSTR)szReader,
|
|
pOCN->dwShareMode,
|
|
pOCN->dwPreferredProtocols,
|
|
&pOCN->hCardHandle,
|
|
&pOCN->dwActiveProtocol);
|
|
|
|
if (SCARD_S_SUCCESS != lReturn)
|
|
{
|
|
// must return hCardHandle of NULL
|
|
pOCN->hCardHandle = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
|
|
LONG SetFinalCardSelection(LPWSTR szReader, LPWSTR szCard, OPENCARDNAMEW_EX* pOCN) // UNICODE
|
|
{
|
|
_ASSERTE(NULL != pOCN);
|
|
|
|
pOCN->hCardHandle = NULL;
|
|
LONG lReturn = SCARD_S_SUCCESS;
|
|
|
|
//
|
|
// Set return values in OCN
|
|
//
|
|
|
|
if (NULL == szReader)
|
|
{
|
|
lReturn = SCARD_F_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if (pOCN->nMaxRdr >= (DWORD)lstrlen(szReader)+1)
|
|
{
|
|
::CopyMemory( (LPVOID)pOCN->lpstrRdr,
|
|
(CONST LPVOID)szReader,
|
|
sizeof(WCHAR)*(lstrlen(szReader)+1));
|
|
}
|
|
else
|
|
{
|
|
pOCN->nMaxRdr = lstrlen(szReader)+1;
|
|
lReturn = SCARD_E_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
if (NULL == szCard)
|
|
{
|
|
lReturn = SCARD_F_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if (pOCN->nMaxCard >= (DWORD)lstrlen(szCard)+1)
|
|
{
|
|
::CopyMemory( (LPVOID)pOCN->lpstrCard,
|
|
(CONST LPVOID)szCard,
|
|
sizeof(WCHAR)*(lstrlen(szCard)+1));
|
|
}
|
|
else
|
|
{
|
|
pOCN->nMaxCard = lstrlen(szCard)+1;
|
|
lReturn = SCARD_E_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Connect to card only if we're still in a successful state,
|
|
//
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
if(NULL != pOCN->lpfnConnect)
|
|
{
|
|
pOCN->hCardHandle = pOCN->lpfnConnect(
|
|
pOCN->hSCardContext,
|
|
szReader,
|
|
szCard,
|
|
pOCN->pvUserData);
|
|
}
|
|
else if (0 != pOCN->dwShareMode)
|
|
{
|
|
lReturn = SCardConnectW(pOCN->hSCardContext,
|
|
(LPCWSTR)szReader,
|
|
pOCN->dwShareMode,
|
|
pOCN->dwPreferredProtocols,
|
|
&pOCN->hCardHandle,
|
|
&pOCN->dwActiveProtocol);
|
|
|
|
if (SCARD_S_SUCCESS != lReturn)
|
|
{
|
|
// must return hCardHandle of NULL
|
|
pOCN->hCardHandle = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return lReturn;
|
|
}
|