//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       api.cxx
//
//  Contents:   Exported APIs from this DLL
//
//  History:    5-Oct-95 BruceFo  Created
//
//--------------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop

#include "resource.h"
#include "dllmain.hxx"
#include "shrpage.hxx"
#include "shrinfo.hxx"
#include "cache.hxx"
#include "util.hxx"

//--------------------------------------------------------------------------

static BOOL WINAPI
SharingDialogHelp(
    HWND   hwndParent,
    LPWSTR pszPath          // this is 'new' memory that I take ownership of
    );

//--------------------------------------------------------------------------
// A couple macros to use a stack buffer if smaller than a default size,
// else use a heap buffer.

// Example:
//      LPWSTR pBuffer;
//      DWORD dwBufLen;
//      GET_BUFFER(pBuffer, dwBufLen, WCHAR, wcslen(pszString) + 1, MAX_PATH);
//      if (NULL == pBuffer) { return NULL; } // couldn't get the buffer
//      ... play with pBuffer
//      FREE_BUFFER(pBuffer);

#define GET_BUFFER(pBuffer, dwBufLen, type, desiredLen, defaultLen) \
    DWORD __desiredLen ## pBuffer = desiredLen;                     \
    type __szTmpBuffer ## pBuffer[defaultLen];                      \
    if (__desiredLen ## pBuffer <= defaultLen)                      \
    {                                                               \
        pBuffer = __szTmpBuffer ## pBuffer;                         \
        dwBufLen = defaultLen;                                      \
    }                                                               \
    else                                                            \
    {                                                               \
        pBuffer = new type[__desiredLen ## pBuffer];                \
        if (NULL != pBuffer)                                        \
        {                                                           \
            dwBufLen = __desiredLen ## pBuffer;                     \
        }                                                           \
    }

#define FREE_BUFFER(pBuffer) \
    if (__szTmpBuffer ## pBuffer != pBuffer) { delete[] pBuffer; }

//--------------------------------------------------------------------------

//+-------------------------------------------------------------------------
//
//  Function:   IsPathSharedW
//
//  Synopsis:   IsPathShared is used by the shell to determine whether to
//              put a "shared folder / hand" icon next to a directory.
//              Different from Windows 95, we don't allow sharing remote
//              directories (e.g., \\brucefo4\c$\foo\bar style paths).
//
//  Arguments:  [lpcszPath] - path to look for
//              [bRefresh]  - TRUE if cache should be refreshed
//
//  Returns:    TRUE if the path is shared, FALSE otherwise
//
//  History:    4-Apr-95    BruceFo  Created
//
//--------------------------------------------------------------------------

BOOL WINAPI
IsPathSharedW(
    LPCWSTR lpcszPath,
    BOOL bRefresh
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);

    appDebugOut((DEB_TRACE,"IsPathSharedW(%ws, %d)\n", lpcszPath, bRefresh));

    OneTimeInit();
    BOOL bSuccess = g_ShareCache.IsPathShared(lpcszPath, bRefresh);

    appDebugOut((DEB_TRACE,
        "IsPathShared(%ws, %ws) = %ws\n",
        lpcszPath,
        bRefresh ? L"refresh" : L"no refresh",
        bSuccess ? L"yes" : L"no"));

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bSuccess;
}


//+-------------------------------------------------------------------------
//
//  Function:   IsPathSharedA
//
//  Synopsis:   See IsPathSharedW
//
//  Arguments:  See IsPathSharedW
//
//  Returns:    See IsPathSharedW
//
//  History:    1-Mar-96    BruceFo  Created
//
//--------------------------------------------------------------------------

BOOL WINAPI
IsPathSharedA(
    LPCSTR lpcszPath,
    BOOL bRefresh
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"IsPathSharedA(%s, %d)\n", lpcszPath, bRefresh));

    if (NULL == lpcszPath)
    {
        return FALSE;   // invalid input!
    }

    LPWSTR pszTmp;
    DWORD dwBufLen;
    GET_BUFFER(pszTmp, dwBufLen, WCHAR, lstrlenA(lpcszPath) + 1, MAX_PATH);
    if (NULL == pszTmp)
    {
        return FALSE;   // didn't get the buffer
    }
    MultiByteToWideChar(CP_ACP, 0, lpcszPath, -1, pszTmp, dwBufLen);
    BOOL bReturn = IsPathSharedW(pszTmp, bRefresh);
    FREE_BUFFER(pszTmp);

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bReturn;
}

