windows-nt/Source/XPSP1/NT/ds/adsi/ldapc/oledserr.c

584 lines
13 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <wchar.h>
#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);
}