/* * Microsoft Confidential * Copyright (C) Microsoft Corporation 1991 * All Rights Reserved. * * * PIFSUB.C * Misc. subroutines for PIFMGR.DLL * * History: * Created 31-Jul-1992 3:30pm by Jeff Parsons */ #include "shellprv.h" #pragma hdrstop #ifdef _X86_ // shell priv can alter the definition of IsDBCSLeadByte! #if defined(FE_SB) #ifdef IsDBCSLeadByte #undef IsDBCSLeadByte #define IsDBCSLeadByte(x) IsDBCSLeadByteEx(CP_ACP,x) #endif #endif /* * Most of the routines in this file will need to stay ANSI. If a UNICODE * version is needed, it is supplied. * * This is because for the most part, the information in the PIF files * is ANSI and needs to stay that way. * * (RickTu) * */ /** lstrcpypadA - copy to fixed-length string, appending trailing blanks * * INPUT * lpszDst -> fixed-length destination string * lpszSrc -> source string * cchMax = size of fixed-length destination string (count of characters) * * OUTPUT * Nothing */ void lstrcpypadA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax) { FunctionName(lstrcpypadA); while (cchMax && *lpszSrc) { cchMax--; *lpszDst++ = *lpszSrc++; } while (cchMax--) { *lpszDst++ = ' '; } } /** lstrcpyncharA - copy variable-length string, until char * * INPUT * lpszDst -> fixed-length destination string * lpszSrc -> source string * cchMax = size of fixed-length destination string (count of characters) * ch = character to stop copying at * * OUTPUT * # of characters copied, excluding terminating NULL */ int lstrcpyncharA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax, CHAR ch) { int cch = 0; FunctionName(lstrcpyncharA); while (--cchMax && *lpszSrc && *lpszSrc != ch) { if (IsDBCSLeadByte(*lpszSrc)) { cch++; *lpszDst++ = *lpszSrc++; if (!*lpszSrc) break; /* Eek! String ends in DBCS lead byte! */ } cch++; *lpszDst++ = *lpszSrc++; } *lpszDst = '\0'; return cch; } /** lstrskipcharA - skip char in variable-length string * * INPUT * lpszSrc -> source string * ch = character to skip * * OUTPUT * # of characters skipped, 0 if none */ int lstrskipcharA(LPCSTR lpszSrc, CHAR ch) { int cch = 0; FunctionName(lstrskipcharA); while (*lpszSrc && *lpszSrc == ch) { cch++; lpszSrc++; } return cch; } /** lstrskiptocharA - skip *to* char in variable-length string * * INPUT * lpszSrc -> source string * ch = character to skip *to* * * OUTPUT * # of characters skipped, 0 if none; if char didn't exist, then all * characters are skipped. */ int lstrskiptocharA(LPCSTR lpszSrc, CHAR ch) { int cch = 0; FunctionName(lstrskiptocharA); while (*lpszSrc && *lpszSrc != ch) { cch++; lpszSrc++; } return cch; } /** lstrcpyfnameA - copy filename appropriately * * INPUT * pszDest -> output buffer * cchDest -> size of destination buffer in characters * pszSrc -> source filename * cbMax = size of output buffer * * OUTPUT * # of characters copied, including quotes if any, excluding terminating NULL */ int lstrcpyfnameA(PSTR pszDest, size_t cchDest, PCSTR pszSrc) { CHAR szDest[MAX_PATH]; // PathQuoteSpacesA requires a MAX_PATH buffer; ensure we're using one HRESULT hr = StringCchCopyA(szDest, ARRAYSIZE(szDest), pszSrc); if (FAILED(hr)) { *pszDest = '\0'; return 0; } PathQuoteSpacesA(szDest); // quote if necessary hr = StringCchCopyA(pszDest, cchDest, szDest); if (FAILED(hr)) { *pszDest = '\0'; return 0; } return lstrlenA(pszDest); } /** lstrunquotefnameA - unquote filename if it contains quotes * * INPUT * lpszDst -> output buffer * lpszSrc -> source filename (quoted or unquoted) * cchMax = size of output buffer (count of characters) * fShort = TRUE if filename should be converted to 8.3 (eg, for real-mode); * -1 if the filename is known to not be quoted and should just be converted * OUTPUT * # of characters copied, excluding terminating NULL */ int lstrunquotefnameA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax, BOOL fShort) { int cch; FunctionName(lstrunquotefnameA); if (fShort != -1) { if (lpszSrc[0] == '\"') { cch = lstrcpyncharA(lpszDst, lpszSrc+1, cchMax, '\"'); } else { cch = lstrcpyncharA(lpszDst, lpszSrc, cchMax, ' '); } lpszSrc = lpszDst; } if (fShort) { HRESULT hr; if (lpszSrc != lpszDst) { // copy so we can work on lpszDst buffer of known size hr = StringCchCopyA(lpszDst, cchMax, lpszSrc); if (FAILED(hr)) { *lpszDst = '\0'; return 0; } } CharToOemBuffA(lpszSrc, lpszDst, cchMax); cch = GetShortPathNameA( lpszSrc, lpszDst, cchMax ); if (cch >= cchMax) { *lpszDst = '\0'; return 0; } if (cch) { // if no error... if (fShort == TRUE) { // if conversion for real-mode... if ((int)GetFileAttributesA(lpszDst) == -1) { // if filename doesn't exist, // then just copy the 8.3 portion // and hope the user's real-mode PATH // ultimately finds it! if (NULL != (lpszSrc = StrRChrA(lpszDst, NULL, '\\'))) { hr = StringCchCopyA(lpszDst, cchMax, lpszSrc+1); if (FAILED(hr)) { *lpszDst = '\0'; return 0; } } } } cch = lstrlenA(lpszDst); // recompute the length of the string } } return cch; } /** lstrskipfnameA - skip filename in string * * INPUT * lpszSrc -> string beginning with filename (quoted or unquoted) * * OUTPUT * # of characters skipped, 0 if none */ int lstrskipfnameA(LPCSTR lpszSrc) { int cch = 0; FunctionName(lstrskipfname); if (lpszSrc[0] == '\"') { cch = lstrskiptocharA(lpszSrc+1, '\"') + 1; if (lpszSrc[cch] == '\"') cch++; } else cch = lstrskiptocharA(lpszSrc, ' '); return cch; } /* * NOTE! The careful definitions of achBuf and achFmt, so that * we can support total output of 2 * MAX_STRING_SIZE bytes. */ int cdecl Warning(HWND hwnd, WORD id, WORD type, ...) { LPCTSTR lpchFmt; PPROPLINK ppl = NULL; TCHAR achBuf[2*MAX_STRING_SIZE]; #define achFmt (&achBuf[MAX_STRING_SIZE]) va_list ArgList; FunctionName(Warning); lpchFmt = achFmt; // We never use MB_FOCUS to mean whatever it's really supposed // to mean; we just use it as a kludge to support warning dialogs // when all we have is a ppl, not an hwnd. if (type & MB_NOFOCUS) { ppl = (PPROPLINK)hwnd; hwnd = NULL; type &= ~MB_NOFOCUS; } else if (hwnd) ppl = ((PPROPLINK)GetWindowLongPtr(hwnd, DWLP_USER))->ppl; if (id == IDS_ERROR + ERROR_NOT_ENOUGH_MEMORY) lpchFmt = TEXT(""); else { if (!LoadString(g_hinst, id, achFmt, MAX_STRING_SIZE)) { ASSERTFAIL(); lpchFmt = TEXT(""); } } va_start(ArgList,type); wvnsprintf(achBuf, MAX_STRING_SIZE, lpchFmt, ArgList); va_end(ArgList); lpchFmt = NULL; if (ppl) { ASSERTTRUE(ppl->iSig == PROP_SIG); if (!(lpchFmt = ppl->lpszTitle)) lpchFmt = ppl->szPathName+ppl->iFileName; } return MessageBox(hwnd, achBuf, lpchFmt, type); } #undef achFmt int MemoryWarning(HWND hwnd) { FunctionName(MemoryWarning); return Warning(hwnd, IDS_ERROR + ERROR_NOT_ENOUGH_MEMORY, MB_ICONEXCLAMATION | MB_OK); } LPTSTR LoadStringSafe(HWND hwnd, UINT id, LPTSTR lpsz, int cchsz) { FunctionName(LoadStringSafe); if (!LoadString(g_hinst, id, lpsz, cchsz)) { ASSERTFAIL(); if (hwnd) { MemoryWarning(hwnd); return NULL; } lpsz = TEXT(""); } return lpsz; } /** SetDlgBits - Check various dialog checkboxes according to given flags * * INPUT * hDlg = HWND of dialog box * pbinf -> array of bitinfo descriptors * cbinf = size of array * wFlags = flags * * OUTPUT * Returns NOTHING */ void SetDlgBits(HWND hDlg, PBINF pbinf, UINT cbinf, WORD wFlags) { FunctionName(SetDlgBits); ASSERTTRUE(cbinf > 0); do { ASSERTTRUE((pbinf->bBit & 0x3F) < 16); CheckDlgButton(hDlg, pbinf->id, !!(wFlags & (1 << (pbinf->bBit & 0x3F))) == !(pbinf->bBit & 0x80)); } while (++pbinf, --cbinf); } /** GetDlgBits - Set various flags according to dialog checkboxes * * INPUT * hDlg = HWND of dialog box * pbinf -> array of bitinfo descriptors * cbinf = size of array * lpwFlags -> flags word * * OUTPUT * Returns NOTHING */ void GetDlgBits(HWND hDlg, PBINF pbinf, UINT cbinf, LPWORD lpwFlags) { WORD wFlags; FunctionName(GetDlgBits); ASSERTTRUE(cbinf > 0); wFlags = *lpwFlags; do { ASSERTTRUE((pbinf->bBit & 0x3F) < 16); if (pbinf->bBit & 0x40) // 0x40 is a special bit mask continue; // that means "set but don't get // this control's value" wFlags &= ~(1 << (pbinf->bBit & 0x3F)); if (!!IsDlgButtonChecked(hDlg, pbinf->id) == !(pbinf->bBit & 0x80)) wFlags |= (1 << (pbinf->bBit & 0x3F)); } while (++pbinf, --cbinf); *lpwFlags = wFlags; } /** SetDlgInts - Set various edit controls according to integer fields * * INPUT * hDlg = HWND of dialog box * pvinf -> array of validation info descriptors * cvinf = size of array * lp -> structure of integers * * OUTPUT * Returns NOTHING */ void SetDlgInts(HWND hDlg, PVINF pvinf, UINT cvinf, LPVOID lp) { WORD wMin, wMax; FunctionName(SetDlgInts); ASSERTTRUE(cvinf > 0); do { wMin = wMax = *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off); if (pvinf->fbOpt & VINF_AUTO) { SendDlgItemMessage(hDlg, pvinf->id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)g_szAuto); AddDlgIntValues(hDlg, pvinf->id, pvinf->iMax); if (wMin == 0) { SetDlgItemText(hDlg, pvinf->id, g_szAuto); continue; } } if (pvinf->fbOpt & VINF_AUTOMINMAX) { SendDlgItemMessage(hDlg, pvinf->id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)g_szAuto); SendDlgItemMessage(hDlg, pvinf->id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)g_szNone); AddDlgIntValues(hDlg, pvinf->id, pvinf->iMax); // When AUTOMINMAX is set, we assume that the field // we're validating is followed in its structure by a // corresponding max WORD. wMax = *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off + sizeof(WORD)); if (wMin == 0 && wMax == 0) { SetDlgItemText(hDlg, pvinf->id, g_szNone); continue; } // Let's try to simplify things by mapping 0xFFFF (aka -1) // to settings that mean "Auto" if (wMin == 0xFFFF || wMax == 0xFFFF) { wMin = 0; wMax = (WORD)pvinf->iMax; } if (wMax == (WORD)pvinf->iMax) { SetDlgItemText(hDlg, pvinf->id, g_szAuto); continue; } if (wMin != wMax) { // // We're in a bit of a quandary here. The settings show // explicit min and max values which are not equal, probably // due to settings inherited from a 3.1 PIF file. We'll // just go with the wMax value. Fortunately for us, we // don't actually have to *do* anything to make this happen. // } } SetDlgItemInt(hDlg, pvinf->id, wMin, pvinf->iMin < 0); } while (++pvinf, --cvinf); } /** AddDlgIntValues - Fill integer combo-box with appropriate values * * INPUT * hDlg = HWND of dialog box * id = dialog control ID * iMax = maximum value * * OUTPUT * Returns NOTHING */ void AddDlgIntValues(HWND hDlg, int id, int iMax) { int iStart, iInc; TCHAR achValue[16]; // HACK to make this do something sensible with the environment max; // they can still enter larger values (up to ENVSIZE_MAX) but I don't // see any sense in encouraging it. -JTP if ((WORD)iMax == ENVSIZE_MAX) iMax = 4096; if ((iMax < 0) || (iMax == 0xFFFF)) // HACK to make this do something sensible iMax = 16384; // with fields that allow huge maximums -JTP iStart = iInc = iMax/16; // arbitrarily chop the range up 16 times while (iStart <= iMax) { StringCchPrintf(achValue, ARRAYSIZE(achValue), TEXT("%d"), iStart); // ok to truncate SendDlgItemMessage(hDlg, id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)achValue); iStart += iInc; } } /** GetDlgInts - Set various integer fields according to dialog edit controls * * INPUT * hDlg = HWND of dialog box * pvinf -> array of validation info descriptors * cvinf = size of array * lp -> structure of integers * * OUTPUT * Returns NOTHING */ void GetDlgInts(HWND hDlg, PVINF pvinf, int cvinf, LPVOID lp) { WORD wMin, wMax; UINT uTemp; BOOL fSuccess; TCHAR achText[32]; FunctionName(GetDlgInts); ASSERTTRUE(cvinf > 0); do { uTemp = GetDlgItemInt(hDlg, pvinf->id, &fSuccess, pvinf->iMin < 0); ASSERT(HIWORD(uTemp)==0); wMin = LOWORD(uTemp); // In case of error, make sure wMin doesn't actually change if (!fSuccess) wMin = *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off); if (pvinf->fbOpt & VINF_AUTO) { GetDlgItemText(hDlg, pvinf->id, achText, ARRAYSIZE(achText)); if (lstrcmpi(achText, g_szAuto) == 0) { wMin = 0; } } if (pvinf->fbOpt & VINF_AUTOMINMAX) { // When AUTOMINMAX is set, we assume that the field // we're validating is followed in its structure by a // corresponding max WORD, which we will ZERO if the // user selects NONE, or set to its MAXIMUM if the user // selects AUTO, or otherwise set to match the specified // MINIMUM. wMax = wMin; GetDlgItemText(hDlg, pvinf->id, achText, ARRAYSIZE(achText)); if (lstrcmpi(achText, g_szAuto) == 0) { wMin = 0; wMax = (WORD)pvinf->iMax; } else if (lstrcmpi(achText, g_szNone) == 0) { wMin = 0; wMax = 0; } *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off + sizeof(WORD)) = wMax; } *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off) = wMin; } while (++pvinf, --cvinf); } /** ValidateDlgInts - Validate that integer fields are value * * INPUT * hDlg = HWND of dialog box * pvinf -> array of validation descriptors * cvinf = size of array * * OUTPUT * Returns TRUE if something is wrong; FALSE if all is okay. */ BOOL ValidateDlgInts(HWND hDlg, PVINF pvinf, int cvinf) { DWORD dw; BOOL fSuccess; TCHAR achText[32]; FunctionName(ValidateDlgInts); ASSERTTRUE(cvinf > 0); do { dw = GetDlgItemInt(hDlg, pvinf->id, &fSuccess, pvinf->iMin < 0); // NOTE: AUTO is for "Auto" only, whereas AUTOMINMAX is for // "Auto" and "None". However, in the interest of simplicity, I // don't complain if either string is used in either case. if (pvinf->fbOpt & (VINF_AUTO | VINF_AUTOMINMAX)) { if (!fSuccess) { GetDlgItemText(hDlg, pvinf->id, achText, ARRAYSIZE(achText)); if (lstrcmpi(achText, g_szNone) == 0 || lstrcmpi(achText, g_szAuto) == 0) { continue; // things be lookin' good, check next int... } } } if (!fSuccess || dw < (DWORD)pvinf->iMin || dw > (DWORD)pvinf->iMax) { Warning(hDlg, pvinf->idMsg, MB_ICONEXCLAMATION | MB_OK, pvinf->iMin, pvinf->iMax); SendDlgItemMessage(hDlg, pvinf->id, EM_SETSEL, 0, MAKELPARAM(0,-1)); SetFocus(GetDlgItem(hDlg, pvinf->id)); return TRUE; // things be lookin' bad, bail out... } } while (++pvinf, --cvinf); return FALSE; } /* * NOTE -- The compiler emits really bad code for some of these guys. * In those cases, we are merely wrapping a call; there is no need to save BP. */ /** LimitDlgItemText - Sets the limit for a dialog edit control * * INPUT * hDlg = HWND of dialog box * iCtl = ID of control * uiLimit = text limit * * OUTPUT * None. */ void LimitDlgItemText(HWND hDlg, int iCtl, UINT uiLimit) { FunctionName(LimitDlgItemText); SendDlgItemMessage(hDlg, iCtl, EM_LIMITTEXT, uiLimit, 0); } /** SetDlgItemPosRange - Sets the pos and range for a dialog slider control * * INPUT * hDlg = HWND of dialog box * iCtl = ID of control * uiPos = Current position * dwRange = Range (min in low word, max in high word) * * OUTPUT * None. */ void SetDlgItemPosRange(HWND hDlg, int iCtl, UINT uiPos, DWORD dwRange) { FunctionName(SetDlgItemPosRange); SendDlgItemMessage(hDlg, iCtl, TBM_SETRANGE, 0, dwRange); SendDlgItemMessage(hDlg, iCtl, TBM_SETPOS, TRUE, uiPos); } /** GetDlgItemPos - Gets the pos of a dialog slider control * * INPUT * hDlg = HWND of dialog box * iCtl = ID of control * * OUTPUT * Trackbar position. */ UINT GetDlgItemPos(HWND hDlg, int iCtl) { FunctionName(GetDlgItemPos); return (UINT)SendDlgItemMessage(hDlg, iCtl, TBM_GETPOS, 0, 0); } /** SetDlgItemPct - Sets the pos for a dialog slider control that measures % * * INPUT * hDlg = HWND of dialog box * iCtl = ID of control * uiPct = Current position (range 0 .. 100) * * OUTPUT * None. */ void SetDlgItemPct(HWND hDlg, int iCtl, UINT uiPct) { FunctionName(SetDlgItemPct); SetDlgItemPosRange(hDlg, iCtl, uiPct / (100/NUM_TICKS), MAKELONG(0, NUM_TICKS)); } /** GetDlgItemPct - Gets the pos of a dialog slider control that measures % * * INPUT * hDlg = HWND of dialog box * iCtl = ID of control * * OUTPUT * Slider position in the range 0 .. 100. */ UINT GetDlgItemPct(HWND hDlg, int iCtl) { FunctionName(GetDlgItemPct); return GetDlgItemPos(hDlg, iCtl) * (100/NUM_TICKS); } /** AdjustRealModeControls - Disables selected items if single-app mode * * If the proplink says that "single-application mode" is enabled, * then hide all controls whose IDs are less than 4000 and show all * controls whose IDs are greater than or equal to 5000. Controls whose * IDs are in the 4000's are immune to all this hiding/showing. Controls * in the 3000's are actually disabled rather than hidden. Controls in * the 6000's are actually disabled rather than hidden as well. * * RST: Ok, this is nice in theory, but now that we've pulled over this * stuff into shell32.dll, we'll have to go off the actual IDC_ * defines instead of the magic #'s of 3000, 4000 and 5000. * * IDC_ICONBMP == 3001 * IDC_PIF_STATIC == 4000 * IDC_REALMODEISABLE == 5001 * * So, when adding things to shell232.rc or ids.h, plan * accordingly. * * INPUT * ppl = proplink * hDlg = HWND of dialog box * * OUTPUT * Dialog items have been disabled/enabled shown/hidden. * Returns nonzero if we are in normal (not single-app) mode. */ BOOL CALLBACK EnableEnumProc(HWND hwnd, LPARAM lp) { int f; LONG l; f = SW_SHOW; l = GetWindowLong(hwnd, GWL_ID); if (!LOWORD(lp) && l < IDC_PIF_STATIC || LOWORD(lp) && l >= IDC_REALMODEDISABLE) f = SW_HIDE; if (l < IDC_ICONBMP || l >= IDC_PIF_STATIC && l < IDC_CONFIGLBL) ShowWindow(hwnd, f); else EnableWindow(hwnd, f == SW_SHOW); return TRUE; } BOOL AdjustRealModeControls(PPROPLINK ppl, HWND hDlg) { BOOL fNormal; FunctionName(AdjustRealModeControls); fNormal = !(ppl->flProp & PROP_REALMODE); EnumChildWindows(hDlg, EnableEnumProc, fNormal); return fNormal; } /** OnWmHelp - Handle a WM_HELP message * * This is called whenever the user presses F1 or clicks the help * button in the title bar. We forward the call on to the help engine. * * INPUT * lparam = LPARAM from WM_HELP message (LPHELPINFO) * pdwHelp = array of DWORDs of help info * * OUTPUT * * None. */ void OnWmHelp(LPARAM lparam, const DWORD *pdwHelp) { FunctionName(OnWmHelp); WinHelp((HWND) ((LPHELPINFO) lparam)->hItemHandle, NULL, HELP_WM_HELP, (DWORD_PTR) (LPTSTR) pdwHelp); } /** OnWmContextMenu - Handle a WM_CONTEXTMENU message * * This is called whenever the user right-clicks on a control. * We forward the call on to the help engine. * * INPUT * wparam = WPARAM from WM_HELP message (HWND) * pdwHelp = array of DWORDs of help info * * OUTPUT * * None. */ void OnWmContextMenu(WPARAM wparam, const DWORD *pdwHelp) { FunctionName(OnWmContextMenu); WinHelp((HWND) wparam, NULL, HELP_CONTEXTMENU, (DWORD_PTR) (LPTSTR) pdwHelp); } #ifdef UNICODE /** PifMgr_WCtoMBPath - Converts UNICODE path to it's ANSI representation * * This is called whenever we need to convert a UNICODE path to it's * best approximation in ANSI. Sometimes this will be a direct mapping, * but sometimes not. We may have to use the short name, etc. * * INPUT * lpUniPath -> pointer UNICODE path (NULL terminated) * lpAnsiPath -> pointer to buffer to hold ANSI path * cchBuf -> size of ANSI buffer, in characters * * OUTPUT * * lpAnsiPath buffer contains ANSI representation of lpUniPath */ void PifMgr_WCtoMBPath(LPWSTR lpUniPath, LPSTR lpAnsiPath, UINT cchBuf ) { WCHAR awchPath[ MAX_PATH ]; // Should be bigger than any PIF string CHAR achPath[ MAX_PATH ]; // Should be bigger than any PIF string UINT cchAnsi = 0; HRESULT hr; FunctionName(PifMgr_WCtoMBPath); // Try converting to Ansi and then converting back and comparing. // If we get back exactly what we started with, this is the "simple" // case. cchAnsi = WideCharToMultiByte( CP_ACP, 0, lpUniPath, -1, achPath, ARRAYSIZE(achPath), NULL, NULL ); if (cchAnsi && (cchAnsi<=cchBuf)) { // Now try converting back MultiByteToWideChar( CP_ACP, 0, achPath, -1, awchPath, ARRAYSIZE(awchPath) ); if (lstrcmp(lpUniPath,awchPath)==0) { // We're done...copy over the string. hr = StringCchCopyA( lpAnsiPath, cchBuf, achPath ); if (FAILED(hr)) { *lpAnsiPath = '\0'; } return; } // Well, the string has some unmappable UNICODE // character in it, so try option #2 -- using the // short path name. goto TryShortPathName; } else { int cch; TryShortPathName: // Hmmm, the best we can do is to use the short path name and map // it to ANSI. cch = GetShortPathName(lpUniPath, awchPath, ARRAYSIZE(awchPath)); if (cch == 0 || cch >= ARRAYSIZE(awchPath)) { *lpAnsiPath = '\0'; return; } cch = WideCharToMultiByte( CP_ACP, 0, awchPath, -1, lpAnsiPath, cchBuf, NULL, NULL ); if (cch == 0) { *lpAnsiPath = '\0'; return; } } } #endif #endif // X86