//+-------------------------------------------------------------------------
//
//  Function:   SharingDialog
//
//  Synopsis:   This API brings up the "Sharing" dialog. This entrypoint is
//              only used by the FAX team, as far as I know. Note that the
//              paths passed in are ANSI---that's because that's what they
//              were in Win95 when the API was defined.
//
//              This API, on NT, only works locally. It does not do remote
//              sharing, as Win95 does. Thus, the pszComputerName parameter
//              is ignored.
//
//  Arguments:  hwndParent      -- parent window
//              pszComputerName -- a computer name. This is ignored!
//              pszPath         -- the path to share.
//
//  Returns:    TRUE if everything went OK, FALSE otherwise
//
//  History:    5-Oct-95    BruceFo  Created
//
//--------------------------------------------------------------------------

BOOL WINAPI
SharingDialogW(
    HWND   hwndParent,
    LPWSTR pszComputerName,
    LPWSTR pszPath
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"SharingDialogW(%ws)\n", pszPath));

    // Parameter validation
    if (NULL == pszPath)
    {
        return FALSE;
    }

    if (NULL != pszComputerName)
    {
        appDebugOut((DEB_TRACE,
            "SharingDialog() API called with a computer name which will be ignored\n"));
    }

    // Make sure the DLL is initialized. Note that this loads up the share
    // cache in this address space. Also, the first thing the dialog does
    // is refresh the cache, so we just wasted the work done to create the
    // cache in the init. Oh well...
    OneTimeInit(TRUE);

    PWSTR pszCopy = NewDup(pszPath);
    if (NULL == pszCopy)
    {
        return FALSE;
    }

    BOOL bReturn = SharingDialogHelp(hwndParent, pszCopy);
    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bReturn;
}

//+-------------------------------------------------------------------------
//
//  Function:   SharingDialogA
//
//  Synopsis:   see SharingDialogW
//
//  Arguments:  see SharingDialogW
//
//  Returns:    see SharingDialogW
//
//  History:    1-Mar-96    BruceFo  Created
//
//--------------------------------------------------------------------------

BOOL WINAPI
SharingDialogA(
    HWND  hwndParent,
    LPSTR pszComputerName,
    LPSTR pszPath
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"SharingDialogA(%s)\n", pszPath));

    // Parameter validation
    if (NULL == pszPath)
    {
        return FALSE;
    }

    if (NULL != pszComputerName)
    {
        appDebugOut((DEB_TRACE,
            "SharingDialog() API called with a computer name which will be ignored\n"));
    }

    // Make sure the DLL is initialized. Note that this loads up the share
    // cache in this address space. Also, the first thing the dialog does
    // is refresh the cache, so we just wasted the work done to create the
    // cache in the init. Oh well...
    OneTimeInit(TRUE);

    DWORD dwLen = lstrlenA(pszPath) + 1;
    PWSTR pszUnicodePath = new WCHAR[dwLen];
    if (NULL == pszUnicodePath)
    {
        appDebugOut((DEB_ERROR,"OUT OF MEMORY\n"));
        return FALSE;
    }

    MultiByteToWideChar(CP_ACP, 0, pszPath, -1, pszUnicodePath, dwLen);
    BOOL bReturn = SharingDialogHelp(hwndParent, pszUnicodePath);
    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bReturn;
}


