windows-nt/Source/XPSP1/NT/com/rpc/ndr20/cssup.cxx
2020-09-26 16:20:57 +08:00

841 lines
22 KiB
C++

/************************************************************************
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;
}