/*++ Copyright (C) Microsoft Corporation, 2000 Module Name: scuisupp Abstract: Support for smart card certificate selection UI Author: Klaus Schutz --*/ #include "stdafx.h" #include #include #include #include #include "calaislb.h" #include "scuisupp.h" #include "StatMon.h" // smart card reader status monitor #include "scevents.h" #include typedef struct _READER_DATA { CERT_ENUM CertEnum; LPTSTR pszReaderName; LPTSTR pszCardName; LPTSTR pszCSPName; } READER_DATA, *PREADER_DATA; typedef struct _THREAD_DATA { // handle to heap of this thread HANDLE hHeap; // the smart card context we use SCARDCONTEXT hSCardContext; // the window we send messages to HWND hWindow; // event to signal that the monitor thread can terminate HANDLE hClose; // thread handle for the monitor thread HANDLE hThread; // number of readers detected DWORD dwNumReaders; // messages to be sent to parent UINT msgReaderArrival; UINT msgReaderRemoval; UINT msgSmartCardInsertion; UINT msgSmartCardRemoval; UINT msgSmartCardStatus; UINT msgSmartCardCertAvail; // reader state array PSCARD_READERSTATE rgReaders; // number of removed readers DWORD dwRemovedReaders; // pointer array of removed reader data PREADER_DATA *ppRemovedReaderData; } THREAD_DATA, *PTHREAD_DATA; #ifndef TEST #define SC_DEBUG(a) #else #define SC_DEBUG(a) _DebugPrint a #undef PostMessage #define PostMessage(a,b,c,d) _PostMessage(a, b, c, d) #undef RegisterWindowMessage #define RegisterWindowMessage(a) _RegisterWindowMessage(a) void __cdecl _DebugPrint( LPCTSTR szFormat, ... ) { TCHAR szBuffer[1024]; va_list ap; va_start(ap, szFormat); _vstprintf(szBuffer, szFormat, ap); OutputDebugString(szBuffer); _tprintf(szBuffer); } #define MAX_MESSAGES 10 static struct { UINT dwMessage; LPCTSTR lpMessage; } Messages[MAX_MESSAGES]; UINT _RegisterWindowMessage( LPCTSTR lpString ) { for (DWORD dwIndex = 0; dwIndex < MAX_MESSAGES; dwIndex += 1) { if (Messages[dwIndex].lpMessage == NULL) { break; } if (_tcscmp(lpString, Messages[dwIndex].lpMessage) == 0) { return Messages[dwIndex].dwMessage; } } Messages[dwIndex].lpMessage = lpString; Messages[dwIndex].dwMessage = dwIndex; return dwIndex; } LPCTSTR _GetWindowMessageString( UINT dwMessage ) { for (DWORD dwIndex = 0; dwIndex < MAX_MESSAGES; dwIndex += 1) { if (Messages[dwIndex].lpMessage == NULL) { return TEXT("(NOT DEFINED)"); } if (dwMessage == Messages[dwIndex].dwMessage) { return Messages[dwIndex].lpMessage; } } return TEXT("(INTERNAL ERROR)"); } LRESULT _PostMessage( HWND hWindow, UINT Msg, WPARAM wParam, LPARAM lParam ) { SC_DEBUG((TEXT("Received message %s\n"), _GetWindowMessageString(Msg))); return 0; } #endif static LPCTSTR FirstString( IN LPCTSTR szMultiString ) /*++ FirstString: This routine returns a pointer to the first string in a multistring, or NULL if there aren't any. Arguments: szMultiString - This supplies the address of the current position within a Multi-string structure. Return Value: The address of the first null-terminated string in the structure, or NULL if there are no strings. Author: Doug Barlow (dbarlow) 11/25/1996 --*/ { LPCTSTR szFirst = NULL; try { if (0 != *szMultiString) szFirst = szMultiString; } catch (...) {} return szFirst; } static LPCTSTR NextString( IN LPCTSTR szMultiString) /*++ NextString: In some cases, the Smartcard API returns multiple strings, separated by Null characters, and terminated by two null characters in a row. This routine simplifies access to such structures. Given the current string in a multi-string structure, it returns the next string, or NULL if no other strings follow the current string. Arguments: szMultiString - This supplies the address of the current position within a Multi-string structure. Return Value: The address of the next Null-terminated string in the structure, or NULL if no more strings follow. Author: Doug Barlow (dbarlow) 8/12/1996 --*/ { LPCTSTR szNext; try { DWORD cchLen = lstrlen(szMultiString); if (0 == cchLen) szNext = NULL; else { szNext = szMultiString + cchLen + 1; if (0 == *szNext) szNext = NULL; } } catch (...) { szNext = NULL; } return szNext; } static void FreeReaderData( PTHREAD_DATA pThreadData, PREADER_DATA pReaderData ) { if (pThreadData->hSCardContext && pReaderData->pszCardName) { SCardFreeMemory(pThreadData->hSCardContext, pReaderData->pszCardName); pReaderData->pszCardName = NULL; pReaderData->CertEnum.pszCardName = NULL; } if (pThreadData->hSCardContext && pReaderData->pszCSPName) { SCardFreeMemory(pThreadData->hSCardContext, pReaderData->pszCSPName); pReaderData->pszCSPName = NULL; } if (pReaderData->CertEnum.pCertContext) { CertFreeCertificateContext(pReaderData->CertEnum.pCertContext); pReaderData->CertEnum.pCertContext = NULL; } pReaderData->CertEnum.dwStatus = 0; } static void UpdateCertificates( PTHREAD_DATA pThreadData, PSCARD_READERSTATE pReaderState ) { PREADER_DATA pReaderData = (PREADER_DATA) pReaderState->pvUserData; FreeReaderData(pThreadData, pReaderData); SC_DEBUG((TEXT("Enumerating certificates on %s...\n"), pReaderState->szReader)); LPTSTR pszContainerName = NULL; LPTSTR pszDefaultContainerName = NULL; PBYTE pbCert = NULL; HCRYPTKEY hKey = NULL; HCRYPTPROV hCryptProv = NULL; __try { pReaderData->CertEnum.dwStatus = SCARD_F_UNKNOWN_ERROR; // Get the name of the card DWORD dwAutoAllocate = SCARD_AUTOALLOCATE; LONG lReturn = SCardListCards( pThreadData->hSCardContext, pReaderState->rgbAtr, NULL, 0, (LPTSTR)&pReaderData->pszCardName, &dwAutoAllocate ); if (lReturn != SCARD_S_SUCCESS) { SC_DEBUG((TEXT("Failed to get card name for card in %s\n"), pReaderState->szReader)); pReaderData->CertEnum.dwStatus = lReturn; __leave; } pReaderData->CertEnum.pszCardName = pReaderData->pszCardName; dwAutoAllocate = SCARD_AUTOALLOCATE; lReturn = SCardGetCardTypeProviderName( pThreadData->hSCardContext, pReaderData->pszCardName, SCARD_PROVIDER_CSP, (LPTSTR)&pReaderData->pszCSPName, &dwAutoAllocate ); if (lReturn != SCARD_S_SUCCESS) { SC_DEBUG((TEXT("Failed to get CSP name from %s\n"), pReaderState->szReader)); pReaderData->CertEnum.dwStatus = SCARD_E_UNKNOWN_CARD; __leave; } pszContainerName = (LPTSTR) HeapAlloc( pThreadData->hHeap, HEAP_ZERO_MEMORY, (_tcslen(pReaderState->szReader) + 10) * sizeof(TCHAR) ); if (pszContainerName == NULL) { pReaderData->CertEnum.dwStatus = SCARD_E_NO_MEMORY; __leave; } _stprintf( pszContainerName, TEXT("\\\\.\\%s\\"), pReaderState->szReader); BOOL fSuccess = CryptAcquireContext( &hCryptProv, pszContainerName, pReaderData->pszCSPName, PROV_RSA_FULL, CRYPT_SILENT ); if (fSuccess == FALSE) { SC_DEBUG(( TEXT("Failed to acquire context on %s (%lx)\n"), pReaderState->szReader, GetLastError() )); pReaderData->CertEnum.dwStatus = GetLastError(); __leave; } // Get the default container name, so we can use it DWORD cbDefaultContainerName; fSuccess = CryptGetProvParam( hCryptProv, PP_CONTAINER, NULL, &cbDefaultContainerName, 0 ); if (!fSuccess) { SC_DEBUG((TEXT("Failed to get default container name from %s\n"), pReaderState->szReader)); pReaderData->CertEnum.dwStatus = GetLastError(); __leave; } pszDefaultContainerName = (LPTSTR) HeapAlloc(pThreadData->hHeap, HEAP_ZERO_MEMORY, cbDefaultContainerName * sizeof(TCHAR)); if (NULL == pszContainerName) { pReaderData->CertEnum.dwStatus = SCARD_E_NO_MEMORY; __leave; } fSuccess = CryptGetProvParam( hCryptProv, PP_CONTAINER, (PBYTE)pszDefaultContainerName, &cbDefaultContainerName, 0 ); if (!fSuccess) { pReaderData->CertEnum.dwStatus = GetLastError(); __leave; } fSuccess = CryptGetUserKey( hCryptProv, AT_KEYEXCHANGE, &hKey ); if (fSuccess == FALSE) { SC_DEBUG((TEXT("Failed to get key from %s\n"), pReaderState->szReader)); pReaderData->CertEnum.dwStatus = GetLastError(); __leave; } // get length of certificate DWORD cbCertLen = 0; fSuccess = CryptGetKeyParam( hKey, KP_CERTIFICATE, NULL, &cbCertLen, 0 ); DWORD dwError = GetLastError(); if (fSuccess == FALSE && dwError != ERROR_MORE_DATA) { SC_DEBUG((TEXT("Failed to get certificate from %s\n"), pReaderState->szReader)); pReaderData->CertEnum.dwStatus = GetLastError(); __leave; } pbCert = (LPBYTE) HeapAlloc(pThreadData->hHeap, HEAP_ZERO_MEMORY, cbCertLen); if (pbCert == NULL) { pReaderData->CertEnum.dwStatus = SCARD_E_NO_MEMORY; __leave; } // read the certificate off the card fSuccess = CryptGetKeyParam( hKey, KP_CERTIFICATE, pbCert, &cbCertLen, 0 ); if (fSuccess == FALSE) { SC_DEBUG((TEXT("Failed to get certificate from %s\n"), pReaderState->szReader)); pReaderData->CertEnum.dwStatus = GetLastError(); __leave; } PCERT_CONTEXT pCertContext = (PCERT_CONTEXT) CertCreateCertificateContext( X509_ASN_ENCODING, pbCert, cbCertLen ); if (pCertContext == NULL) { __leave; } pReaderData->CertEnum.dwStatus = SCARD_S_SUCCESS; pReaderData->CertEnum.pCertContext = pCertContext; SC_DEBUG((TEXT("Found new certificate on %s\n"), pReaderState->szReader)); PostMessage( pThreadData->hWindow, pThreadData->msgSmartCardCertAvail, (WPARAM) &pReaderData->CertEnum, 0 ); } __finally { // // free all allocated memory // if (NULL != pszContainerName) { HeapFree(pThreadData->hHeap, 0, pszContainerName); } if (NULL != pszDefaultContainerName) { HeapFree(pThreadData->hHeap, 0, pszDefaultContainerName); } if (NULL != pbCert) { HeapFree(pThreadData->hHeap, 0, pbCert); } if (NULL != hKey) { CryptDestroyKey(hKey); } if (NULL != hCryptProv) { CryptReleaseContext(hCryptProv, 0); } } } static void StopMonitorReaders( PTHREAD_DATA pThreadData ) { _ASSERT(pThreadData != NULL); SetEvent(pThreadData->hClose); if (pThreadData->hSCardContext) { SCardCancel(pThreadData->hSCardContext); } } static void RemoveCard( PTHREAD_DATA pThreadData, PREADER_DATA pReaderData ) { SC_DEBUG((TEXT("Smart Card removed from %s\n"), pReaderData->CertEnum.pszReaderName)); PostMessage( pThreadData->hWindow, pThreadData->msgSmartCardRemoval, (WPARAM) &pReaderData->CertEnum, 0 ); } static BOOL AddReader( PTHREAD_DATA pThreadData, LPCTSTR pszNewReader ) { PSCARD_READERSTATE pScardReaderState = (PSCARD_READERSTATE) HeapReAlloc( pThreadData->hHeap, HEAP_ZERO_MEMORY, pThreadData->rgReaders, (pThreadData->dwNumReaders + 1) * (sizeof(SCARD_READERSTATE)) ); if (pScardReaderState == NULL) { return FALSE; } pThreadData->rgReaders = pScardReaderState; PREADER_DATA pReaderData = (PREADER_DATA) HeapAlloc( pThreadData->hHeap, HEAP_ZERO_MEMORY, sizeof(READER_DATA) ); if (pReaderData == NULL) { return FALSE; } pThreadData->rgReaders[pThreadData->dwNumReaders].pvUserData = pReaderData; LPTSTR pszReaderName = (LPTSTR) HeapAlloc( pThreadData->hHeap, HEAP_ZERO_MEMORY, (_tcslen(pszNewReader) + 1) * sizeof(TCHAR) ); if (pszReaderName == NULL) { return FALSE; } _tcscpy(pszReaderName, pszNewReader); pReaderData->pszReaderName = pszReaderName; pThreadData->rgReaders[pThreadData->dwNumReaders].szReader = pszReaderName; pThreadData->rgReaders[pThreadData->dwNumReaders].dwCurrentState = SCARD_STATE_EMPTY; pReaderData->CertEnum.pszReaderName = (LPTSTR) pszReaderName; pThreadData->dwNumReaders++; PostMessage( pThreadData->hWindow, pThreadData->msgReaderArrival, (WPARAM) &pReaderData->CertEnum, 0 ); return TRUE; } static BOOL RemoveReader( PTHREAD_DATA pThreadData, PSCARD_READERSTATE pReaderState ) { PREADER_DATA pReaderData = (PREADER_DATA) pReaderState->pvUserData; if (pReaderState->dwCurrentState & SCARD_STATE_PRESENT) { RemoveCard( pThreadData, pReaderData ); } SC_DEBUG((TEXT("Reader %s removed\n"), pReaderData->CertEnum.pszReaderName)); PostMessage( pThreadData->hWindow, pThreadData->msgReaderRemoval, (WPARAM) &pReaderData->CertEnum, 0 ); // build an array of reader data that needs to be deleted on exit PREADER_DATA *ppRemovedReaderData = NULL; if (pThreadData->dwRemovedReaders == 0) { ppRemovedReaderData = (PREADER_DATA *) HeapAlloc( pThreadData->hHeap, HEAP_ZERO_MEMORY, sizeof(PREADER_DATA) ); } else { ppRemovedReaderData = (PREADER_DATA *) HeapReAlloc( pThreadData->hHeap, HEAP_ZERO_MEMORY, pThreadData->ppRemovedReaderData, (pThreadData->dwRemovedReaders + 1) * sizeof(PREADER_DATA) ); } if (ppRemovedReaderData == NULL) { return FALSE; } // add the reader data to the list of stuff that needs to be freed on exit pThreadData->ppRemovedReaderData = ppRemovedReaderData; pThreadData->ppRemovedReaderData[pThreadData->dwRemovedReaders] = pReaderData; for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; dwIndex += 1) { if (pReaderState == &pThreadData->rgReaders[dwIndex]) { // check if the reader we remove is not the last or the only one. if (pThreadData->dwNumReaders > 1 && dwIndex != pThreadData->dwNumReaders - 1) { // put the reader from the end of the list into this available slot pThreadData->rgReaders[dwIndex] = pThreadData->rgReaders[pThreadData->dwNumReaders - 1]; } // shrink the reader state array PSCARD_READERSTATE pReaders = (PSCARD_READERSTATE) HeapReAlloc( pThreadData->hHeap, 0, pThreadData->rgReaders, (pThreadData->dwNumReaders - 1) * sizeof(SCARD_READERSTATE) ); if (pReaders == NULL) { return FALSE; } pThreadData->rgReaders = pReaders; break; } } pThreadData->dwNumReaders -= 1; pThreadData->dwRemovedReaders += 1; return TRUE; } static BOOL RemoveAllReaders( PTHREAD_DATA pThreadData ) { if (pThreadData->rgReaders == NULL) { return TRUE; } // This loop will destroy all the readers starting with the first one // dwIndex doesn't have to be incremented. pThreadData->dwNumReaders is // decremented in RemoveReader for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; ) { if (RemoveReader( pThreadData, &pThreadData->rgReaders[dwIndex] ) == FALSE) { return FALSE; } } // Remove the PnP pseudo reader HeapFree( pThreadData->hHeap, 0, pThreadData->rgReaders ); pThreadData->rgReaders = NULL; return TRUE; } static DWORD StartMonitorReaders( LPVOID pData ) { PTHREAD_DATA pThreadData = (PTHREAD_DATA) pData; LPCTSTR szReaderNameList = NULL; // // We use this outer loop to restart in case the // resource manager was stopped // __try { pThreadData->rgReaders = (PSCARD_READERSTATE) HeapAlloc( pThreadData->hHeap, HEAP_ZERO_MEMORY, sizeof(SCARD_READERSTATE) ); if (pThreadData->rgReaders == NULL) { __leave; } pThreadData->rgReaders[0].szReader = SCPNP_NOTIFICATION; pThreadData->rgReaders[0].dwCurrentState = 0; pThreadData->dwNumReaders = 1; while (WaitForSingleObject(pThreadData->hClose, 0) == WAIT_TIMEOUT) { // Acquire context with resource manager LONG lReturn = SCardEstablishContext( SCARD_SCOPE_USER, NULL, NULL, &pThreadData->hSCardContext ); if (SCARD_S_SUCCESS != lReturn) { // The prev. call should never fail // It's better to terminate this thread. __leave; } szReaderNameList = NULL; DWORD dwAutoAllocate = SCARD_AUTOALLOCATE; // now list the available readers lReturn = SCardListReaders( pThreadData->hSCardContext, SCARD_DEFAULT_READERS, (LPTSTR)&szReaderNameList, &dwAutoAllocate ); if (SCARD_S_SUCCESS == lReturn) { // bugbug - this pointer should not be modified for (LPCTSTR szReader = FirstString( szReaderNameList ); szReader != NULL; szReader = NextString(szReader)) { BOOL fFound = FALSE; // now check if this reader is already in the reader array for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; dwIndex++) { if (lstrcmp( szReader, pThreadData->rgReaders[dwIndex].szReader ) == 0) { fFound = TRUE; break; } } if (fFound == FALSE) { if (AddReader(pThreadData, szReader) == FALSE) { __leave; } } } } BOOL fNewReader = FALSE; // analyze newly inserted cards while (WaitForSingleObject(pThreadData->hClose, 0) == WAIT_TIMEOUT && fNewReader == FALSE) { lReturn = SCardGetStatusChange( pThreadData->hSCardContext, INFINITE, pThreadData->rgReaders, pThreadData->dwNumReaders ); if (SCARD_E_SYSTEM_CANCELLED == lReturn) { // the smart card system has been stopped // send notification that all readers are gone if (RemoveAllReaders(pThreadData) == FALSE) { __leave; } // Wait until it restarted HANDLE hCalaisStarted = CalaisAccessStartedEvent(); if (hCalaisStarted == NULL) { // no way to recover. stop cert prop StopMonitorReaders(pThreadData); break; } HANDLE lHandles[2] = { hCalaisStarted, pThreadData->hClose }; lReturn = WaitForMultipleObjectsEx( 2, lHandles, FALSE, INFINITE, FALSE ); if (lReturn != WAIT_OBJECT_0) { // We stop if an error occured StopMonitorReaders(pThreadData); break; } // Otherwise the resource manager has been restarted break; } if (SCARD_S_SUCCESS != lReturn) { StopMonitorReaders(pThreadData); break; } // Enumerate the readers and for every card change send a message for (DWORD dwIndex = 1; dwIndex < pThreadData->dwNumReaders; dwIndex++) { // Check if the reader has been removed if ((pThreadData->rgReaders[dwIndex].dwEventState & SCARD_STATE_UNAVAILABLE)) { if (RemoveReader( pThreadData, &pThreadData->rgReaders[dwIndex] ) == FALSE) { __leave; } // Continue the loop with the same index dwIndex--; continue; } // check if this is a card insertion if ((pThreadData->rgReaders[dwIndex].dwCurrentState & SCARD_STATE_EMPTY) && (pThreadData->rgReaders[dwIndex].dwEventState & SCARD_STATE_PRESENT)) { PREADER_DATA pReaderData = (PREADER_DATA) pThreadData->rgReaders[dwIndex].pvUserData; SC_DEBUG((TEXT("Smart Card inserted into %s\n"), pThreadData->rgReaders[dwIndex].szReader)); PostMessage( pThreadData->hWindow, pThreadData->msgSmartCardInsertion, (WPARAM) &pReaderData->CertEnum, 0 ); // read in all certificates UpdateCertificates( pThreadData, &pThreadData->rgReaders[dwIndex] ); PostMessage( pThreadData->hWindow, pThreadData->msgSmartCardStatus, (WPARAM) &pReaderData->CertEnum, 0 ); } // check if this is a card removal if ((pThreadData->rgReaders[dwIndex].dwCurrentState & SCARD_STATE_PRESENT) && (pThreadData->rgReaders[dwIndex].dwEventState & SCARD_STATE_EMPTY)) { PREADER_DATA pReaderData = (PREADER_DATA) pThreadData->rgReaders[dwIndex].pvUserData; RemoveCard(pThreadData, pReaderData); // we can't update the certificates because it would delete // some memory data that the caller could reference. } // Update the "current state" of this reader pThreadData->rgReaders[dwIndex].dwCurrentState = pThreadData->rgReaders[dwIndex].dwEventState; } // check if a new reader showed up if ((pThreadData->dwNumReaders == 1 || pThreadData->rgReaders[0].dwCurrentState != 0) && pThreadData->rgReaders[0].dwEventState & SCARD_STATE_CHANGED) { fNewReader = TRUE; } pThreadData->rgReaders[0].dwCurrentState = pThreadData->rgReaders[0].dwEventState; } // Clean up if (NULL != szReaderNameList) { SCardFreeMemory(pThreadData->hSCardContext, (PVOID) szReaderNameList); szReaderNameList = NULL; } if (NULL != pThreadData->hSCardContext) { SCardReleaseContext(pThreadData->hSCardContext); pThreadData->hSCardContext = NULL; } } } __finally { if (NULL != szReaderNameList) { SCardFreeMemory(pThreadData->hSCardContext, (PVOID) szReaderNameList); } if (NULL != pThreadData->hSCardContext) { SCardReleaseContext(pThreadData->hSCardContext); pThreadData->hSCardContext = NULL; } RemoveAllReaders(pThreadData); SC_DEBUG((TEXT("Terminating monitor thread\n"))); } return TRUE; } HSCARDUI WINAPI SCardUIInit( HWND hWindow ) { PTHREAD_DATA pThreadData = (PTHREAD_DATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(THREAD_DATA)); BOOL fSuccess = FALSE; __try { if (pThreadData == NULL) { __leave; } pThreadData->hHeap = GetProcessHeap(); pThreadData->hWindow = hWindow; pThreadData->msgReaderArrival = RegisterWindowMessage(TEXT(SCARDUI_READER_ARRIVAL)); pThreadData->msgReaderRemoval = RegisterWindowMessage(TEXT(SCARDUI_READER_REMOVAL)); pThreadData->msgSmartCardInsertion = RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_INSERTION)); pThreadData->msgSmartCardRemoval = RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_REMOVAL)); pThreadData->msgSmartCardStatus = RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_STATUS)); pThreadData->msgSmartCardCertAvail = RegisterWindowMessage(TEXT(SCARDUI_SMART_CARD_CERT_AVAIL)); pThreadData->hClose = CreateEvent( NULL, TRUE, FALSE, NULL ); if (pThreadData->hClose == NULL) { __leave; } pThreadData->hThread = CreateThread( NULL, 0, StartMonitorReaders, (LPVOID) pThreadData, CREATE_SUSPENDED, NULL ); if (pThreadData->hThread == NULL) { __leave; } ResumeThread(pThreadData->hThread); fSuccess = TRUE; } __finally { if (fSuccess == FALSE) { if (pThreadData && pThreadData->hClose) { CloseHandle(pThreadData->hClose); } if (pThreadData) { HeapFree(pThreadData->hHeap, 0, pThreadData); pThreadData = NULL; } } } return (HSCARDUI) pThreadData; } DWORD WINAPI SCardUIExit( HSCARDUI hSCardUI ) /*++ Routine Description: Stops cert. propagation when the user logs out. Arguments: lpvParam - Winlogon notification info. --*/ { PTHREAD_DATA pThreadData = (PTHREAD_DATA) hSCardUI; if(NULL != pThreadData->hThread) { DWORD dwStatus; StopMonitorReaders(pThreadData); dwStatus = WaitForSingleObject( pThreadData->hThread, INFINITE ); _ASSERT(dwStatus == WAIT_OBJECT_0); CloseHandle(pThreadData->hClose); // now free all data for (DWORD dwIndex = 0; dwIndex < pThreadData->dwRemovedReaders; dwIndex++) { FreeReaderData( pThreadData, pThreadData->ppRemovedReaderData[dwIndex] ); HeapFree( pThreadData->hHeap, 0, pThreadData->ppRemovedReaderData[dwIndex]->pszReaderName ); HeapFree( pThreadData->hHeap, 0, pThreadData->ppRemovedReaderData[dwIndex] ); } HeapFree(pThreadData->hHeap, 0, pThreadData); } return ERROR_SUCCESS; } #ifdef TEST #include __cdecl main( int argc, char ** argv ) { HSCARDUI hScardUi; hScardUi = SCardUIInit(NULL); while (TRUE) { _sleep(1000); if (_kbhit()) { _getch(); SCardUIExit(hScardUi); break; } } hScardUi = SCardUIInit(NULL); while (TRUE) { _sleep(1000); if (_kbhit()) { SCardUIExit(hScardUi); return 0; } } } #endif