/*++ Copyright (c) 1990-1995 Microsoft Corporation Module Name: resource.c Abstract: This module contains functions to load resources Author: 29-Aug-1995 Tue 12:29:27 created -by- Daniel Chou (danielc) [Environment:] NT Windows - Common Printer Driver UI DLL. [Notes:] Revision History: --*/ #include "precomp.h" #pragma hdrstop #define DBG_CPSUIFILENAME DbgResource #define DBG_GETSTR0 0x00000001 #define DBG_GETSTR1 0x00000002 #define DBG_GETICON 0x00000004 #define DBG_COMPOSESTR 0x00000008 #define DBG_ADD_SPACE 0x00000010 #define DBG_ADD_WCHAR 0x00000020 #define DBG_AMPERCENT 0x00000040 DEFINE_DBGVAR(0); extern HINSTANCE hInstDLL; // // Remove Ampercent will remove a '&' sign, if two '&' signs (ie. '&&') then // only one is removed, ALSO if a '(&X)' (ie. SPACE + & + Left Parenthesis + // Single Character + Right Parenthesis) then it consider to be a Localization // hot key indicator, then the whole ' (&X)' will be removed // #define REMOVE_AMPERCENT(CHTYPE) \ { \ CHTYPE *pOrg; \ CHTYPE *pCopy; \ UINT cRemoved; \ CHTYPE ch; \ \ \ cRemoved = 0; \ pOrg = \ pCopy = pStr; \ \ CPSUIDBG(DBG_AMPERCENT, ("RemoveAmpercent (ORG)='%ws'", pOrg)); \ \ do { \ \ while ((ch = *pStr++) && (ch != (CHTYPE)'&')) { \ \ if (cRemoved) { \ \ *pCopy = ch; \ } \ \ ++pCopy; \ } \ \ if (ch) { \ \ ++cRemoved; \ \ if (*pStr == (CHTYPE)'&') { \ \ *pCopy++ = *pStr++; \ \ } else if ((*(pCopy - 1) == (CHTYPE)'(') && \ (*(pStr + 1) == (CHTYPE)')')) { \ \ cRemoved += 3; \ ch = (CHTYPE)')'; \ pCopy -= 1; \ pStr += 2; \ \ if ((*pStr == (CHTYPE)' ') && \ ((pCopy == pOrg) || \ ((pCopy > pOrg) && \ (*(pCopy - 1) == (CHTYPE)' ')))) { \ \ CPSUIDBG(DBG_AMPERCENT, ("Extra SPACE")); \ \ if (pCopy == pOrg) { \ \ ++pStr; \ \ } else { \ \ --pCopy; \ } \ \ ++cRemoved; \ } \ } \ } \ \ } while (ch); \ \ if (cRemoved) { \ \ *pCopy = (CHTYPE)'\0'; \ \ CPSUIDBG(DBG_AMPERCENT, (" RemoveAmpercent (%3ld)='%ws'", \ cRemoved, pOrg)); \ } \ \ return(cRemoved); \ } UINT RemoveAmpersandA( LPSTR pStr ) /*++ Routine Description: This function remove ampersand from a string, the string must be writable Arguments: pStr - string to be serarch and remove the ampersand if found Return Value: UINT, count of ampersands removed Author: 19-Sep-1995 Tue 21:55:19 created -by- Daniel Chou (danielc) Revision History: --*/ { REMOVE_AMPERCENT(CHAR); } UINT RemoveAmpersandW( LPWSTR pStr ) /*++ Routine Description: This function remove ampersand from a string, the string must be writable Arguments: pwStr - string to be serarch and remove the ampersand if found Return Value: UINT, count of ampersands removed Author: 19-Sep-1995 Tue 21:55:19 created -by- Daniel Chou (danielc) Revision History: --*/ { REMOVE_AMPERCENT(WCHAR); } UINT DupAmpersandW( LPWSTR pwStr, INT cChar, INT cMaxChar ) /*++ Routine Description: This function remove ampersand from a string, the string must be writable Arguments: pwStr - string to be serarch and remove the ampersand if found Return Value: UINT, count of ampersands removed Author: 19-Sep-1995 Tue 21:55:19 created -by- Daniel Chou (danielc) Revision History: --*/ { LPWSTR pw; LPWSTR pwCopy; INT i; INT cAdd = 0; if (((i = cChar) > 0) && ((cMaxChar -= cChar) > 0)) { pw = pwStr; while ((i--) && (cAdd < cMaxChar)) { if (*pw++ == L'&') { ++cAdd; } } if (cAdd) { pw = pwStr + cChar; pwCopy = pw + cAdd; CPSUIASSERT(0, "DupAmpersandW(): pwStr[%u] is not NULL", *pw == L'\0', IntToPtr(cChar)); while (pwCopy >= pwStr) { if ((*pwCopy-- = *pw--) == L'&') { *pwCopy-- = L'&'; } } } } return((UINT)cAdd); } UINT GetString( PGSBUF pGSBuf, LPTSTR pStr ) /*++ Routine Description: This function load the string either from caller (ANSI or UNICODE) or from common UI DLL, if the pStr is valid then it will just it, after getting the correct string, it will convert it to UNICODE as needed Arguments: pGSBuf - Pointer to GSBUF structure and following structure must set pTVWnd - Pointer to the TVWND which has all then information needed. pBuf - Pointer to the begining of the buffer (LPWSTR) pEndBuf - Pointer to the end of the buffer pStr - Pointer to the string to be converted Return Value: UINT - Count of character stored in the pBuf not include the NULL terminator, if the pBuf only has one character left then it always store a NULL and return 0 Author: 29-Aug-1995 Tue 12:30:49 created -by- Daniel Chou (danielc) First version 31-Aug-1995 Thu 10:58:04 updated -by- Daniel Chou (danielc) Re-write to do UNICODE conversion and ANSI call checking 05-Feb-1996 Mon 12:20:28 updated -by- Daniel Chou (danielc) Fix the bug when UNICODE we do lstrcpy without checking the buffer size. Revision History: --*/ { LPWSTR pBuf; WORD Flags; GSBUF GSBuf = *pGSBuf; UINT RemoveAmpersandOff = 0; INT cChar; INT Len = 0; // // Make pBuf pointed to the first available character // pBuf = pGSBuf->pBuf; Flags = pGSBuf->Flags; CPSUIASSERT(0, "GetString(pBuf=NULL)", pBuf, 0); if (pGSBuf->pEndBuf) { cChar = (INT)(pGSBuf->pEndBuf - pBuf); } else { cChar = MAX_RES_STR_CHARS; } // // Check if we have room to convert the string, make sure we reduced the // cChar by one for the NULL terminator // if ((pStr == NULL) || (cChar < 2)) { if (pStr) { CPSUIWARN(("GetString: pStr=%08lx, Buffer cChar=%ld too smaller", pStr, cChar)); } *pBuf = L'\0'; return(0); } if (pGSBuf->chPreAdd != L'\0') { CPSUIDBG(DBG_GETSTR0, ("GetString(): Pre-Add Char = '%wc'", pGSBuf->chPreAdd)); // // If we pre-add character first then do it now // if ((*pBuf++ = pGSBuf->chPreAdd) == L'&') { RemoveAmpersandOff = 1; } cChar--; pGSBuf->chPreAdd = L'\0'; } if (--cChar < 1) { CPSUIDBG(DBG_GETSTR1, ("GetString()=Only has one character for SPACE")); NULL; } else if (!VALID_PTR(pStr)) { HINSTANCE hInst; WORD ResID; // // Apperantly this is the resource ID, the LoadString() will not // write exceed the cChar included the NULL terminator according to // the Win32 help file. At here we know we either have to convert // the ASCII string to UNICODE or we load the UNICODE string to the // buffer already // ResID = LOWORD(LODWORD(pStr)); CPSUIDBGBLK({ if ((ResID >= IDI_CPSUI_ICONID_FIRST) && (ResID <= IDI_CPSUI_ICONID_LAST)) { CPSUIERR(("ResID=%ld is in icon ID range, change it", ResID)); ResID = ResID - IDI_CPSUI_ICONID_FIRST + IDS_CPSUI_STRID_FIRST; } }) if ((ResID >= IDS_CPSUI_STRID_FIRST) && (ResID <= IDS_CPSUI_STRID_LAST)) { hInst = hInstDLL; if (Flags & GBF_INT_NO_PREFIX) { Flags &= ~GBF_PREFIX_OK; } } else { hInst = (Flags & GBF_IDS_INT_CPSUI) ? hInstDLL : pGSBuf->hInst; CPSUIASSERT(0, "GetString(hInst=NULL, %08lx)", pStr, 0); } // // Now loaded from common UI DLL directly to the user buffer // if (Len = LoadString(hInst, ResID, pBuf, cChar)) { pBuf += Len; } else { pBuf = pGSBuf->pBuf; CPSUIERR(("LoadString(ID=%ld) FAILED", ResID)); } } else if ((Flags & GBF_COPYWSTR) || (!(Flags & GBF_ANSI_CALL))) { // // We have UNICODE string but may need to put into the buffer // if (Len = lstrlen(pStr)) { if (Len > cChar) { Len = cChar; } CopyMemory(pBuf, pStr, sizeof(WCHAR) * Len); pBuf += Len; } } else { // // We are loading the ANSI string // if (Len = lstrlenA((LPSTR)pStr)) { if (Len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pStr, Len, pBuf, cChar)) { pBuf += Len; } else { // // Conversion is not complete so make sure it NULL terminated // pBuf = pGSBuf->pBuf; CPSUIWARN(("GetString: pstr='%hs', Buffer reach limit=%ld, Len=%ld", pStr, cChar, Len)); } CPSUIDBG(DBG_GETSTR0, ("Convert to UNICODE, Len=%d, cChar=%d", Len, cChar)); } } // // Save the new index back and return the len to the caller // *pBuf = L'\0'; Len = (INT)(pBuf - pGSBuf->pBuf); if (!(Flags & GBF_PREFIX_OK)) { Len -= RemoveAmpersandW(pGSBuf->pBuf + RemoveAmpersandOff); } if (Flags & GBF_DUP_PREFIX) { Len += DupAmpersandW(pGSBuf->pBuf, Len, (INT)(UINT)(pGSBuf->pEndBuf - pGSBuf->pBuf)); } CPSUIDBG(DBG_GETSTR1, ("GetString()=%ws (%d/%d)", pGSBuf->pBuf, Len, cChar)); CPSUIASSERT(0, "GetString() : Len != Real Len (%ld)", Len == lstrlen(pGSBuf->pBuf), IntToPtr(lstrlen(pGSBuf->pBuf))); pGSBuf->pBuf += Len; return((UINT)Len); } UINT GSBufAddNumber( PGSBUF pGSBuf, DWORD Number, BOOL Sign ) /*++ Routine Description: Convert a number to a string with the limitation of GSBUF Arguments: pGSBuf - Pointer to GSBUF structure and following structure must set pTVWnd - Pointer to the TVWND which has all then information needed. pBuf - Pointer to the begining of the buffer (LPWSTR) pEndBuf - Pointer to the end of the buffer Number - LONG number to be converted Sign - if TRUE then Number is a sign long number else it is a unsigned DWORD Return Value: UINT total bytes converted to the string Author: 21-Feb-1996 Wed 12:17:00 created -by- Daniel Chou (danielc) Revision History: --*/ { WCHAR wBuf[50]; UINT cChar; UINT Len = 0; if ((cChar = (INT)(pGSBuf->pEndBuf - pGSBuf->pBuf - 1)) > 0) { StringCchPrintfW(wBuf, COUNT_ARRAY(wBuf), (Sign) ? L"%ld" : L"%lu", Number); if((Len = lstrlenW(wBuf)) > 0) { if (Len > cChar) { Len = cChar; } CopyMemory(pGSBuf->pBuf, wBuf, sizeof(WCHAR) * Len); pGSBuf->pBuf += Len; *(pGSBuf->pBuf) = L'\0'; } } return Len; } UINT GSBufAddWChar( PGSBUF pGSBuf, UINT IntCharStrID, UINT Count ) /*++ Routine Description: Add a single character to the GSBuf Arguments: pGSBuf - Pointer to GSBUF structure and following structure must set pTVWnd - Pointer to the TVWND which has all then information needed. pBuf - Pointer to the begining of the buffer (LPWSTR) pEndBuf - Pointer to the end of the buffer wch - a single character to be added Return Value: BOOLEAN, true if succeed else false Author: 21-Feb-1996 Wed 12:00:24 created -by- Daniel Chou (danielc) Revision History: --*/ { WCHAR wCh[2]; UINT cAvai; if ((Count) && ((cAvai = (UINT)(pGSBuf->pEndBuf - pGSBuf->pBuf)) > 1) && (LoadString(hInstDLL, IntCharStrID, wCh, COUNT_ARRAY(wCh)))) { CPSUIDBG(DBG_ADD_WCHAR, ("GSBufAddWChar(%08lx, %u, %u)=%u of '%wc'", pGSBuf, IntCharStrID, Count, (Count > (cAvai - 1)) ? cAvai - 1 : Count, wCh[0])); if (Count > (cAvai -= 1)) { Count = cAvai; } cAvai = Count; while (cAvai--) { *(pGSBuf->pBuf)++ = wCh[0]; } *(pGSBuf->pBuf) = L'\0'; return(Count); } else { CPSUIERR(("GSBufAddWChar(%08lx, %u, %u) FAILED", pGSBuf, IntCharStrID, Count)); return(0); } } UINT GSBufAddSpace( PGSBUF pGSBuf, UINT Count ) /*++ Routine Description: Arguments: Return Value: Author: 20-Jul-1996 Sat 00:59:47 created -by- Daniel Chou (danielc) Revision History: --*/ { static WCHAR wSpace[2] = { 0, 0 }; UINT cAvai; if (wSpace[0] == L'\0') { LoadString(hInstDLL, IDS_INT_CPSUI_SPACE_CHAR, wSpace, COUNT_ARRAY(wSpace)); } if ((wSpace[0] != L'\0') && (Count) && ((cAvai = (UINT)(pGSBuf->pEndBuf - pGSBuf->pBuf)) > 1)) { CPSUIDBG(DBG_ADD_SPACE, ("GSBufAddSpace(%08lx, %u)=%u of '%wc'", pGSBuf, Count, (Count > (cAvai - 1)) ? cAvai - 1 : Count, wSpace[0])); if (Count > (cAvai -= 1)) { Count = cAvai; } cAvai = Count; while (cAvai--) { *(pGSBuf->pBuf)++ = wSpace[0]; } *(pGSBuf->pBuf) = L'\0'; return(Count); } else { CPSUIERR(("GSBufAddSpace(%08lx, %u) FAILED", pGSBuf, Count)); return(0); } } UINT GetStringBuffer( HINSTANCE hInst, WORD GBFlags, WCHAR chPreAdd, LPTSTR pStr, LPWSTR pBuf, UINT cwBuf ) /*++ Routine Description: Arguments: Return Value: Author: 07-Sep-1995 Thu 10:45:09 created -by- Daniel Chou (danielc) Revision History: --*/ { GSBUF GSBuf; GSBuf.hInst = hInst; GSBuf.Flags = GBFlags; GSBuf.pBuf = (LPWSTR)pBuf; GSBuf.pEndBuf = (LPWSTR)pBuf + cwBuf; GSBuf.chPreAdd = chPreAdd; return(GetString(&GSBuf, pStr)); } LONG LoadCPSUIString( LPTSTR pStr, UINT cStr, UINT StrResID, BOOL AnsiCall ) /*++ Routine Description: Arguments: Return Value: Author: 08-Feb-1996 Thu 13:36:12 created -by- Daniel Chou (danielc) Revision History: --*/ { if ((pStr) && (cStr)) { UINT Len = 0; if ((StrResID >= IDS_CPSUI_STRID_FIRST) && (StrResID <= IDS_CPSUI_STRID_LAST)) { if (AnsiCall) { if (Len = LoadStringA(hInstDLL, StrResID, (LPSTR)pStr, cStr)) { Len -= RemoveAmpersandA((LPSTR)pStr); } } else { if (Len = LoadString(hInstDLL, StrResID, (LPWSTR)pStr, cStr)) { Len -= RemoveAmpersandW((LPWSTR)pStr); } } } return((LONG)Len); } else { return(-1); } } UINT ComposeStrData( HINSTANCE hInst, WORD GBFlags, LPWSTR pBuf, UINT cwBuf, UINT IntFormatStrID, LPTSTR pStr, DWORD dw1, DWORD dw2 ) /*++ Routine Description: Arguments: Return Value: Author: 19-Jul-1996 Fri 17:11:19 created -by- Daniel Chou (danielc) Revision History: --*/ { TCHAR szFormat[MAX_RES_STR_CHARS * 3]; LPTSTR pData; LPTSTR pFinal; UINT Count; UINT i; UINT cb; ZeroMemory(szFormat, sizeof(szFormat)); if ((IntFormatStrID) && (i = LoadString(hInstDLL, IntFormatStrID, (LPTSTR)szFormat, MAX_RES_STR_CHARS))) { cb = ARRAYSIZE(szFormat) - i - 1; pData = szFormat + i + 1; if (!pStr) { // // Skip the pStr if it passed as NULL // StringCchPrintf(pFinal = pData, cb, szFormat, dw1, dw2); Count = lstrlen(pFinal); } else { i = GetStringBuffer(hInst, (WORD)(GBFlags | GBF_INT_NO_PREFIX), (WCHAR)0, pStr, pData, MAX_RES_STR_CHARS); cb = cb - i - 1; pFinal = pData + i + 1; StringCchPrintf(pFinal, cb, szFormat, pData, dw1, dw2); Count = lstrlen(pFinal); } if (Count > (cwBuf - 1)) { Count = cwBuf - 1; szFormat[Count] = '\0'; } CopyMemory(pBuf, pFinal, (Count + 1) * sizeof(TCHAR)); CPSUIDBG(DBG_COMPOSESTR, ("ComposeString('%ws', '%ws', %lu, %lu)='%ws' [%u]", szFormat, pData, dw1, dw2, pBuf, Count)); } else { Count = GetStringBuffer(hInst, GBFlags, (WCHAR)0, pStr, pBuf, cwBuf); CPSUIDBG(DBG_COMPOSESTR, ("ComposeString(%08lx, %lu, %lu)=FAILED, '%ws' [%u]", pStr, dw1, dw2, pBuf, Count)); } return(Count); }