// // strinout.cpp // // Unicode wrappers (String converters) // // Copyright(C) Microsoft Corporation 2000 // Author: Nadim Abdo (nadima) // // based on code from shell\shlwapi\unicwrap.* // #include "stdafx.h" #include "cstrinout.h" //+--------------------------------------------------------------------------- // // Member: CConvertStr::Free // // Synopsis: Frees string if alloc'd and initializes to NULL. // //---------------------------------------------------------------------------- void CConvertStr::Free() { if (_pstr != _ach && HIWORD64(_pstr) != 0 && !IsAtom()) { delete [] _pstr; } _pstr = NULL; } //+--------------------------------------------------------------------------- // // Member: CConvertStrW::Free // // Synopsis: Frees string if alloc'd and initializes to NULL. // //---------------------------------------------------------------------------- void CConvertStrW::Free() { if (_pwstr != _awch && HIWORD64(_pwstr) != 0) { delete [] _pwstr; } _pwstr = NULL; } //+--------------------------------------------------------------------------- // // Member: CStrInW::Init // // Synopsis: Converts a LPSTR function argument to a LPWSTR. // // Arguments: [pstr] -- The function argument. May be NULL or an atom // (HIWORD64(pwstr) == 0). // // [cch] -- The number of characters in the string to // convert. If -1, the string is assumed to be // NULL terminated and its length is calculated. // // Modifies: [this] // //---------------------------------------------------------------------------- void CStrInW::Init(LPCSTR pstr, int cch) { int cchBufReq; _cwchLen = 0; // Check if string is NULL or an atom. if (HIWORD64(pstr) == 0) { _pwstr = (LPWSTR) pstr; return; } ASSERT(cch == -1 || cch > 0); // // Convert string to preallocated buffer, and return if successful. // // Since the passed in buffer may not be null terminated, we have // a problem if cch==ARRAYSIZE(_awch), because MultiByteToWideChar // will succeed, and we won't be able to null terminate the string! // Decrease our buffer by one for this case. // _cwchLen = MultiByteToWideChar( CP_ACP, 0, pstr, cch, _awch, ARRAYSIZE(_awch)-1); if (_cwchLen > 0) { // Some callers don't NULL terminate. // // We could check "if (-1 != cch)" before doing this, // but always doing the null is less code. // _awch[_cwchLen] = 0; if (0 == _awch[_cwchLen-1]) // account for terminator _cwchLen--; _pwstr = _awch; return; } // // Alloc space on heap for buffer. // cchBufReq = MultiByteToWideChar( CP_ACP, 0, pstr, cch, NULL, 0 ); // Again, leave room for null termination cchBufReq++; ASSERT(cchBufReq > 0); _pwstr = new WCHAR[cchBufReq]; if (!_pwstr) { // On failure, the argument will point to the empty string. _awch[0] = 0; _pwstr = _awch; return; } ASSERT(HIWORD64(_pwstr)); _cwchLen = MultiByteToWideChar( CP_ACP, 0, pstr, cch, _pwstr, cchBufReq ); #if DBG == 1 /* { */ if (0 == _cwchLen) { int errcode = GetLastError(); ASSERT(0 && "MultiByteToWideChar failed in unicode wrapper."); } #endif /* } */ // Again, make sure we're always null terminated ASSERT(_cwchLen < cchBufReq); _pwstr[_cwchLen] = 0; if (0 == _pwstr[_cwchLen-1]) // account for terminator _cwchLen--; } //+--------------------------------------------------------------------------- // // Member: CStrIn::CStrIn // // Synopsis: Inits the class. // // NOTE: Don't inline this function or you'll increase code size // by pushing -1 on the stack for each call. // //---------------------------------------------------------------------------- CStrIn::CStrIn(LPCWSTR pwstr) : CConvertStr(CP_ACP) { Init(pwstr, -1); } CStrIn::CStrIn(UINT uCP, LPCWSTR pwstr) : CConvertStr(uCP) { Init(pwstr, -1); } //+--------------------------------------------------------------------------- // // Member: CStrIn::Init // // Synopsis: Converts a LPWSTR function argument to a LPSTR. // // Arguments: [pwstr] -- The function argument. May be NULL or an atom // (HIWORD(pwstr) == 0). // // [cwch] -- The number of characters in the string to // convert. If -1, the string is assumed to be // NULL terminated and its length is calculated. // // Modifies: [this] // // Note: We ignore AreFileApisANSI() and always use CP_ACP. // The reason is that nobody uses SetFileApisToOEM() except // console apps, and once you set file APIs to OEM, you // cannot call shell/user/gdi APIs, since they assume ANSI // regardless of the FileApis setting. So you end up in // this horrible messy state where the filename APIs interpret // the strings as OEM but SHELL32 interprets the strings // as ANSI and you end up with a big mess. // //---------------------------------------------------------------------------- void CStrIn::Init(LPCWSTR pwstr, int cwch) { int cchBufReq; #if DBG == 1 /* { */ int errcode; #endif /* } */ _cchLen = 0; // Check if string is NULL or an atom. if (HIWORD64(pwstr) == 0 || IsAtom()) { _pstr = (LPSTR) pwstr; return; } if ( cwch == 0 ) { *_ach = '\0'; _pstr = _ach; return; } // // Convert string to preallocated buffer, and return if successful. // _cchLen = WideCharToMultiByte( _uCP, 0, pwstr, cwch, _ach, ARRAYSIZE(_ach)-1, NULL, NULL); if (_cchLen > 0) { // This is DBCS safe since byte before _cchLen is last character _ach[_cchLen] = 0; // this may not be safe if the last character // was a multibyte character... if (_ach[_cchLen-1]==0) _cchLen--; // account for terminator _pstr = _ach; return; } cchBufReq = WideCharToMultiByte( CP_ACP, 0, pwstr, cwch, NULL, 0, NULL, NULL); cchBufReq++; ASSERT(cchBufReq > 0); _pstr = new char[cchBufReq]; if (!_pstr) { // On failure, the argument will point to the empty string. _ach[0] = 0; _pstr = _ach; return; } ASSERT(HIWORD64(_pstr)); _cchLen = WideCharToMultiByte( _uCP, 0, pwstr, cwch, _pstr, cchBufReq, NULL, NULL); #if DBG == 1 /* { */ if (_cchLen < 0) { errcode = GetLastError(); ASSERT(0 && "WideCharToMultiByte failed in unicode wrapper."); } #endif /* } */ // Again, make sure we're always null terminated ASSERT(_cchLen < cchBufReq); _pstr[_cchLen] = 0; if (0 == _pstr[_cchLen-1]) // account for terminator _cchLen--; } //+--------------------------------------------------------------------------- // // Member: CStrInMulti::CStrInMulti // // Synopsis: Converts mulitple LPWSTRs to a multiple LPSTRs. // // Arguments: [pwstr] -- The strings to convert. // // Modifies: [this] // //---------------------------------------------------------------------------- CStrInMulti::CStrInMulti(LPCWSTR pwstr) { LPCWSTR pwstrT; // We don't handle atoms because we don't need to. ASSERT(HIWORD64(pwstr)); // // Count number of characters to convert. // pwstrT = pwstr; if (pwstr) { do { while (*pwstrT++) ; } while (*pwstrT++); } Init(pwstr, (int)(pwstrT - pwstr)); } //+--------------------------------------------------------------------------- // // Member: CPPFIn::CPPFIn // // Synopsis: Inits the class. Truncates the filename to MAX_PATH // so Win9x DBCS won't fault. Win9x SBCS silently truncates // to MAX_PATH, so we're bug-for-bug compatible. // //---------------------------------------------------------------------------- CPPFIn::CPPFIn(LPCWSTR pwstr) { SHUnicodeToAnsi(pwstr, _ach, ARRAYSIZE(_ach)); } //+--------------------------------------------------------------------------- // // Member: CStrOut::CStrOut // // Synopsis: Allocates enough space for an out buffer. // // Arguments: [pwstr] -- The Unicode buffer to convert to when destroyed. // May be NULL. // // [cwchBuf] -- The size of the buffer in characters. // // Modifies: [this]. // //---------------------------------------------------------------------------- CStrOut::CStrOut(LPWSTR pwstr, int cwchBuf) : CConvertStr(CP_ACP) { Init(pwstr, cwchBuf); } CStrOut::CStrOut(UINT uCP, LPWSTR pwstr, int cwchBuf) : CConvertStr(uCP) { Init(pwstr, cwchBuf); } void CStrOut::Init(LPWSTR pwstr, int cwchBuf) { ASSERT(cwchBuf >= 0); _pwstr = pwstr; _cwchBuf = cwchBuf; if (!pwstr) { // Force cwchBuf = 0 because many callers (in particular, registry // munging functions) pass garbage as the length because they know // it will be ignored. _cwchBuf = 0; _pstr = NULL; return; } ASSERT(HIWORD64(pwstr)); // Initialize buffer in case Windows API returns an error. _ach[0] = 0; // Use preallocated buffer if big enough. if (cwchBuf * 2 <= ARRAYSIZE(_ach)) { _pstr = _ach; return; } // Allocate buffer. _pstr = new char[cwchBuf * 2]; if (!_pstr) { // // On failure, the argument will point to a zero-sized buffer initialized // to the empty string. This should cause the Windows API to fail. // ASSERT(cwchBuf > 0); _pwstr[0] = 0; _cwchBuf = 0; _pstr = _ach; return; } ASSERT(HIWORD64(_pstr)); _pstr[0] = 0; } //+--------------------------------------------------------------------------- // // Member: CStrOutW::CStrOutW // // Synopsis: Allocates enough space for an out buffer. // // Arguments: [pstr] -- The MBCS buffer to convert to when destroyed. // May be NULL. // // [cchBuf] -- The size of the buffer in characters. // // Modifies: [this]. // //---------------------------------------------------------------------------- CStrOutW::CStrOutW(LPSTR pstr, int cchBuf) { ASSERT(cchBuf >= 0); _pstr = pstr; _cchBuf = cchBuf; if (!pstr) { // Force cchBuf = 0 because many callers (in particular, registry // munging functions) pass garbage as the length because they know // it will be ignored. _cchBuf = 0; _pwstr = NULL; return; } ASSERT(HIWORD64(pstr)); // Initialize buffer in case Windows API returns an error. _awch[0] = 0; // Use preallocated buffer if big enough. if (cchBuf <= ARRAYSIZE(_awch)) { _pwstr = _awch; return; } // Allocate buffer. _pwstr = new WCHAR[cchBuf]; if (!_pwstr) { // // On failure, the argument will point to a zero-sized buffer initialized // to the empty string. This should cause the Windows API to fail. // ASSERT(cchBuf > 0); _pstr[0] = 0; _cchBuf = 0; _pwstr = _awch; return; } ASSERT(HIWORD64(_pwstr)); _pwstr[0] = 0; } //+--------------------------------------------------------------------------- // // Member: CStrOut::ConvertIncludingNul // // Synopsis: Converts the buffer from MBCS to Unicode // // Return: Character count INCLUDING the trailing '\0' // //---------------------------------------------------------------------------- int CStrOut::ConvertIncludingNul() { int cch; if (!_pstr) return 0; cch = SHAnsiToUnicodeCP(_uCP, _pstr, _pwstr, _cwchBuf); #if DBG == 1 /* { */ if (cch == 0 && _cwchBuf > 0) { int errcode = GetLastError(); ASSERT(0 && "SHAnsiToUnicode failed in unicode wrapper."); } #endif /* } */ Free(); return cch; } //+--------------------------------------------------------------------------- // // Member: CStrOutW::ConvertIncludingNul // // Synopsis: Converts the buffer from Unicode to MBCS // // Return: Character count INCLUDING the trailing '\0' // //---------------------------------------------------------------------------- int CStrOutW::ConvertIncludingNul() { int cch; if (!_pwstr) return 0; cch = SHUnicodeToAnsi(_pwstr, _pstr, _cchBuf); #if DBG == 1 /* { */ if (cch == 0 && _cchBuf > 0) { int errcode = GetLastError(); ASSERT(0 && "SHUnicodeToAnsi failed in unicode wrapper."); } #endif /* } */ Free(); return cch; } //+--------------------------------------------------------------------------- // // Member: CStrOut::ConvertExcludingNul // // Synopsis: Converts the buffer from MBCS to Unicode // // Return: Character count EXCLUDING the trailing '\0' // //---------------------------------------------------------------------------- int CStrOut::ConvertExcludingNul() { int ret = ConvertIncludingNul(); if (ret > 0) { ret -= 1; } return ret; } //+--------------------------------------------------------------------------- // // Member: CStrOut::CopyNoConvert // // Synopsis: Copies to the output buffer without converting // // Return: Character count EXCLUDING the trailing '\0' // //---------------------------------------------------------------------------- int CStrOut::CopyNoConvert(int srcBytes) { if (!_pstr || !_pwstr) { return 0; } if (srcBytes < (int)(_cwchBuf * sizeof(WCHAR))) { memcpy(_pwstr, _pstr, srcBytes); Free(); return srcBytes; } else { return 0; } } //+--------------------------------------------------------------------------- // // Member: CStrOut::~CStrOut // // Synopsis: Converts the buffer from MBCS to Unicode. // // Note: Don't inline this function, or you'll increase code size as // both ConvertIncludingNul() and CConvertStr::~CConvertStr will be // called inline. // //---------------------------------------------------------------------------- CStrOut::~CStrOut() { ConvertIncludingNul(); } //+--------------------------------------------------------------------------- // // Member: CStrOutW::~CStrOutW // // Synopsis: Converts the buffer from Unicode to MBCS. // // Note: Don't inline this function, or you'll increase code size as // both ConvertIncludingNul() and CConvertStr::~CConvertStr will be // called inline. // //---------------------------------------------------------------------------- CStrOutW::~CStrOutW() { ConvertIncludingNul(); }