#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include "MarshalPC.h" #include #include "carddbg.h" #if (!defined(UNICODE) && !defined(_UNICODE)) #define SCARDSTATUS "SCardStatusA" #define GETOPENCARDNAME "GetOpenCardNameA" #else #define SCARDSTATUS "SCardStatusW" #define GETOPENCARDNAME "GetOpenCardNameW" #endif typedef LONG (WINAPI *LPFNSCARDESTABLISHCONTEXT)(DWORD, LPCVOID, LPCVOID, LPSCARDCONTEXT); typedef LONG (WINAPI *LPFNSCARDSTATUS)(SCARDHANDLE, LPTSTR, LPDWORD, LPDWORD, LPDWORD, LPBYTE, LPDWORD); typedef LONG (WINAPI *LPFNGETOPENCARDNAME)(LPOPENCARDNAME); typedef LONG (WINAPI *LPFNSCARDTRANSMIT)(SCARDHANDLE, LPCSCARD_IO_REQUEST, LPCBYTE, DWORD, LPSCARD_IO_REQUEST, LPBYTE, LPDWORD); typedef LONG (WINAPI *LPFNDISCONNECT)(SCARDHANDLE, DWORD); typedef LONG (WINAPI *LPFNSCARDRELEASECONTEXT)(SCARDCONTEXT); typedef LONG (WINAPI *LPFNSCARDBEGINTRANSACTION)(SCARDHANDLE); typedef LONG (WINAPI *LPFNSCARDENDTRANSACTION)(SCARDHANDLE, DWORD); typedef struct { HINSTANCE hPCSCInst; // winscard HINSTANCE hPCSCInst2; // scarddlg LPFNSCARDESTABLISHCONTEXT lpfnEstablish; LPFNGETOPENCARDNAME lpfnOpenCard; LPFNSCARDSTATUS lpfnStatus; LPFNSCARDTRANSMIT lpfnSCardTransmit; LPFNDISCONNECT lpfnDisconnect; LPFNSCARDRELEASECONTEXT lpfnRelease; LPFNSCARDBEGINTRANSACTION lpfnSCardBeginTransaction; LPFNSCARDENDTRANSACTION lpfnSCardEndTransaction; } PCSC_CTX; #define REAL_PCSC 0 #define FAKE_PCSC 1 static PCSC_CTX axCtx[2] = // Array of contexts for each PC/SC { {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL} }; static LONG _GetCardHandle(LPCWSTR mszCardNames, LPMYSCARDHANDLE phCard); static LONG WINAPI _MySCWTransmit(SCARDHANDLE hCard, LPCBYTE lpbIn, DWORD dwIn, LPBYTE lpBOut, LPDWORD pdwOut); // bEnd = 0 -> LITTLE_ENDIAN ; otherwise -> BIG_ENDIAN static LONG WINAPI hScwSetEndianness(SCARDHANDLE hCard, BOOL bEnd); #define MAX_NAME 256 SCODE WINAPI hScwAttachToCard(SCARDHANDLE hCard, LPCWSTR mszCardNames, LPSCARDHANDLE phCard) { return hScwAttachToCardEx(hCard, mszCardNames, 0x00, phCard); } SCODE WINAPI hScwAttachToCardEx(SCARDHANDLE hCard, LPCWSTR mszCardNames, BYTE byINS, LPSCARDHANDLE phCard) { LPMYSCARDHANDLE phTmp = NULL; SCODE ret = SCARD_S_SUCCESS; LOG_BEGIN_PROXY(hScwAttachToCardEx); __try { if (phCard == NULL) RaiseException(STATUS_INVALID_PARAM, 0, 0, 0); phTmp = (LPMYSCARDHANDLE)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, sizeof(MYSCARDHANDLE)); if ((hCard == (SCARDHANDLE)NULL) && (mszCardNames == NULL)) // No PC/SC { phTmp->dwFlags = FLAG_NOT_PCSC; *phCard = (SCARDHANDLE)phTmp; return ret; } if ((hCard == (SCARDHANDLE)NULL) || (mszCardNames == NULL)) // real PC/SC { // In this case we will be using PC/SC so we init the structure if (axCtx[REAL_PCSC].hPCSCInst == NULL) { axCtx[REAL_PCSC].hPCSCInst = LoadLibrary(_T("winscard.dll")); axCtx[REAL_PCSC].hPCSCInst2 = LoadLibrary(_T("scarddlg.dll")); } if ((axCtx[REAL_PCSC].hPCSCInst == NULL) || (axCtx[REAL_PCSC].hPCSCInst2 == NULL)) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); if (axCtx[REAL_PCSC].lpfnEstablish == NULL) { // Set all calls to DLL once and for all axCtx[REAL_PCSC].lpfnEstablish = (LPFNSCARDESTABLISHCONTEXT)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst, "SCardEstablishContext"); if (axCtx[REAL_PCSC].lpfnEstablish == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[REAL_PCSC].lpfnOpenCard = (LPFNGETOPENCARDNAME)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst2, GETOPENCARDNAME); if (axCtx[REAL_PCSC].lpfnOpenCard == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[REAL_PCSC].lpfnStatus = (LPFNSCARDSTATUS)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst, SCARDSTATUS); if (axCtx[REAL_PCSC].lpfnStatus == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[REAL_PCSC].lpfnSCardTransmit = (LPFNSCARDTRANSMIT)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst, "SCardTransmit"); if (axCtx[REAL_PCSC].lpfnSCardTransmit == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[REAL_PCSC].lpfnDisconnect = (LPFNDISCONNECT)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst, "SCardDisconnect"); if (axCtx[REAL_PCSC].lpfnDisconnect == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[REAL_PCSC].lpfnRelease = (LPFNSCARDRELEASECONTEXT)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst, "SCardReleaseContext"); if (axCtx[REAL_PCSC].lpfnRelease == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[REAL_PCSC].lpfnSCardBeginTransaction = (LPFNSCARDBEGINTRANSACTION)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst, "SCardBeginTransaction"); if (axCtx[REAL_PCSC].lpfnSCardBeginTransaction == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[REAL_PCSC].lpfnSCardEndTransaction = (LPFNSCARDENDTRANSACTION)GetProcAddress(axCtx[REAL_PCSC].hPCSCInst, "SCardEndTransaction"); if (axCtx[REAL_PCSC].lpfnSCardEndTransaction == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); } phTmp->dwFlags = FLAG_REALPCSC; } else if ((hCard == NULL_TX) || (mszCardNames == NULL_TX_NAME)) // PC/SC for simulator { // In this case we will be using PC/SC so we init the structure if (axCtx[FAKE_PCSC].hPCSCInst == NULL) { axCtx[FAKE_PCSC].hPCSCInst = LoadLibrary(_T("scwwinscard.dll")); axCtx[FAKE_PCSC].hPCSCInst2 = axCtx[FAKE_PCSC].hPCSCInst; } if (axCtx[FAKE_PCSC].hPCSCInst == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); if (axCtx[FAKE_PCSC].lpfnEstablish == NULL) { // Set all calls to DLL once and for all axCtx[FAKE_PCSC].lpfnEstablish = (LPFNSCARDESTABLISHCONTEXT)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst, "SCardEstablishContext"); if (axCtx[FAKE_PCSC].lpfnEstablish == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[FAKE_PCSC].lpfnOpenCard = (LPFNGETOPENCARDNAME)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst2, GETOPENCARDNAME); if (axCtx[FAKE_PCSC].lpfnOpenCard == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[FAKE_PCSC].lpfnStatus = (LPFNSCARDSTATUS)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst, SCARDSTATUS); if (axCtx[FAKE_PCSC].lpfnStatus == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[FAKE_PCSC].lpfnSCardTransmit = (LPFNSCARDTRANSMIT)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst, "SCardTransmit"); if (axCtx[FAKE_PCSC].lpfnSCardTransmit == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[FAKE_PCSC].lpfnDisconnect = (LPFNDISCONNECT)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst, "SCardDisconnect"); if (axCtx[FAKE_PCSC].lpfnDisconnect == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[FAKE_PCSC].lpfnRelease = (LPFNSCARDRELEASECONTEXT)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst, "SCardReleaseContext"); if (axCtx[FAKE_PCSC].lpfnRelease == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[FAKE_PCSC].lpfnSCardBeginTransaction = (LPFNSCARDBEGINTRANSACTION)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst, "SCardBeginTransaction"); if (axCtx[FAKE_PCSC].lpfnSCardBeginTransaction == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); axCtx[FAKE_PCSC].lpfnSCardEndTransaction = (LPFNSCARDENDTRANSACTION)GetProcAddress(axCtx[FAKE_PCSC].hPCSCInst, "SCardEndTransaction"); if (axCtx[FAKE_PCSC].lpfnSCardEndTransaction == NULL) RaiseException(STATUS_NO_SERVICE, 0, 0, 0); } phTmp->dwFlags = FLAG_FAKEPCSC; } else RaiseException(STATUS_INVALID_PARAM, 0, 0, 0); if ((hCard == (SCARDHANDLE)NULL) || (hCard == NULL_TX)) // Dialog wanted { phTmp->dwFlags |= FLAG_MY_ATTACH; ret = (SCODE)_GetCardHandle(mszCardNames, phTmp); } else phTmp->hCard = hCard; // Get the protocol if (ret == SCARD_S_SUCCESS) { DWORD dwLenReader, dwState, dwATRLength; BYTE abyATR[32]; TCHAR wszReader[MAX_NAME]; dwLenReader = MAX_NAME; dwATRLength = 32; ret = (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnStatus)( phTmp->hCard, wszReader, &dwLenReader, &dwState, &phTmp->dwProtocol, abyATR, &dwATRLength); // Set the default callback because we are in PC/SC config here if (ret == SCARD_S_SUCCESS) { phTmp->byINS = byINS; ret = hScwSetTransmitCallback((SCARDHANDLE)phTmp, _MySCWTransmit); if (ret == SCARD_S_SUCCESS) *phCard = (SCARDHANDLE)phTmp; } else RaiseException(STATUS_INTERNAL_ERROR, 0, 0, 0); } else RaiseException(STATUS_INTERNAL_ERROR, 0, 0, 0); } __except(EXCEPTION_EXECUTE_HANDLER) { if (phTmp) HeapFree(GetProcessHeap(), 0, phTmp); if (ret == SCARD_S_SUCCESS) { switch(GetExceptionCode()) { case STATUS_INVALID_PARAM: ret = MAKESCODE(SCW_E_INVALIDPARAM); break; case STATUS_NO_MEMORY: case STATUS_ACCESS_VIOLATION: ret = MAKESCODE(SCW_E_BUFFERTOOSMALL); break; case STATUS_NO_SERVICE: ret = SCARD_E_NO_SERVICE; break; default: ret = SCARD_F_UNKNOWN_ERROR; } } // Otherwise ret was set already } return ret; } SCODE WINAPI hScwDetachFromCard(SCARDHANDLE hCard) { SCODE ret = SCARD_S_SUCCESS; LPMYSCARDHANDLE phTmp = (LPMYSCARDHANDLE)hCard; LOG_BEGIN_PROXY(hScwDetachFromCard); __try { if (phTmp == NULL) RaiseException(STATUS_INVALID_PARAM, 0, 0, 0); if (phTmp->dwFlags & FLAG_MY_ATTACH) { (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnDisconnect)(phTmp->hCard, SCARD_LEAVE_CARD); (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnRelease)(phTmp->hCtx); } HeapFree(GetProcessHeap(), 0, phTmp); } __except(EXCEPTION_EXECUTE_HANDLER) { switch(GetExceptionCode()) { case STATUS_INVALID_PARAM: ret = MAKESCODE(SCW_E_INVALIDPARAM); break; default: ret = SCARD_F_UNKNOWN_ERROR; } } return ret; } static LONG WINAPI hScwSetEndianness(SCARDHANDLE hCard, BOOL bEnd) { SCODE ret = SCARD_S_SUCCESS; LPMYSCARDHANDLE phTmp = (LPMYSCARDHANDLE)hCard; LOG_BEGIN_PROXY(hScwSetEndianness); __try { if (phTmp == NULL) RaiseException(STATUS_INVALID_PARAM, 0, 0, 0); if (bEnd) phTmp->dwFlags |= FLAG_BIGENDIAN; else phTmp->dwFlags &= ~FLAG_BIGENDIAN; } __except(EXCEPTION_EXECUTE_HANDLER) { switch(GetExceptionCode()) { case STATUS_INVALID_PARAM: ret = MAKESCODE(SCW_E_INVALIDPARAM); break; default: ret = SCARD_F_UNKNOWN_ERROR; } } return ret; } // This is the right time to get proxy information // Is proxy supported, what's the endianness and the buffer size SCODE WINAPI hScwSetTransmitCallback(SCARDHANDLE hCard, LPFNSCWTRANSMITPROC lpfnProc) { SCODE ret = SCARD_S_SUCCESS; LPMYSCARDHANDLE phTmp = (LPMYSCARDHANDLE)hCard; __try { if ((phTmp == NULL) || (lpfnProc == NULL)) RaiseException(STATUS_INVALID_PARAM, 0, 0, 0); phTmp->lpfnTransmit = lpfnProc; // Get the proxy info { ISO_HEADER xHdr; BYTE rgData[] = {2, 108, 0, 116, 0, 0}; // 2 param, 0 as UINT8 by ref, 0 as UINT16 by ref BYTE rgRes[1+1+2]; // RetCode + Endianness + TheBuffer size TCOUNT OutLen = sizeof(rgRes); UINT16 wSW; xHdr.CLA = 0; xHdr.INS = phTmp->byINS; xHdr.P1 = 0xFF; // Get proxy config xHdr.P2 = 0x00; ret = hScwExecute(hCard, &xHdr, rgData, sizeof(rgData), rgRes, &OutLen, &wSW); if (SCARD_S_SUCCESS == ret) { // Status OK & expected length & RC=SCW_S_OK if ((wSW == 0x9000) && (OutLen == sizeof(rgRes)) && (rgRes[0] == 0)) // Version 1.0 { hScwSetEndianness(hCard, rgRes[1]); if (rgRes[1] == 0) // LITTLE_ENDIAN phTmp->bResLen = rgRes[2] - 2; // SW!!! else phTmp->bResLen = rgRes[3] - 2; // SW!!! phTmp->dwFlags |= FLAG_ISPROXY; phTmp->dwFlags |= VERSION_1_0; } else if ((wSW == 0x9011) && (OutLen == sizeof(rgRes) - 1)) // Version 1.1 { hScwSetEndianness(hCard, rgRes[0]); if (rgRes[0] == 0) // LITTLE_ENDIAN phTmp->bResLen = rgRes[1] - 2; // SW!!! else phTmp->bResLen = rgRes[2] - 2; // SW!!! phTmp->dwFlags |= FLAG_ISPROXY; phTmp->dwFlags |= VERSION_1_1; } // else there will be no proxy support but you can still use the Dll } else // There will be no proxy support though but you can still use the Dll ret = SCARD_S_SUCCESS; } } __except(EXCEPTION_EXECUTE_HANDLER) { switch(GetExceptionCode()) { case STATUS_INVALID_PARAM: ret = MAKESCODE(SCW_E_INVALIDPARAM); break; default: ret = SCARD_F_UNKNOWN_ERROR; } } return ret; } LONG WINAPI SCWTransmit(SCARDHANDLE hCard, LPCBYTE lpbIn, DWORD dwIn, LPBYTE lpBOut, LPDWORD pdwOut) { SCODE ret = SCARD_S_SUCCESS; LPMYSCARDHANDLE phTmp = (LPMYSCARDHANDLE)hCard; __try { if (phTmp == NULL) RaiseException(STATUS_INVALID_PARAM, 0, 0, 0); DebugPrintBytes(L"bytes transmitted", (PBYTE) lpbIn, dwIn); ret = (*phTmp->lpfnTransmit)(hCard, lpbIn, dwIn, lpBOut, pdwOut); if (NULL != pdwOut) DebugPrintBytes(L"bytes received", lpBOut, *pdwOut); } __except(EXCEPTION_EXECUTE_HANDLER) { switch(GetExceptionCode()) { case STATUS_INVALID_PARAM: ret = MAKESCODE(SCW_E_INVALIDPARAM); break; default: ret = SCARD_F_UNKNOWN_ERROR; } } return ret; } const SCARD_IO_REQUEST g_xIORT0 = { SCARD_PROTOCOL_T0, sizeof(SCARD_IO_REQUEST) }, g_xIORT1 = { SCARD_PROTOCOL_T1, sizeof(SCARD_IO_REQUEST) }; static LONG WINAPI _MySCWTransmit(SCARDHANDLE hCard, LPCBYTE lpbIn, DWORD dwIn, LPBYTE lpBOut, LPDWORD pdwOut) { SCARD_IO_REQUEST xIOR; LONG ret = SCARD_S_SUCCESS; LPMYSCARDHANDLE phTmp = (LPMYSCARDHANDLE)hCard; __try { if (phTmp == NULL) RaiseException(STATUS_INVALID_PARAM, 0, 0, 0); if (phTmp->dwProtocol == SCARD_PROTOCOL_T1) { memcpy(&xIOR, &g_xIORT1, sizeof(xIOR)); ret = (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnSCardTransmit)(phTmp->hCard, &xIOR, lpbIn, dwIn, &xIOR, lpBOut, pdwOut); } else { DWORD dwOut = *pdwOut; memcpy(&xIOR, &g_xIORT0, sizeof(xIOR)); __try { ret = (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnSCardBeginTransaction)(phTmp->hCard); if (ret == SCARD_S_SUCCESS) { ret = (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnSCardTransmit)(phTmp->hCard, &xIOR, lpbIn, dwIn, &xIOR, lpBOut, &dwOut); } if (ret == SCARD_S_SUCCESS) { if ((dwOut == 2) && ((lpBOut[0] == 0x61) || (lpBOut[0] == 0x9F))) { BYTE abGR[] = {0x00, 0xC0, 0x00, 0x00, 0x00}; abGR[4] = lpBOut[1]; ret = (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnSCardTransmit)(phTmp->hCard, &xIOR, abGR, 5, &xIOR, lpBOut, pdwOut); } else *pdwOut = dwOut; } } __finally { (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnSCardEndTransaction)(phTmp->hCard, SCARD_LEAVE_CARD); } } } __except(EXCEPTION_EXECUTE_HANDLER) { switch(GetExceptionCode()) { case STATUS_INVALID_PARAM: ret = MAKESCODE(SCW_E_INVALIDPARAM); break; default: ret = SCARD_F_UNKNOWN_ERROR; } } return ret; } static TCHAR lpstrGroupNames[] = _TEXT("SCard$DefaultReaders\0"); static LONG _GetCardHandle(LPCWSTR mszCardNames, LPMYSCARDHANDLE phCard) { LONG lRes; OPENCARDNAME xOCN; TCHAR wszReader[MAX_NAME]; TCHAR wszCard[MAX_NAME]; TCHAR wszCN[MAX_NAME]; DWORD len; LPCWSTR lpwstr = mszCardNames; LPTSTR lpstrCardNames = wszCN; xOCN.nMaxCardNames = 0; #if (!defined(UNICODE) && !defined(_UNICODE)) while (*lpwstr) { wsprintf(lpstrCardNames, "%S", lpwstr); // Conversion len = wcslen(lpwstr) + 1; // Add the trailing 0 xOCN.nMaxCardNames += len; lpwstr += len; lpstrCardNames += len; } #else while (*lpwstr) { wcscpy(lpstrCardNames, lpwstr); len = wcslen(lpwstr) + 1; // Add the trailing 0 xOCN.nMaxCardNames += len; lpwstr += len; lpstrCardNames += len; } #endif xOCN.nMaxCardNames++; // Add the trailing 0 *lpstrCardNames = 0; lRes = (*axCtx[phCard->dwFlags & FLAG_MASKPCSC].lpfnEstablish)(SCARD_SCOPE_USER, NULL, NULL, &phCard->hCtx); if (lRes == SCARD_S_SUCCESS) { xOCN.dwStructSize = sizeof(xOCN); xOCN.hwndOwner = NULL; // probably called from console anyway xOCN.hSCardContext = phCard->hCtx; xOCN.lpstrGroupNames = lpstrGroupNames; xOCN.nMaxGroupNames = sizeof(lpstrGroupNames)/sizeof(TCHAR); xOCN.lpstrCardNames = wszCN; xOCN.rgguidInterfaces = NULL; xOCN.cguidInterfaces = 0; xOCN.lpstrRdr = wszReader; xOCN.nMaxRdr = MAX_NAME/sizeof(TCHAR); xOCN.lpstrCard = wszCard; xOCN.nMaxCard = MAX_NAME/sizeof(TCHAR); xOCN.lpstrTitle = _TEXT("Insert Card:"); xOCN.dwFlags = SC_DLG_MINIMAL_UI; xOCN.pvUserData = NULL; xOCN.dwShareMode = SCARD_SHARE_SHARED; xOCN.dwPreferredProtocols = SCARD_PROTOCOL_T1 | SCARD_PROTOCOL_T0; xOCN.lpfnConnect = NULL; xOCN.lpfnCheck = NULL; xOCN.lpfnDisconnect = NULL; lRes = (*axCtx[phCard->dwFlags & FLAG_MASKPCSC].lpfnOpenCard)(&xOCN); } if (lRes == SCARD_S_SUCCESS) { phCard->hCard = xOCN.hCardHandle; } return lRes; } SCODE WINAPI hScwSCardBeginTransaction(SCARDHANDLE hCard) { SCODE ret; LPMYSCARDHANDLE phTmp = (LPMYSCARDHANDLE)hCard; if ((phTmp->dwFlags & FLAG_REALPCSC) == FLAG_REALPCSC) ret = (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnSCardBeginTransaction)(phTmp->hCard); else ret = SCARD_S_SUCCESS; // No transactions on simulator return ret; } SCODE WINAPI hScwSCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition) { SCODE ret; LPMYSCARDHANDLE phTmp = (LPMYSCARDHANDLE)hCard; if ((phTmp->dwFlags & FLAG_REALPCSC) == FLAG_REALPCSC) ret = (*axCtx[phTmp->dwFlags & FLAG_MASKPCSC].lpfnSCardEndTransaction)(phTmp->hCard, dwDisposition); else ret = SCARD_S_SUCCESS; // No transactions on simulator return ret; }