858 lines
22 KiB
C++
858 lines
22 KiB
C++
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
lasterr.c
|
||
|
||
Abstract:
|
||
|
||
Contains the entry point for
|
||
WNetGetLastErrorW
|
||
WNetSetLastErrorW
|
||
MultinetGetErrorTextW
|
||
|
||
Also contains the following support routines:
|
||
MprAllocErrorRecord
|
||
MprFreeErrorRecord
|
||
MprFindErrorRecord
|
||
|
||
Author:
|
||
|
||
Dan Lafferty (danl) 17-Oct-1991
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
09-Aug-1996 anirudhs
|
||
Fixed bug in MprFindErrorRecord. Replaced MprFreeErrorRecord with
|
||
MprFreeAllErrorRecords (see mprinit.cxx). Moved some definitions
|
||
from mprdata.h here.
|
||
|
||
20-Jun-1995 anirudhs
|
||
Added MultinetGetErrorTextW. Removed CurrentThreadId parameter to
|
||
MprFindErrorRecord. Some other cleanups.
|
||
|
||
11-Aug-1993 danl
|
||
WNetGetLastErrorW: Got rid of TCHAR stuff and committed to UNICODE.
|
||
Also, consistently use number of characters rather than number
|
||
of bytes.
|
||
|
||
08-Jun-1993 danl
|
||
Now we handle the case where WNetSetLastError is called with a
|
||
threadId that we don't know about. This happens in the case where
|
||
a sub-thread does a LoadLibrary on MPR.DLL. We only get
|
||
notification of the one thread attaching. Yet other threads within
|
||
that process may call the WinNet functions.
|
||
|
||
17-Oct-1991 danl
|
||
Created
|
||
|
||
--*/
|
||
//
|
||
// INCLUDES
|
||
//
|
||
|
||
#include "precomp.hxx"
|
||
#include <tstring.h> // STRSIZE, STRCPY
|
||
#include "mprres.h" // IDS_XERR_UNKNOWN
|
||
|
||
//=======================
|
||
// Data Structures
|
||
//=======================
|
||
|
||
typedef struct _ERROR_RECORD {
|
||
struct _ERROR_RECORD *Prev;
|
||
struct _ERROR_RECORD *Next;
|
||
DWORD ThreadId;
|
||
DWORD ErrorCode;
|
||
LPTSTR ErrorText; // This is an allocated buffer
|
||
LPTSTR ProviderName; // This is an allocated buffer
|
||
} ERROR_RECORD, *LPERROR_RECORD;
|
||
|
||
|
||
//=======================
|
||
// MACROS/DEFINES
|
||
//=======================
|
||
|
||
#define FIND_END_OF_LIST(record) while(record->Next != NULL) { \
|
||
record=record->Next; \
|
||
}
|
||
|
||
#define REMOVE_FROM_LIST(record) record->Prev->Next = record->Next; \
|
||
if (record->Next != NULL) { \
|
||
record->Next->Prev = record->Prev; \
|
||
}
|
||
|
||
#define ADD_TO_LIST(record, newRec) FIND_END_OF_LIST(record) \
|
||
record->Next = newRec; \
|
||
newRec->Prev = record; \
|
||
newRec->Next = NULL;
|
||
|
||
//
|
||
// The string (in English) is 44 characters long (+1 for the NULL)
|
||
//
|
||
#define UNKNOWN_ERROR_LENGTH 80
|
||
|
||
|
||
//=======================
|
||
// GLOBALS
|
||
//=======================
|
||
|
||
extern HMODULE hDLL; // DLL instance handle
|
||
|
||
//
|
||
// Local Functions
|
||
//
|
||
|
||
LPERROR_RECORD
|
||
MprAllocErrorRecord(
|
||
VOID);
|
||
|
||
LPERROR_RECORD
|
||
MprFindErrorRecord(
|
||
VOID);
|
||
|
||
|
||
//
|
||
// Global Data Structures
|
||
//
|
||
|
||
ERROR_RECORD MprErrorRecList; // Initialized to zeros by loader
|
||
CRITICAL_SECTION MprErrorRecCritSec; // Initialized in mprinit.cxx
|
||
|
||
|
||
|
||
DWORD
|
||
WNetGetLastErrorW(
|
||
OUT LPDWORD lpError,
|
||
OUT LPWSTR lpErrorBuf,
|
||
IN DWORD nErrorBufLen,
|
||
OUT LPWSTR lpNameBuf,
|
||
IN DWORD nNameBufLen
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allows users to obtain the error code and accompanying
|
||
text when they receive a WN_EXTENDED_ERROR in response to a WNet 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.
|
||
|
||
nErrorBufLen - 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.
|
||
|
||
nNameBufLen - 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:
|
||
|
||
WN_SUCCESS - if the call was successful.
|
||
|
||
WN_BAD_POINTER - One or more of the passed in pointers is bad.
|
||
|
||
WN_DEVICE_ERROR - 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 nameStringLen;
|
||
DWORD textStringLen;
|
||
DWORD status = WN_SUCCESS;
|
||
|
||
//
|
||
// Screen the parameters as best we can.
|
||
//
|
||
__try {
|
||
//
|
||
// If output buffers are provided, Probe them.
|
||
//
|
||
|
||
*lpError = WN_SUCCESS;
|
||
|
||
if (IsBadWritePtr(lpErrorBuf, nErrorBufLen * sizeof(WCHAR)) ||
|
||
IsBadWritePtr(lpNameBuf, nNameBufLen * sizeof(WCHAR)))
|
||
{
|
||
status = WN_BAD_POINTER;
|
||
}
|
||
}
|
||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
status = GetExceptionCode();
|
||
if (status != EXCEPTION_ACCESS_VIOLATION) {
|
||
MPR_LOG(ERROR,"WNetGetLastError:Unexpected Exception 0x%lx\n",status);
|
||
}
|
||
status = WN_BAD_POINTER;
|
||
}
|
||
|
||
if (status != WN_SUCCESS) {
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Get the current thread's error record.
|
||
//
|
||
errorRecord = MprFindErrorRecord();
|
||
|
||
if (errorRecord != NULL) {
|
||
//
|
||
// The record was found in the linked list.
|
||
// See if there is a buffer to put data into.
|
||
//
|
||
if (nErrorBufLen > 0) {
|
||
//
|
||
// Check to see if there is error text to return.
|
||
// If not, indicate a 0 length string.
|
||
//
|
||
if(errorRecord->ErrorText == 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.
|
||
//
|
||
textStringLen = wcslen(errorRecord->ErrorText);
|
||
|
||
if(nErrorBufLen < textStringLen + 1) {
|
||
textStringLen = nErrorBufLen - 1;
|
||
}
|
||
|
||
//
|
||
// textStringLen now contains the number of characters we
|
||
// will copy without the NULL terminator.
|
||
//
|
||
wcsncpy(lpErrorBuf, errorRecord->ErrorText, textStringLen);
|
||
*(lpErrorBuf + textStringLen) = L'\0';
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there is a Name Buffer to put the provider into, then...
|
||
//
|
||
if (nNameBufLen > 0) {
|
||
//
|
||
// See if the Provider Name will fit in the user buffer.
|
||
//
|
||
nameStringLen = errorRecord->ProviderName ?
|
||
(wcslen(errorRecord->ProviderName) + 1) :
|
||
1 ;
|
||
|
||
//
|
||
// If the user buffer is smaller than the required size,
|
||
// set up to copy the smaller of the two.
|
||
//
|
||
if(nNameBufLen < nameStringLen + 1) {
|
||
nameStringLen = nNameBufLen - 1;
|
||
}
|
||
|
||
if (errorRecord->ProviderName) {
|
||
wcsncpy(lpNameBuf, errorRecord->ProviderName, nameStringLen);
|
||
*(lpNameBuf + nameStringLen) = L'\0';
|
||
}
|
||
else {
|
||
*lpNameBuf = L'\0';
|
||
}
|
||
}
|
||
*lpError = errorRecord->ErrorCode;
|
||
|
||
return(WN_SUCCESS);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// If we get here, a record for the current thread could not be found.
|
||
//
|
||
*lpError = WN_SUCCESS;
|
||
if (nErrorBufLen > 0) {
|
||
*lpErrorBuf = L'\0';
|
||
}
|
||
if (nNameBufLen > 0) {
|
||
*lpNameBuf = L'\0';
|
||
}
|
||
return(WN_SUCCESS);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
WNetSetLastErrorW(
|
||
IN DWORD err,
|
||
IN LPWSTR lpError,
|
||
IN LPWSTR lpProvider
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used by Network Providers to set extended errors.
|
||
It saves away the error information in a "per thread" data structure.
|
||
This function also calls SetLastError with the WN_EXTENDED_ERROR.
|
||
|
||
Arguments:
|
||
|
||
err - The error that occured. This may be a Windows defined error,
|
||
in which case LpError is ignored. or it may be ERROR_EXTENDED_ERROR
|
||
to indicate that the provider has a network specific error to report.
|
||
|
||
lpError - String describing a network specific error.
|
||
|
||
lpProvider - String naming the network provider raising the error.
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
|
||
--*/
|
||
{
|
||
DWORD status = WN_SUCCESS;
|
||
LPERROR_RECORD errorRecord;
|
||
|
||
//
|
||
// Set the extended error status that tells the user they need to
|
||
// call WNetGetLastError to obtain further information.
|
||
//
|
||
SetLastError(WN_EXTENDED_ERROR);
|
||
|
||
//
|
||
// Get the Error Record for the current thread.
|
||
//
|
||
errorRecord = MprFindErrorRecord();
|
||
|
||
//
|
||
// if there isn't a record for the current thread, then add it.
|
||
//
|
||
if (errorRecord == NULL)
|
||
{
|
||
errorRecord = MprAllocErrorRecord();
|
||
|
||
if (errorRecord == NULL)
|
||
{
|
||
MPR_LOG0(ERROR,"WNetSetLastError: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->ErrorCode = err;
|
||
|
||
LocalFree(errorRecord->ProviderName);
|
||
errorRecord->ProviderName = NULL;
|
||
|
||
LocalFree(errorRecord->ErrorText);
|
||
errorRecord->ErrorText = NULL;
|
||
|
||
//
|
||
// Allocate memory for the provider name.
|
||
//
|
||
__try {
|
||
errorRecord->ProviderName = (WCHAR *) LocalAlloc(LPTR, STRSIZE(lpProvider));
|
||
}
|
||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// We have a problem with lpProvider.
|
||
//
|
||
status = GetExceptionCode();
|
||
if (status != EXCEPTION_ACCESS_VIOLATION) {
|
||
MPR_LOG(ERROR,"WNetSetLastError:Unexpected Exception 0x%lx\n",status);
|
||
}
|
||
status = WN_BAD_POINTER;
|
||
}
|
||
|
||
if (status != WN_SUCCESS) {
|
||
return;
|
||
}
|
||
|
||
if (errorRecord->ProviderName == NULL) {
|
||
//
|
||
// Unable to allocate memory for the Provider Name for the error
|
||
// record.
|
||
//
|
||
MPR_LOG(ERROR,
|
||
"WNetSetLastError:Unable to allocate mem for ProviderName\n",0);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Copy the string to the newly allocated buffer.
|
||
//
|
||
wcscpy(errorRecord->ProviderName, lpProvider);
|
||
|
||
//
|
||
// Allocate memory for the storage of the error text.
|
||
//
|
||
__try {
|
||
errorRecord->ErrorText = (WCHAR *) LocalAlloc(LPTR,STRSIZE(lpError));
|
||
}
|
||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// We have a problem with lpError.
|
||
//
|
||
status = GetExceptionCode();
|
||
if (status != EXCEPTION_ACCESS_VIOLATION) {
|
||
MPR_LOG(ERROR,"WNetSetLastError:Unexpected Exception 0x%lx\n",status);
|
||
}
|
||
status = WN_BAD_POINTER;
|
||
}
|
||
if (status != WN_SUCCESS) {
|
||
errorRecord->ErrorText = NULL;
|
||
}
|
||
|
||
if (errorRecord->ErrorText == NULL) {
|
||
|
||
//
|
||
// If we were unsuccessful in allocating for the ErrorText, then
|
||
// abort. The ErrorText Pointer has already been set to null.
|
||
//
|
||
MPR_LOG(ERROR,"WNetSetLastError:Unable to Alloc for ErrorText %d\n",
|
||
GetLastError());
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Copy the error text into the newly allocated buffer.
|
||
//
|
||
wcscpy(errorRecord->ErrorText, lpError);
|
||
return;
|
||
}
|
||
|
||
LPERROR_RECORD
|
||
MprFindErrorRecord(
|
||
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 CurrentThreadId = GetCurrentThreadId();
|
||
|
||
EnterCriticalSection(&MprErrorRecCritSec);
|
||
for (errorRecord = MprErrorRecList.Next;
|
||
errorRecord != NULL;
|
||
errorRecord = errorRecord->Next)
|
||
{
|
||
if (errorRecord->ThreadId == CurrentThreadId)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
LeaveCriticalSection(&MprErrorRecCritSec);
|
||
return(errorRecord);
|
||
}
|
||
|
||
|
||
LPERROR_RECORD
|
||
MprAllocErrorRecord(
|
||
VOID)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates and initializes an Error Record for the
|
||
current thread. Then it places the error record in the global
|
||
MprErrorRecList.
|
||
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
|
||
WNetSetLastError, 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)LocalAlloc(LPTR,sizeof (ERROR_RECORD));
|
||
|
||
if (errorRecord == NULL) {
|
||
MPR_LOG1(ERROR,"MprAllocErrorRecord:LocalAlloc Failed %d\n",
|
||
GetLastError());
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Initialize the error record
|
||
//
|
||
errorRecord->ThreadId = GetCurrentThreadId();
|
||
errorRecord->ErrorCode = WN_SUCCESS;
|
||
errorRecord->ErrorText = NULL;
|
||
|
||
//
|
||
// Add the record to the linked list.
|
||
//
|
||
EnterCriticalSection(&MprErrorRecCritSec);
|
||
|
||
record = &MprErrorRecList;
|
||
ADD_TO_LIST(record, errorRecord);
|
||
|
||
LeaveCriticalSection(&MprErrorRecCritSec);
|
||
|
||
return errorRecord;
|
||
}
|
||
|
||
|
||
VOID
|
||
MprFreeAllErrorRecords(
|
||
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;
|
||
|
||
EnterCriticalSection(&MprErrorRecCritSec);
|
||
|
||
for (LPERROR_RECORD record = MprErrorRecList.Next;
|
||
record != NULL;
|
||
record = nextRecord)
|
||
{
|
||
MPR_LOG1(TRACE,"MprFreeErrorRecord: Freeing Record for thread 0x%x\n",
|
||
record->ThreadId);
|
||
|
||
LocalFree(record->ErrorText);
|
||
LocalFree(record->ProviderName);
|
||
|
||
nextRecord = record->Next;
|
||
|
||
LocalFree(record);
|
||
}
|
||
|
||
MprErrorRecList.Next = NULL;
|
||
|
||
LeaveCriticalSection(&MprErrorRecCritSec);
|
||
}
|
||
|
||
|
||
DWORD
|
||
MultinetGetErrorTextW(
|
||
OUT LPWSTR lpErrorTextBuf OPTIONAL,
|
||
IN OUT LPDWORD lpnErrorBufSize,
|
||
OUT LPWSTR lpProviderNameBuf OPTIONAL,
|
||
IN OUT LPDWORD lpnNameBufSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is an internal interface between the shell and the MPR. It
|
||
combines the work of calling GetLastError and WNetGetLastError into
|
||
one call. It returns the text for the last error that occurred in
|
||
a WNet API in the current thread. The error text could have been
|
||
customized by the provider calling WNetSetLastError.
|
||
|
||
Arguments:
|
||
|
||
lpErrorTextBuf - Pointer to buffer that will receive a null-terminated
|
||
string describing the error. May be NULL if not required.
|
||
|
||
lpnErrorBufSize - On entry, indicates the size (in characters) of
|
||
lpErrorTextBuf. A buffer of at least 256 bytes is recommended.
|
||
If the buffer is too small for the error string, the string will
|
||
simply be truncated (it is still guaranteed to be null terminated).
|
||
May be NULL if lpErrorTextBuf is NULL.
|
||
|
||
lpProviderNameBuf - This points to a buffer that will receive the name
|
||
of the provider that raised the error, if it is known. May be NULL
|
||
if not required.
|
||
|
||
lpnNameBufSize - This value indicates the size (in characters) of
|
||
lpProviderNameBuf. If the buffer is too small for the provider
|
||
name, the name will simply be truncated (it is still guaranteed to
|
||
be null terminated). May be NULL if lpProviderNameBuf is NULL.
|
||
|
||
Return Value:
|
||
|
||
WN_SUCCESS - if the call was successful.
|
||
|
||
WN_BAD_POINTER - One or more of the passed in pointers is bad.
|
||
|
||
Other errors from WNetGetLastErrorW or FormatMessageW.
|
||
|
||
Differences from Win 95:
|
||
|
||
The provider name is returned as an empty string except in cases when
|
||
the error was set by calling WNetSetLastError.
|
||
|
||
Win 95 implements the feature that the caller can call other Win32 APIs
|
||
before calling this API without losing the last error from the last
|
||
WNet API. This is not implemented here. The caller must preserve the
|
||
last error by calling GetLastError after the WNet API and SetLastError
|
||
before MultinetGetErrorText.
|
||
|
||
Win 95 gives providers the ability to customize error strings for
|
||
standard Win32 error codes by calling NPSSetCustomText. We do not.
|
||
|
||
--*/
|
||
{
|
||
DWORD status = WN_SUCCESS;
|
||
DWORD nErrorBufSize = 0;
|
||
DWORD nNameBufSize = 0;
|
||
|
||
//
|
||
// Probe output buffers, and initialize to empty strings
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(lpErrorTextBuf))
|
||
{
|
||
if (IS_BAD_WCHAR_BUFFER(lpErrorTextBuf, lpnErrorBufSize))
|
||
{
|
||
status = WN_BAD_POINTER;
|
||
}
|
||
else
|
||
{
|
||
nErrorBufSize = *lpnErrorBufSize;
|
||
if (nErrorBufSize > 0)
|
||
{
|
||
lpErrorTextBuf[0] = L'\0';
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(lpProviderNameBuf))
|
||
{
|
||
if (IS_BAD_WCHAR_BUFFER(lpProviderNameBuf, lpnNameBufSize))
|
||
{
|
||
status = WN_BAD_POINTER;
|
||
}
|
||
else
|
||
{
|
||
nNameBufSize = *lpnNameBufSize;
|
||
if (nNameBufSize > 0)
|
||
{
|
||
lpProviderNameBuf[0] = L'\0';
|
||
}
|
||
}
|
||
}
|
||
|
||
if (status != WN_SUCCESS)
|
||
{
|
||
return status;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the last error that occurred in this thread
|
||
//
|
||
DWORD LastError = GetLastError(); // last error in this thread
|
||
DWORD ErrorCode = LastError; // message id to retrieve
|
||
LPWSTR ErrorText = NULL; // error text to return
|
||
LPWSTR ProviderName = NULL; // provider name to return
|
||
BOOL bFreeErrorText = FALSE; // whether to call LocalFree
|
||
|
||
//
|
||
// If it's an extended error, look in this thread's error record for
|
||
// the actual error code and strings
|
||
//
|
||
if (LastError == WN_EXTENDED_ERROR)
|
||
{
|
||
LPERROR_RECORD errorRecord = MprFindErrorRecord();
|
||
|
||
if (errorRecord)
|
||
{
|
||
ErrorCode = errorRecord->ErrorCode;
|
||
ErrorText = errorRecord->ErrorText;
|
||
ProviderName = errorRecord->ProviderName;
|
||
}
|
||
else
|
||
{
|
||
// No error record found.
|
||
// Either someone called SetLastError(WN_EXTENDED_ERROR) without
|
||
// calling WNetSetErrorText, or an error record couldn't be
|
||
// allocated because of lack of memory.
|
||
// We'll return the standard message for WN_EXTENDED_ERROR.
|
||
MPR_LOG0(ERROR,"MultinetGetErrorTextW:Couldn't retrieve extended error\n");
|
||
}
|
||
}
|
||
|
||
WCHAR Buffer[UNKNOWN_ERROR_LENGTH] = L"";
|
||
|
||
//
|
||
// Compute the final error text
|
||
//
|
||
if (ARGUMENT_PRESENT(lpErrorTextBuf))
|
||
{
|
||
//
|
||
// If it wasn't an extended error, or we didn't get a custom error
|
||
// string from WNetGetLastError, load the standard message for the
|
||
// error code.
|
||
//
|
||
if (ErrorText == NULL)
|
||
{
|
||
if (FormatMessage(
|
||
FORMAT_MESSAGE_FROM_SYSTEM | // get msg from system
|
||
FORMAT_MESSAGE_ALLOCATE_BUFFER | // let system alloc buffer
|
||
FORMAT_MESSAGE_IGNORE_INSERTS, // no parms in msg
|
||
NULL, // no source module or buffer
|
||
ErrorCode, // message id
|
||
0, // use default language id
|
||
(LPWSTR) &ErrorText,// pointer to buffer for message
|
||
nErrorBufSize, // min num of chars to allocate
|
||
NULL // message parameters (none)
|
||
))
|
||
{
|
||
bFreeErrorText = TRUE; // ErrorText was allocated by the system
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Couldn't get a message from the system, so use a catch-all
|
||
//
|
||
int cch = LoadString(hDLL,
|
||
IDS_XERR_UNKNOWN,
|
||
Buffer,
|
||
LENGTH(Buffer));
|
||
|
||
//
|
||
// If this fails, up the value of UNKNOWN_ERROR_LENGTH
|
||
//
|
||
ASSERT(cch > 0 && cch < LENGTH(Buffer));
|
||
|
||
ErrorText = Buffer;
|
||
}
|
||
}
|
||
|
||
ASSERT (ErrorText != NULL);
|
||
|
||
//
|
||
// Decide whether to copy the error text to the caller's buffer
|
||
//
|
||
DWORD ReqLen = wcslen(ErrorText) + 1 ;
|
||
if (ReqLen > nErrorBufSize)
|
||
{
|
||
*lpnErrorBufSize = ReqLen;
|
||
status = WN_MORE_DATA;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Compute final provider name, decide whether to copy to caller's buffer
|
||
//
|
||
if (ARGUMENT_PRESENT(lpProviderNameBuf))
|
||
{
|
||
if (ProviderName == NULL)
|
||
{
|
||
ProviderName = L"";
|
||
}
|
||
|
||
DWORD ReqLen = wcslen(ProviderName) + 1;
|
||
if (ReqLen > nNameBufSize)
|
||
{
|
||
*lpnNameBufSize = ReqLen;
|
||
status = WN_MORE_DATA;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Copy strings
|
||
//
|
||
if (status == WN_SUCCESS)
|
||
{
|
||
if (ARGUMENT_PRESENT(lpErrorTextBuf))
|
||
{
|
||
wcscpy(lpErrorTextBuf, ErrorText);
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(lpProviderNameBuf))
|
||
{
|
||
wcscpy(lpProviderNameBuf, ProviderName);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free buffer allocated by FormatMessage
|
||
//
|
||
if (bFreeErrorText)
|
||
{
|
||
LocalFree(ErrorText);
|
||
}
|
||
|
||
//
|
||
// FormatMessage, LocalFree, or other APIs we may have called from this API,
|
||
// could have changed the last error, so restore it now.
|
||
//
|
||
SetLastError(LastError);
|
||
|
||
return status;
|
||
}
|