Leaked source code of windows server 2003
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.

937 lines
20 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1999 - 1999
  3. Module Name:
  4. SCard
  5. Abstract:
  6. The ISCard interface lets you open and manage a connection to a smart card.
  7. Each connection to a card requires a single, corresponding instance of the
  8. ISCard interface.
  9. The smart card resource manager must be available whenever an instance of
  10. ISCard is created. If this service is unavailable, creation of the
  11. interface will fail.
  12. The following example shows a typical use of the ISCard interface. The
  13. ISCard interface is used to connect to the smart card, submit a
  14. transaction, and release the smart card.
  15. To submit a transaction to a specific card
  16. 1) Create an ISCard interface.
  17. 2) Attach to a smart card by specifying a smart card reader or by using a
  18. previously established, valid handle.
  19. 3) Create transaction commands with ISCardCmd, and ISCardISO7816 smart
  20. card interfaces.
  21. 4) Use ISCard to submit the transaction commands for processing by the
  22. smart card.
  23. 5) Use ISCard to release the smart card.
  24. 6) Release the ISCard interface.
  25. Author:
  26. Doug Barlow (dbarlow) 6/24/1999
  27. Notes:
  28. ?Notes?
  29. --*/
  30. #ifndef WIN32_LEAN_AND_MEAN
  31. #define WIN32_LEAN_AND_MEAN
  32. #endif
  33. #include "stdafx.h"
  34. #include "ByteBuffer.h"
  35. #include "SCard.h"
  36. #include "Conversion.h"
  37. /////////////////////////////////////////////////////////////////////////////
  38. // CSCard
  39. /*++
  40. CSCard::get_Atr:
  41. The get_Atr method retrieves an ATR string of the smart card.
  42. Arguments:
  43. ppAtr [out, retval] Pointer to a byte buffer in the form of an IStream that
  44. will contain the ATR string on return.
  45. Return Value:
  46. The return value is an HRESULT. A value of S_OK indicates the call was
  47. successful.
  48. Remarks:
  49. Author:
  50. Doug Barlow (dbarlow) 6/24/1999
  51. --*/
  52. #undef __SUBROUTINE__
  53. #define __SUBROUTINE__ TEXT("CSCard::get_Atr")
  54. STDMETHODIMP
  55. CSCard::get_Atr(
  56. /* [retval][out] */ LPBYTEBUFFER __RPC_FAR *ppAtr)
  57. {
  58. HRESULT hReturn = S_OK;
  59. CByteBuffer *pMyBuffer = NULL;
  60. try
  61. {
  62. CBuffer bfAtr(36);
  63. LONG lSts;
  64. DWORD dwLen = 0;
  65. if (NULL == *ppAtr)
  66. {
  67. *ppAtr = pMyBuffer = NewByteBuffer();
  68. if (NULL == pMyBuffer)
  69. throw (HRESULT)E_OUTOFMEMORY;
  70. }
  71. if (NULL != m_hCard)
  72. {
  73. dwLen = bfAtr.Space();
  74. lSts = SCardStatus(
  75. m_hCard,
  76. NULL, 0, // Reader name
  77. NULL, // State
  78. NULL, // Protocol
  79. bfAtr.Access(),
  80. &dwLen);
  81. if (SCARD_S_SUCCESS != lSts)
  82. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  83. bfAtr.Resize(dwLen);
  84. }
  85. BufferToByteBuffer(bfAtr, ppAtr);
  86. pMyBuffer = NULL;
  87. }
  88. catch (HRESULT hError)
  89. {
  90. hReturn = hError;
  91. }
  92. catch (...)
  93. {
  94. hReturn = E_INVALIDARG;
  95. }
  96. if (NULL != pMyBuffer)
  97. {
  98. pMyBuffer->Release();
  99. *ppAtr = NULL;
  100. }
  101. return hReturn;
  102. }
  103. /*++
  104. CSCard::get_CardHandle:
  105. The get_CardHandle method retrieves the handle for a connected smart card.
  106. Returns (*pHandle) == NULL if not connected.
  107. Arguments:
  108. pHandle [out, retval] Pointer to the card handle on return.
  109. Return Value:
  110. The return value is an HRESULT. A value of S_OK indicates the call was
  111. successful.
  112. Remarks:
  113. Author:
  114. Doug Barlow (dbarlow) 6/24/1999
  115. --*/
  116. #undef __SUBROUTINE__
  117. #define __SUBROUTINE__ TEXT("CSCard::get_CardHandle")
  118. STDMETHODIMP
  119. CSCard::get_CardHandle(
  120. /* [retval][out] */ HSCARD __RPC_FAR *pHandle)
  121. {
  122. HRESULT hReturn = S_OK;
  123. try
  124. {
  125. *pHandle = m_hCard;
  126. }
  127. catch (...)
  128. {
  129. hReturn = E_INVALIDARG;
  130. }
  131. return hReturn;
  132. }
  133. /*++
  134. CSCard::get_Context:
  135. The get_Context method retrieves the current resource manager context
  136. handle. Returns (*pContext) == NULL if no context has been established.
  137. Arguments:
  138. pContext [out, retval] Pointer to the context handle on return.
  139. Return Value:
  140. The return value is an HRESULT. A value of S_OK indicates the call was
  141. successful.
  142. Remarks:
  143. The resource manager context is set by calling the smart card function
  144. SCardEstablishContext.
  145. Author:
  146. Doug Barlow (dbarlow) 6/24/1999
  147. --*/
  148. #undef __SUBROUTINE__
  149. #define __SUBROUTINE__ TEXT("CSCard::get_Context")
  150. STDMETHODIMP
  151. CSCard::get_Context(
  152. /* [retval][out] */ HSCARDCONTEXT __RPC_FAR *pContext)
  153. {
  154. HRESULT hReturn = S_OK;
  155. try
  156. {
  157. *pContext = Context();
  158. }
  159. catch (HRESULT hError)
  160. {
  161. hReturn = hError;
  162. }
  163. catch (...)
  164. {
  165. hReturn = E_INVALIDARG;
  166. }
  167. return hReturn;
  168. }
  169. /*++
  170. CSCard::get_Protocol:
  171. The get_Protocol retrieves the identifier of the protocol currently in use
  172. on the smart card.
  173. Arguments:
  174. pProtocol [out, retval] Pointer to the protocol identifier.
  175. Return Value:
  176. The return value is an HRESULT. A value of S_OK indicates the call was
  177. successful.
  178. Remarks:
  179. Author:
  180. Doug Barlow (dbarlow) 6/24/1999
  181. --*/
  182. #undef __SUBROUTINE__
  183. #define __SUBROUTINE__ TEXT("CSCard::get_Protocol")
  184. STDMETHODIMP
  185. CSCard::get_Protocol(
  186. /* [retval][out] */ SCARD_PROTOCOLS __RPC_FAR *pProtocol)
  187. {
  188. HRESULT hReturn = S_OK;
  189. try
  190. {
  191. LONG lSts;
  192. if (NULL != m_hCard)
  193. {
  194. lSts = SCardStatus(
  195. m_hCard,
  196. NULL, 0, // Reader name
  197. NULL, // State
  198. (LPDWORD)pProtocol, // Protocol
  199. NULL, 0); // ATR
  200. if (SCARD_S_SUCCESS != lSts)
  201. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  202. }
  203. else
  204. *pProtocol = (SCARD_PROTOCOLS)SCARD_PROTOCOL_UNDEFINED;
  205. }
  206. catch (HRESULT hError)
  207. {
  208. hReturn = hError;
  209. }
  210. catch (...)
  211. {
  212. hReturn = E_INVALIDARG;
  213. }
  214. return hReturn;
  215. }
  216. /*++
  217. CSCard::get_Status:
  218. The get_Status method retrieves the current state of the smart card.
  219. Arguments:
  220. pStatus [out, retval] Pointer to the state variable.
  221. Return Value:
  222. The return value is an HRESULT. A value of S_OK indicates the call was
  223. successful.
  224. Remarks:
  225. Author:
  226. Doug Barlow (dbarlow) 6/24/1999
  227. --*/
  228. #undef __SUBROUTINE__
  229. #define __SUBROUTINE__ TEXT("CSCard::get_Status")
  230. STDMETHODIMP
  231. CSCard::get_Status(
  232. /* [retval][out] */ SCARD_STATES __RPC_FAR *pStatus)
  233. {
  234. HRESULT hReturn = S_OK;
  235. try
  236. {
  237. LONG lSts;
  238. if (NULL != m_hCard)
  239. {
  240. lSts = SCardStatus(
  241. m_hCard,
  242. NULL, 0, // Reader name
  243. (LPDWORD)pStatus, // State
  244. NULL, // Protocol
  245. NULL, 0); // ATR
  246. if (SCARD_S_SUCCESS != lSts)
  247. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  248. }
  249. else
  250. *pStatus = (SCARD_STATES)SCARD_UNKNOWN;
  251. }
  252. catch (HRESULT hError)
  253. {
  254. hReturn = hError;
  255. }
  256. catch (...)
  257. {
  258. hReturn = E_INVALIDARG;
  259. }
  260. return hReturn;
  261. }
  262. /*++
  263. CSCard::AttachByHandle:
  264. The AttachByHandle method attaches this object to an open and configured
  265. smart card handle.
  266. Arguments:
  267. hCard [in] Handle to an open connection to a smart card.
  268. Return Value:
  269. The return value is an HRESULT. A value of S_OK indicates the call was
  270. successful.
  271. Remarks:
  272. Author:
  273. Doug Barlow (dbarlow) 6/24/1999
  274. --*/
  275. #undef __SUBROUTINE__
  276. #define __SUBROUTINE__ TEXT("CSCard::AttachByHandle")
  277. STDMETHODIMP
  278. CSCard::AttachByHandle(
  279. /* [in] */ HSCARD hCard)
  280. {
  281. HRESULT hReturn = S_OK;
  282. try
  283. {
  284. LONG lSts;
  285. if (NULL != m_hMyCard)
  286. {
  287. lSts = SCardDisconnect(m_hMyCard, SCARD_RESET_CARD);
  288. if (SCARD_S_SUCCESS != lSts)
  289. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  290. m_hMyCard = NULL;
  291. }
  292. m_dwProtocol = 0;
  293. m_hCard = hCard;
  294. }
  295. catch (HRESULT hError)
  296. {
  297. hReturn = hError;
  298. }
  299. catch (...)
  300. {
  301. hReturn = E_INVALIDARG;
  302. }
  303. return hReturn;
  304. }
  305. /*++
  306. CSCard::AttachByReader:
  307. The AttachByReader method opens the smart card in the named reader.
  308. Arguments:
  309. bstrReaderName [in] Pointer to the name of the smart card reader.
  310. ShareMode [in, defaultvalue(EXCLUSIVE)] Mode in which to claim access to
  311. the smart card.
  312. Values Description
  313. EXCLUSIVE No one else use this connection to the smart card.
  314. SHARED Other applications can use this connection.
  315. PrefProtocol [in, defaultvalue(T0)] Preferred protocol values:
  316. T0
  317. T1
  318. Raw
  319. T0|T1
  320. Return Value:
  321. The return value is an HRESULT. A value of S_OK indicates the call was
  322. successful.
  323. Remarks:
  324. Author:
  325. Doug Barlow (dbarlow) 6/24/1999
  326. --*/
  327. #undef __SUBROUTINE__
  328. #define __SUBROUTINE__ TEXT("CSCard::AttachByReader")
  329. STDMETHODIMP
  330. CSCard::AttachByReader(
  331. /* [in] */ BSTR bstrReaderName,
  332. /* [defaultvalue][in] */ SCARD_SHARE_MODES ShareMode,
  333. /* [defaultvalue][in] */ SCARD_PROTOCOLS PrefProtocol)
  334. {
  335. HRESULT hReturn = S_OK;
  336. try
  337. {
  338. LONG lSts;
  339. DWORD dwProto;
  340. CTextString tzReader;
  341. tzReader = bstrReaderName;
  342. if (NULL != m_hMyCard)
  343. {
  344. lSts = SCardDisconnect(m_hMyCard, SCARD_RESET_CARD);
  345. if (SCARD_S_SUCCESS != lSts)
  346. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  347. m_hMyCard = NULL;
  348. }
  349. m_dwProtocol = 0;
  350. lSts = SCardConnect(
  351. Context(),
  352. tzReader,
  353. (DWORD)ShareMode,
  354. (DWORD)PrefProtocol,
  355. &m_hMyCard,
  356. &dwProto);
  357. if (SCARD_S_SUCCESS != lSts)
  358. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  359. m_hCard = m_hMyCard;
  360. }
  361. catch (HRESULT hError)
  362. {
  363. hReturn = hError;
  364. }
  365. catch (...)
  366. {
  367. hReturn = E_INVALIDARG;
  368. }
  369. return hReturn;
  370. }
  371. /*++
  372. CSCard::Detach:
  373. The Detach method closes the open connection to the smart card.
  374. Arguments:
  375. Disposition [in, defaultvalue(LEAVE)] Indicates what should be done with
  376. the card in the connected reader.
  377. Values Description
  378. LEAVE Leaves the smart card in the current state.
  379. RESET Resets the smart card to some known state.
  380. UNPOWER Removes power from the smart card.
  381. EJECT Ejects the smart card if the reader has eject capabilities.
  382. Return Value:
  383. The return value is an HRESULT. A value of S_OK indicates the call was
  384. successful.
  385. Remarks:
  386. Author:
  387. Doug Barlow (dbarlow) 6/24/1999
  388. --*/
  389. #undef __SUBROUTINE__
  390. #define __SUBROUTINE__ TEXT("CSCard::Detach")
  391. STDMETHODIMP
  392. CSCard::Detach(
  393. /* [defaultvalue][in] */ SCARD_DISPOSITIONS Disposition)
  394. {
  395. HRESULT hReturn = S_OK;
  396. try
  397. {
  398. LONG lSts;
  399. m_dwProtocol = 0;
  400. lSts = SCardDisconnect(m_hCard, (DWORD)Disposition);
  401. if (SCARD_S_SUCCESS != lSts)
  402. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  403. m_hCard = m_hMyCard = NULL;
  404. }
  405. catch (HRESULT hError)
  406. {
  407. hReturn = hError;
  408. }
  409. catch (...)
  410. {
  411. hReturn = E_INVALIDARG;
  412. }
  413. return hReturn;
  414. }
  415. /*++
  416. CSCard::LockSCard:
  417. The LockSCard method claims exclusive access to the smart card.
  418. Arguments:
  419. None
  420. Return Value:
  421. The return value is an HRESULT. A value of S_OK indicates the call was
  422. successful.
  423. Remarks:
  424. Author:
  425. Doug Barlow (dbarlow) 6/24/1999
  426. --*/
  427. #undef __SUBROUTINE__
  428. #define __SUBROUTINE__ TEXT("CSCard::LockSCard")
  429. STDMETHODIMP
  430. CSCard::LockSCard(
  431. void)
  432. {
  433. HRESULT hReturn = S_OK;
  434. try
  435. {
  436. LONG lSts;
  437. lSts = SCardBeginTransaction(m_hCard);
  438. if (SCARD_S_SUCCESS != lSts)
  439. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  440. }
  441. catch (HRESULT hError)
  442. {
  443. hReturn = hError;
  444. }
  445. catch (...)
  446. {
  447. hReturn = E_INVALIDARG;
  448. }
  449. return hReturn;
  450. }
  451. /*++
  452. CSCard::ReAttach:
  453. The ReAttach method resets, or reinitializes, the smart card.
  454. Arguments:
  455. ShareMode [in, defaultvalue(EXCLUSIVE)] Mode in which to share or
  456. exclusively own the connection to the smart card.
  457. Values Description
  458. EXCLUSIVE No one else use this connection to the smart card.
  459. SHARED Other applications can use this connection.
  460. InitState [in, defaultvalue(LEAVE)] Indicates what to do with the card.
  461. Values Description
  462. LEAVE Leaves the smart card in the current state.
  463. RESET Resets the smart card to some known state.
  464. UNPOWER Removes power from the smart card.
  465. EJECT Ejects the smart card if the reader has eject capabilities.
  466. Return Value:
  467. The return value is an HRESULT. A value of S_OK indicates the call was
  468. successful.
  469. Remarks:
  470. Author:
  471. Doug Barlow (dbarlow) 6/24/1999
  472. --*/
  473. #undef __SUBROUTINE__
  474. #define __SUBROUTINE__ TEXT("CSCard::ReAttach")
  475. STDMETHODIMP
  476. CSCard::ReAttach(
  477. /* [defaultvalue][in] */ SCARD_SHARE_MODES ShareMode,
  478. /* [defaultvalue][in] */ SCARD_DISPOSITIONS InitState)
  479. {
  480. HRESULT hReturn = S_OK;
  481. try
  482. {
  483. DWORD dwProto;
  484. LONG lSts;
  485. m_dwProtocol = 0;
  486. lSts = SCardReconnect(
  487. m_hCard,
  488. (DWORD)ShareMode,
  489. SCARD_PROTOCOL_Tx,
  490. (DWORD)InitState,
  491. &dwProto);
  492. if (SCARD_S_SUCCESS != lSts)
  493. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  494. }
  495. catch (HRESULT hError)
  496. {
  497. hReturn = hError;
  498. }
  499. catch (...)
  500. {
  501. hReturn = E_INVALIDARG;
  502. }
  503. return hReturn;
  504. }
  505. /*++
  506. CSCard::Transaction:
  507. The Transaction method executes a write and read operation on the smart
  508. card command (APDU) object. The reply string from the smart card for the
  509. command string defined in the card that was sent to the smart card will be
  510. accessible after this function returns.
  511. Arguments:
  512. ppCmd [in, out] Pointer to the smart card command object.
  513. Return Value:
  514. The return value is an HRESULT. A value of S_OK indicates the call was
  515. successful.
  516. Remarks:
  517. ?Remarks?
  518. Author:
  519. Doug Barlow (dbarlow) 6/24/1999
  520. --*/
  521. #undef __SUBROUTINE__
  522. #define __SUBROUTINE__ TEXT("CSCard::Transaction")
  523. STDMETHODIMP
  524. CSCard::Transaction(
  525. /* [out][in] */ LPSCARDCMD __RPC_FAR *ppCmd)
  526. {
  527. HRESULT hReturn = S_OK;
  528. CByteBuffer *pbyApdu = NewByteBuffer();
  529. try
  530. {
  531. HRESULT hr;
  532. LONG lSts;
  533. DWORD dwFlags = 0;
  534. CBuffer bfResponse, bfPciRqst, bfPciRsp, bfApdu;
  535. //
  536. // Get the protocol.
  537. //
  538. if (0 == m_dwProtocol)
  539. {
  540. lSts = SCardStatus(
  541. m_hCard,
  542. NULL, 0, // Reader name
  543. NULL, // State
  544. &m_dwProtocol, // Protocol
  545. NULL, 0); // ATR
  546. if (SCARD_S_SUCCESS != lSts)
  547. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  548. }
  549. ASSERT(0 != m_dwProtocol);
  550. //
  551. // Get The APDU
  552. //
  553. if (NULL == pbyApdu)
  554. throw (HRESULT)E_OUTOFMEMORY;
  555. hr = (*ppCmd)->get_Apdu((LPBYTEBUFFER *)&pbyApdu);
  556. if (FAILED(hr))
  557. throw hr;
  558. ByteBufferToBuffer(pbyApdu, bfApdu);
  559. //
  560. // Convert it to a TPDU.
  561. //
  562. switch (m_dwProtocol)
  563. {
  564. case SCARD_PROTOCOL_T0:
  565. {
  566. BYTE bAltCla;
  567. LPBYTE pbAltCla = NULL;
  568. if (SUCCEEDED((*ppCmd)->get_AlternateClassId(&bAltCla)))
  569. pbAltCla = &bAltCla;
  570. ApduToTpdu_T0(
  571. m_hCard,
  572. SCARD_PCI_T0,
  573. bfApdu.Access(),
  574. bfApdu.Length(),
  575. dwFlags,
  576. bfPciRsp,
  577. bfResponse,
  578. pbAltCla);
  579. break;
  580. }
  581. case SCARD_PROTOCOL_T1:
  582. {
  583. BYTE bNad;
  584. LPBYTE pbCur, pbEnd;
  585. DWORD dwI;
  586. bfPciRqst.Set((LPBYTE)SCARD_PCI_T1, sizeof(SCARD_IO_REQUEST));
  587. if (SUCCEEDED((*ppCmd)->get_Nad(&bNad)))
  588. {
  589. bfPciRqst.Presize(bfPciRqst.Length() + sizeof(DWORD), TRUE);
  590. bfPciRqst.Append((LPBYTE)"\0x81\0x01", 2);
  591. bfPciRqst.Append(&bNad, 1);
  592. }
  593. dwI = 0;
  594. bfPciRqst.Append(
  595. (LPBYTE)&dwI,
  596. bfPciRqst.Length() % sizeof(DWORD));
  597. ((LPSCARD_IO_REQUEST)bfPciRqst.Access())->cbPciLength = bfPciRqst.Length();
  598. ApduToTpdu_T1(
  599. m_hCard,
  600. (LPSCARD_IO_REQUEST)bfPciRqst.Access(),
  601. bfApdu.Access(),
  602. bfApdu.Length(),
  603. dwFlags,
  604. bfPciRsp,
  605. bfResponse);
  606. pbEnd = bfPciRsp.Access();
  607. pbCur = pbEnd + sizeof(SCARD_PCI_T1);
  608. pbEnd += bfPciRsp.Length();
  609. while (pbCur < pbEnd)
  610. {
  611. switch (*pbCur++)
  612. {
  613. case 0x00:
  614. break;
  615. case 0x81:
  616. bNad = *(++pbCur);
  617. hr = (*ppCmd)->put_ReplyNad(bNad);
  618. break;
  619. default:
  620. pbCur += *pbCur + 1;
  621. }
  622. }
  623. break;
  624. }
  625. default:
  626. throw (HRESULT)SCARD_E_CARD_UNSUPPORTED;
  627. }
  628. //
  629. // Write the response back to the ISCardCommand object.
  630. //
  631. BufferToByteBuffer(bfResponse, (LPBYTEBUFFER *)&pbyApdu);
  632. hr = (*ppCmd)->put_ApduReply(pbyApdu);
  633. if (FAILED(hr))
  634. throw hr;
  635. }
  636. catch (HRESULT hError)
  637. {
  638. hReturn = hError;
  639. }
  640. catch (...)
  641. {
  642. hReturn = E_INVALIDARG;
  643. }
  644. if (NULL != pbyApdu)
  645. pbyApdu->Release();
  646. return hReturn;
  647. }
  648. /*++
  649. CSCard::UnlockSCard:
  650. The UnlockSCard method releases exclusive access to the smart card.
  651. Arguments:
  652. Disposition [in, defaultvalue(LEAVE)] Indicates what should be done with
  653. the card in the connected reader.
  654. Values Description
  655. LEAVE Leaves the smart card in the current state.
  656. RESET Resets the smart card to some known state.
  657. UNPOWER Removes power from the smart card.
  658. EJECT Ejects the smart card if the reader has eject capabilities.
  659. Return Value:
  660. The return value is an HRESULT. A value of S_OK indicates the call was
  661. successful.
  662. Remarks:
  663. Author:
  664. Doug Barlow (dbarlow) 6/24/1999
  665. --*/
  666. #undef __SUBROUTINE__
  667. #define __SUBROUTINE__ TEXT("CSCard::UnlockSCard")
  668. STDMETHODIMP
  669. CSCard::UnlockSCard(
  670. /* [defaultvalue][in] */ SCARD_DISPOSITIONS Disposition)
  671. {
  672. HRESULT hReturn = S_OK;
  673. try
  674. {
  675. LONG lSts;
  676. lSts = SCardEndTransaction(m_hCard, (DWORD)Disposition);
  677. if (SCARD_S_SUCCESS != lSts)
  678. throw (HRESULT)HRESULT_FROM_WIN32(lSts);
  679. }
  680. catch (HRESULT hError)
  681. {
  682. hReturn = hError;
  683. }
  684. catch (...)
  685. {
  686. hReturn = E_INVALIDARG;
  687. }
  688. return hReturn;
  689. }