static BOOL WINAPI
SharingDialogHelp(
    HWND   hwndParent,
    LPWSTR pszPath          // this is 'new' memory that I take ownership of
    )
{
    CSharingPropertyPage* pPage = new CSharingPropertyPage(pszPath, TRUE);
    if (NULL == pPage)
    {
        delete[] pszPath;
        return FALSE;
    }

    HRESULT hr = pPage->InitInstance();
    if (FAILED(hr))
    {
        delete pPage;
        return FALSE;
    }

    PROPSHEETPAGE psp;

    psp.dwSize      = sizeof(psp);    // no extra data.
    psp.dwFlags     = PSP_USEREFPARENT | PSP_USECALLBACK;
    psp.hInstance   = g_hInstance;
    psp.pszTemplate = MAKEINTRESOURCE(IDD_SHARE_PROPERTIES);
    psp.hIcon       = NULL;
    psp.pszTitle    = NULL;
    psp.pfnDlgProc  = CSharingPropertyPage::DlgProcPage;
    psp.lParam      = (LPARAM)pPage;  // transfer ownership
    psp.pfnCallback = CSharingPropertyPage::PageCallback;
    psp.pcRefParent = &g_NonOLEDLLRefs;

    INT_PTR ret = DialogBoxParam(
                    g_hInstance,
                    MAKEINTRESOURCE(IDD_SHARE_PROPERTIES),
                    hwndParent,
                    CSharingPropertyPage::DlgProcPage,
                    (LPARAM)&psp);
    if (-1 == ret)
    {
        appDebugOut((DEB_ERROR,"DialogBoxParam() error, 0x%08lx\n",GetLastError()));
        delete pPage;
        return FALSE;
    }

    // Now we must simulate a property sheet destroy.

    CSharingPropertyPage::PageCallback(NULL, PSPCB_RELEASE, &psp);

    return TRUE;
}


// APPCOMPAT: there appears to be a bug in the Win95 code where they think
// they're storing and using "\\machine\share", but it appears they are
// actually storing and using "machine\share".

DWORD
CopyShareNameToBuffer(
    IN     CShareInfo* p,
    IN OUT LPWSTR lpszNameBuf,
    IN     DWORD cchNameBufLen
    )
{
    appAssert(NULL != lpszNameBuf);
    appAssert(0 != cchNameBufLen);

    WCHAR szLocalComputer[MAX_COMPUTERNAME_LENGTH + 1];
    DWORD nSize = ARRAYLEN(szLocalComputer);
    if (!GetComputerName(szLocalComputer, &nSize))
    {
        return GetLastError();
    }

    /* Two slashes + server name + slash + share name + null terminator. */

    DWORD computerLen = wcslen(szLocalComputer);
    DWORD shareLen    = wcslen(p->GetNetname());
    if (2 + computerLen + 1 + shareLen + 1 <= cchNameBufLen)
    {
        /* Return network resource name as UNC path. */

        lpszNameBuf[0] = L'\\';
        lpszNameBuf[1] = L'\\';
        wcscpy(lpszNameBuf + 2, szLocalComputer);
        *(lpszNameBuf + 2 + computerLen) = L'\\';
        wcscpy(lpszNameBuf + 2 + computerLen + 1, p->GetNetname());
        return ERROR_SUCCESS;
    }
    else
    {
        return ERROR_MORE_DATA;
    }
}


//+-------------------------------------------------------------------------
//
//  Function:   GetNetResourceFromLocalPathW
//
//  Synopsis:   Used by shell link tracking code.
//
//  Arguments:  [lpcszPath]      Path we're concerned about.
//              [lpszNameBuf]    If path is shared, UNC path to share goes here.
//              [cchNameBufLen] length of lpszNameBuf buffer in characters
//              [pdwNetType]     net type of local server, e.g., WNNC_NET_LANMAN
//
//  Returns:    TRUE if path is shared and net resource information
//              returned, else FALSE.
//
//  Notes:      *lpszNameBuf and *pwNetType are only valid if TRUE is returned.
//
//  Example:    If c:\documents is shared as MyDocs on machine Scratch, then
//              calling GetNetResourceFromLocalPath(c:\documents, ...) will
//              set lpszNameBuf to \\Scratch\MyDocs.
//
//  History:    3-Mar-96    BruceFo  Created from Win95 sources
//
//--------------------------------------------------------------------------

