/*++ Copyright (c) 1991 Microsoft Corporation Module Name: mpransi.cxx Abstract: Contains Ansi Entry points for the MPR api. Author: Dan Lafferty (danl) 20-Dec-1991 Environment: User Mode -Win32 Notes: I may want to add a buffer size parameter to ConvertToAnsi Revision History: 08-Aug-1996 anirudhs Major revision (simplification): Converted all remaining APIs to the smaller, faster interpreted scheme. Added ANSI_API_ macros. Eliminated helper functions used by the old scheme. These changes shrink this file by about 1400 lines. 16-Feb-1996 anirudhs Added InputParmsToUnicode, OutputBufferToAnsi and helper functions. These form a smaller, faster, interpreted scheme for writing the Ansi APIs. This scheme is smaller chiefly because it eliminates a very large amount of code duplication present in the previous scheme. This also makes the Ansi APIs less bug-prone. It is faster chiefly because intermediate storage is allocated with a single heap allocation per API, rather than several. Also, the number of passes to scan and copy data is minimized. 06-Oct-1995 anirudhs MprMakeUnicodeNetRes and related functions: Removed duplicated code for the string fields of the net resource; added code to iterate over the string fields instead. Fixed access violation and memory leaks. 24-Aug-1992 danl For WNetGetConnection & WNetGetUser, we allocate a buffer twice the size of the user buffer. The data is placed in this buffer. Then we check to see if the data will fit in the user buffer after it is translated to Ansi. The presence of DBSC characters may make it not fit. In which case, we return the required number of bytes. This number assumes worse-case where all characters are DBCS characters. 20-Dec-1991 danl created --*/ // // INCLUDES // #include "precomp.hxx" #include // strlen // // CONSTANTS // #define MAX_STRINGS_PER_API 6 // // The following masks are used to indicate which fields in the NetResource // structure are used by an API. // The values must match the NRWField and NRAField arrays. // #define NETRESFIELD_LOCALNAME 0x00000001 #define NETRESFIELD_REMOTENAME 0x00000002 #define NETRESFIELD_COMMENT 0x00000004 #define NETRESFIELD_PROVIDER 0x00000008 #define NUMBER_OF_NETRESFIELD 4 // // Combinations of the NETRESFIELD_ constants, for passing to InputParmsToUnicode. // #define NETRES_LRP "\xB" // local name, remote name, provider #define NETRES_RP "\xA" // remote name, provider // // Alignment macros // These macros assume that sizeof(WCHAR) and sizeof(DWORD) are powers of 2 // #define ROUND_UP_TO_WCHAR(x) (((DWORD)(x) + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1)) #define ROUND_UP_TO_DWORD(x) (((DWORD)(x) + sizeof(DWORD) - 1) & ~(sizeof(DWORD) - 1)) #define IS_WCHAR_ALIGNED(x) (((ULONG_PTR)(x) & (sizeof(WCHAR) - 1)) == 0) #define IS_DWORD_ALIGNED(x) (((ULONG_PTR)(x) & (sizeof(DWORD) - 1)) == 0) // // Nearly every API ends this way // #define SET_AND_RETURN(status) \ if (status != NO_ERROR) \ { \ SetLastError(status); \ } \ \ return status; // // This is the general pattern of an Ansi wrapper for an API with no // output Ansi parameters. There are some exceptions. // #define ANSI_API_WITHOUT_ANSI_OUTPUT(NUMBER_OF_PARMS, \ ANSI_PARM_ASSIGNMENT, \ INSTRUCTION_STRING, \ UNICODE_CALL) \ \ DWORD status; \ LPBYTE tempBuffer = NULL; \ ANSI_PARM AParm[NUMBER_OF_PARMS]; \ UNICODE_PARM UParm[NUMBER_OF_PARMS]; \ \ ANSI_PARM_ASSIGNMENT \ \ status = InputParmsToUnicode(INSTRUCTION_STRING, AParm, UParm, &tempBuffer); \ \ if (status == WN_SUCCESS) \ { \ status = UNICODE_CALL \ } \ \ LocalFree(tempBuffer); \ \ SET_AND_RETURN(status) // // This is the general pattern of an Ansi wrapper for an API that // has output Ansi parameters. There are some exceptions. // #define ANSI_API_WITH_ANSI_OUTPUT(NUMBER_OF_PARMS, \ ANSI_PARM_ASSIGNMENT, \ INSTRUCTION_STRING, \ UNICODE_CALL, \ OUTPUT_CALL) \ \ DWORD status; \ LPBYTE tempBuffer = NULL; \ ANSI_PARM AParm[NUMBER_OF_PARMS]; \ UNICODE_PARM UParm[NUMBER_OF_PARMS]; \ \ ANSI_PARM_ASSIGNMENT \ \ status = InputParmsToUnicode(INSTRUCTION_STRING, AParm, UParm, &tempBuffer); \ \ if (status == WN_SUCCESS) \ { \ status = UNICODE_CALL \ \ if (status == WN_SUCCESS) \ { \ status = OUTPUT_CALL \ } \ } \ \ LocalFree(tempBuffer); \ \ SET_AND_RETURN(status) // // STRUCTURES // // These unions are defined so that parameters of various types can be passed // to the generic routine InputParmsToUnicode. // CODEWORK: By using these unions, we have lost type safety, and this could // cause some bugs to go undetected. To get back type safety, ANSI_PARM and // UNICODE_PARM could be made into "smart union" classes, with overloaded // assignment and cast operators that, in the checked build, remember the // type of the data that they are assigned, and assert if they are used as // any other type of data. // This would also make the code neater by allowing initializers like // ANSI_PARM AParm[] = { lpName, lpUserName, lpnLength }; typedef union { DWORD dword; LPCSTR lpcstr; LPNETRESOURCEA lpNetResA; LPVOID lpvoid; LPDWORD lpdword; } ANSI_PARM; typedef union { DWORD dword; LPBYTE lpbyte; LPWSTR lpwstr; LPNETRESOURCEW lpNetResW; } UNICODE_PARM; class ANSI_OUT_BUFFER { private: const LPBYTE _Start; // Pointer to start of buffer const DWORD _Size; // Total number of bytes in buffer DWORD _Used; // Number of bytes used (may exceed Size) public: ANSI_OUT_BUFFER(LPBYTE Start, DWORD Size) : _Start(Start), _Size (Size), _Used (0) { } BYTE * Next() const { return _Start + _Used; } BOOL Overflow() const { return (_Used > _Size); } DWORD FreeSpace() const { return (Overflow() ? 0 : _Size - _Used); } BOOL HasRoomFor(DWORD Request) const { return (_Used + Request <= _Size); } void AddUsed(DWORD Request) { _Used += Request; } DWORD GetUsage() const { return _Used; } }; // // STATIC DATA // // // These arrays of members are used to iterate through the string fields // of a net resource. // The order must match the NETRESFIELD_ definitions. // LPWSTR NETRESOURCEW::* const NRWField[NUMBER_OF_NETRESFIELD] = { &NETRESOURCEW::lpLocalName, &NETRESOURCEW::lpRemoteName, &NETRESOURCEW::lpComment, &NETRESOURCEW::lpProvider }; LPSTR NETRESOURCEA::* const NRAField[NUMBER_OF_NETRESFIELD] = { &NETRESOURCEA::lpLocalName, &NETRESOURCEA::lpRemoteName, &NETRESOURCEA::lpComment, &NETRESOURCEA::lpProvider }; // // Local Functions // DWORD InputParmsToUnicode( IN LPCSTR Instructions, IN const ANSI_PARM InputParms[], OUT UNICODE_PARM OutputParms[], OUT LPBYTE * ppBuffer ); DWORD StringParmToUnicodePass1( IN LPCSTR StringParm, OUT PANSI_STRING AnsiString, OUT PUNICODE_STRING UnicodeString, IN OUT PULONG BufferOffset ); DWORD StringParmToUnicodePass2( IN OUT PANSI_STRING AnsiString, OUT PUNICODE_STRING UnicodeString, IN const BYTE * BufferStart, OUT LPWSTR * Result ); DWORD OutputBufferToAnsi( IN char BufferFormat, IN LPBYTE SourceBuffer, OUT LPVOID AnsiBuffer, IN OUT LPDWORD pcbBufferSize ); DWORD OutputStringToAnsi( IN LPCWSTR UnicodeIn, IN OUT ANSI_OUT_BUFFER * Buf ); DWORD OutputStringToAnsiInPlace( IN LPWSTR UnicodeIn ); DWORD OutputNetResourceToAnsi( IN NETRESOURCEW * lpNetResW, IN OUT ANSI_OUT_BUFFER * Buf ); DWORD InputParmsToUnicode( IN LPCSTR Instructions, IN const ANSI_PARM InputParms[], OUT UNICODE_PARM OutputParms[], OUT LPBYTE * ppBuffer ) /*++ Routine Description: This function converts the caller's input parameters to Unicode. If necessary, it allocates one temporary buffer in which it stores the intermediate Unicode parameters. This minimizes the cost of calls to LocalAlloc. Arguments: Instructions - A string of characters, roughly one for each member of the InputParms array, describing the action to be taken on each InputParms member. Recognized values for the characters are: 'S' (String) - InputParms member is an LPSTR to be converted to Unicode. Store a pointer to the Unicode string in the corresponding OutputParms member. 'N' (NetResource) - InputParms member is a LPNETRESOURCEA to be converted to a NETRESOURCEW. The next character in Instructions is a bitmask of the NETRESFIELD_ constants, indicating which fields of the NETRESOURCEA to convert. Store a pointer to the NETRESOURCEW in the corresponding OutputParms member. 'B' (Buffer) - InputParms member (say InputParms[i]) is a pointer to an output buffer. InputParms[i+1] is a pointer to a DWORD indicating the buffer size in bytes. Probe the buffer for write. Allocate an area of double the size (i.e. of size (*InputParms[i+1])*sizeof(WCHAR)) in the intermediate buffer. Store a pointer to this area of the buffer in OutputParms[i]. Store the size of this area in OutputParms[i+1]. If InputParms[i] is NULL, store NULL in OutputParms[i], and ignore InputParms[i+1]. (In other words, the buffer pointer is optional; the size pointer is required if the buffer pointer is present and ignored if the buffer pointer is absent.) 'Bs' (Buffer beginning with structure) - Same as 'B', but the first N bytes of the output buffer, where N is stored in InputParms[i+2], are supposed to hold a fixed-size structure, not strings. When calculating the size of the intermediate area, double the size of the rest of the buffer, but not the size of the structure. CODEWORK: Also verify that the buffer is DWORD-aligned? InputParms - An array of parameters to the Ansi API, described by the Instructions parameter. OutputParms - An array of the same size as InputParms, to hold the converted Unicode parameters. ppBuffer - A pointer to the intermediate buffer allocated by this function will be stored here. It must be freed by a single call to LocalFree, regardless of the return value from this function. Return Value: WN_SUCCESS WN_OUT_OF_MEMORY WN_BAD_POINTER History: 16-Feb-1996 anirudhs Created. Notes: The function works by making two passes through the Instructions string. In the first pass the string lengths are determined and saved, and the required size of the temporary buffer is calculated. In the second pass the parameters are actually converted to Unicode. --*/ { ANSI_STRING AnsiStrings [MAX_STRINGS_PER_API] = {0}; UNICODE_STRING UnicodeStrings[MAX_STRINGS_PER_API] = {0}; ULONG Bytes = 0; // Size of buffer to allocate DWORD status = WN_SUCCESS; // // The caller must have initialized the buffer pointer to NULL, so // he can free the buffer even if this function fails. // ASSERT(*ppBuffer == NULL); __try { // // For two passes through Instructions // #define FIRST_PASS (iPass == 0) for (ULONG iPass = 0; iPass <= 1; iPass++) { ULONG iString = 0; // Index into AnsiStrings and UnicodeStrings // // For each character in Instructions // const CHAR * pInstruction; // Pointer into Instructions ULONG iParm; // Index into InputParms and OutputParms for (pInstruction = Instructions, iParm = 0; *pInstruction; pInstruction++, iParm++) { MPR_LOG(ANSI, "Processing instruction '%hc'\n", *pInstruction); switch (*pInstruction) { case 'B': // // The next 2 InputParms are a buffer pointer and size. // Note that this code could cause an exception. // if (InputParms[iParm].lpvoid == NULL) { // A NULL pointer stays NULL; the size pointer is ignored OutputParms[iParm].lpbyte = NULL; } else if (FIRST_PASS) { // Probe the original buffer if (IS_BAD_BYTE_BUFFER(InputParms[iParm].lpvoid, InputParms[iParm+1].lpdword)) { status = WN_BAD_POINTER; __leave; } // Reserve the intermediate buffer area Bytes = ROUND_UP_TO_DWORD(Bytes); OutputParms[iParm].dword = Bytes; OutputParms[iParm+1].dword = (*InputParms[iParm+1].lpdword) * sizeof(WCHAR); // Check for an optional 's' in Instructions if (*(pInstruction+1) == 's') { // CODEWORK: Check for DWORD alignment on RISC? // if (!IS_DWORD_ALIGNED(InputParms[iParm].lpvoid)) // { status = WN_BAD_POINTER; __leave; } // InputParms[iParm+2].dword holds the size of the // fixed-length structure that will go at the start // of the buffer. We don't want to multiply its // size by sizeof(WCHAR). if (OutputParms[iParm+1].dword/sizeof(WCHAR) < InputParms[iParm+2].dword) { OutputParms[iParm+1].dword /= sizeof(WCHAR); } else { OutputParms[iParm+1].dword -= InputParms[iParm+2].dword*(sizeof(WCHAR)-1); } } Bytes += OutputParms[iParm+1].dword; } else // Non-NULL pointer, second pass { // Convert the offset to a pointer OutputParms[iParm].lpbyte = *ppBuffer + OutputParms[iParm].dword; ASSERT(IS_DWORD_ALIGNED(OutputParms[iParm].lpbyte)); } iParm++; // iParm+1 was for the buffer size if (*(pInstruction+1) == 's') { pInstruction++; iParm++; // iParm+2 was for the fixed structure size } break; case 'S': // // InputParm is a string to be converted. // A NULL string stays NULL. // if (FIRST_PASS) { ASSERT(iString < MAX_STRINGS_PER_API); Bytes = ROUND_UP_TO_WCHAR(Bytes); status = StringParmToUnicodePass1( InputParms[iParm].lpcstr, &AnsiStrings[iString], &UnicodeStrings[iString], &Bytes); } else { status = StringParmToUnicodePass2( &AnsiStrings[iString], &UnicodeStrings[iString], *ppBuffer, &OutputParms[iParm].lpwstr); } if (status != WN_SUCCESS) { __leave; } iString++; break; case 'N': // // InputParm is a NETRESOURCEA to be converted, and the // next character in Instructions tells which of its string // fields are to be converted. // NULL strings remain NULL; ignored fields are copied // unchanged. // pInstruction++; if (InputParms[iParm].lpNetResA == NULL) { // A null netresource stays null OutputParms[iParm].lpNetResW = NULL; break; } { // First deal with the fixed-size part of the structure. const NETRESOURCEA *pNetResA = InputParms[iParm].lpNetResA; NETRESOURCEW *pNetResW; if (FIRST_PASS) { // Reserve space for the NETRESOURCEW Bytes = ROUND_UP_TO_DWORD(Bytes); OutputParms[iParm].dword = Bytes; Bytes += sizeof(NETRESOURCEW); ASSERT(IS_WCHAR_ALIGNED(Bytes)); } else { // Copy fixed-size fields and NULL pointers pNetResW = (NETRESOURCEW *) (*ppBuffer + OutputParms[iParm].dword); ASSERT(IS_DWORD_ALIGNED(pNetResW)); RtlCopyMemory(pNetResW, pNetResA, sizeof(NETRESOURCEA)); OutputParms[iParm].lpNetResW = pNetResW; } // Next add each non-null string specified in the // field mask. CHAR FieldMask = *pInstruction; ASSERT(FieldMask != 0); for (ULONG iField = 0; iField < NUMBER_OF_NETRESFIELD; iField++) { if ((FieldMask >> iField) & 1) { if (FIRST_PASS) { ASSERT(iString < MAX_STRINGS_PER_API); status = StringParmToUnicodePass1( pNetResA->*NRAField[iField], &AnsiStrings[iString], &UnicodeStrings[iString], &Bytes); } else { status = StringParmToUnicodePass2( &AnsiStrings[iString], &UnicodeStrings[iString], *ppBuffer, &(pNetResW->*NRWField[iField])); } if (status != WN_SUCCESS) { __leave; } iString++; } } } break; default: ASSERT(0); } } if (FIRST_PASS) { // // Actually allocate the space for the Unicode parameters // *ppBuffer = (LPBYTE) LocalAlloc(0, Bytes); if (*ppBuffer == NULL) { status = GetLastError(); MPR_LOG2(ERROR, "InputParmsToUnicode: LocalAlloc for %lu bytes failed, %lu\n", Bytes, status); __leave; } } } } __except(MPR_EXCEPTION_FILTER) { #if DBG == 1 status = GetExceptionCode(); if (status != EXCEPTION_ACCESS_VIOLATION) { MPR_LOG(ERROR,"InputParmsToUnicode: Unexpected Exception %#lx\n",status); } #endif status = WN_BAD_POINTER; } return status; } DWORD StringParmToUnicodePass1 ( IN LPCSTR StringParm, OUT PANSI_STRING AnsiString, OUT PUNICODE_STRING UnicodeString, IN OUT PULONG BufferOffset ) /*++ Routine Description: Helper function for InputParmsToUnicode. --*/ { RtlInitAnsiString( AnsiString, StringParm ); if (StringParm == NULL) { return WN_SUCCESS; } // Save the offset to the memory for this Unicode string, to be converted // to a pointer after the memory is allocated ULONG UnicodeLength = RtlAnsiStringToUnicodeSize( AnsiString ); if (UnicodeLength > MAXUSHORT) { MPR_LOG(ERROR, "Unicode size of Ansi string parm is %lu, exceeds MAXUSHORT\n", UnicodeLength); return WN_BAD_VALUE; } UnicodeString->Buffer = (LPWSTR) UlongToPtr(*BufferOffset); UnicodeString->MaximumLength = (USHORT) UnicodeLength; *BufferOffset = ROUND_UP_TO_DWORD(*BufferOffset + UnicodeLength); return WN_SUCCESS; } DWORD StringParmToUnicodePass2 ( IN OUT PANSI_STRING AnsiString, OUT PUNICODE_STRING UnicodeString, IN const BYTE * BufferStart, OUT LPWSTR * Result ) /*++ Routine Description: Helper function for InputParmsToUnicode. --*/ { if (AnsiString->Buffer == NULL) { *Result = NULL; // NOTE: the UnicodeString is not initialized in this case return WN_SUCCESS; } // Convert the previously stored buffer offset into a pointer UnicodeString->Buffer = (LPWSTR) (BufferStart + (ULONG_PTR) UnicodeString->Buffer); ASSERT(IS_WCHAR_ALIGNED(UnicodeString->Buffer)); // Convert the string to Unicode NTSTATUS ntstatus = RtlAnsiStringToUnicodeString(UnicodeString, AnsiString, FALSE); if (!NT_SUCCESS(ntstatus)) { MPR_LOG(ERROR, "RtlAnsiStringToUnicodeString failed %#lx\n", ntstatus); return RtlNtStatusToDosError(ntstatus); } *Result = UnicodeString->Buffer; return WN_SUCCESS; } DWORD OutputBufferToAnsi( IN char BufferFormat, IN LPBYTE SourceBuffer, OUT LPVOID AnsiBuffer, IN OUT LPDWORD pcbBufferSize ) /*++ Routine Description: This function converts the data in the result buffer that was returned from a Unicode API into Ansi and stores it in the Ansi caller's result buffer. If the caller's buffer isn't large enough it saves the required size in *pcbBufferSize and returns WN_MORE_DATA. Nearly all the WNet APIs that have output buffers have only a single field in the output buffer, so this API takes only a single character, rather than a string, for the buffer format. APIs with more complicated output buffers should handle the complexity themselves, by directly calling the functions that this function calls. Arguments: BufferFormat - A character indicating the format of the SourceBuffer field. Recognized values are: 'S' - SourceBuffer contains a Unicode string. Convert it to Ansi and store the Ansi version in AnsiBuffer. 'N' - SourceBuffer contains a NETRESOURCEW with its associated strings. Convert it to Ansi and store the Ansi version in AnsiBuffer. SourceBuffer - The output buffer returned from a Unicode API. This must not be NULL. AnsiBuffer - The output buffer that the caller of the Ansi API supplied. This must not be NULL. pcbBufferSize - On entry, the size of AnsiBuffer in bytes. If the function returns WN_MORE_DATA, the required size is stored here; otherwise this is unmodified. This must not be NULL (must be a writeable DWORD pointer). Return Value: WN_SUCCESS - successful. WN_MORE_DATA - The buffer specified by AnsiBuffer and pcbBufferSize was not large enough to hold the converted data from SourceBuffer. In this case the required buffer size (in bytes) is written to *pcbBufferSize. The contents of AnsiBuffer are undefined (it will be partially filled). History: 16-Feb-1996 anirudhs Created. Notes: --*/ { // Doesn't handle optional parameters for now ASSERT(SourceBuffer != NULL && AnsiBuffer != NULL && pcbBufferSize != NULL); ANSI_OUT_BUFFER Buf((LPBYTE) AnsiBuffer, *pcbBufferSize); DWORD status; switch (BufferFormat) { case 'S': status = OutputStringToAnsi((LPCWSTR) SourceBuffer, &Buf); break; case 'N': status = OutputNetResourceToAnsi((NETRESOURCEW *) SourceBuffer, &Buf); break; default: ASSERT(0); return ERROR_INVALID_LEVEL; } // // Map the results to the conventions followed by the WNet APIs // if (status == WN_SUCCESS) { if (Buf.Overflow()) { *pcbBufferSize = Buf.GetUsage(); status = WN_MORE_DATA; } } else { ASSERT(status != WN_MORE_DATA); } return status; } DWORD OutputStringToAnsi( IN LPCWSTR UnicodeIn, IN OUT ANSI_OUT_BUFFER * Buf ) /*++ Routine Description: This function converts a Unicode string to Ansi and calculates the number of bytes required to store it. If the caller passes a buffer that has enough remaining free space, it stores the Ansi data in the buffer. Otherwise it just increments the buffer's space usage by the number of bytes required. Arguments: UnicodeIn - A Unicode string to be converted to Ansi. This must not be NULL. Buf - A structure whose elements are interpreted as follows: _Start - Start address of a buffer to contain the Ansi data. This buffer must be writeable, or an exception will occur. _Size - The total size of the buffer for the Ansi data. _Used - On entry, the number of bytes in the buffer that have already been used. The function will begin writing data at _Start + _Used and will never write past the total size specified by _Size. If there is not enough room left in the buffer it will be partially filled or unmodified. On a successful return, _Used is incremented by the number of bytes that would be required to store the converted Ansi data, whether or not it was actually stored in the buffer. (This is done because the WNet APIs need to return the required buffer size if the caller's buffer was too small.) The use of this structure simplifies the writing of routines that use this function and need to convert multiple fields of Unicode data. Callers that need to convert only a single field can use OutputBufferToAnsi. Return Value: WN_SUCCESS - successful. The Ansi data was written to the buffer if Buf->_Used <= Buf->_Size. Otherwise, Buf->_Used was incremented without completely writing the data. Note that WN_MORE_DATA is never returned. History: 16-Feb-1996 anirudhs Created. Notes: --*/ { NTSTATUS ntStatus; UNICODE_STRING unicodeString; ANSI_STRING ansiString; ASSERT(UnicodeIn != NULL); // Doesn't handle optional parameters for now // // Initialize the string structures // RtlInitUnicodeString(&unicodeString, UnicodeIn); ansiString.Buffer = (PCHAR) Buf->Next(); ansiString.MaximumLength = (Buf->FreeSpace() > MAXUSHORT ? MAXUSHORT : (USHORT) Buf->FreeSpace() ); // // Call the conversion function // ntStatus = RtlUnicodeStringToAnsiString ( &ansiString, // Destination &unicodeString, // Source (BOOLEAN)FALSE); // Don't allocate the destination if (NT_SUCCESS(ntStatus)) { // Add on the buffer space we used Buf->AddUsed(ansiString.Length + 1); ASSERT(! Buf->Overflow()); return WN_SUCCESS; } else if (ntStatus == STATUS_BUFFER_OVERFLOW) { // We couldn't fit the string in the buffer, but still figure out // how much buffer space we would have used if we could Buf->AddUsed(RtlUnicodeStringToAnsiSize(&unicodeString)); ASSERT(Buf->Overflow()); return WN_SUCCESS; } else { MPR_LOG(ERROR, "RtlUnicodeStringToAnsiString failed %#lx\n", ntStatus); DWORD status = RtlNtStatusToDosError(ntStatus); ASSERT(status != WN_MORE_DATA); return status; } } DWORD OutputNetResourceToAnsi( IN NETRESOURCEW * lpNetResW, IN OUT ANSI_OUT_BUFFER * Buf ) /*++ Routine Description: This function converts a NETRESOURCEW and its associated Unicode strings to Ansi and returns the number of bytes required to store them. If the caller passes a buffer that has enough remaining free space, it stores the Ansi data in the buffer. Arguments: lpNetResW - A Unicode net resource to be converted to Ansi. This must not be NULL. Buf - same as OutputStringToAnsi. Return Value: Same as OutputStringToAnsi. History: 16-Feb-1996 anirudhs Created. Notes: --*/ { // // Copy the fixed-size part of the structure, including NULL pointers, // and/or add on the buffer space it would take // LPNETRESOURCEA lpNetResA = (LPNETRESOURCEA) Buf->Next(); if (Buf->HasRoomFor(sizeof(NETRESOURCEA))) { RtlCopyMemory(lpNetResA, lpNetResW, sizeof(NETRESOURCEA)); } Buf->AddUsed(sizeof(NETRESOURCEA)); // // Copy each non-NULL string field, // and/or add on the buffer space it would take // for (DWORD iField = 0; iField < NUMBER_OF_NETRESFIELD; iField++) { if (lpNetResW->*NRWField[iField] != NULL) { // Save a pointer to the Ansi string we are about to create // in the Ansi net resource lpNetResA->*NRAField[iField] = (LPSTR) Buf->Next(); // Convert the string DWORD status = OutputStringToAnsi(lpNetResW->*NRWField[iField], Buf); if (status != WN_SUCCESS) { ASSERT(status != WN_MORE_DATA); return status; } } } return WN_SUCCESS; } DWORD OutputStringToAnsiInPlace( IN LPWSTR UnicodeIn ) /*++ Routine Description: This function converts a Unicode string to Ansi in place. This is the same as OutputStringToAnsi, optimized for in-place conversions. Arguments: UnicodeIn - A Unicode string to be converted to Ansi. This may be NULL, in which case the function does nothing. Return Value: WN_SUCCESS - successful. Note that WN_MORE_DATA is never returned. History: 08-Aug-1996 anirudhs Created. Notes: --*/ { NTSTATUS ntStatus; UNICODE_STRING unicodeString; ANSI_STRING ansiString; if (UnicodeIn == NULL) { return WN_SUCCESS; } // // Initialize the string structures // RtlInitUnicodeString(&unicodeString, UnicodeIn); ansiString.Buffer = (PCHAR) UnicodeIn; ansiString.MaximumLength = unicodeString.MaximumLength; // // Call the conversion function // ntStatus = RtlUnicodeStringToAnsiString ( &ansiString, // Destination &unicodeString, // Source (BOOLEAN)FALSE); // Don't allocate the destination ASSERT(ntStatus != STATUS_BUFFER_OVERFLOW); if (NT_SUCCESS(ntStatus)) { return WN_SUCCESS; } else { MPR_LOG(ERROR, "RtlUnicodeStringToAnsiString failed %#lx\n", ntStatus); DWORD status = RtlNtStatusToDosError(ntStatus); ASSERT(status != WN_MORE_DATA); return status; } } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// DWORD APIENTRY WNetGetNetworkInformationA( IN LPCSTR lpProvider, IN OUT LPNETINFOSTRUCT lpNetInfoStruct ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpProvider; , "S", WNetGetNetworkInformationW(UParm[0].lpwstr, lpNetInfoStruct); ) } DWORD APIENTRY WNetGetProviderNameA( IN DWORD dwNetType, OUT LPSTR lpProviderName, IN OUT LPDWORD lpBufferSize ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITH_ANSI_OUTPUT( 2, AParm[0].lpvoid = lpProviderName; AParm[1].lpdword = lpBufferSize; , "B", WNetGetProviderNameW(dwNetType, UParm[0].lpwstr, lpBufferSize); , OutputBufferToAnsi('S', UParm[0].lpbyte, lpProviderName, lpBufferSize); ) } DWORD WNetGetProviderTypeA( IN LPCSTR lpProvider, OUT LPDWORD lpdwNetType ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpProvider; , "S", WNetGetProviderTypeW(UParm[0].lpwstr, lpdwNetType); ) } DWORD APIENTRY WNetAddConnectionA ( IN LPCSTR lpRemoteName, IN LPCSTR lpPassword, IN LPCSTR lpLocalName ) /*++ Routine Description: Arguments: Return Value: --*/ { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[3]; UNICODE_PARM UParm[3]; AParm[0].lpcstr = lpRemoteName; AParm[1].lpcstr = lpPassword; AParm[2].lpcstr = lpLocalName; UParm[1].lpwstr = NULL; status = InputParmsToUnicode("SSS", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { status = WNetAddConnectionW( UParm[0].lpwstr, UParm[1].lpwstr, UParm[2].lpwstr ); } MprClearString(UParm[1].lpwstr); LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD APIENTRY WNetAddConnection2A ( IN LPNETRESOURCEA lpNetResource, IN LPCSTR lpPassword, IN LPCSTR lpUserName, IN DWORD dwFlags ) /*++ Routine Description: Arguments: Return Value: --*/ { return (WNetUseConnectionA( NULL, lpNetResource, lpPassword, lpUserName, dwFlags, NULL, NULL, NULL )); } DWORD APIENTRY WNetAddConnection3A ( IN HWND hwndOwner, IN LPNETRESOURCEA lpNetResource, IN LPCSTR lpPassword, IN LPCSTR lpUserName, IN DWORD dwFlags ) /*++ Routine Description: Arguments: Return Value: --*/ { return (WNetUseConnectionA( hwndOwner, lpNetResource, lpPassword, lpUserName, dwFlags, NULL, NULL, NULL )); } DWORD APIENTRY WNetUseConnectionA( IN HWND hwndOwner, IN LPNETRESOURCEA lpNetResource, IN LPCSTR lpPassword, IN LPCSTR lpUserID, IN DWORD dwFlags, OUT LPSTR lpAccessName OPTIONAL, IN OUT LPDWORD lpBufferSize OPTIONAL, // Optional only if lpAccessName absent OUT LPDWORD lpResult ) /*++ Routine Description: Arguments: Return Value: --*/ { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[5]; UNICODE_PARM UParm[5]; AParm[0].lpNetResA = lpNetResource; AParm[1].lpcstr = lpPassword; AParm[2].lpcstr = lpUserID; AParm[3].lpvoid = lpAccessName; AParm[4].lpdword = lpBufferSize; UParm[1].lpwstr = NULL; status = InputParmsToUnicode("N" NETRES_LRP "SSB", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { status = WNetUseConnectionW( hwndOwner, UParm[0].lpNetResW, UParm[1].lpwstr, UParm[2].lpwstr, dwFlags, UParm[3].lpwstr, lpBufferSize, lpResult ); if (status == WN_SUCCESS) { if (ARGUMENT_PRESENT(lpAccessName)) { // // Note: At this point, we know that lpBufferSize is writeable. // status = OutputBufferToAnsi( 'S', UParm[3].lpbyte, lpAccessName, lpBufferSize); } } } MprClearString(UParm[1].lpwstr); LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD APIENTRY WNetCancelConnection2A ( IN LPCSTR lpName, IN DWORD dwFlags, IN BOOL fForce ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpName; , "S", WNetCancelConnection2W(UParm[0].lpwstr, dwFlags, fForce); ) } DWORD APIENTRY WNetCancelConnectionA ( IN LPCSTR lpName, IN BOOL fForce ) /*++ Routine Description: This routine is provided for Win 3.1 compatibility. Arguments: Return Value: --*/ { return WNetCancelConnection2A( lpName, CONNECT_UPDATE_PROFILE, fForce ) ; } DWORD APIENTRY WNetGetConnectionA ( IN LPCSTR lpLocalName, OUT LPSTR lpRemoteName, IN OUT LPDWORD lpnLength ) /*++ Routine Description: This function returns the RemoteName that is associated with a LocalName (or drive letter). Arguments: lpLocalName - This is a pointer to the string that contains the LocalName. lpRemoteName - This is a pointer to the buffer that will contain the RemoteName string upon exit. lpnLength - This is a pointer to the size (in characters) of the buffer that is to be filled in with the RemoteName string. It is assumed upon entry, that characters are all single byte characters. If the buffer is too small and WN_MORE_DATA is returned, the data at this location contains buffer size information - in number of characters (bytes). This information indicates how large the buffer should be (in bytes) to obtain the remote name. It is assumed that all Unicode characteres translate into DBCS characters. Return Value: --*/ { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[3]; UNICODE_PARM UParm[3]; AParm[0].lpcstr = lpLocalName; AParm[1].lpvoid = lpRemoteName; AParm[2].lpdword = lpnLength; status = InputParmsToUnicode("SB", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { status = WNetGetConnectionW(UParm[0].lpwstr, UParm[1].lpwstr, lpnLength); if (status == WN_SUCCESS || status == WN_CONNECTION_CLOSED) { DWORD tempStatus = OutputBufferToAnsi('S', UParm[1].lpbyte, lpRemoteName, lpnLength); if (tempStatus != WN_SUCCESS) { status = tempStatus; } } } LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD APIENTRY WNetGetConnection2A ( IN LPSTR lpLocalName, OUT LPVOID lpBuffer, IN OUT LPDWORD lpnLength ) /*++ Routine Description: This function returns the RemoteName that is associated with a LocalName (or drive letter) and the provider name that made the connection. Arguments: lpLocalName - This is a pointer to the string that contains the LocalName. lpBuffer - This is a pointer to the buffer that will contain the WNET_CONNECTIONINFO structure upon exit. lpnLength - This is a pointer to the size (in characters) of the buffer that is to be filled in with the RemoteName string. It is assumed upon entry, that characters are all single byte characters. If the buffer is too small and WN_MORE_DATA is returned, the data at this location contains buffer size information - in number of characters (bytes). This information indicates how large the buffer should be (in bytes) to obtain the remote name. It is assumed that all Unicode characters translate into DBCS characters. Return Value: --*/ { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[4]; UNICODE_PARM UParm[4]; AParm[0].lpcstr = lpLocalName; AParm[1].lpvoid = lpBuffer; AParm[2].lpdword = lpnLength; AParm[3].dword = sizeof(WNET_CONNECTIONINFO); status = InputParmsToUnicode("SBs", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { status = WNetGetConnection2W( UParm[0].lpwstr, UParm[1].lpbyte, &UParm[2].dword ); if (status == WN_SUCCESS || status == WN_CONNECTION_CLOSED) { ANSI_OUT_BUFFER Buf((LPBYTE) lpBuffer, *lpnLength); // // Copy the fixed-size part of the structure, including NULL pointers, // and/or add on the buffer space it would take // WNET_CONNECTIONINFOW * pconninfow = (WNET_CONNECTIONINFOW *) UParm[1].lpbyte; WNET_CONNECTIONINFOA * pconninfoa = (WNET_CONNECTIONINFOA *) Buf.Next(); ASSERT(Buf.HasRoomFor(sizeof(WNET_CONNECTIONINFOA))); RtlCopyMemory(pconninfoa, pconninfow, sizeof(WNET_CONNECTIONINFOA)); Buf.AddUsed(sizeof(WNET_CONNECTIONINFOA)); // // Copy each non-NULL string field, // and/or add on the buffer space it would take // DWORD tempStatus = WN_SUCCESS; if (pconninfow->lpRemoteName != NULL) { pconninfoa->lpRemoteName = (LPSTR) Buf.Next(); tempStatus = OutputStringToAnsi(pconninfow->lpRemoteName, &Buf); } if (tempStatus == WN_SUCCESS && pconninfow->lpProvider != NULL) { pconninfoa->lpProvider = (LPSTR) Buf.Next(); tempStatus = OutputStringToAnsi(pconninfow->lpProvider, &Buf); } // // Map the results to WNet API conventions // if (tempStatus != WN_SUCCESS) { status = tempStatus; } else if (Buf.Overflow()) { *lpnLength = Buf.GetUsage(); status = WN_MORE_DATA; } } else if (status == WN_MORE_DATA) { // // Adjust the required buffer size for ansi/DBCS. // // We don't know how many characters will be required so we have to // assume the worst case (all characters are DBCS characters). // *lpnLength = UParm[2].dword; } } LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD APIENTRY WNetGetConnection3A( IN LPCSTR lpLocalName, IN LPCSTR lpProviderName OPTIONAL, IN DWORD dwLevel, OUT LPVOID lpBuffer, IN OUT LPDWORD lpBufferSize // in bytes ) /*++ Routine Description: Arguments: Return Value: --*/ { // For the only supported level, the output buffer is a DWORD, so no // conversion of the output buffer is necessary ANSI_API_WITHOUT_ANSI_OUTPUT( 2, AParm[0].lpcstr = lpLocalName; AParm[1].lpcstr = lpProviderName; , "SS", WNetGetConnection3W( UParm[0].lpwstr, UParm[1].lpwstr, dwLevel, lpBuffer, lpBufferSize ); ) } DWORD WNetGetUniversalNameA ( IN LPCSTR lpLocalPath, IN DWORD dwInfoLevel, OUT LPVOID lpBuffer, IN OUT LPDWORD lpBufferSize ) /*++ Routine Description: Arguments: Return Value: --*/ { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[4]; UNICODE_PARM UParm[4]; DWORD dwStructSize = (dwInfoLevel == UNIVERSAL_NAME_INFO_LEVEL) ? sizeof(UNIVERSAL_NAME_INFO) : (dwInfoLevel == REMOTE_NAME_INFO_LEVEL) ? sizeof(REMOTE_NAME_INFO) : 0; AParm[0].lpcstr = lpLocalPath; AParm[1].lpvoid = lpBuffer; AParm[2].lpdword = lpBufferSize; AParm[3].dword = dwStructSize; status = InputParmsToUnicode("SBs", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { status = WNetGetUniversalNameW( UParm[0].lpwstr, dwInfoLevel, UParm[1].lpbyte, &UParm[2].dword ); if (status == WN_SUCCESS || status == WN_CONNECTION_CLOSED) { DWORD tempStatus = WN_SUCCESS; ANSI_OUT_BUFFER Buf((LPBYTE) lpBuffer, *lpBufferSize); // // Copy the fixed-size part of the structure, including NULL pointers, // and/or add on the buffer space it would take // ASSERT(Buf.HasRoomFor(dwStructSize)); RtlCopyMemory(Buf.Next(), UParm[1].lpbyte, dwStructSize); if (dwInfoLevel == REMOTE_NAME_INFO_LEVEL) { // ----------------------------------- // REMOTE_NAME_INFO_LEVEL // ----------------------------------- LPREMOTE_NAME_INFOW pRemoteNameInfoW = (LPREMOTE_NAME_INFOW) UParm[1].lpbyte; LPREMOTE_NAME_INFOA pRemoteNameInfoA = (LPREMOTE_NAME_INFOA) Buf.Next(); Buf.AddUsed(dwStructSize); // // Convert the returned Unicode string and string size back to // ansi. // if (pRemoteNameInfoW->lpUniversalName != NULL) { pRemoteNameInfoA->lpUniversalName = (LPSTR) Buf.Next(); tempStatus = OutputStringToAnsi(pRemoteNameInfoW->lpUniversalName, &Buf); } if (tempStatus == WN_SUCCESS && pRemoteNameInfoW->lpConnectionName != NULL) { pRemoteNameInfoA->lpConnectionName = (LPSTR) Buf.Next(); tempStatus = OutputStringToAnsi(pRemoteNameInfoW->lpConnectionName, &Buf); } if (tempStatus == WN_SUCCESS && pRemoteNameInfoW->lpRemainingPath != NULL) { pRemoteNameInfoA->lpRemainingPath = (LPSTR) Buf.Next(); tempStatus = OutputStringToAnsi(pRemoteNameInfoW->lpRemainingPath, &Buf); } } else { // ----------------------------------- // Must be UNIVERSAL_NAME_INFO_LEVEL // ----------------------------------- ASSERT(dwInfoLevel == UNIVERSAL_NAME_INFO_LEVEL); LPUNIVERSAL_NAME_INFOW pUniNameInfoW = (LPUNIVERSAL_NAME_INFOW) UParm[1].lpbyte; LPUNIVERSAL_NAME_INFOA pUniNameInfoA = (LPUNIVERSAL_NAME_INFOA) Buf.Next(); Buf.AddUsed(dwStructSize); // // Convert the returned Unicode string and string size back to // ansi. // if (pUniNameInfoW->lpUniversalName != NULL) { pUniNameInfoA->lpUniversalName = (LPSTR) Buf.Next(); tempStatus = OutputStringToAnsi(pUniNameInfoW->lpUniversalName, &Buf); } } // // Map the results to WNet API conventions // if (tempStatus != WN_SUCCESS) { status = tempStatus; } else if (Buf.Overflow()) { *lpBufferSize = Buf.GetUsage(); status = WN_MORE_DATA; } } else if (status == WN_MORE_DATA) { // // Adjust the required buffer size for ansi/DBCS. // // We don't know how many characters will be required so we have to // assume the worst case (all characters are DBCS characters). // *lpBufferSize = UParm[2].dword; } } LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD APIENTRY WNetSetConnectionA( IN LPCSTR lpName, IN DWORD dwProperties, IN OUT LPVOID pvValues ) /*++ Routine Description: Arguments: Return Value: --*/ { // // pvValues points to various types of structures depending on the value // of dwProperties. // Currently there is only one valid value for dwProperties, and its // corresponding pvValues points to a DWORD, so we don't need to worry // about converting pvValues to Unicode. // ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpName; , "S", WNetSetConnectionW(UParm[0].lpwstr, dwProperties, pvValues); ) } DWORD APIENTRY MultinetGetConnectionPerformanceA( IN LPNETRESOURCEA lpNetResource, OUT LPNETCONNECTINFOSTRUCT lpNetConnectInfoStruct ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpNetResA = lpNetResource; , "N" NETRES_LRP, MultinetGetConnectionPerformanceW( UParm[0].lpNetResW, lpNetConnectInfoStruct); ) } DWORD APIENTRY WNetOpenEnumA ( IN DWORD dwScope, IN DWORD dwType, IN DWORD dwUsage, IN LPNETRESOURCEA lpNetResource, OUT LPHANDLE lphEnum ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpNetResA = lpNetResource; , "N" NETRES_RP, WNetOpenEnumW(dwScope, dwType, dwUsage, UParm[0].lpNetResW, lphEnum); ) } DWORD APIENTRY WNetEnumResourceA ( IN HANDLE hEnum, IN OUT LPDWORD lpcCount, OUT LPVOID lpBuffer, IN OUT LPDWORD lpBufferSize ) /*++ Routine Description: This function calls the unicode version of WNetEnumResource and then converts the strings that are returned into ansi strings. Since the user provided buffer is used to contain the unicode strings, that buffer should be allocated with the size of unicode strings in mind. Arguments: Return Value: --*/ { DWORD status = WNetEnumResourceW( hEnum, lpcCount, lpBuffer, lpBufferSize); if (status == WN_SUCCESS) { // // The output buffer contains an array of NETRESOURCEWs, plus strings. // Convert the Unicode strings pointed to by these NETRESOURCEWs // to Ansi strings, in place. // LPNETRESOURCEW lpNetResW = (LPNETRESOURCEW) lpBuffer; for (DWORD i=0; i<*lpcCount; i++, lpNetResW++) { for (UINT iField = 0; iField < NUMBER_OF_NETRESFIELD; iField++) { if (lpNetResW->*NRWField[iField] != NULL) { status = OutputStringToAnsiInPlace( lpNetResW->*NRWField[iField]); if (status != WN_SUCCESS) { MPR_LOG0(ERROR,"WNetEnumResourceA: Couldn't convert all structs\n"); status = WN_SUCCESS; *lpcCount = i; break; // breaks out of both loops } } } } } SET_AND_RETURN(status) } DWORD APIENTRY WNetGetResourceInformationA( IN LPNETRESOURCEA lpNetResource, OUT LPVOID lpBuffer, IN OUT LPDWORD lpBufferSize, OUT LPSTR * lplpSystem ) { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[4]; UNICODE_PARM UParm[4]; AParm[0].lpNetResA = lpNetResource; AParm[1].lpvoid = lpBuffer; AParm[2].lpdword = lpBufferSize; AParm[3].dword = sizeof(NETRESOURCE); status = InputParmsToUnicode("N" NETRES_RP "Bs", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { status = WNetGetResourceInformationW( UParm[0].lpNetResW, UParm[1].lpbyte, &UParm[2].dword, (LPWSTR *) lplpSystem ); if (status == WN_SUCCESS) { ANSI_OUT_BUFFER Buf((LPBYTE) lpBuffer, *lpBufferSize); // // Convert the Unicode netresource returned to Ansi // status = OutputNetResourceToAnsi(UParm[1].lpNetResW, &Buf); if (status == WN_SUCCESS) { // // Convert the Unicode string (*lplpSystem) returned to Ansi // LPWSTR lpSystemW = * (LPWSTR *) lplpSystem; if (lpSystemW != NULL) { *lplpSystem = (LPSTR) Buf.Next(); status = OutputStringToAnsi(lpSystemW, &Buf); } } // // Map the results to WNet API conventions // if (status == WN_SUCCESS && Buf.Overflow()) { *lpBufferSize = Buf.GetUsage(); status = WN_MORE_DATA; } } else if (status == WN_MORE_DATA) { // // Adjust the required buffer size for ansi/DBCS. // // We don't know how many characters will be required so we have to // assume the worst case (all characters are DBCS characters). // *lpBufferSize = UParm[2].dword; } } LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD APIENTRY WNetGetResourceParentA( IN LPNETRESOURCEA lpNetResource, OUT LPVOID lpBuffer, IN OUT LPDWORD lpBufferSize ) { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[4]; UNICODE_PARM UParm[4]; AParm[0].lpNetResA = lpNetResource; AParm[1].lpvoid = lpBuffer; AParm[2].lpdword = lpBufferSize; AParm[3].dword = sizeof(NETRESOURCE); status = InputParmsToUnicode("N" NETRES_RP "Bs", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { status = WNetGetResourceParentW( UParm[0].lpNetResW, UParm[1].lpbyte, &UParm[2].dword ); if (status == WN_SUCCESS) { // // Convert the Unicode netresource returned to Ansi // status = OutputBufferToAnsi('N', UParm[1].lpbyte, lpBuffer, lpBufferSize); } else if (status == WN_MORE_DATA) { // // Adjust the required buffer size for ansi/DBCS. // // We don't know how many characters will be required so we have to // assume the worst case (all characters are DBCS characters). // *lpBufferSize = UParm[2].dword; } } LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD APIENTRY WNetGetUserA ( IN LPCSTR lpName, OUT LPSTR lpUserName, IN OUT LPDWORD lpnLength ) /*++ Routine Description: This function retreives the current default user name or the username used to establish a network connection. Arguments: lpName - Points to a null-terminated string that specifies either the name or the local device to return the user name for, or a network name that the user has made a connection to. If the pointer is NULL, the name of the current user is returned. lpUserName - Points to a buffer to receive the null-terminated user name. lpnLength - Specifies the size (in characters) of the buffer pointed to by the lpUserName parameter. If the call fails because the buffer is not big enough, this location is used to return the required buffer size. Return Value: --*/ { ANSI_API_WITH_ANSI_OUTPUT( 3, AParm[0].lpcstr = lpName; AParm[1].lpvoid = lpUserName; AParm[2].lpdword = lpnLength; , "SB", WNetGetUserW(UParm[0].lpwstr, UParm[1].lpwstr, lpnLength); , OutputBufferToAnsi('S', UParm[1].lpbyte, lpUserName, lpnLength); ) } DWORD RestoreConnectionA0 ( IN HWND hwnd, IN LPSTR lpDevice ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpDevice; , "S", WNetRestoreConnectionW(hwnd, UParm[0].lpwstr); ) } DWORD WNetGetDirectoryTypeA ( IN LPSTR lpName, OUT LPINT lpType, IN BOOL bFlushCache ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpName; , "S", WNetGetDirectoryTypeW(UParm[0].lpwstr, lpType, bFlushCache); ) } DWORD WNetDirectoryNotifyA ( IN HWND hwnd, IN LPSTR lpDir, IN DWORD dwOper ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpDir; , "S", WNetDirectoryNotifyW(hwnd, UParm[0].lpwstr, dwOper); ) } DWORD APIENTRY WNetGetLastErrorA ( OUT LPDWORD lpError, OUT LPSTR lpErrorBuf, IN DWORD nErrorBufSize, OUT LPSTR lpNameBuf, IN DWORD nNameBufSize ) /*++ Routine Description: Arguments: Return Value: --*/ { DWORD status; // // We re-use the Ansi buffers for the Unicode API. // There are no input Ansi parameters to convert to Unicode. // // Call the Unicode version of the function. // Note: The sizes for the buffers that are passed in assume that // the returned unicode strings will return DBCS characters. // status = WNetGetLastErrorW( lpError, (LPWSTR) lpErrorBuf, nErrorBufSize / sizeof(WCHAR), (LPWSTR) lpNameBuf, nNameBufSize / sizeof(WCHAR) ); // // Convert the returned strings to Ansi, in place. // There should be no buffer overflow. // if (status == WN_SUCCESS) { status = OutputStringToAnsiInPlace((LPWSTR) lpErrorBuf); if (status == WN_SUCCESS) { status = OutputStringToAnsiInPlace((LPWSTR) lpNameBuf); } } SET_AND_RETURN(status) } VOID WNetSetLastErrorA( IN DWORD err, IN LPSTR lpError, IN LPSTR lpProviders ) /*++ Routine Description: Arguments: Return Value: --*/ { DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[2]; UNICODE_PARM UParm[2]; AParm[0].lpcstr = lpError; AParm[1].lpcstr = lpProviders; status = InputParmsToUnicode("SS", AParm, UParm, &tempBuffer); if (status != WN_SUCCESS) { UParm[0].lpwstr = NULL; UParm[1].lpwstr = NULL; } WNetSetLastErrorW(err, UParm[0].lpwstr, UParm[1].lpwstr); LocalFree(tempBuffer); return; } DWORD APIENTRY MultinetGetErrorTextA( OUT LPSTR lpErrorTextBuf OPTIONAL, IN OUT LPDWORD lpnErrorBufSize OPTIONAL, OUT LPSTR lpProviderNameBuf OPTIONAL, IN OUT LPDWORD lpnNameBufSize OPTIONAL ) /*++ Routine Description: Arguments: Return Value: --*/ { // CODEWORK: This could be simplified by re-using the Unicode buffers, // like WNetGetLastErrorA. DWORD status; LPBYTE tempBuffer = NULL; ANSI_PARM AParm[4]; UNICODE_PARM UParm[4]; AParm[0].lpvoid = lpErrorTextBuf; AParm[1].lpdword = lpnErrorBufSize; AParm[2].lpvoid = lpProviderNameBuf; AParm[3].lpdword = lpnNameBufSize; status = InputParmsToUnicode("BB", AParm, UParm, &tempBuffer); if (status == WN_SUCCESS) { // Remember the sizes before calling the function DWORD nErrorBufSize; DWORD nNameBufSize; if (ARGUMENT_PRESENT(lpErrorTextBuf)) { nErrorBufSize = *lpnErrorBufSize; } if (ARGUMENT_PRESENT(lpProviderNameBuf)) { nNameBufSize = *lpnNameBufSize; } status = MultinetGetErrorTextW( UParm[0].lpwstr, lpnErrorBufSize, UParm[2].lpwstr, lpnNameBufSize ); if (status == WN_SUCCESS || status == WN_MORE_DATA) { if (ARGUMENT_PRESENT(lpErrorTextBuf) && nErrorBufSize == *lpnErrorBufSize) { // The Unicode API must have written the error text buffer DWORD status2 = OutputBufferToAnsi( 'S', UParm[0].lpbyte, lpErrorTextBuf, lpnErrorBufSize); if (status2 != WN_SUCCESS) { status = status2; } } } if (status == WN_SUCCESS || status == WN_MORE_DATA) { if (ARGUMENT_PRESENT(lpProviderNameBuf) && nNameBufSize == *lpnNameBufSize) { // The Unicode API must have written the provider name buffer DWORD status2 = OutputBufferToAnsi( 'S', UParm[2].lpbyte, lpProviderNameBuf, lpnNameBufSize); if (status2 != WN_SUCCESS) { status = status2; } } } } LocalFree(tempBuffer); SET_AND_RETURN(status) } DWORD WNetPropertyDialogA ( HWND hwndParent, DWORD iButton, DWORD nPropSel, LPSTR lpszName, DWORD nType ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITHOUT_ANSI_OUTPUT( 1, AParm[0].lpcstr = lpszName; , "S", WNetPropertyDialogW(hwndParent, iButton, nPropSel, UParm[0].lpwstr, nType); ) } DWORD WNetGetPropertyTextA ( IN DWORD iButton, IN DWORD nPropSel, IN LPSTR lpszName, OUT LPSTR lpszButtonName, IN DWORD nButtonNameLen, IN DWORD nType ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITH_ANSI_OUTPUT( 3, AParm[0].lpcstr = lpszName; AParm[1].lpvoid = lpszButtonName; AParm[2].lpdword = &nButtonNameLen; , "SB", WNetGetPropertyTextW(iButton, nPropSel, UParm[0].lpwstr, UParm[1].lpwstr, nButtonNameLen, nType); , OutputBufferToAnsi('S', UParm[1].lpbyte, lpszButtonName, &nButtonNameLen); ) } DWORD APIENTRY WNetFormatNetworkNameA( IN LPCSTR lpProvider, IN LPCSTR lpRemoteName, OUT LPSTR lpFormattedName, IN OUT LPDWORD lpnLength, // In characters! IN DWORD dwFlags, IN DWORD dwAveCharPerLine ) /*++ Routine Description: Arguments: Return Value: --*/ { ANSI_API_WITH_ANSI_OUTPUT( 4, AParm[0].lpcstr = lpProvider; AParm[1].lpcstr = lpRemoteName; AParm[2].lpvoid = lpFormattedName; AParm[3].lpdword = lpnLength; , "SSB", WNetFormatNetworkNameW( UParm[0].lpwstr, UParm[1].lpwstr, UParm[2].lpwstr, lpnLength, dwFlags, dwAveCharPerLine ); , OutputBufferToAnsi('S', UParm[2].lpbyte, lpFormattedName, lpnLength); ) }