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.

583 lines
13 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. oledserr.c
  5. Abstract:
  6. Contains the entry point for
  7. ADsGetLastError
  8. ADsSetLastError
  9. ADsFreeAllErrorRecords
  10. Also contains the following support routines:
  11. ADsAllocErrorRecord
  12. ADsFreeErrorRecord
  13. ADsFindErrorRecord
  14. Author:
  15. Ram Viswanathan (ramv) 09-24-1996
  16. appropriated from mpr project. Originally written by danl.
  17. Environment:
  18. User Mode - Win32
  19. Revision History:
  20. 09-Sep-1996 ramv
  21. Copied from mpr project and made the following modifications.
  22. Renamed all errors to Active Directory errors. ADsGetLastError and
  23. ADsSetLastError now return an HRESULT giving status.
  24. --*/
  25. //
  26. // INCLUDES
  27. //
  28. #include <nt.h>
  29. #include <ntrtl.h>
  30. #include <nturtl.h>
  31. #include <windows.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <tchar.h>
  35. #include <wchar.h>
  36. #include "memory.h"
  37. #include "oledsdbg.h"
  38. #include "oledserr.h"
  39. #if DBG
  40. DECLARE_INFOLEVEL(ADsErr);
  41. DECLARE_DEBUG(ADsErr);
  42. #define ADsErrDebugOut(x) ADsErrInlineDebugOut x
  43. #else
  44. #define ADsErrDebugOut(x)
  45. #endif
  46. //
  47. // Global Data Structures
  48. //
  49. ERROR_RECORD ADsErrorRecList; // Initialized to zeros by loader
  50. CRITICAL_SECTION ADsErrorRecCritSec; // Initialized in libmain.cxx
  51. HRESULT
  52. ADsGetLastError(
  53. OUT LPDWORD lpError,
  54. OUT LPWSTR lpErrorBuf,
  55. IN DWORD dwErrorBufLen,
  56. OUT LPWSTR lpNameBuf,
  57. IN DWORD dwNameBufLen
  58. )
  59. /*++
  60. Routine Description:
  61. This function allows users to obtain the error code and accompanying
  62. text when they receive a ERROR_EXTENDED_ERROR in response to a ADs API
  63. function call.
  64. Arguments:
  65. lpError - This is a pointer to the location that will receive the
  66. error code.
  67. lpErrorBuf - This points to a buffer that will receive the null
  68. terminated string describing the error.
  69. dwErrorBufLen - This value that indicates the size (in characters) of
  70. lpErrorBuf.
  71. If the buffer is too small to receive an error string, the
  72. string will simply be truncated. (it is still guaranteed to be
  73. null terminated). A buffer of at least 256 bytes is recommended.
  74. lpNameBuf - This points to a buffer that will receive the name of
  75. the provider that raised the error.
  76. dwNameBufLen - This value indicates the size (in characters) of lpNameBuf.
  77. If the buffer is too small to receive an error string, the
  78. string will simply be truncated. (it is still guaranteed to be
  79. null terminated).
  80. Return Value:
  81. S_OK- if the call was successful.
  82. E_POINTER - One or more of the passed in pointers is bad.
  83. ERROR_BAD_DEVICE - This indicates that the threadID for the current
  84. thread could not be found in that table anywhere. This should
  85. never happen.
  86. --*/
  87. {
  88. LPERROR_RECORD errorRecord;
  89. DWORD dwNameStringLen;
  90. DWORD dwTextStringLen;
  91. HRESULT hr = S_OK;
  92. DWORD dwStatus = ERROR_SUCCESS;
  93. //
  94. // Screen the parameters as best we can.
  95. //
  96. if (!lpError || !lpErrorBuf || !lpNameBuf) {
  97. // some error, the user is never going to see this
  98. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  99. hr = E_POINTER;
  100. }
  101. if (dwStatus != ERROR_SUCCESS) {
  102. return(hr);
  103. }
  104. //
  105. // Get the current thread's error record.
  106. //
  107. errorRecord = ADsFindErrorRecord();
  108. if (errorRecord != NULL) {
  109. //
  110. // The record was found in the linked list.
  111. // See if there is a buffer to put data into.
  112. //
  113. if (dwErrorBufLen > 0) {
  114. //
  115. // Check to see if there is error text to return.
  116. // If not, indicate a 0 length string.
  117. //
  118. if(errorRecord->pszErrorText == NULL) {
  119. *lpErrorBuf = L'\0';
  120. }
  121. else {
  122. //
  123. // If the error text won't fit into the user buffer, fill it
  124. // as best we can, and NULL terminate it.
  125. //
  126. dwTextStringLen = (DWORD) wcslen(errorRecord->pszErrorText);
  127. if(dwErrorBufLen < dwTextStringLen + 1) {
  128. dwTextStringLen = dwErrorBufLen - 1;
  129. }
  130. //
  131. // dwTextStringLen now contains the number of characters we
  132. // will copy without the NULL terminator.
  133. //
  134. wcsncpy(lpErrorBuf, errorRecord->pszErrorText, dwTextStringLen);
  135. *(lpErrorBuf + dwTextStringLen) = L'\0';
  136. }
  137. }
  138. //
  139. // If there is a Name Buffer to put the provider into, then...
  140. //
  141. if (dwNameBufLen > 0) {
  142. //
  143. // See if the Provider Name will fit in the user buffer.
  144. //
  145. dwNameStringLen = errorRecord->pszProviderName ?
  146. ((DWORD)wcslen(errorRecord->pszProviderName) + 1) :
  147. 1 ;
  148. //
  149. // If the user buffer is smaller than the required size,
  150. // set up to copy the smaller of the two.
  151. //
  152. if(dwNameBufLen < dwNameStringLen + 1) {
  153. dwNameStringLen = dwNameBufLen - 1;
  154. }
  155. if (errorRecord->pszProviderName) {
  156. wcsncpy(lpNameBuf, errorRecord->pszProviderName, dwNameStringLen);
  157. *(lpNameBuf + dwNameStringLen) = L'\0';
  158. }
  159. else {
  160. *lpNameBuf = L'\0';
  161. }
  162. }
  163. *lpError = errorRecord->dwErrorCode;
  164. return(S_OK);
  165. }
  166. else {
  167. //
  168. // If we get here, a record for the current thread could not be found.
  169. //
  170. *lpError = ERROR_SUCCESS;
  171. if (dwErrorBufLen > 0) {
  172. *lpErrorBuf = L'\0';
  173. }
  174. if (dwNameBufLen > 0) {
  175. *lpNameBuf = L'\0';
  176. }
  177. return(S_OK);
  178. }
  179. }
  180. VOID
  181. ADsSetLastError(
  182. IN DWORD dwErr,
  183. IN LPCWSTR pszError,
  184. IN LPCWSTR pszProvider
  185. )
  186. /*++
  187. Routine Description:
  188. This function is used by Active Directory Providers to set extended errors.
  189. It saves away the error information in a "per thread" data structure.
  190. Arguments:
  191. dwErr - The error that occured. This may be a Windows defined error,
  192. in which case pszError is ignored. or it may be ERROR_EXTENDED_ERROR
  193. to indicate that the provider has a network specific error to report.
  194. pszError - String describing a network specific error.
  195. pszProvider - String naming the network provider raising the error.
  196. Return Value:
  197. none
  198. --*/
  199. {
  200. DWORD dwStatus = ERROR_SUCCESS;
  201. LPERROR_RECORD errorRecord;
  202. //
  203. // Get the Error Record for the current thread.
  204. //
  205. errorRecord = ADsFindErrorRecord();
  206. //
  207. // if there isn't a record for the current thread, then add it.
  208. //
  209. if (errorRecord == NULL)
  210. {
  211. errorRecord = ADsAllocErrorRecord();
  212. if (errorRecord == NULL)
  213. {
  214. ADsErrDebugOut((DEB_TRACE,
  215. "ADsSetLastError:Could not allocate Error Record\n"));
  216. return;
  217. }
  218. }
  219. //
  220. // Update the error code in the error record. At the same time,
  221. // free up any old strings since they are now obsolete, and init
  222. // the pointer to NULL. Also set the ProviderName pointer in the
  223. // ErrorRecord to point to the provider's name.
  224. //
  225. errorRecord->dwErrorCode = dwErr;
  226. if(errorRecord->pszProviderName){
  227. FreeADsMem(errorRecord->pszProviderName);
  228. }
  229. errorRecord->pszProviderName = NULL;
  230. if(errorRecord->pszErrorText){
  231. FreeADsMem(errorRecord->pszErrorText);
  232. }
  233. errorRecord->pszErrorText = NULL;
  234. //
  235. // Allocate memory for the provider name.
  236. //
  237. if (pszProvider) {
  238. errorRecord->pszProviderName = (WCHAR *)AllocADsMem(
  239. ((DWORD)wcslen(pszProvider) +1) * sizeof(WCHAR));
  240. if (!(errorRecord->pszProviderName)) {
  241. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  242. } else {
  243. //
  244. // Copy the string to the newly allocated buffer.
  245. //
  246. wcscpy(errorRecord->pszProviderName, pszProvider);
  247. }
  248. }
  249. if (dwStatus != ERROR_SUCCESS) {
  250. return;
  251. }
  252. //
  253. // Allocate memory for the storage of the error text.
  254. //
  255. if (pszError) {
  256. errorRecord->pszErrorText = (WCHAR *) AllocADsMem(
  257. ((DWORD)wcslen(pszError) +1)* sizeof(WCHAR));
  258. if (errorRecord->pszErrorText) {
  259. //
  260. // Copy the error text into the newly allocated buffer.
  261. //
  262. wcscpy(errorRecord->pszErrorText, pszError);
  263. }
  264. // We do not really care about an error because we
  265. // are going to return anyway.
  266. }
  267. return;
  268. }
  269. LPERROR_RECORD
  270. ADsFindErrorRecord(
  271. VOID)
  272. /*++
  273. Routine Description:
  274. Looks through the linked list of ErrorRecords in search of one for
  275. the current thread.
  276. Arguments:
  277. none
  278. Return Value:
  279. Returns LPERROR_RECORD if an error record was found. Otherwise, it
  280. returns NULL.
  281. --*/
  282. {
  283. LPERROR_RECORD errorRecord;
  284. DWORD dwCurrentThreadId = GetCurrentThreadId();
  285. EnterCriticalSection(&ADsErrorRecCritSec);
  286. for (errorRecord = ADsErrorRecList.Next;
  287. errorRecord != NULL;
  288. errorRecord = errorRecord->Next)
  289. {
  290. if (errorRecord->dwThreadId == dwCurrentThreadId)
  291. {
  292. break;
  293. }
  294. }
  295. LeaveCriticalSection(&ADsErrorRecCritSec);
  296. return(errorRecord);
  297. }
  298. LPERROR_RECORD
  299. ADsAllocErrorRecord(
  300. VOID)
  301. /*++
  302. Routine Description:
  303. This function allocates and initializes an Error Record for the
  304. current thread. Then it places the error record in the global
  305. ADsErrorRecList.
  306. Even if the thread exits, the record is not freed until the DLL unloads.
  307. This is OK because this function is called only if a provider calls
  308. ADsSetLastError, which is rare.
  309. Arguments:
  310. none
  311. Return Value:
  312. TRUE - The operation completed successfully
  313. FALSE - An error occured in the allocation.
  314. Note:
  315. --*/
  316. {
  317. LPERROR_RECORD record;
  318. LPERROR_RECORD errorRecord;
  319. //
  320. // Allocate memory for the storage of the error message
  321. // and add the record to the linked list.
  322. //
  323. errorRecord = (LPERROR_RECORD)AllocADsMem(sizeof (ERROR_RECORD));
  324. if (errorRecord == NULL) {
  325. ADsErrDebugOut((
  326. DEB_TRACE,
  327. "ADsAllocErrorRecord:LocalAlloc Failed %d\n",
  328. GetLastError()
  329. ));
  330. return NULL;
  331. }
  332. //
  333. // Initialize the error record
  334. //
  335. errorRecord->dwThreadId = GetCurrentThreadId();
  336. errorRecord->dwErrorCode = ERROR_SUCCESS;
  337. errorRecord->pszErrorText = NULL;
  338. //
  339. // Add the record to the linked list.
  340. //
  341. EnterCriticalSection(&ADsErrorRecCritSec);
  342. record = &ADsErrorRecList;
  343. ADD_TO_LIST(record, errorRecord);
  344. LeaveCriticalSection(&ADsErrorRecCritSec);
  345. return errorRecord;
  346. }
  347. VOID
  348. ADsFreeAllErrorRecords(
  349. VOID)
  350. /*++
  351. Routine Description:
  352. This function is called when the DLL is unloading due to a FreeLibrary
  353. call. It frees all the error records (for all threads) that have been
  354. created since the DLL was loaded. If there is a pointer to a text string
  355. in a record, the buffer for that string is freed also.
  356. Arguments:
  357. Return Value:
  358. Note:
  359. --*/
  360. {
  361. LPERROR_RECORD nextRecord;
  362. LPERROR_RECORD record;
  363. EnterCriticalSection(&ADsErrorRecCritSec);
  364. for (record = ADsErrorRecList.Next;
  365. record != NULL;
  366. record = nextRecord)
  367. {
  368. ADsErrDebugOut(
  369. (DEB_TRACE,
  370. "ADsFreeErrorRecord: Freeing Record for thread 0x%x\n",
  371. record->dwThreadId ));
  372. if(record->pszErrorText){
  373. FreeADsMem(record->pszErrorText);
  374. }
  375. record->pszErrorText = NULL;
  376. if(record->pszProviderName){
  377. FreeADsMem(record->pszProviderName);
  378. }
  379. record->pszProviderName = NULL;
  380. nextRecord = record->Next;
  381. if(record){
  382. FreeADsMem(record);
  383. }
  384. record = NULL;
  385. }
  386. ADsErrorRecList.Next = NULL;
  387. LeaveCriticalSection(&ADsErrorRecCritSec);
  388. }
  389. VOID
  390. ADsFreeThreadErrorRecords(
  391. VOID)
  392. /*++
  393. Routine Description:
  394. This function is called when the DLL is unloading due to a FreeLibrary
  395. call. It frees all the error records (for all threads) that have been
  396. created since the DLL was loaded. If there is a pointer to a text string
  397. in a record, the buffer for that string is freed also.
  398. Arguments:
  399. Return Value:
  400. Note:
  401. --*/
  402. {
  403. LPERROR_RECORD record;
  404. DWORD dwThreadId = GetCurrentThreadId();
  405. EnterCriticalSection(&ADsErrorRecCritSec);
  406. for (record = ADsErrorRecList.Next;
  407. record != NULL;
  408. record = record->Next)
  409. {
  410. ADsErrDebugOut(
  411. (DEB_TRACE,
  412. "ADsFreeErrorRecord: Freeing Record for thread 0x%x\n",
  413. record->dwThreadId ));
  414. if (record->dwThreadId == dwThreadId) {
  415. REMOVE_FROM_LIST(record);
  416. if(record->pszErrorText){
  417. FreeADsMem(record->pszErrorText);
  418. record->pszErrorText = NULL;
  419. }
  420. if(record->pszProviderName){
  421. FreeADsMem(record->pszProviderName);
  422. record->pszProviderName = NULL;
  423. }
  424. FreeADsMem(record);
  425. break;
  426. }
  427. }
  428. LeaveCriticalSection(&ADsErrorRecCritSec);
  429. }