BOOL WINAPI
GetNetResourceFromLocalPathW(
    IN     LPCWSTR lpcszPath,
    IN OUT LPWSTR lpszNameBuf,
    IN     DWORD cchNameBufLen,
    OUT    PDWORD pdwNetType
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"GetNetResourceFromLocalPathW(%ws)\n", lpcszPath));

    // do some parameter validation
    if (NULL == lpcszPath || NULL == lpszNameBuf || NULL == pdwNetType || 0 == cchNameBufLen)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        InterlockedDecrement((long*)&g_NonOLEDLLRefs);
        return FALSE;
    }

    OneTimeInit();

    // Parameters seem OK (pointers might still point to bad memory);
    // do the work.

    CShareInfo* pShareList = new CShareInfo();  // dummy head node
    if (NULL == pShareList)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        InterlockedDecrement((long*)&g_NonOLEDLLRefs);
        return FALSE;   // out of memory
    }

    BOOL bReturn = FALSE;
    DWORD dwLastError;
    DWORD cShares;
    HRESULT hr = g_ShareCache.ConstructList(lpcszPath, pShareList, &cShares);
    if (SUCCEEDED(hr))
    {
        // Now, we have a list of (possibly zero) shares. The user is asking for
        // one of them. Give them the first normal, non-special share. If there
        // doesn't exist a non-special share, then give them a special share.

        if (cShares > 0)
        {
            BOOL bFoundOne = FALSE;
            CShareInfo* p;

            for (p = (CShareInfo*) pShareList->Next();
                 p != pShareList;
                 p = (CShareInfo*) p->Next())
            {
                if (p->GetType() == STYPE_DISKTREE)
                {
                    // found a share for this one.
                    bFoundOne = TRUE;
                    break;
                }
            }

            if (!bFoundOne)
            {
                for (p = (CShareInfo*) pShareList->Next();
                     p != pShareList;
                     p = (CShareInfo*) p->Next())
                {
                    if (p->GetType() == (STYPE_SPECIAL | STYPE_DISKTREE))
                    {
                        bFoundOne = TRUE;
                        break;
                    }
                }
            }

            if (bFoundOne)
            {
                dwLastError = CopyShareNameToBuffer(p, lpszNameBuf, cchNameBufLen);
                if (ERROR_SUCCESS == dwLastError)
                {
                    bReturn = TRUE;
                    *pdwNetType = WNNC_NET_LANMAN; // we only support LanMan
                }
            }
            else
            {
                // nothing found!
                dwLastError = ERROR_BAD_NET_NAME;
            }
        }
        else
        {
            dwLastError = ERROR_BAD_NET_NAME;
        }
    }
    else
    {
        dwLastError = ERROR_OUTOFMEMORY;
    }

    DeleteShareInfoList(pShareList, TRUE);

    if (!bReturn)
    {
        SetLastError(dwLastError);
    }

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bReturn;
}


BOOL WINAPI
GetNetResourceFromLocalPathA(
    IN     LPCSTR lpcszPath,
    IN OUT LPSTR lpszNameBuf,
    IN     DWORD cchNameBufLen,
    OUT    PDWORD pdwNetType
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"GetNetResourceFromLocalPathA(%s)\n", lpcszPath));
    BOOL bReturn = FALSE;
    LPWSTR pszPathTmp, pszNameTmp;
    DWORD dwPathBufLen, dwNameBufLen;
    GET_BUFFER(pszPathTmp, dwPathBufLen, WCHAR, lstrlenA(lpcszPath) + 1, MAX_PATH);
    if (NULL != pszPathTmp)
    {
        MultiByteToWideChar(CP_ACP, 0, lpcszPath, -1, pszPathTmp, dwPathBufLen);

        GET_BUFFER(pszNameTmp, dwNameBufLen, WCHAR, cchNameBufLen, MAX_PATH);
        if (NULL != pszNameTmp)
        {
            // got the buffers, now party...
            bReturn = GetNetResourceFromLocalPathW(pszPathTmp, pszNameTmp, cchNameBufLen, pdwNetType);
            if (bReturn)
            {
                // now convert the return string back
                WideCharToMultiByte(CP_ACP, 0,
                        pszNameTmp, -1,
                        lpszNameBuf, cchNameBufLen,
                        NULL, NULL);
            }

            FREE_BUFFER(pszNameTmp);
        }
        else
        {
            // didn't get the buffer
            SetLastError(ERROR_OUTOFMEMORY);
        }

        FREE_BUFFER(pszPathTmp);
    }
    else
    {
        // didn't get the buffer
        SetLastError(ERROR_OUTOFMEMORY);
    }

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bReturn;
}


