/*++ Copyright (c) 1996 Microsoft Corporation Module Name: oledserr.c Abstract: Contains the entry point for ADsGetLastError ADsSetLastError ADsFreeAllErrorRecords Also contains the following support routines: ADsAllocErrorRecord ADsFreeErrorRecord ADsFindErrorRecord Author: Ram Viswanathan (ramv) 09-24-1996 appropriated from mpr project. Originally written by danl. Environment: User Mode - Win32 Revision History: 09-Sep-1996 ramv Copied from mpr project and made the following modifications. Renamed all errors to Active Directory errors. ADsGetLastError and ADsSetLastError now return an HRESULT giving status. --*/ // // INCLUDES // #include #include #include #include #include #include #include #include #include "memory.h" #include "oledsdbg.h" #include "oledserr.h" #if DBG DECLARE_INFOLEVEL(ADsErr); DECLARE_DEBUG(ADsErr); #define ADsErrDebugOut(x) ADsErrInlineDebugOut x #else #define ADsErrDebugOut(x) #endif // // Global Data Structures // ERROR_RECORD ADsErrorRecList; // Initialized to zeros by loader CRITICAL_SECTION ADsErrorRecCritSec; // Initialized in libmain.cxx HRESULT ADsGetLastError( OUT LPDWORD lpError, OUT LPWSTR lpErrorBuf, IN DWORD dwErrorBufLen, OUT LPWSTR lpNameBuf, IN DWORD dwNameBufLen ) /*++ Routine Description: This function allows users to obtain the error code and accompanying text when they receive a ERROR_EXTENDED_ERROR in response to a ADs API function call. Arguments: lpError - This is a pointer to the location that will receive the error code. lpErrorBuf - This points to a buffer that will receive the null terminated string describing the error. dwErrorBufLen - This value that indicates the size (in characters) of lpErrorBuf. If the buffer is too small to receive an error string, the string will simply be truncated. (it is still guaranteed to be null terminated). A buffer of at least 256 bytes is recommended. lpNameBuf - This points to a buffer that will receive the name of the provider that raised the error. dwNameBufLen - This value indicates the size (in characters) of lpNameBuf. If the buffer is too small to receive an error string, the string will simply be truncated. (it is still guaranteed to be null terminated). Return Value: S_OK- if the call was successful. E_POINTER - One or more of the passed in pointers is bad. ERROR_BAD_DEVICE - This indicates that the threadID for the current thread could not be found in that table anywhere. This should never happen. --*/ { LPERROR_RECORD errorRecord; DWORD dwNameStringLen; DWORD dwTextStringLen; HRESULT hr = S_OK; DWORD dwStatus = ERROR_SUCCESS; // // Screen the parameters as best we can. // if (!lpError || !lpErrorBuf || !lpNameBuf) { // some error, the user is never going to see this dwStatus = ERROR_NOT_ENOUGH_MEMORY; hr = E_POINTER; } if (dwStatus != ERROR_SUCCESS) { return(hr); } // // Get the current thread's error record. // errorRecord = ADsFindErrorRecord(); if (errorRecord != NULL) { // // The record was found in the linked list. // See if there is a buffer to put data into. // if (dwErrorBufLen > 0) { // // Check to see if there is error text to return. // If not, indicate a 0 length string. // if(errorRecord->pszErrorText == NULL) { *lpErrorBuf = L'\0'; } else { // // If the error text won't fit into the user buffer, fill it // as best we can, and NULL terminate it. // dwTextStringLen = (DWORD) wcslen(errorRecord->pszErrorText); if(dwErrorBufLen < dwTextStringLen + 1) { dwTextStringLen = dwErrorBufLen - 1; } // // dwTextStringLen now contains the number of characters we // will copy without the NULL terminator. // wcsncpy(lpErrorBuf, errorRecord->pszErrorText, dwTextStringLen); *(lpErrorBuf + dwTextStringLen) = L'\0'; } } // // If there is a Name Buffer to put the provider into, then... // if (dwNameBufLen > 0) { // // See if the Provider Name will fit in the user buffer. // dwNameStringLen = errorRecord->pszProviderName ? ((DWORD)wcslen(errorRecord->pszProviderName) + 1) : 1 ; // // If the user buffer is smaller than the required size, // set up to copy the smaller of the two. // if(dwNameBufLen < dwNameStringLen + 1) { dwNameStringLen = dwNameBufLen - 1; } if (errorRecord->pszProviderName) { wcsncpy(lpNameBuf, errorRecord->pszProviderName, dwNameStringLen); *(lpNameBuf + dwNameStringLen) = L'\0'; } else { *lpNameBuf = L'\0'; } } *lpError = errorRecord->dwErrorCode; return(S_OK); } else { // // If we get here, a record for the current thread could not be found. // *lpError = ERROR_SUCCESS; if (dwErrorBufLen > 0) { *lpErrorBuf = L'\0'; } if (dwNameBufLen > 0) { *lpNameBuf = L'\0'; } return(S_OK); } } VOID ADsSetLastError( IN DWORD dwErr, IN LPCWSTR pszError, IN LPCWSTR pszProvider ) /*++ Routine Description: This function is used by Active Directory Providers to set extended errors. It saves away the error information in a "per thread" data structure. Arguments: dwErr - The error that occured. This may be a Windows defined error, in which case pszError is ignored. or it may be ERROR_EXTENDED_ERROR to indicate that the provider has a network specific error to report. pszError - String describing a network specific error. pszProvider - String naming the network provider raising the error. Return Value: none --*/ { DWORD dwStatus = ERROR_SUCCESS; LPERROR_RECORD errorRecord; // // Get the Error Record for the current thread. // errorRecord = ADsFindErrorRecord(); // // if there isn't a record for the current thread, then add it. // if (errorRecord == NULL) { errorRecord = ADsAllocErrorRecord(); if (errorRecord == NULL) { ADsErrDebugOut((DEB_TRACE, "ADsSetLastError:Could not allocate Error Record\n")); return; } } // // Update the error code in the error record. At the same time, // free up any old strings since they are now obsolete, and init // the pointer to NULL. Also set the ProviderName pointer in the // ErrorRecord to point to the provider's name. // errorRecord->dwErrorCode = dwErr; if(errorRecord->pszProviderName){ FreeADsMem(errorRecord->pszProviderName); } errorRecord->pszProviderName = NULL; if(errorRecord->pszErrorText){ FreeADsMem(errorRecord->pszErrorText); } errorRecord->pszErrorText = NULL; // // Allocate memory for the provider name. // if (pszProvider) { errorRecord->pszProviderName = (WCHAR *)AllocADsMem( ((DWORD)wcslen(pszProvider) +1) * sizeof(WCHAR)); if (!(errorRecord->pszProviderName)) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; } else { // // Copy the string to the newly allocated buffer. // wcscpy(errorRecord->pszProviderName, pszProvider); } } if (dwStatus != ERROR_SUCCESS) { return; } // // Allocate memory for the storage of the error text. // if (pszError) { errorRecord->pszErrorText = (WCHAR *) AllocADsMem( ((DWORD)wcslen(pszError) +1)* sizeof(WCHAR)); if (errorRecord->pszErrorText) { // // Copy the error text into the newly allocated buffer. // wcscpy(errorRecord->pszErrorText, pszError); } // We do not really care about an error because we // are going to return anyway. } return; } LPERROR_RECORD ADsFindErrorRecord( VOID) /*++ Routine Description: Looks through the linked list of ErrorRecords in search of one for the current thread. Arguments: none Return Value: Returns LPERROR_RECORD if an error record was found. Otherwise, it returns NULL. --*/ { LPERROR_RECORD errorRecord; DWORD dwCurrentThreadId = GetCurrentThreadId(); EnterCriticalSection(&ADsErrorRecCritSec); for (errorRecord = ADsErrorRecList.Next; errorRecord != NULL; errorRecord = errorRecord->Next) { if (errorRecord->dwThreadId == dwCurrentThreadId) { break; } } LeaveCriticalSection(&ADsErrorRecCritSec); return(errorRecord); } LPERROR_RECORD ADsAllocErrorRecord( VOID) /*++ Routine Description: This function allocates and initializes an Error Record for the current thread. Then it places the error record in the global ADsErrorRecList. Even if the thread exits, the record is not freed until the DLL unloads. This is OK because this function is called only if a provider calls ADsSetLastError, which is rare. Arguments: none Return Value: TRUE - The operation completed successfully FALSE - An error occured in the allocation. Note: --*/ { LPERROR_RECORD record; LPERROR_RECORD errorRecord; // // Allocate memory for the storage of the error message // and add the record to the linked list. // errorRecord = (LPERROR_RECORD)AllocADsMem(sizeof (ERROR_RECORD)); if (errorRecord == NULL) { ADsErrDebugOut(( DEB_TRACE, "ADsAllocErrorRecord:LocalAlloc Failed %d\n", GetLastError() )); return NULL; } // // Initialize the error record // errorRecord->dwThreadId = GetCurrentThreadId(); errorRecord->dwErrorCode = ERROR_SUCCESS; errorRecord->pszErrorText = NULL; // // Add the record to the linked list. // EnterCriticalSection(&ADsErrorRecCritSec); record = &ADsErrorRecList; ADD_TO_LIST(record, errorRecord); LeaveCriticalSection(&ADsErrorRecCritSec); return errorRecord; } VOID ADsFreeAllErrorRecords( VOID) /*++ Routine Description: This function is called when the DLL is unloading due to a FreeLibrary call. It frees all the error records (for all threads) that have been created since the DLL was loaded. If there is a pointer to a text string in a record, the buffer for that string is freed also. Arguments: Return Value: Note: --*/ { LPERROR_RECORD nextRecord; LPERROR_RECORD record; EnterCriticalSection(&ADsErrorRecCritSec); for (record = ADsErrorRecList.Next; record != NULL; record = nextRecord) { ADsErrDebugOut( (DEB_TRACE, "ADsFreeErrorRecord: Freeing Record for thread 0x%x\n", record->dwThreadId )); if(record->pszErrorText){ FreeADsMem(record->pszErrorText); } record->pszErrorText = NULL; if(record->pszProviderName){ FreeADsMem(record->pszProviderName); } record->pszProviderName = NULL; nextRecord = record->Next; if(record){ FreeADsMem(record); } record = NULL; } ADsErrorRecList.Next = NULL; LeaveCriticalSection(&ADsErrorRecCritSec); } VOID ADsFreeThreadErrorRecords( VOID) /*++ Routine Description: This function is called when the DLL is unloading due to a FreeLibrary call. It frees all the error records (for all threads) that have been created since the DLL was loaded. If there is a pointer to a text string in a record, the buffer for that string is freed also. Arguments: Return Value: Note: --*/ { LPERROR_RECORD record; DWORD dwThreadId = GetCurrentThreadId(); EnterCriticalSection(&ADsErrorRecCritSec); for (record = ADsErrorRecList.Next; record != NULL; record = record->Next) { ADsErrDebugOut( (DEB_TRACE, "ADsFreeErrorRecord: Freeing Record for thread 0x%x\n", record->dwThreadId )); if (record->dwThreadId == dwThreadId) { REMOVE_FROM_LIST(record); if(record->pszErrorText){ FreeADsMem(record->pszErrorText); record->pszErrorText = NULL; } if(record->pszProviderName){ FreeADsMem(record->pszProviderName); record->pszProviderName = NULL; } FreeADsMem(record); break; } } LeaveCriticalSection(&ADsErrorRecCritSec); }