Source code of Windows XP (NT5)
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.

670 lines
15 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1996 - 1999
  3. Module Name:
  4. StatMon
  5. Abstract:
  6. This file contains the implementation of CScStatusMonitor
  7. (an object that watches for status changes of readers recognized
  8. by the smart card service, handling PNP and requests for copies of
  9. the status array)
  10. Author:
  11. Amanda Matlosz 02/26/98
  12. Environment:
  13. Win32, C++ with exceptions
  14. Revision History:
  15. Notes:
  16. --*/
  17. #include "statmon.h"
  18. // forward declarations of private functions
  19. UINT ScStatusChangeProc(LPVOID pParam);
  20. /////
  21. // CScStatusMonitor
  22. CScStatusMonitor::~CScStatusMonitor()
  23. {
  24. if (stopped != m_status)
  25. {
  26. Stop();
  27. }
  28. }
  29. /*++
  30. Start:
  31. The monitor will fail to start if:
  32. Any of the arguments are missing.
  33. Two SCARDCONTEXTs could not be acquired from the RM.
  34. An error occurs while retrieving a list of readers from the RM.
  35. Or the status thread fails to start.
  36. Arguments:
  37. hWnd -- The monitor's owner's HWND, for sending notification messages.
  38. uiMsg -- The message that will be posted to the hWnd of the monitor's owner
  39. when a change in status has occurred.
  40. szGroupNames -- Reader groups we're interested in. see "winscard.h"
  41. Return Value:
  42. LONG.
  43. Author:
  44. Amanda Matlosz 02/26/98
  45. Notes:
  46. If already started, causes a restart.
  47. All the arguments are required.
  48. --*/
  49. LONG CScStatusMonitor::Start(HWND hWnd, UINT uiMsg, LPCTSTR szGroupNames)
  50. {
  51. LONG lReturn = SCARD_S_SUCCESS;
  52. // the monitor must be uninitialized or stopped before it can start
  53. if (uninitialized != m_status && stopped != m_status)
  54. {
  55. Stop();
  56. }
  57. if (NULL == hWnd || 0 == uiMsg)
  58. {
  59. // invalid parameters
  60. m_status = uninitialized;
  61. return ERROR_INVALID_PARAMETER;
  62. }
  63. m_hwnd = hWnd;
  64. m_uiStatusChangeMsg = uiMsg;
  65. if (NULL == szGroupNames || 0 == _tcslen(szGroupNames))
  66. {
  67. m_strGroupNames = SCARD_DEFAULT_READERS;
  68. }
  69. //
  70. // Get two contexts from the resource manager to use,
  71. // one for the monitor itself, and one for it's status-watching thread
  72. //
  73. m_hContext = NULL;
  74. m_hInternalContext = NULL;
  75. lReturn = SCardEstablishContext(SCARD_SCOPE_USER,
  76. NULL,
  77. NULL,
  78. &m_hContext);
  79. if (SCARD_S_SUCCESS == lReturn)
  80. {
  81. lReturn = SCardEstablishContext(SCARD_SCOPE_USER,
  82. NULL,
  83. NULL,
  84. &m_hInternalContext);
  85. }
  86. if (SCARD_S_SUCCESS != lReturn)
  87. {
  88. m_status = no_service;
  89. if (NULL != m_hContext)
  90. {
  91. SCardReleaseContext(m_hContext);
  92. m_hContext = NULL;
  93. }
  94. if (NULL != m_hInternalContext)
  95. {
  96. SCardReleaseContext(m_hInternalContext);
  97. m_hInternalContext = NULL;
  98. }
  99. }
  100. //
  101. // If we successfully got a context, go ahead and initialize
  102. // the internal reader status array & kick off status thread
  103. //
  104. else
  105. {
  106. lReturn = InitInternalReaderStatus();
  107. }
  108. if (SCARD_S_SUCCESS == lReturn)
  109. {
  110. m_status = running;
  111. // kick off status thread
  112. m_pStatusThrd = AfxBeginThread((AFX_THREADPROC)ScStatusChangeProc,
  113. (LPVOID)this,
  114. THREAD_PRIORITY_NORMAL,
  115. 0,
  116. CREATE_SUSPENDED);
  117. if (NULL == m_pStatusThrd)
  118. {
  119. m_status = stopped;
  120. return GetLastError();
  121. }
  122. m_pStatusThrd->m_bAutoDelete = FALSE; // don't delete the thread on completion
  123. m_pStatusThrd->ResumeThread();
  124. }
  125. return lReturn;
  126. }
  127. /*++
  128. Stop:
  129. In order to stop the monitor, the SCARDCONTEXTS are canceled, the
  130. status thread is shut down, and data members that are only valid while
  131. running are cleaned up.
  132. Arguments:
  133. None.
  134. Return Value:
  135. None.
  136. Author:
  137. Amanda Matlosz 02/26/98
  138. Notes:
  139. --*/
  140. void CScStatusMonitor::Stop()
  141. {
  142. m_status = stopped;
  143. // tell thread to stop now
  144. SCardCancel(m_hInternalContext);
  145. if (NULL != m_pStatusThrd)
  146. {
  147. DWORD dwRet = WaitForSingleObject(m_pStatusThrd->m_hThread, INFINITE); // for testing: 10000
  148. _ASSERTE(WAIT_OBJECT_0 == dwRet);
  149. delete m_pStatusThrd;
  150. m_pStatusThrd = NULL;
  151. }
  152. // clear out internal scardcontext
  153. SCardReleaseContext(m_hInternalContext);
  154. m_hInternalContext = NULL;
  155. // Empty external readerstatusarray
  156. EmptyExternalReaderStatus();
  157. // Empty internal readerstatusarray
  158. if (NULL != m_pInternalReaderStatus)
  159. {
  160. delete[] m_pInternalReaderStatus;
  161. m_pInternalReaderStatus = NULL;
  162. }
  163. m_dwInternalNumReaders = 0;
  164. // Close main scardcontext so nothing can happen 'til restart
  165. SCardReleaseContext(m_hContext);
  166. m_hContext = NULL;
  167. }
  168. /*++
  169. EmptyExternalReaderStatus:
  170. This empties out the external CSCardReaderStateArray, deleting
  171. all CSCardReaderState objects it has pointers to.
  172. Arguments:
  173. None.
  174. Return Value:
  175. None.
  176. Author:
  177. Amanda Matlosz 02/26/98
  178. Notes:
  179. --*/
  180. void CScStatusMonitor::EmptyExternalReaderStatus(void)
  181. {
  182. for (int nIndex = (int)m_aReaderStatus.GetUpperBound(); 0 <= nIndex; nIndex--)
  183. {
  184. delete m_aReaderStatus[nIndex];
  185. }
  186. m_aReaderStatus.RemoveAll();
  187. }
  188. /*++
  189. GetReaderStatus:
  190. This returns copies of the Readers (CSCardReaderState) in the
  191. "external" array.
  192. It is assumed that the user is handing us an empty
  193. CSCardReaderStateArray, or one that is safe to be emptied.
  194. Arguments:
  195. aReaderStatus -- a reference to a CSCardReaderStateArray that will
  196. receive the values of the new array. If it is not empty, all the objects
  197. pointed to will be deleted and removed.
  198. Return Value:
  199. None.
  200. Author:
  201. Amanda Matlosz 02/26/98
  202. Notes:
  203. --*/
  204. void CScStatusMonitor::GetReaderStatus(CSCardReaderStateArray& aReaderStatus)
  205. {
  206. m_csRdrStsLock.Lock();
  207. // Make sure they gave us an empty array;
  208. // empty it out for them politely if they didn't.
  209. if (0 != aReaderStatus.GetSize())
  210. {
  211. for (int i = (int)aReaderStatus.GetUpperBound(); i>=0; i--)
  212. {
  213. delete aReaderStatus[i];
  214. }
  215. aReaderStatus.RemoveAll();
  216. }
  217. // build external copy of internal readerstatusarray
  218. CSCardReaderState* pReader = NULL;
  219. for (int i = 0; i <= m_aReaderStatus.GetUpperBound(); i++)
  220. {
  221. pReader = new CSCardReaderState(m_aReaderStatus[i]);
  222. ASSERT(NULL != pReader); // otherwise, fudge
  223. if (NULL != pReader)
  224. {
  225. aReaderStatus.Add(pReader);
  226. }
  227. }
  228. m_csRdrStsLock.Unlock();
  229. }
  230. /*++
  231. SetReaderStatus:
  232. The external ReaderStatus array is set to mirror the internal
  233. READERSTATUSARRAY, with some embellishment.
  234. If the external ReaderStatus array is empty, it will be built.
  235. It if it not empty, it is assumed to be the correct length.
  236. The CScStatusMonitor's parent is notified before returning.
  237. Arguments:
  238. None.
  239. Return Value:
  240. None.
  241. Author:
  242. Amanda Matlosz 02/26/98
  243. Notes:
  244. --*/
  245. void CScStatusMonitor::SetReaderStatus()
  246. {
  247. m_csRdrStsLock.Lock();
  248. long lReturn = SCARD_S_SUCCESS;
  249. CSCardReaderState* pReader = NULL;
  250. //
  251. // if ext readerstatusarray is empty, initialize it
  252. //
  253. if (0 == m_aReaderStatus.GetSize())
  254. {
  255. for (DWORD dwIndex=0; dwIndex<m_dwInternalNumReaders; dwIndex++)
  256. {
  257. pReader = new CSCardReaderState();
  258. ASSERT(NULL != pReader); // If not, fudge.
  259. if (NULL != pReader)
  260. {
  261. pReader->strReader = (LPCTSTR)m_pInternalReaderStatus[dwIndex].szReader;
  262. pReader->dwCurrentState = m_pInternalReaderStatus[dwIndex].dwCurrentState;
  263. pReader->dwEventState = m_pInternalReaderStatus[dwIndex].dwEventState;
  264. pReader->cbAtr = 0;
  265. pReader->strCard = _T("");
  266. pReader->dwState = 0;
  267. m_aReaderStatus.Add(pReader);
  268. }
  269. }
  270. pReader = NULL;
  271. }
  272. //
  273. // Set everything in the external array to match the internal.
  274. // It's safe to assume that both the internal and external
  275. // arrays match reader for reader.
  276. //
  277. for (DWORD dwIndex=0; dwIndex<m_dwInternalNumReaders; dwIndex++)
  278. {
  279. pReader = m_aReaderStatus.GetAt(dwIndex);
  280. bool fNewCard = false;
  281. if (NULL == pReader)
  282. {
  283. ASSERT(FALSE); // this should be initialized at this point!
  284. TRACE(_T("CScStatusMonitor::SetReaderStatus external array does not match internal array."));
  285. break;
  286. }
  287. // set state
  288. pReader->dwEventState = m_pInternalReaderStatus[dwIndex].dwEventState;
  289. pReader->dwCurrentState = m_pInternalReaderStatus[dwIndex].dwCurrentState;
  290. // NO CARD
  291. if(pReader->dwEventState & SCARD_STATE_EMPTY)
  292. {
  293. pReader->dwState = SC_STATUS_NO_CARD;
  294. }
  295. // CARD in reader: SHARED, EXCLUSIVE, FREE, UNKNOWN ?
  296. else if(pReader->dwEventState & SCARD_STATE_PRESENT)
  297. {
  298. if (pReader->dwEventState & SCARD_STATE_MUTE)
  299. {
  300. pReader->dwState = SC_STATUS_UNKNOWN;
  301. }
  302. else if (pReader->dwEventState & SCARD_STATE_INUSE)
  303. {
  304. if(pReader->dwEventState & SCARD_STATE_EXCLUSIVE)
  305. {
  306. pReader->dwState = SC_STATUS_EXCLUSIVE;
  307. }
  308. else
  309. {
  310. pReader->dwState = SC_STATUS_SHARED;
  311. }
  312. }
  313. else
  314. {
  315. pReader->dwState = SC_SATATUS_AVAILABLE;
  316. }
  317. }
  318. // READER ERROR: at this point, something's gone wrong
  319. else // m_ReaderState.dwEventState & SCARD_STATE_UNAVAILABLE
  320. {
  321. pReader->dwState = SC_STATUS_ERROR;
  322. }
  323. //
  324. // ATR and CardName: reset to empty if card is not available/responding
  325. // else, query RM for first card name to match ATR
  326. //
  327. if (SC_STATUS_NO_CARD == pReader->dwState ||
  328. SC_STATUS_UNKNOWN == pReader->dwState ||
  329. SC_STATUS_ERROR == pReader->dwState )
  330. {
  331. pReader->strCard.Empty();
  332. pReader->cbAtr = 0;
  333. }
  334. else
  335. {
  336. LPTSTR szCardName = NULL;
  337. DWORD dwNumChar = SCARD_AUTOALLOCATE;
  338. pReader->cbAtr = m_pInternalReaderStatus[dwIndex].cbAtr;
  339. memcpy(pReader->rgbAtr,
  340. m_pInternalReaderStatus[dwIndex].rgbAtr,
  341. m_pInternalReaderStatus[dwIndex].cbAtr);
  342. lReturn = SCardListCards(m_hInternalContext,
  343. (LPCBYTE)pReader->rgbAtr,
  344. NULL,
  345. (DWORD)0,
  346. (LPTSTR)&szCardName,
  347. &dwNumChar);
  348. if (SCARD_S_SUCCESS == lReturn)
  349. {
  350. pReader->strCard = (LPCTSTR)szCardName;
  351. SCardFreeMemory(m_hInternalContext, (LPVOID)szCardName);
  352. }
  353. else
  354. {
  355. pReader->strCard.Empty();
  356. }
  357. }
  358. } // Now the two arrays are in sync
  359. m_csRdrStsLock.Unlock();
  360. ::PostMessage(m_hwnd, m_uiStatusChangeMsg, 0, (LONG)lReturn);
  361. }
  362. /*++
  363. InitInternalReaderStatus:
  364. This resets the internal READERSTATUSARRAY to <empty> before calling
  365. SCardListReaders; if there are no readers, the array will remain empty;
  366. if the RM is down, an error will be returned.
  367. Arguments:
  368. None.
  369. Return Value:
  370. 0 on success; WIN32 error message otherwise.
  371. Author:
  372. Amanda Matlosz 02/26/98
  373. Notes:
  374. --*/
  375. LONG CScStatusMonitor::InitInternalReaderStatus()
  376. {
  377. LONG lReturn = SCARD_S_SUCCESS;
  378. //
  379. // Get list of readers from Resource manager
  380. //
  381. if (NULL != m_pInternalReaderStatus)
  382. {
  383. delete[] m_pInternalReaderStatus;
  384. }
  385. DWORD dwNameLength = SCARD_AUTOALLOCATE;
  386. m_szReaderNames = NULL;
  387. m_dwInternalNumReaders = 0;
  388. lReturn = SCardListReaders(m_hContext,
  389. (LPTSTR)(LPCTSTR)m_strGroupNames,
  390. (LPTSTR)&m_szReaderNames,
  391. &dwNameLength);
  392. if(SCARD_S_SUCCESS == lReturn)
  393. {
  394. // make a readerstatusarray big enough for all readers
  395. m_dwInternalNumReaders = MStringCount(m_szReaderNames);
  396. _ASSERTE(0 != m_dwInternalNumReaders);
  397. m_pInternalReaderStatus = new SCARD_READERSTATE[m_dwInternalNumReaders];
  398. if (NULL != m_pInternalReaderStatus)
  399. {
  400. // use the list of readers to build a readerstate array
  401. LPCTSTR pchReader = m_szReaderNames;
  402. int nIndex = 0;
  403. while(0 != *pchReader)
  404. {
  405. m_pInternalReaderStatus[nIndex].szReader = pchReader;
  406. m_pInternalReaderStatus[nIndex].dwCurrentState = SCARD_STATE_UNAWARE;
  407. pchReader += lstrlen(pchReader)+1;
  408. nIndex++;
  409. }
  410. }
  411. else
  412. {
  413. lReturn = SCARD_E_NO_MEMORY;
  414. }
  415. }
  416. else if (SCARD_E_NO_READERS_AVAILABLE == lReturn)
  417. {
  418. m_status = no_readers;
  419. if(NULL != m_szReaderNames)
  420. {
  421. SCardFreeMemory(m_hContext, (LPVOID)m_szReaderNames);
  422. m_szReaderNames = NULL;
  423. }
  424. }
  425. // else m_status == unknown?
  426. // this array, and the m_szReaderNames used to build it, are now property of
  427. // the StatusChangeProc...
  428. return lReturn;
  429. }
  430. /*++
  431. ScStatusChangeProc:
  432. Arguments:
  433. pParam - CScStatusMonitor*
  434. Return Value:
  435. 0 on success; WIN32 error message otherwise.
  436. Author:
  437. Amanda Matlosz 02/26/98
  438. Notes:
  439. --*/
  440. UINT ScStatusChangeProc(LPVOID pParam)
  441. {
  442. UINT uiReturn = 0;
  443. if(NULL != pParam)
  444. {
  445. return ((CScStatusMonitor*)pParam)->GetStatusChangeProc();
  446. }
  447. return SCARD_E_INVALID_PARAMETER;
  448. }
  449. UINT CScStatusMonitor::GetStatusChangeProc()
  450. {
  451. LONG lReturn = SCARD_S_SUCCESS;
  452. while (stopped != m_status)
  453. {
  454. // Wait for change in status (safe to use pMonitor's internal vars)
  455. lReturn = SCardGetStatusChange(m_hInternalContext,
  456. INFINITE,
  457. m_pInternalReaderStatus,
  458. m_dwInternalNumReaders);
  459. // inform monitor that given status has changed (Only on success!)
  460. if (SCARD_S_SUCCESS == lReturn)
  461. {
  462. SetReaderStatus();
  463. }
  464. else
  465. {
  466. //
  467. // If the context has been cancelled, quit quietly
  468. // Otherwise, announce that the thread is aborting prematurely
  469. //
  470. m_status = stopped;
  471. if(SCARD_E_CANCELLED != lReturn)
  472. {
  473. // TODO: ? wrap in critsec ?
  474. m_pStatusThrd = NULL;
  475. // TODO: ? end crit sec ?
  476. ::PostMessage(m_hwnd, m_uiStatusChangeMsg, 0, (LONG)lReturn);
  477. }
  478. break;
  479. }
  480. // Prep the array for the next GetStatusChange call
  481. for(DWORD dwIndex=0; dwIndex<m_dwInternalNumReaders; dwIndex++)
  482. {
  483. m_pInternalReaderStatus[dwIndex].dwCurrentState =
  484. m_pInternalReaderStatus[dwIndex].dwEventState;
  485. }
  486. }
  487. // Clean Up
  488. if(NULL != m_szReaderNames)
  489. {
  490. SCardFreeMemory(m_hContext, (LPVOID)m_szReaderNames);
  491. m_szReaderNames = NULL;
  492. }
  493. return (UINT)0;
  494. }