/************************************************************************ Copyright (c) 1999-2000 Microsoft Corporation Module Name : cssup.cxx Abstract : Support routines for international character (cs) support. Author : Mike Warning MikeW August 1999. Revision History : ***********************************************************************/ #include "ndrp.h" #include "cssup.h" BOOL GetThreadACP( unsigned long *cp, error_status_t *pStatus) /*++ Routine Description : Get the current codepage of this thread. Arguments : cp -- Pointer to where to return the codepage pStatus -- Pointer to where to return the status of the operation Return : TRUE and RPC_S_OK if we we're able to determine the codepage, FALSE and a Win32 derived error code if not. --*/ { CPINFOEX info; if ( ! GetCPInfoEx( CP_THREAD_ACP, 0, &info ) ) { *pStatus = HRESULT_FROM_WIN32( GetLastError() ); return FALSE; } *pStatus = RPC_S_OK; *cp = info.CodePage; return TRUE; } ulong TranslateCodeset( ulong Codeset) /*++ Routine Description : Translate the given generic codeset value (CP_ACP, CP_OEMCP, or CP_THREAD_ACP) to it's true value. Arguments : Codeset -- The value to translate Return : The true value. --*/ { CPINFOEX info; if ( ! GetCPInfoEx( Codeset, 0, &info ) ) RpcRaiseException( HRESULT_FROM_WIN32( GetLastError () ) ); return info.CodePage; } ulong NdrpGetSetCSTagMarshall( PMIDL_STUB_MESSAGE pStubMsg, uchar * pMemory, NDR_CS_TAG_FORMAT * pTagFormat) /*++ Routine Description : Extract the codeset referred to by pTagFormat and save it in the stub message for later buffer sizing / marshalling. Arguments : pStubMsg -- The stub message pMemory -- (possibly) The pointer to the tag value pTagFormat -- Pointer to FC_CS_TAG in the format string Return : The codeset --*/ { ulong Codeset; InitializeStubCSInfo( pStubMsg ); // // If there is no tag getting routine then the value of the tag is on // the stack like a normal parameter. If there is a tag routine, then // the parameter is NOT on the stack (pMemory is invalid) and we need // to call the tag routine to get the value. // if ( NDR_INVALID_TAG_ROUTINE_INDEX == pTagFormat->TagRoutineIndex ) { Codeset = * (ulong *) pMemory; } else { CS_TAG_GETTING_ROUTINE *TagRoutines; CS_TAG_GETTING_ROUTINE GetTagRoutine; ulong SendingCodeset; ulong DesiredReceivingCodeset; ulong ReceivingCodeset; error_status_t status; if ( ! pStubMsg->IsClient ) DesiredReceivingCodeset = pStubMsg->pCSInfo->DesiredReceivingCodeset; TagRoutines = pStubMsg->StubDesc->CsRoutineTables->pTagGettingRoutines; GetTagRoutine = TagRoutines[ pTagFormat->TagRoutineIndex ]; GetTagRoutine( pStubMsg->RpcMsg->Handle, ! pStubMsg->IsClient, &SendingCodeset, &DesiredReceivingCodeset, &ReceivingCodeset, &status ); if ( RPC_S_OK != status ) RpcRaiseException( status ); if ( pTagFormat->Flags.STag ) Codeset = SendingCodeset; else if ( pTagFormat->Flags.DRTag ) Codeset = DesiredReceivingCodeset; else Codeset = ReceivingCodeset; } // // Don't allow generic psuedo codesets on the wire. Translate them to // thier real values // // REVIEW: The is true for the standard sizing/translation routines but // for user specified ones they should ideally be able to do // anything they want. // if ( CP_ACP == Codeset || CP_OEMCP == Codeset || CP_THREAD_ACP == Codeset ) Codeset = TranslateCodeset( Codeset ); // // Save the values away in the stub message for array size/marshal/etc. // if ( pTagFormat->Flags.STag ) pStubMsg->pCSInfo->WireCodeset = Codeset; if (pTagFormat->Flags.DRTag ) pStubMsg->pCSInfo->DesiredReceivingCodeset = Codeset; return Codeset; } ulong NdrpGetSetCSTagUnmarshall( PMIDL_STUB_MESSAGE pStubMsg, NDR_CS_TAG_FORMAT * pTagFormat) /*++ Routine Description : Extract the codeset in the buffer and save it in the stub message for later memory sizing / unmarshalling. Arguments : pStubMsg -- The stub message pTagFormat -- Pointer to FC_CS_TAG in the format string Return : The codeset --*/ { ulong Codeset; InitializeStubCSInfo( pStubMsg ); Codeset = * (ulong *) pStubMsg->Buffer; if ( pTagFormat->Flags.STag && ! pStubMsg->IsClient ) pStubMsg->pCSInfo->WireCodeset = Codeset; if ( pTagFormat->Flags.DRTag ) pStubMsg->pCSInfo->DesiredReceivingCodeset = Codeset; if ( pTagFormat->Flags.RTag && pStubMsg->IsClient ) pStubMsg->pCSInfo->WireCodeset = Codeset; return Codeset; } void GenericBufferSize( unsigned long DestCodeSet, unsigned long SourceCodeSet, unsigned long SourceBufferSize, IDL_CS_CONVERT * ConversionType, unsigned long * DestBufferSize, error_status_t * status) /*++ Routine Description : Estimate the length of the buffer needed to hold [SourceBufferSize] characters if they we're translated into [DestCodeSet]. Arguments : DestCodeSet - The codeset that the data will be translated to SourceCodeSet - The source codeset SourceBufferSize - The number of characters in the source codeset ConversionType - Returns whether or not conversion is needed DestBufferSize - Where to put the estimated buffer size status - The return status --*/ { int DestMaxCharSize; *status = NO_ERROR; // Determine the maximum size of a character on the wire in bytes if ( CP_UNICODE == DestCodeSet ) { DestMaxCharSize = 2; } else { CPINFO cpinfo; if ( ! GetCPInfo( DestCodeSet, &cpinfo ) ) { *status = HRESULT_FROM_WIN32( GetLastError() ); return; } DestMaxCharSize = cpinfo.MaxCharSize; } // Worst case: each char in the local buffer expands to the maximum number // of bytes for a char in the network codeset if ( NULL != DestBufferSize ) *DestBufferSize = SourceBufferSize * DestMaxCharSize; if ( SourceCodeSet == DestCodeSet ) *ConversionType = IDL_CS_NO_CONVERT; else *ConversionType = IDL_CS_NEW_BUFFER_CONVERT; } void RPC_ENTRY cs_byte_net_size( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, unsigned long LocalBufferSize, IDL_CS_CONVERT * ConversionType, unsigned long * NetworkBufferSize, error_status_t * status) /*++ Routine Description : Estimate the length of the buffer needed to hold [LocalBufferSize] characters if they are translated from the current thread codeset into [NetworkCodeSet]. Arguments : hBinding - The RPC binding handle NetworkCodeSet - The codeset that the data will be translated to LocalBufferSize - The number of bytes in the data ConversionType - Returns whether the conversion can be done inplace NetworkBufferSize - Where to put the estimated buffer size status - The return status --*/ { ulong LocalCP; if ( ! GetThreadACP( &LocalCP, status ) ) return; // No conversion is necessary if the local and destination codesets are // the same. GenericBufferSize( NetworkCodeSet, LocalCP, LocalBufferSize, ConversionType, NetworkBufferSize, status); } void RPC_ENTRY wchar_t_net_size( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, unsigned long LocalBufferSize, IDL_CS_CONVERT * ConversionType, unsigned long * NetworkBufferSize, error_status_t * status) /*++ Routine Description : Estimate the length of the buffer needed to hold [LocalBufferSize] characters if they are translated from the current thread codeset into [NetworkCodeSet]. Arguments : hBinding - The RPC binding handle NetworkCodeSet - The codeset that the data will be translated to LocalBufferSize - The number of bytes in the data ConversionType - Returns whether the conversion can be done inplace NetworkBufferSize - Where to put the estimated buffer size status - The return status --*/ { ulong LocalCP = CP_UNICODE; GenericBufferSize( NetworkCodeSet, LocalCP, LocalBufferSize, ConversionType, NetworkBufferSize, status); } void RPC_ENTRY cs_byte_local_size( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, unsigned long NetworkBufferSize, IDL_CS_CONVERT * ConversionType, unsigned long * LocalBufferSize, error_status_t * status) /*++ Routine Description : Estimate the length of the buffer needed to hold [NetworkBufferSize] characters if they are translated from [NetworkCodeSet] to the local thread codeset. Arguments : hBinding - The RPC binding handle NetworkCodeSet - The codeset that the data will be translated to NetworkBufferSize - The number of bytes in the data ConversionType - Returns whether the conversion can be done inplace LocalBufferSize - Where to put the estimated buffer size status - The return status --*/ { ulong LocalCP; if ( ! GetThreadACP(&LocalCP, status) ) return; // In Unicode the minimum character size is 2 so we can save a bit of // memory cutting the apparent source buffer size if ( CP_UNICODE == NetworkCodeSet ) NetworkBufferSize /= 2; GenericBufferSize( LocalCP, NetworkCodeSet, NetworkBufferSize, ConversionType, LocalBufferSize, status); } void RPC_ENTRY wchar_t_local_size( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, unsigned long NetworkBufferSize, IDL_CS_CONVERT * ConversionType, unsigned long * LocalBufferSize, error_status_t * status) /*++ Routine Description : Estimate the length of the buffer needed to hold [NetworkBufferSize] characters if they are translated from [NetworkCodeSet] to the local thread codeset. Arguments : hBinding - The RPC binding handle NetworkCodeSet - The codeset that the data will be translated to NetworkBufferSize - The number of bytes in the data ConversionType - Returns whether the conversion can be done inplace LocalBufferSize - Where to put the estimated buffer size status - The return status --*/ { ulong LocalCP = CP_UNICODE; // In Unicode the minimum character size is 2 so we can save a bit of // memory cutting the apparent source buffer size if ( CP_UNICODE == NetworkCodeSet ) NetworkBufferSize /= 2; // No conversion is necessary if the local and destination codesets are // the same. GenericBufferSize( LocalCP, NetworkCodeSet, NetworkBufferSize, ConversionType, LocalBufferSize, status); } void GenericCSConvert( unsigned long DestCodeSet, void *DestData, unsigned long DestBufferSize, unsigned long SourceCodeSet, void *SourceData, unsigned long SourceBufferSize, unsigned long *BytesWritten, error_status_t *status) /*++ Routine Description : Convert data from one character encoding to another. Arguments : DestCodeSet - The target encoding DestData - The target buffer DestBufferSize - The size of the target buffer in bytes SourceCodeset - The source encoding SourceData - The source data SourceBufferSize - The size of the source data in bytes BytesWritten - The number of bytes written to the target buffer status - The return status --*/ { wchar_t *TempBuffer = NULL; ulong BytesWrittenBuffer; *status = RPC_S_OK; // BytesWritten can be NULL in various circumstances. Make the following // code a bit more generic by making sure it always points at something. if ( NULL == BytesWritten ) BytesWritten = &BytesWrittenBuffer; // If the source and destination code sets are the same, just memcpy. // If there are 0 bytes in the source we don't need to do anything. if ( DestCodeSet == SourceCodeSet || 0 == SourceBufferSize) { if ( DestBufferSize < SourceBufferSize ) { *status = RPC_S_BUFFER_TOO_SMALL; return; } CopyMemory( DestData, SourceData, SourceBufferSize ); *BytesWritten = SourceBufferSize; return; } // We can't convert from a non-Unicode codeset to a different non-Unicode // codeset in one go, we have to convert to Unicode first. So regardless // of what the destionation is supposed to be make the source Unicode. if ( CP_UNICODE != SourceCodeSet ) { ulong TempBufferSize; if ( CP_UNICODE != DestCodeSet ) { TempBufferSize = SourceBufferSize * 2; TempBuffer = (wchar_t *) I_RpcAllocate( TempBufferSize ); if ( NULL == TempBuffer ) RpcRaiseException( RPC_S_OUT_OF_MEMORY ); } else { TempBufferSize = DestBufferSize; TempBuffer = (wchar_t *) DestData; } *BytesWritten = MultiByteToWideChar( SourceCodeSet, 0, (char *) SourceData, SourceBufferSize, TempBuffer, TempBufferSize / 2 ); if ( 0 == *BytesWritten ) *status = GetLastError(); *BytesWritten *= 2; SourceData = TempBuffer; SourceBufferSize = *BytesWritten; } // Convert to the destination codeset if it's not Unicode. if ( RPC_S_OK == *status && CP_UNICODE != DestCodeSet ) { *BytesWritten = WideCharToMultiByte( DestCodeSet, 0, (wchar_t *) SourceData, SourceBufferSize / 2, (char *) DestData, DestBufferSize, NULL, NULL); if ( 0 == *BytesWritten ) *status = HRESULT_FROM_WIN32( GetLastError() ); } if ( TempBuffer != DestData ) I_RpcFree( TempBuffer ); } void RPC_ENTRY cs_byte_to_netcs( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, cs_byte * LocalData, unsigned long LocalDataLength, byte * NetworkData, unsigned long * NetworkDataLength, error_status_t * status) /*++ Routine Description : Convert data from the current thread codeset to the network codeset Arguments : hBinding - The RPC binding handle NetworkCodeSet - The target codeset LocalData - The source data LocalDataLength - The size of the source data in bytes NetworkData - The target buffer NetworkDataLength - The number of bytes written to the target buffer status - The return status --*/ { unsigned long LocalCP; if ( ! GetThreadACP( &LocalCP, status ) ) return; // // For reasons known only to the gods, DCE didn't think it important to // include the size of the destination buffer as a parameter. It // *shouldn't* be an issue because in theory XXX_net_size was called to // properly size the buffer. Just to be inconsistent, they did include // it in XXX_from_netcs. // GenericCSConvert( NetworkCodeSet, NetworkData, INT_MAX, LocalCP, LocalData, LocalDataLength, NetworkDataLength, status); } void RPC_ENTRY wchar_t_to_netcs( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, wchar_t * LocalData, unsigned long LocalDataLength, byte * NetworkData, unsigned long * NetworkDataLength, error_status_t * status) /*++ Routine Description : Convert data from Unicode to the network codeset Arguments : hBinding - The RPC binding handle NetworkCodeSet - The target codeset LocalData - The source data LocalDataLength - The size of the source data in bytes NetworkData - The target buffer NetworkDataLength - The number of bytes written to the target buffer status - The return status --*/ { unsigned long LocalCP = CP_UNICODE; // // For reasons known only to the gods, DCE didn't think it important to // include the size of the destination buffer as a parameter. It // *shouldn't* be an issue because in theory XXX_net_size was called to // properly size the buffer. Just to be inconsistent, they did include // it in XXX_from_netcs. // GenericCSConvert( NetworkCodeSet, NetworkData, INT_MAX, LocalCP, LocalData, LocalDataLength * 2, // We want bytes not chars NetworkDataLength, status); } void RPC_ENTRY cs_byte_from_netcs( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, cs_byte * NetworkData, unsigned long NetworkDataLength, unsigned long LocalDataBufferSize, byte * LocalData, unsigned long * LocalDataLength, error_status_t * status) /*++ Routine Description : Convert data from the network codeset to the current thread codeset Arguments : hBinding - The RPC binding handle NetworkCodeSet - The source codeset NetworkData - The source data NetworkDataLength - The size of the source data in bytes LocalDataBufferSize - the size of the target buffer in bytes LocalData - The target buffer LocalDataLength - The number written to the target buffer status - The return status --*/ { unsigned long LocalCP; if ( ! GetThreadACP( &LocalCP, status ) ) return; GenericCSConvert( LocalCP, LocalData, LocalDataBufferSize, NetworkCodeSet, NetworkData, NetworkDataLength, LocalDataLength, status); } void RPC_ENTRY wchar_t_from_netcs( RPC_BINDING_HANDLE hBinding, unsigned long NetworkCodeSet, wchar_t * NetworkData, unsigned long NetworkDataLength, unsigned long LocalDataBufferSize, byte * LocalData, unsigned long * LocalDataLength, error_status_t * status) /*++ Routine Description : Convert data from the network codeset to the current thread codeset Arguments : hBinding - The RPC binding handle NetworkCodeSet - The source codeset NetworkData - The source data NetworkDataLength - The size of the source data in bytes LocalDataBufferSize - the size of the target buffer in bytes LocalData - The target buffer LocalDataLength - The number written to the target buffer status - The return status --*/ { unsigned long LocalCP = CP_UNICODE; GenericCSConvert( LocalCP, LocalData, LocalDataBufferSize * 2, // Bytes not chars NetworkCodeSet, NetworkData, NetworkDataLength, LocalDataLength, status); if ( LocalDataLength ) *LocalDataLength /= 2; // Chars not bytes } void RPC_ENTRY RpcCsGetTags( handle_t hBinding, int ServerSide, unsigned long * SendingTag, unsigned long * DesiredReceivingTag, unsigned long * ReceivingTag, error_status_t * status) /*++ Routine Description : Determine the codesets to use Arguments : hBinding - The RPC binding handle ServerSide - FALSE if this is the client SendingTag - Pointer to the returned sending tag DesiredReceivingTag - Pointer to the returned desired receiving tag ReceivingTag - Pointer to the returned receiving tag status - The return status Notes : On the server side, DesiredReceivingTag is an input instead of an output. The ReceivingTag will be set to this value. --*/ { ulong Codeset; if ( ! GetThreadACP( &Codeset, status ) ) return; if ( SendingTag ) * SendingTag = Codeset; if ( DesiredReceivingTag && ! ServerSide ) * DesiredReceivingTag = Codeset; if ( ReceivingTag ) { if ( ServerSide && DesiredReceivingTag ) * ReceivingTag = * DesiredReceivingTag; else * ReceivingTag = Codeset; } * status = RPC_S_OK; }