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.

857 lines
22 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. lasterr.c
  5. Abstract:
  6. Contains the entry point for
  7. WNetGetLastErrorW
  8. WNetSetLastErrorW
  9. MultinetGetErrorTextW
  10. Also contains the following support routines:
  11. MprAllocErrorRecord
  12. MprFreeErrorRecord
  13. MprFindErrorRecord
  14. Author:
  15. Dan Lafferty (danl) 17-Oct-1991
  16. Environment:
  17. User Mode - Win32
  18. Revision History:
  19. 09-Aug-1996 anirudhs
  20. Fixed bug in MprFindErrorRecord. Replaced MprFreeErrorRecord with
  21. MprFreeAllErrorRecords (see mprinit.cxx). Moved some definitions
  22. from mprdata.h here.
  23. 20-Jun-1995 anirudhs
  24. Added MultinetGetErrorTextW. Removed CurrentThreadId parameter to
  25. MprFindErrorRecord. Some other cleanups.
  26. 11-Aug-1993 danl
  27. WNetGetLastErrorW: Got rid of TCHAR stuff and committed to UNICODE.
  28. Also, consistently use number of characters rather than number
  29. of bytes.
  30. 08-Jun-1993 danl
  31. Now we handle the case where WNetSetLastError is called with a
  32. threadId that we don't know about. This happens in the case where
  33. a sub-thread does a LoadLibrary on MPR.DLL. We only get
  34. notification of the one thread attaching. Yet other threads within
  35. that process may call the WinNet functions.
  36. 17-Oct-1991 danl
  37. Created
  38. --*/
  39. //
  40. // INCLUDES
  41. //
  42. #include "precomp.hxx"
  43. #include <tstring.h> // STRSIZE, STRCPY
  44. #include "mprres.h" // IDS_XERR_UNKNOWN
  45. //=======================
  46. // Data Structures
  47. //=======================
  48. typedef struct _ERROR_RECORD {
  49. struct _ERROR_RECORD *Prev;
  50. struct _ERROR_RECORD *Next;
  51. DWORD ThreadId;
  52. DWORD ErrorCode;
  53. LPTSTR ErrorText; // This is an allocated buffer
  54. LPTSTR ProviderName; // This is an allocated buffer
  55. } ERROR_RECORD, *LPERROR_RECORD;
  56. //=======================
  57. // MACROS/DEFINES
  58. //=======================
  59. #define FIND_END_OF_LIST(record) while(record->Next != NULL) { \
  60. record=record->Next; \
  61. }
  62. #define REMOVE_FROM_LIST(record) record->Prev->Next = record->Next; \
  63. if (record->Next != NULL) { \
  64. record->Next->Prev = record->Prev; \
  65. }
  66. #define ADD_TO_LIST(record, newRec) FIND_END_OF_LIST(record) \
  67. record->Next = newRec; \
  68. newRec->Prev = record; \
  69. newRec->Next = NULL;
  70. //
  71. // The string (in English) is 44 characters long (+1 for the NULL)
  72. //
  73. #define UNKNOWN_ERROR_LENGTH 80
  74. //=======================
  75. // GLOBALS
  76. //=======================
  77. extern HMODULE hDLL; // DLL instance handle
  78. //
  79. // Local Functions
  80. //
  81. LPERROR_RECORD
  82. MprAllocErrorRecord(
  83. VOID);
  84. LPERROR_RECORD
  85. MprFindErrorRecord(
  86. VOID);
  87. //
  88. // Global Data Structures
  89. //
  90. ERROR_RECORD MprErrorRecList; // Initialized to zeros by loader
  91. CRITICAL_SECTION MprErrorRecCritSec; // Initialized in mprinit.cxx
  92. DWORD
  93. WNetGetLastErrorW(
  94. OUT LPDWORD lpError,
  95. OUT LPWSTR lpErrorBuf,
  96. IN DWORD nErrorBufLen,
  97. OUT LPWSTR lpNameBuf,
  98. IN DWORD nNameBufLen
  99. )
  100. /*++
  101. Routine Description:
  102. This function allows users to obtain the error code and accompanying
  103. text when they receive a WN_EXTENDED_ERROR in response to a WNet API
  104. function call.
  105. Arguments:
  106. lpError - This is a pointer to the location that will receive the
  107. error code.
  108. lpErrorBuf - This points to a buffer that will receive the null
  109. terminated string describing the error.
  110. nErrorBufLen - This value that indicates the size (in characters) of
  111. lpErrorBuf.
  112. If the buffer is too small to receive an error string, the
  113. string will simply be truncated. (it is still guaranteed to be
  114. null terminated). A buffer of at least 256 bytes is recommended.
  115. lpNameBuf - This points to a buffer that will receive the name of
  116. the provider that raised the error.
  117. nNameBufLen - This value indicates the size (in characters) of lpNameBuf.
  118. If the buffer is too small to receive an error string, the
  119. string will simply be truncated. (it is still guaranteed to be
  120. null terminated).
  121. Return Value:
  122. WN_SUCCESS - if the call was successful.
  123. WN_BAD_POINTER - One or more of the passed in pointers is bad.
  124. WN_DEVICE_ERROR - This indicates that the threadID for the current
  125. thread could not be found in that table anywhere. This should
  126. never happen.
  127. --*/
  128. {
  129. LPERROR_RECORD errorRecord;
  130. DWORD nameStringLen;
  131. DWORD textStringLen;
  132. DWORD status = WN_SUCCESS;
  133. //
  134. // Screen the parameters as best we can.
  135. //
  136. __try {
  137. //
  138. // If output buffers are provided, Probe them.
  139. //
  140. *lpError = WN_SUCCESS;
  141. if (IsBadWritePtr(lpErrorBuf, nErrorBufLen * sizeof(WCHAR)) ||
  142. IsBadWritePtr(lpNameBuf, nNameBufLen * sizeof(WCHAR)))
  143. {
  144. status = WN_BAD_POINTER;
  145. }
  146. }
  147. __except (EXCEPTION_EXECUTE_HANDLER) {
  148. status = GetExceptionCode();
  149. if (status != EXCEPTION_ACCESS_VIOLATION) {
  150. MPR_LOG(ERROR,"WNetGetLastError:Unexpected Exception 0x%lx\n",status);
  151. }
  152. status = WN_BAD_POINTER;
  153. }
  154. if (status != WN_SUCCESS) {
  155. return(status);
  156. }
  157. //
  158. // Get the current thread's error record.
  159. //
  160. errorRecord = MprFindErrorRecord();
  161. if (errorRecord != NULL) {
  162. //
  163. // The record was found in the linked list.
  164. // See if there is a buffer to put data into.
  165. //
  166. if (nErrorBufLen > 0) {
  167. //
  168. // Check to see if there is error text to return.
  169. // If not, indicate a 0 length string.
  170. //
  171. if(errorRecord->ErrorText == NULL) {
  172. *lpErrorBuf = L'\0';
  173. }
  174. else {
  175. //
  176. // If the error text won't fit into the user buffer, fill it
  177. // as best we can, and NULL terminate it.
  178. //
  179. textStringLen = wcslen(errorRecord->ErrorText);
  180. if(nErrorBufLen < textStringLen + 1) {
  181. textStringLen = nErrorBufLen - 1;
  182. }
  183. //
  184. // textStringLen now contains the number of characters we
  185. // will copy without the NULL terminator.
  186. //
  187. wcsncpy(lpErrorBuf, errorRecord->ErrorText, textStringLen);
  188. *(lpErrorBuf + textStringLen) = L'\0';
  189. }
  190. }
  191. //
  192. // If there is a Name Buffer to put the provider into, then...
  193. //
  194. if (nNameBufLen > 0) {
  195. //
  196. // See if the Provider Name will fit in the user buffer.
  197. //
  198. nameStringLen = errorRecord->ProviderName ?
  199. (wcslen(errorRecord->ProviderName) + 1) :
  200. 1 ;
  201. //
  202. // If the user buffer is smaller than the required size,
  203. // set up to copy the smaller of the two.
  204. //
  205. if(nNameBufLen < nameStringLen + 1) {
  206. nameStringLen = nNameBufLen - 1;
  207. }
  208. if (errorRecord->ProviderName) {
  209. wcsncpy(lpNameBuf, errorRecord->ProviderName, nameStringLen);
  210. *(lpNameBuf + nameStringLen) = L'\0';
  211. }
  212. else {
  213. *lpNameBuf = L'\0';
  214. }
  215. }
  216. *lpError = errorRecord->ErrorCode;
  217. return(WN_SUCCESS);
  218. }
  219. else {
  220. //
  221. // If we get here, a record for the current thread could not be found.
  222. //
  223. *lpError = WN_SUCCESS;
  224. if (nErrorBufLen > 0) {
  225. *lpErrorBuf = L'\0';
  226. }
  227. if (nNameBufLen > 0) {
  228. *lpNameBuf = L'\0';
  229. }
  230. return(WN_SUCCESS);
  231. }
  232. }
  233. VOID
  234. WNetSetLastErrorW(
  235. IN DWORD err,
  236. IN LPWSTR lpError,
  237. IN LPWSTR lpProvider
  238. )
  239. /*++
  240. Routine Description:
  241. This function is used by Network Providers to set extended errors.
  242. It saves away the error information in a "per thread" data structure.
  243. This function also calls SetLastError with the WN_EXTENDED_ERROR.
  244. Arguments:
  245. err - The error that occured. This may be a Windows defined error,
  246. in which case LpError is ignored. or it may be ERROR_EXTENDED_ERROR
  247. to indicate that the provider has a network specific error to report.
  248. lpError - String describing a network specific error.
  249. lpProvider - String naming the network provider raising the error.
  250. Return Value:
  251. none
  252. --*/
  253. {
  254. DWORD status = WN_SUCCESS;
  255. LPERROR_RECORD errorRecord;
  256. //
  257. // Set the extended error status that tells the user they need to
  258. // call WNetGetLastError to obtain further information.
  259. //
  260. SetLastError(WN_EXTENDED_ERROR);
  261. //
  262. // Get the Error Record for the current thread.
  263. //
  264. errorRecord = MprFindErrorRecord();
  265. //
  266. // if there isn't a record for the current thread, then add it.
  267. //
  268. if (errorRecord == NULL)
  269. {
  270. errorRecord = MprAllocErrorRecord();
  271. if (errorRecord == NULL)
  272. {
  273. MPR_LOG0(ERROR,"WNetSetLastError:Could not allocate Error Record\n");
  274. return;
  275. }
  276. }
  277. //
  278. // Update the error code in the error record. At the same time,
  279. // free up any old strings since they are now obsolete, and init
  280. // the pointer to NULL. Also set the ProviderName pointer in the
  281. // ErrorRecord to point to the provider's name.
  282. //
  283. errorRecord->ErrorCode = err;
  284. LocalFree(errorRecord->ProviderName);
  285. errorRecord->ProviderName = NULL;
  286. LocalFree(errorRecord->ErrorText);
  287. errorRecord->ErrorText = NULL;
  288. //
  289. // Allocate memory for the provider name.
  290. //
  291. __try {
  292. errorRecord->ProviderName = (WCHAR *) LocalAlloc(LPTR, STRSIZE(lpProvider));
  293. }
  294. __except (EXCEPTION_EXECUTE_HANDLER) {
  295. //
  296. // We have a problem with lpProvider.
  297. //
  298. status = GetExceptionCode();
  299. if (status != EXCEPTION_ACCESS_VIOLATION) {
  300. MPR_LOG(ERROR,"WNetSetLastError:Unexpected Exception 0x%lx\n",status);
  301. }
  302. status = WN_BAD_POINTER;
  303. }
  304. if (status != WN_SUCCESS) {
  305. return;
  306. }
  307. if (errorRecord->ProviderName == NULL) {
  308. //
  309. // Unable to allocate memory for the Provider Name for the error
  310. // record.
  311. //
  312. MPR_LOG(ERROR,
  313. "WNetSetLastError:Unable to allocate mem for ProviderName\n",0);
  314. return;
  315. }
  316. //
  317. // Copy the string to the newly allocated buffer.
  318. //
  319. wcscpy(errorRecord->ProviderName, lpProvider);
  320. //
  321. // Allocate memory for the storage of the error text.
  322. //
  323. __try {
  324. errorRecord->ErrorText = (WCHAR *) LocalAlloc(LPTR,STRSIZE(lpError));
  325. }
  326. __except (EXCEPTION_EXECUTE_HANDLER) {
  327. //
  328. // We have a problem with lpError.
  329. //
  330. status = GetExceptionCode();
  331. if (status != EXCEPTION_ACCESS_VIOLATION) {
  332. MPR_LOG(ERROR,"WNetSetLastError:Unexpected Exception 0x%lx\n",status);
  333. }
  334. status = WN_BAD_POINTER;
  335. }
  336. if (status != WN_SUCCESS) {
  337. errorRecord->ErrorText = NULL;
  338. }
  339. if (errorRecord->ErrorText == NULL) {
  340. //
  341. // If we were unsuccessful in allocating for the ErrorText, then
  342. // abort. The ErrorText Pointer has already been set to null.
  343. //
  344. MPR_LOG(ERROR,"WNetSetLastError:Unable to Alloc for ErrorText %d\n",
  345. GetLastError());
  346. return;
  347. }
  348. //
  349. // Copy the error text into the newly allocated buffer.
  350. //
  351. wcscpy(errorRecord->ErrorText, lpError);
  352. return;
  353. }
  354. LPERROR_RECORD
  355. MprFindErrorRecord(
  356. VOID)
  357. /*++
  358. Routine Description:
  359. Looks through the linked list of ErrorRecords in search of one for
  360. the current thread.
  361. Arguments:
  362. none
  363. Return Value:
  364. Returns LPERROR_RECORD if an error record was found. Otherwise, it
  365. returns NULL.
  366. --*/
  367. {
  368. LPERROR_RECORD errorRecord;
  369. DWORD CurrentThreadId = GetCurrentThreadId();
  370. EnterCriticalSection(&MprErrorRecCritSec);
  371. for (errorRecord = MprErrorRecList.Next;
  372. errorRecord != NULL;
  373. errorRecord = errorRecord->Next)
  374. {
  375. if (errorRecord->ThreadId == CurrentThreadId)
  376. {
  377. break;
  378. }
  379. }
  380. LeaveCriticalSection(&MprErrorRecCritSec);
  381. return(errorRecord);
  382. }
  383. LPERROR_RECORD
  384. MprAllocErrorRecord(
  385. VOID)
  386. /*++
  387. Routine Description:
  388. This function allocates and initializes an Error Record for the
  389. current thread. Then it places the error record in the global
  390. MprErrorRecList.
  391. Even if the thread exits, the record is not freed until the DLL unloads.
  392. This is OK because this function is called only if a provider calls
  393. WNetSetLastError, which is rare.
  394. Arguments:
  395. none
  396. Return Value:
  397. TRUE - The operation completed successfully
  398. FALSE - An error occured in the allocation.
  399. Note:
  400. --*/
  401. {
  402. LPERROR_RECORD record;
  403. LPERROR_RECORD errorRecord;
  404. //
  405. // Allocate memory for the storage of the error message
  406. // and add the record to the linked list.
  407. //
  408. errorRecord = (LPERROR_RECORD)LocalAlloc(LPTR,sizeof (ERROR_RECORD));
  409. if (errorRecord == NULL) {
  410. MPR_LOG1(ERROR,"MprAllocErrorRecord:LocalAlloc Failed %d\n",
  411. GetLastError());
  412. return NULL;
  413. }
  414. //
  415. // Initialize the error record
  416. //
  417. errorRecord->ThreadId = GetCurrentThreadId();
  418. errorRecord->ErrorCode = WN_SUCCESS;
  419. errorRecord->ErrorText = NULL;
  420. //
  421. // Add the record to the linked list.
  422. //
  423. EnterCriticalSection(&MprErrorRecCritSec);
  424. record = &MprErrorRecList;
  425. ADD_TO_LIST(record, errorRecord);
  426. LeaveCriticalSection(&MprErrorRecCritSec);
  427. return errorRecord;
  428. }
  429. VOID
  430. MprFreeAllErrorRecords(
  431. VOID)
  432. /*++
  433. Routine Description:
  434. This function is called when the DLL is unloading due to a FreeLibrary
  435. call. It frees all the error records (for all threads) that have been
  436. created since the DLL was loaded. If there is a pointer to a text string
  437. in a record, the buffer for that string is freed also.
  438. Arguments:
  439. Return Value:
  440. Note:
  441. --*/
  442. {
  443. LPERROR_RECORD nextRecord;
  444. EnterCriticalSection(&MprErrorRecCritSec);
  445. for (LPERROR_RECORD record = MprErrorRecList.Next;
  446. record != NULL;
  447. record = nextRecord)
  448. {
  449. MPR_LOG1(TRACE,"MprFreeErrorRecord: Freeing Record for thread 0x%x\n",
  450. record->ThreadId);
  451. LocalFree(record->ErrorText);
  452. LocalFree(record->ProviderName);
  453. nextRecord = record->Next;
  454. LocalFree(record);
  455. }
  456. MprErrorRecList.Next = NULL;
  457. LeaveCriticalSection(&MprErrorRecCritSec);
  458. }
  459. DWORD
  460. MultinetGetErrorTextW(
  461. OUT LPWSTR lpErrorTextBuf OPTIONAL,
  462. IN OUT LPDWORD lpnErrorBufSize,
  463. OUT LPWSTR lpProviderNameBuf OPTIONAL,
  464. IN OUT LPDWORD lpnNameBufSize
  465. )
  466. /*++
  467. Routine Description:
  468. This is an internal interface between the shell and the MPR. It
  469. combines the work of calling GetLastError and WNetGetLastError into
  470. one call. It returns the text for the last error that occurred in
  471. a WNet API in the current thread. The error text could have been
  472. customized by the provider calling WNetSetLastError.
  473. Arguments:
  474. lpErrorTextBuf - Pointer to buffer that will receive a null-terminated
  475. string describing the error. May be NULL if not required.
  476. lpnErrorBufSize - On entry, indicates the size (in characters) of
  477. lpErrorTextBuf. A buffer of at least 256 bytes is recommended.
  478. If the buffer is too small for the error string, the string will
  479. simply be truncated (it is still guaranteed to be null terminated).
  480. May be NULL if lpErrorTextBuf is NULL.
  481. lpProviderNameBuf - This points to a buffer that will receive the name
  482. of the provider that raised the error, if it is known. May be NULL
  483. if not required.
  484. lpnNameBufSize - This value indicates the size (in characters) of
  485. lpProviderNameBuf. If the buffer is too small for the provider
  486. name, the name will simply be truncated (it is still guaranteed to
  487. be null terminated). May be NULL if lpProviderNameBuf is NULL.
  488. Return Value:
  489. WN_SUCCESS - if the call was successful.
  490. WN_BAD_POINTER - One or more of the passed in pointers is bad.
  491. Other errors from WNetGetLastErrorW or FormatMessageW.
  492. Differences from Win 95:
  493. The provider name is returned as an empty string except in cases when
  494. the error was set by calling WNetSetLastError.
  495. Win 95 implements the feature that the caller can call other Win32 APIs
  496. before calling this API without losing the last error from the last
  497. WNet API. This is not implemented here. The caller must preserve the
  498. last error by calling GetLastError after the WNet API and SetLastError
  499. before MultinetGetErrorText.
  500. Win 95 gives providers the ability to customize error strings for
  501. standard Win32 error codes by calling NPSSetCustomText. We do not.
  502. --*/
  503. {
  504. DWORD status = WN_SUCCESS;
  505. DWORD nErrorBufSize = 0;
  506. DWORD nNameBufSize = 0;
  507. //
  508. // Probe output buffers, and initialize to empty strings
  509. //
  510. if (ARGUMENT_PRESENT(lpErrorTextBuf))
  511. {
  512. if (IS_BAD_WCHAR_BUFFER(lpErrorTextBuf, lpnErrorBufSize))
  513. {
  514. status = WN_BAD_POINTER;
  515. }
  516. else
  517. {
  518. nErrorBufSize = *lpnErrorBufSize;
  519. if (nErrorBufSize > 0)
  520. {
  521. lpErrorTextBuf[0] = L'\0';
  522. }
  523. }
  524. }
  525. if (ARGUMENT_PRESENT(lpProviderNameBuf))
  526. {
  527. if (IS_BAD_WCHAR_BUFFER(lpProviderNameBuf, lpnNameBufSize))
  528. {
  529. status = WN_BAD_POINTER;
  530. }
  531. else
  532. {
  533. nNameBufSize = *lpnNameBufSize;
  534. if (nNameBufSize > 0)
  535. {
  536. lpProviderNameBuf[0] = L'\0';
  537. }
  538. }
  539. }
  540. if (status != WN_SUCCESS)
  541. {
  542. return status;
  543. }
  544. //
  545. // Get the last error that occurred in this thread
  546. //
  547. DWORD LastError = GetLastError(); // last error in this thread
  548. DWORD ErrorCode = LastError; // message id to retrieve
  549. LPWSTR ErrorText = NULL; // error text to return
  550. LPWSTR ProviderName = NULL; // provider name to return
  551. BOOL bFreeErrorText = FALSE; // whether to call LocalFree
  552. //
  553. // If it's an extended error, look in this thread's error record for
  554. // the actual error code and strings
  555. //
  556. if (LastError == WN_EXTENDED_ERROR)
  557. {
  558. LPERROR_RECORD errorRecord = MprFindErrorRecord();
  559. if (errorRecord)
  560. {
  561. ErrorCode = errorRecord->ErrorCode;
  562. ErrorText = errorRecord->ErrorText;
  563. ProviderName = errorRecord->ProviderName;
  564. }
  565. else
  566. {
  567. // No error record found.
  568. // Either someone called SetLastError(WN_EXTENDED_ERROR) without
  569. // calling WNetSetErrorText, or an error record couldn't be
  570. // allocated because of lack of memory.
  571. // We'll return the standard message for WN_EXTENDED_ERROR.
  572. MPR_LOG0(ERROR,"MultinetGetErrorTextW:Couldn't retrieve extended error\n");
  573. }
  574. }
  575. WCHAR Buffer[UNKNOWN_ERROR_LENGTH] = L"";
  576. //
  577. // Compute the final error text
  578. //
  579. if (ARGUMENT_PRESENT(lpErrorTextBuf))
  580. {
  581. //
  582. // If it wasn't an extended error, or we didn't get a custom error
  583. // string from WNetGetLastError, load the standard message for the
  584. // error code.
  585. //
  586. if (ErrorText == NULL)
  587. {
  588. if (FormatMessage(
  589. FORMAT_MESSAGE_FROM_SYSTEM | // get msg from system
  590. FORMAT_MESSAGE_ALLOCATE_BUFFER | // let system alloc buffer
  591. FORMAT_MESSAGE_IGNORE_INSERTS, // no parms in msg
  592. NULL, // no source module or buffer
  593. ErrorCode, // message id
  594. 0, // use default language id
  595. (LPWSTR) &ErrorText,// pointer to buffer for message
  596. nErrorBufSize, // min num of chars to allocate
  597. NULL // message parameters (none)
  598. ))
  599. {
  600. bFreeErrorText = TRUE; // ErrorText was allocated by the system
  601. }
  602. else
  603. {
  604. //
  605. // Couldn't get a message from the system, so use a catch-all
  606. //
  607. int cch = LoadString(hDLL,
  608. IDS_XERR_UNKNOWN,
  609. Buffer,
  610. LENGTH(Buffer));
  611. //
  612. // If this fails, up the value of UNKNOWN_ERROR_LENGTH
  613. //
  614. ASSERT(cch > 0 && cch < LENGTH(Buffer));
  615. ErrorText = Buffer;
  616. }
  617. }
  618. ASSERT (ErrorText != NULL);
  619. //
  620. // Decide whether to copy the error text to the caller's buffer
  621. //
  622. DWORD ReqLen = wcslen(ErrorText) + 1 ;
  623. if (ReqLen > nErrorBufSize)
  624. {
  625. *lpnErrorBufSize = ReqLen;
  626. status = WN_MORE_DATA;
  627. }
  628. }
  629. //
  630. // Compute final provider name, decide whether to copy to caller's buffer
  631. //
  632. if (ARGUMENT_PRESENT(lpProviderNameBuf))
  633. {
  634. if (ProviderName == NULL)
  635. {
  636. ProviderName = L"";
  637. }
  638. DWORD ReqLen = wcslen(ProviderName) + 1;
  639. if (ReqLen > nNameBufSize)
  640. {
  641. *lpnNameBufSize = ReqLen;
  642. status = WN_MORE_DATA;
  643. }
  644. }
  645. //
  646. // Copy strings
  647. //
  648. if (status == WN_SUCCESS)
  649. {
  650. if (ARGUMENT_PRESENT(lpErrorTextBuf))
  651. {
  652. wcscpy(lpErrorTextBuf, ErrorText);
  653. }
  654. if (ARGUMENT_PRESENT(lpProviderNameBuf))
  655. {
  656. wcscpy(lpProviderNameBuf, ProviderName);
  657. }
  658. }
  659. //
  660. // Free buffer allocated by FormatMessage
  661. //
  662. if (bFreeErrorText)
  663. {
  664. LocalFree(ErrorText);
  665. }
  666. //
  667. // FormatMessage, LocalFree, or other APIs we may have called from this API,
  668. // could have changed the last error, so restore it now.
  669. //
  670. SetLastError(LastError);
  671. return status;
  672. }