//+-------------------------------------------------------------------------
//
//  Function:   GetLocalPathFromNetResourceW
//
//  Synopsis:   Used by shell link tracking code.
//
//  Arguments:  [lpcszName]     A UNC path we're concerned about.
//              [dwNetType]     net type of local server, e.g., WNNC_NET_LANMAN
//              [lpszLocalPathBuf]   Buffer to place local path of UNC path
//              [cchLocalPathBufLen] length of lpszLocalPathBuf buffer in
//                                   characters
//              [pbIsLocal]     Set to TRUE if lpcszName points to a local
//                              resource.
//
//  Returns:
//
//  Notes:      *lpszLocalPathBuf and *pbIsLocal are only valid if
//              TRUE is returned.
//
//  Example:    If c:\documents is shared as MyDocs on machine Scratch, then
//              calling GetLocalPathFromNetResource(\\Scratch\MyDocs, ...) will
//              set lpszLocalPathBuf to c:\documents.
//
//  History:    3-Mar-96    BruceFo  Created from Win95 sources
//
//--------------------------------------------------------------------------

BOOL WINAPI
GetLocalPathFromNetResourceW(
    IN     LPCWSTR lpcszName,
    IN     DWORD dwNetType,
    IN OUT LPWSTR lpszLocalPathBuf,
    IN     DWORD cchLocalPathBufLen,
    OUT    PBOOL pbIsLocal
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"GetLocalPathFromNetResourceW(%ws)\n", lpcszName));
    OneTimeInit();

    BOOL bReturn = FALSE;
    DWORD dwLastError;

    *pbIsLocal = FALSE;

    if (g_fSharingEnabled)
    {
        if (0 != dwNetType && HIWORD(dwNetType) == HIWORD(WNNC_NET_LANMAN))
        {
            /* Is the network resource name a UNC path on this machine? */

            WCHAR szLocalComputer[MAX_COMPUTERNAME_LENGTH + 1];
            DWORD nSize = ARRAYLEN(szLocalComputer);
            if (!GetComputerName(szLocalComputer, &nSize))
            {
                dwLastError = GetLastError();
            }
            else
            {
                dwLastError = ERROR_BAD_NET_NAME;

                DWORD dwLocalComputerLen = wcslen(szLocalComputer);
                if (   lpcszName[0] == L'\\'
                    && lpcszName[1] == L'\\'
                    && (0 == _wcsnicmp(lpcszName + 2, szLocalComputer, dwLocalComputerLen))
                    )
                {
                    LPCWSTR lpcszSep = &(lpcszName[2 + dwLocalComputerLen]);
                    if (*lpcszSep == L'\\')
                    {
                        *pbIsLocal = TRUE;

                        WCHAR szLocalPath[MAX_PATH];
                        if (g_ShareCache.IsExistingShare(lpcszSep + 1, NULL, szLocalPath))
                        {
                            if (wcslen(szLocalPath) < cchLocalPathBufLen)
                            {
                                wcscpy(lpszLocalPathBuf, szLocalPath);
                                dwLastError = ERROR_SUCCESS;
                                bReturn = TRUE;
                            }
                            else
                            {
                                dwLastError = ERROR_MORE_DATA;
                            }
                        }
                    }
                }
            }
        }
        else
        {
            dwLastError = ERROR_BAD_PROVIDER;
        }
    }
    else
    {
        appDebugOut((DEB_TRACE,"GetLocalPathFromNetResourceW: sharing not enabled\n"));
        dwLastError = ERROR_BAD_NET_NAME;
    }

    if (!bReturn)
    {
        SetLastError(dwLastError);
    }

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bReturn;
}


