shdup.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 18k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //
  2. // Functions in shell32 that did not exist in Win95, or which exist
  3. // on NT differently from Win95.
  4. //
  5. #include "priv.h"
  6. #include "unicppdutil.h"
  7. const TCHAR c_szWallpaper[] = TEXT("Wallpaper");
  8. #ifdef UNICODE
  9. #error This file assumes that shell32 is ANSI.
  10. #endif
  11. // Note that the OLESTR gets freed, so don't try to use it later
  12. BOOL WINAPI StrRetToStrN(LPSTR szOut, UINT uszOut, LPSTRRET pStrRet, LPCITEMIDLIST pidl)
  13. {
  14.     switch (pStrRet->uType)
  15.     {
  16.     case STRRET_WSTR:
  17.         SHUnicodeToAnsi(pStrRet->pOleStr, szOut, uszOut);
  18.         SHFree(pStrRet->pOleStr);
  19.         break;
  20.     case STRRET_CSTR:
  21.         lstrcpyn(szOut, pStrRet->cStr, uszOut);
  22.         break;
  23.     case STRRET_OFFSET:
  24.         if (pidl)
  25.         {
  26.             ualstrcpyn(szOut, STRRET_OFFPTR(pidl,pStrRet), uszOut);
  27.             break;
  28.         }
  29.         goto punt;
  30.     default:
  31.         ASSERT( FALSE && "Bad STRRET uType");
  32. punt:
  33.         if (uszOut)
  34.         {
  35.             *szOut = TEXT('');
  36.         }
  37.         return(FALSE);
  38.     }
  39.     return(TRUE);
  40. }
  41. //
  42. // Win95 did not support the CSIDL_FLAG_CREATE flag, so we have to use
  43. // the secret SHCloneSpecialIDList function.
  44. //
  45. STDAPI_(BOOL) SHGetSpecialFolderPath(HWND hwnd, LPTSTR pszPath, int nFolder, BOOL fCreate)
  46. {
  47.     LPITEMIDLIST pidl;
  48.     *pszPath = 0;
  49.     pidl = SHCloneSpecialIDList(hwnd, nFolder, fCreate);
  50.     if (pidl) {
  51.         BOOL fRet = SHGetPathFromIDList(pidl, pszPath);
  52.         ILFree(pidl);
  53.         return fRet;
  54.     }
  55.     return FALSE;
  56. }
  57. STDAPI_(BOOL) ILGetDisplayNameEx(IShellFolder *psfRoot, LPCITEMIDLIST pidl, LPTSTR pszName, int fType)
  58. {
  59.     STRRET srName;
  60.     DWORD dwGDNFlags;
  61.     if (!pszName)
  62.         return FALSE;
  63.     *pszName = 0;
  64.     if (!pidl)
  65.         return FALSE;
  66.     // no root specified, get the desktop folder as the default
  67.     if (!psfRoot)
  68.     {
  69.         // We never release it, but that's okay, because the shell never
  70.         // destroys it.
  71.         SHGetDesktopFolder(&psfRoot);
  72.         ASSERT(psfRoot);
  73.         if (psfRoot == NULL)
  74.             return FALSE;
  75.     }
  76.     switch (fType)
  77.     {
  78.     case ILGDN_FULLNAME:
  79.         dwGDNFlags = SHGDN_FORPARSING | SHGDN_FORADDRESSBAR;
  80. SingleLevelPidl:
  81.         if (SUCCEEDED(psfRoot->GetDisplayNameOf(pidl, dwGDNFlags, &srName)))
  82.         {
  83.             StrRetToStrN(pszName, MAX_PATH, &srName, pidl);
  84.             return TRUE;
  85.         }
  86.         break;
  87.     case ILGDN_INFOLDER:
  88.     case ILGDN_ITEMONLY:
  89.         dwGDNFlags = fType == ILGDN_INFOLDER ? SHGDN_INFOLDER : SHGDN_NORMAL;
  90.         if (!ILIsEmpty(pidl))
  91.         {
  92.             LPCITEMIDLIST pidlLast = ILFindLastID(pidl);
  93.             if (pidlLast != pidl)
  94.             {
  95.                 LPITEMIDLIST pidlParent = ILClone(pidl);
  96.                 if (pidlParent)
  97.                 {
  98.                     BOOL bRet = FALSE;
  99.                     IShellFolder *psfParent;
  100.                     ILRemoveLastID(pidlParent);   // strip to parent of item
  101.                     if (SUCCEEDED(psfRoot->BindToObject(pidlParent, NULL, IID_IShellFolder, (LPVOID *)&psfParent)))
  102.                     {
  103.                         if (SUCCEEDED(psfParent->GetDisplayNameOf(pidlLast, dwGDNFlags, &srName)))
  104.                         {
  105.                             StrRetToStrN(pszName, MAX_PATH, &srName, pidlLast);
  106.                             bRet = TRUE;
  107.                         }
  108.                         psfParent->Release();
  109.                     }
  110.                     ILFree(pidlParent);
  111.                     return bRet;
  112.                 }
  113.                 return FALSE;       // out of memory
  114.             }
  115.         }
  116.         goto SingleLevelPidl;
  117.         break;
  118.     }
  119.     return FALSE;
  120. }
  121. STDAPI_(LPITEMIDLIST) ILCreateFromPathW(IN LPCWSTR pszPath)
  122. {
  123.     WCHAR wszPath[MAX_PATH];
  124.     LPITEMIDLIST pidl = NULL;
  125.     IShellFolder *psfRoot;
  126.     HRESULT hres;
  127.     ULONG cchEaten;
  128.     // Sigh.  ParseDisplayName requires a writable input buffer.
  129.     StrCpyNW(wszPath, pszPath, ARRAYSIZE(wszPath));
  130.     // We never release it, but that's okay, because the shell never
  131.     // destroys it.
  132.     hres = SHGetDesktopFolder(&psfRoot);
  133.     if (SUCCEEDED(hres)) {
  134.         hres = psfRoot->ParseDisplayName(NULL, NULL, wszPath, &cchEaten, &pidl, NULL);
  135.     }
  136.     ASSERT(SUCCEEDED(hres) ? pidl != NULL : pidl == NULL);
  137.     return pidl;
  138. }
  139. HRESULT Invoke_OnConnectionPointerContainer(IUnknown * punk, REFIID riidCP, DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
  140. {
  141.     HRESULT hr = S_OK;     // Assume no errors.
  142.     IConnectionPointContainer * pcpc; 
  143.     hr = punk->QueryInterface(IID_IConnectionPointContainer, (LPVOID*)&pcpc);
  144.     if (EVAL(SUCCEEDED(hr)))
  145.     {
  146.         IConnectionPoint * pcp;
  147.         hr = pcpc->FindConnectionPoint(riidCP, &pcp);
  148.         if (EVAL(SUCCEEDED(hr)))
  149.         {
  150.             IEnumConnections * pec;
  151.             hr = pcp->EnumConnections(&pec);
  152.             if (EVAL(SUCCEEDED(hr)))
  153.             {
  154.                 CONNECTDATA cd;
  155.                 ULONG cFetched;
  156.                 while (S_OK == (hr = pec->Next(1, &cd, &cFetched)))
  157.                 {
  158.                     LPDISPATCH pdisp;
  159.                     ASSERT(1 == cFetched);
  160.                     hr = cd.pUnk->QueryInterface(IID_IDispatch, (LPVOID *) &pdisp);
  161.                     if (EVAL(SUCCEEDED(hr)))
  162.                     {
  163.                         DISPPARAMS dispparams = {0};
  164.                         
  165.                         if (!pdispparams)
  166.                             pdispparams = &dispparams;
  167.                         hr = pdisp->Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  168.                         pdisp->Release();
  169.                     }
  170.                 }
  171.                 pec->Release();
  172.             }
  173.             pcp->Release();
  174.         }
  175.         pcpc->Release();
  176.     }
  177.     return hr;
  178. }
  179. //
  180. // helper function for pulling ITypeInfo out of our typelib
  181. //
  182. HRESULT Shell32GetTypeInfo(LCID lcid, UUID uuid, ITypeInfo **ppITypeInfo)
  183. {
  184.     HRESULT    hr;
  185.     ITypeLib  *pITypeLib;
  186.     // Just in case we can't find the type library anywhere
  187.     *ppITypeInfo = NULL;
  188.     /*
  189.      * The type libraries are registered under 0 (neutral),
  190.      * 7 (German), and 9 (English) with no specific sub-
  191.      * language, which would make them 407 or 409 and such.
  192.      * If you are sensitive to sub-languages, then use the
  193.      * full LCID instead of just the LANGID as done here.
  194.      */
  195.     hr = LoadRegTypeLib(LIBID_Shell32, 1, 0, PRIMARYLANGID(lcid), &pITypeLib);
  196.     /*
  197.      * If LoadRegTypeLib fails, try loading directly with
  198.      * LoadTypeLib, which will register the library for us.
  199.      * Note that there's no default case here because the
  200.      * prior switch will have filtered lcid already.
  201.      *
  202.      * NOTE:  You should prepend your DIR registry key to the
  203.      * .TLB name so you don't depend on it being it the PATH.
  204.      * This sample will be updated later to reflect this.
  205.      */
  206.     if (FAILED(hr))
  207.     {
  208.         OLECHAR wszPath[MAX_PATH];
  209. #ifdef UNICODE
  210.         GetModuleFileName(HINST_THISDLL, wszPath, ARRAYSIZE(wszPath));
  211. #else
  212.         TCHAR szPath[MAX_PATH];
  213.         GetModuleFileName(HINST_THISDLL, szPath, ARRAYSIZE(szPath));
  214.         MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, ARRAYSIZE(wszPath));
  215. #endif
  216.         switch (PRIMARYLANGID(lcid))
  217.         {
  218.         case LANG_NEUTRAL:
  219.         case LANG_ENGLISH:
  220.             hr=LoadTypeLib(wszPath, &pITypeLib);
  221.             break;
  222.         }
  223.     }
  224.     if (SUCCEEDED(hr))
  225.     {
  226.         //Got the type lib, get type info for the interface we want
  227.         hr=pITypeLib->GetTypeInfoOfGuid(uuid, ppITypeInfo);
  228.         pITypeLib->Release();
  229.     }
  230.     return(hr);
  231. }
  232. STDAPI_(DWORD)
  233. SHWNetGetConnection(LPCTSTR lpLocalName, LPTSTR lpRemoteName, LPDWORD lpnLength)
  234. {
  235.     TCHAR szLocalName[3];
  236.     if (lpLocalName && lstrlen(lpLocalName) > 2)
  237.     {
  238.         // Kludge allert, don't pass c: to API, instead only pass C:
  239.         szLocalName[0] = lpLocalName[0];
  240.         szLocalName[1] = TEXT(':');
  241.         szLocalName[2] = 0;
  242.         lpLocalName = szLocalName;
  243.     }
  244.     return WNetGetConnection(lpLocalName, lpRemoteName, lpnLength);
  245. }
  246. void *  __cdecl operator new( unsigned int nSize )
  247. {
  248.     LPVOID pv; 
  249.     // Zero init just to save some headaches
  250.     pv = ((LPVOID)LocalAlloc(LPTR, nSize));
  251.     return pv;
  252. }
  253. void  __cdecl operator delete(void *pv)
  254. {
  255.     if (pv) {
  256. #ifdef DEBUG
  257.         memset(pv, 0xfe, LocalSize((HLOCAL)pv));
  258. #endif
  259.         LocalFree((HLOCAL)pv);
  260.     }
  261. }
  262. // Comdlg32 stuff
  263. HINSTANCE s_hmodComdlg32 = NULL;
  264. PFNGETOPENFILENAME g_pfnGetOpenFileName = NULL;
  265. void Comdlg32DLL_Term()
  266. {
  267.     if (s_hmodComdlg32) {
  268.         FreeLibrary(s_hmodComdlg32);
  269.         s_hmodComdlg32 = NULL;
  270.         g_pfnGetOpenFileName = NULL;
  271.     }
  272. }
  273. BOOL Comdlg32DLL_Init(void)
  274. {
  275.     if (NULL != s_hmodComdlg32)
  276.         return(TRUE);       // already loaded.
  277.     s_hmodComdlg32 = LoadLibrary(TEXT("comdlg32.dll"));
  278.     if (!s_hmodComdlg32)
  279.     {
  280.         s_hmodComdlg32 = NULL;       // make sure it is NULL
  281.         return(FALSE);
  282.     }
  283. #ifdef UNICODE
  284.     g_pfnGetOpenFileName = (PFNGETOPENFILENAME)GetProcAddress(s_hmodComdlg32,
  285.             "GetOpenFileNameW");
  286. #else
  287.     g_pfnGetOpenFileName = (PFNGETOPENFILENAME)GetProcAddress(s_hmodComdlg32,
  288.             "GetOpenFileNameA");
  289. #endif
  290.     if (!g_pfnGetOpenFileName)
  291.     {
  292.         // BUGBUG: The next call to Comdlg32_Init will incorrectly return TRUE
  293.         ASSERT(FALSE);
  294.         Comdlg32DLL_Term();    // Free our usage of the DLL for now...
  295.         return(FALSE);
  296.     }
  297.     return TRUE;
  298. }
  299. BOOL  APIENTRY GetOpenFileName(LPOPENFILENAME pofn)
  300. {
  301.     if (Comdlg32DLL_Init()) {
  302.         return g_pfnGetOpenFileName(pofn);
  303.     } else {
  304.         return FALSE;
  305.     }
  306. }
  307. #ifdef DEBUG
  308. LRESULT WINAPI SendMessageD( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
  309. {
  310.     ASSERTNONCRITICAL;
  311. #ifdef UNICODE
  312.     return SendMessageW(hWnd, Msg, wParam, lParam);
  313. #else
  314.     return SendMessageA(hWnd, Msg, wParam, lParam);
  315. #endif
  316. }
  317. #endif // DEBUG
  318. #ifdef DEBUG
  319. BOOL  g_bInDllEntry;
  320. #endif
  321. // Normally we'd just GetProcAddress from KERNEL32.  However,
  322. // kernel prohibits anyone from getting a no-named export from
  323. // itself.  So we've added a no-named export to SHELL32 which
  324. // will call directly into GetProcessDword on the Win95 platform.
  325. STDAPI_(DWORD)
  326. GetProcessDword(DWORD idProcess, LONG iIndex)
  327. {
  328.     DWORD dwRet;
  329.     if (g_fRunningOnNT) {
  330.         dwRet = 0;
  331.     } else {
  332.         dwRet = SHGetProcessDword(idProcess, iIndex);
  333.     }
  334.     return dwRet;
  335. }
  336. // Copied from shell32, which does not export this.
  337. // The fsmenu code needs this function.
  338. STDAPI_(LPITEMIDLIST) _ILCreate(UINT cbSize)
  339. {
  340.     LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbSize);
  341.     if (pidl)
  342.         memset(pidl, 0, cbSize);      // needed for external task allicator
  343.     return pidl;
  344. }
  345. //===========================
  346. // BUGBUG -- This is dup'd in shell32 and shdocvw, each one differently!
  347. // Put a common version into shlwapi and call it a day.
  348. #define     SZAPPNAME   TEXT("Explorer")
  349. #define     SZDEFAULT   TEXT(".Default")
  350. void IEPlaySound(LPCTSTR pszSound, BOOL fSysSound)
  351. {
  352.     TCHAR szKey[256];
  353.     // check the registry first
  354.     // if there's nothing registered, we blow off the play,
  355.     // but we don't set the MM_DONTLOAD flag so that if they register
  356.     // something we will play it
  357.     wsprintf(szKey, TEXT("AppEvents\Schemes\Apps\%s\%s\.current"), 
  358.         (fSysSound ? SZDEFAULT : SZAPPNAME), pszSound);
  359.     TCHAR szFileName[MAX_PATH];
  360.     szFileName[0] = 0;
  361.     LONG cbSize = SIZEOF(szFileName);
  362.     // note the test for an empty string, PlaySound will play the Default Sound if we
  363.     // give it a sound it cannot find...
  364.     if ((RegQueryValue(HKEY_CURRENT_USER, szKey, szFileName, &cbSize) == ERROR_SUCCESS) 
  365.         && cbSize && szFileName[0] != 0) 
  366.     {
  367.         //
  368.         // Unlike SHPlaySound in shell32.dll, we get the registry value
  369.         // above and pass it to PlaySound with SND_FILENAME instead of
  370.         // SDN_APPLICATION, so that we play sound even if the application
  371.         // is not Explroer.exe (such as IExplore.exe or WebBrowserOC).
  372.         //
  373.         PlaySound(szFileName, NULL, SND_FILENAME | SND_ASYNC);
  374.     }
  375. }
  376. //===========================================================================
  377. //
  378. //  Functions that accept TCHAR in shell32, which means that we have to
  379. //  convert from our TCHAR into whatever character set the shell is using.
  380. //
  381. //  WARNING!  We assume that all input string buffers are mo larger than
  382. //  MAX_PATH!  And that all output buffers are exactly MAX_PATH in length!
  383. //
  384. //
  385. //  "SCHAR" is the opposite of "TCHAR".  If we are ANSI, then SCHAR is
  386. //  UNICODE, and vice versa.
  387. //
  388. //  "SHCHAR" is the character set the shell is using.
  389. //
  390. #ifdef UNICODE
  391. typedef  CHAR SCHAR;
  392. #define  SHTCharToSChar     SHTCharToAnsi
  393. #define  SCharToTChar       SHAnsiToTChar
  394. #define  ShellIsTChar()     g_fRunningOnNT
  395. #else
  396. typedef WCHAR SCHAR;
  397. #define  SHTCharToSChar     SHTCharToUnicode
  398. #define  SCharToTChar       SHUnicodeToTChar
  399. #define  ShellIsTChar()     !g_fRunningOnNT
  400. #endif
  401. typedef union {
  402.     SCHAR ssz[MAX_PATH];
  403.     TCHAR tsz[1];
  404. } STRINGCONVERT;
  405. //
  406. //  Convert a TCHAR to an SHCHAR.  Some APIs allow NULL, so we let those
  407. //  go clean through.
  408. //
  409. //  We must cast away const-ness because we don't know whether our incoming
  410. //  parameter was const or non-const.
  411. //
  412. LPTSTR TCharToShChar(LPCTSTR ptszSrc, STRINGCONVERT *psc)
  413. {
  414.     if (ShellIsTChar() || ptszSrc == NULL) {
  415.         return (LPTSTR)ptszSrc;
  416.     } else {
  417.         SHTCharToSChar(ptszSrc, psc->ssz, ARRAYSIZE(psc->ssz));
  418.         return psc->tsz;
  419.     }
  420. }
  421. void ShCharToTChar(LPTSTR ptszSrc, STRINGCONVERT *psc)
  422. {
  423.     if (ShellIsTChar()) {
  424.     } else {
  425.         SCharToTChar(psc->ssz, ptszSrc, ARRAYSIZE(psc->ssz));
  426.     }
  427. }
  428. // Quickie macro which means "Convert to/from the shell character set".
  429. // C = Convert, DC = DeConvert
  430. // Only one parameter can use this macro, since we have only one
  431. // conversion buffer.
  432. #define C(s)        TCharToShChar(s, &sc)
  433. #define DC(s)       ShCharToTChar(s, &sc)
  434. //
  435. //  C2 and DC2 do the same thing to our second conversion buffer.
  436. //  Similary for 3 and 4 (for APIs that have that many strings).
  437. #define C2(s)       TCharToShChar(s, &sc2)
  438. #define DC2(s)      ShCharToTChar(s, &sc2)
  439. #define C3(s)       TCharToShChar(s, &sc3)
  440. #define DC3(s)      ShCharToTChar(s, &sc3)
  441. #define C4(s)       TCharToShChar(s, &sc4)
  442. #define DC4(s)      ShCharToTChar(s, &sc4)
  443. // Delay-load-like macro that does all the grunky work.
  444. // CSETTHUNK assumes that the Shell32 name is already declared elsewhere.
  445. // CSETTHUNKDECL will put out our own private declaration because nobody
  446. // declared it yet.
  447. #define CSETTHUNK(retval, fn, parg, arg) 
  448.     STDAPI_(retval) _##fn parg           
  449.     {                                    
  450.         STRINGCONVERT sc;                
  451.         return fn arg;                   
  452.     }                                    
  453. #define CSETTHUNKDECL(retval, fn, parg, arg) 
  454.     STDAPI_(retval)    fn parg;          
  455.     STDAPI_(retval) _##fn parg           
  456.     {                                    
  457.         STRINGCONVERT sc;                
  458.         return fn arg;                   
  459.     }                                    
  460. #undef Shell_GetCachedImageIndex
  461. CSETTHUNK(
  462. int, Shell_GetCachedImageIndex,
  463.         (LPCTSTR pszIconPath, int iIconIndex, UINT uIconFlags),
  464.         (      C(pszIconPath),    iIconIndex,      uIconFlags));
  465. #undef Win32DeleteFile
  466. CSETTHUNK(
  467. BOOL, Win32DeleteFile,
  468.         (LPCTSTR pszFile),
  469.         (      C(pszFile)));
  470. #undef IsLFNDrive
  471. CSETTHUNKDECL(
  472. BOOL, IsLFNDrive,
  473.         (LPCTSTR pszPath),
  474.         (      C(pszPath)));
  475. #undef ILCreateFromPath
  476. CSETTHUNKDECL(
  477. LPITEMIDLIST, ILCreateFromPath,
  478.         (LPCTSTR pszPath),
  479.         (      C(pszPath)));
  480. #undef SHSimpleIDListFromPath
  481. CSETTHUNK(
  482. LPITEMIDLIST, SHSimpleIDListFromPath,
  483.         (LPCTSTR pszPath),
  484.         (      C(pszPath)));
  485. #undef SHRunControlPanel
  486. CSETTHUNK(
  487. int, SHRunControlPanel,
  488.         (LPCTSTR pszOrig_cmdline, HWND errwnd),
  489.         (      C(pszOrig_cmdline),     errwnd));
  490. //
  491. //  PathResolve is annoying because of the layers of strings it munges.
  492. //
  493. //  lpszPath is an INOUT parameter (hence gets a C and DC).
  494. //  rgpszDirs[0] is an IN parameter (so it gets a C2 and no DC2).
  495. //
  496. #undef PathResolve
  497. STDAPI_(BOOL) _PathResolve(LPTSTR lpszPath, LPCTSTR rgpszDirs[], UINT fFlags)
  498. {
  499.     STRINGCONVERT sc, sc2;
  500.     BOOL fRc;
  501.     // HACKHACK!
  502.     // We assume dirs has only one element since the only case
  503.     // this is called is in dde.cpp.  Right?
  504.     if (rgpszDirs && rgpszDirs[0]) {
  505.         rgpszDirs[0] = C2(rgpszDirs[0]); // overload the pointer to pass through...
  506.         // None of our callers ask for more than one dir, right?
  507.         if (EVAL(rgpszDirs[1] == 0)) {
  508.             rgpszDirs[1] = NULL;
  509.         }
  510.     }
  511.     fRc = PathResolve(C(lpszPath), rgpszDirs, fFlags);
  512.     DC(lpszPath);                       // Convert it back on the way out
  513.     return fRc;
  514. }
  515. //
  516. // PathYetAnotherMakeUniqueName is annoying because, well look at all
  517. // those strings!
  518. //
  519. #undef PathYetAnotherMakeUniqueName
  520. STDAPI_(BOOL)
  521. _PathYetAnotherMakeUniqueName(IN OUT LPTSTR  pszUniqueName,
  522.                               IN     LPCTSTR pszPath,
  523.                               IN     LPCTSTR pszShort,
  524.                               IN     LPCTSTR pszFileSpec)
  525. {
  526.     STRINGCONVERT sc, sc2, sc3, sc4;
  527.     BOOL fRc;
  528.     fRc = PathYetAnotherMakeUniqueName(C(pszUniqueName),
  529.                                       C2(pszPath),
  530.                                       C3(pszShort),
  531.                                       C4(pszFileSpec));
  532.     DC(pszUniqueName);              // Unconvert the OUT parameter
  533.     return fRc;
  534. }
  535. #undef PathQualify
  536. STDAPI_(void) _PathQualify(IN OUT LPTSTR pszDir)
  537. {
  538.     STRINGCONVERT sc;
  539.     PathQualify(C(pszDir));
  540.     DC(pszDir);                     // Unconvert the OUT parameter
  541. }