BOOL WINAPI
GetLocalPathFromNetResourceA(
    IN     LPCSTR lpcszName,
    IN     DWORD dwNetType,
    IN OUT LPSTR lpszLocalPathBuf,
    IN     DWORD cchLocalPathBufLen,
    OUT    PBOOL pbIsLocal
    )
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"GetLocalPathFromNetResourceA(%s)\n", lpcszName));

    BOOL bReturn = FALSE;
    LPWSTR pszLocalPathTmp, pszNameTmp;
    DWORD dwPathBufLen, dwNameBufLen;
    GET_BUFFER(pszLocalPathTmp, dwPathBufLen, WCHAR, cchLocalPathBufLen, MAX_PATH);
    if (NULL != pszLocalPathTmp)
    {
        GET_BUFFER(pszNameTmp, dwNameBufLen, WCHAR, lstrlenA(lpszLocalPathBuf) + 1, MAX_PATH);
        if (NULL != pszNameTmp)
        {
            MultiByteToWideChar(CP_ACP, 0, lpcszName, -1, pszNameTmp, dwNameBufLen);

            // got the buffers, now party...
            bReturn = GetLocalPathFromNetResourceW(pszNameTmp, dwNetType, pszLocalPathTmp, cchLocalPathBufLen, pbIsLocal);
            if (bReturn)
            {
                // now convert the return string back
                WideCharToMultiByte(CP_ACP, 0,
                        pszLocalPathTmp, -1,
                        lpszLocalPathBuf, cchLocalPathBufLen,
                        NULL, NULL);
            }

            FREE_BUFFER(pszNameTmp);
        }
        else
        {
            // didn't get the buffer
            SetLastError(ERROR_OUTOFMEMORY);
        }

        FREE_BUFFER(pszLocalPathTmp);
    }
    else
    {
        // didn't get the buffer
        SetLastError(ERROR_OUTOFMEMORY);
    }

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);
    return bReturn;
}

STDAPI CanShareFolderW(LPCWSTR pszPath)
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"CanShareFolderW(%s)\n", pszPath));
    OneTimeInit();

    HRESULT hr = S_FALSE;

    if (g_fSharingEnabled || IsSimpleUI())
    {
        if ( (pszPath[0] >= L'A' && pszPath[0] <= L'Z') && pszPath[1] == L':')
        {
            WCHAR szRoot[4];

            szRoot[0] = pszPath[0];
            szRoot[1] = TEXT(':');
            szRoot[2] = TEXT('\\');
            szRoot[3] = 0;

            UINT uType = GetDriveType(szRoot);

            switch (uType)
            {
                case DRIVE_UNKNOWN:
                case DRIVE_NO_ROOT_DIR:
                case DRIVE_REMOTE:
                    hr = S_FALSE;
                    break;
        
                case DRIVE_FIXED:
                case DRIVE_REMOVABLE:
                    {
                        WCHAR szDesktopIni[MAX_PATH];
                        PathCombine(szDesktopIni, pszPath, TEXT("desktop.ini"));
                        hr = GetPrivateProfileInt(TEXT(".ShellClassInfo"), TEXT("Sharing"), TRUE, szDesktopIni) ? S_OK : S_FALSE;
                    }
                    break;

                default:
                   hr = S_OK;
                   break;
            }

            //
            // NTRAID#NTBUG9-353119-2001/04/10-jeffreys
            //
            // We need to call PathIsDirectory to prevent the "Share this
            // folder" task from appearing in the webview pane of CAB and
            // ZIP folders. (NTBUG9 #319149 and 319153)
            //
            // However, PathIsDirectory fails with ERROR_NOT_READY on an
            // empty CD or removable drive, which is a case we want to allow
            // or the Sharing page will not show. (NTBUG9 #353119)
            //
            if (S_OK == hr && !PathIsDirectory(pszPath))
            {
                hr = S_FALSE;

                if (GetLastError() == ERROR_NOT_READY &&
                    (DRIVE_CDROM == uType || DRIVE_REMOVABLE == uType) &&
                    PathIsRootW(pszPath))
                {
                    // Ok to share an empty CD or removable drive
                    hr = S_OK;
                }
            }
        }
    }

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);

    return hr;
}

STDAPI ShowShareFolderUIW(HWND hwndParent, LPCWSTR pszPath)
{
    InterlockedIncrement((long*)&g_NonOLEDLLRefs);
    appDebugOut((DEB_TRACE,"ShowShareFolderUIW(%s)\n", pszPath));

    TCHAR szShare[50];
    LoadString(g_hInstance, IDS_MSGTITLE, szShare, ARRAYLEN(szShare));
    SHObjectProperties(hwndParent, SHOP_FILEPATH, pszPath, szShare);

    InterlockedDecrement((long*)&g_NonOLEDLLRefs);

    return S_OK;
}