#include "shellprv.h"
#include "tbmenu.h"
#include "isfband.h"
#include "isfmenu.h"

#include "mluisupp.h"
#define SMFORWARD(x) if (!_psm) { return E_FAIL; } else return _psm->x

class CTrackShellMenu : public ITrackShellMenu,
                        public IShellMenu2,
                        public IObjectWithSite,
                        public IServiceProvider
{
public:
    // *** IUnknown ***
    virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    virtual STDMETHODIMP_(ULONG) AddRef(void);
    virtual STDMETHODIMP_(ULONG) Release(void);

    // *** IShellMenu methods ***
    virtual STDMETHODIMP Initialize(IShellMenuCallback* psmc, UINT uId, UINT uIdAncestor, DWORD dwFlags);
    virtual STDMETHODIMP GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId, 
                                    UINT* puIdAncestor, DWORD* pdwFlags);
    virtual STDMETHODIMP SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hkey, DWORD dwFlags);
    virtual STDMETHODIMP GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj);
    virtual STDMETHODIMP SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags);
    virtual STDMETHODIMP GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags);
    virtual STDMETHODIMP InvalidateItem(LPSMDATA psmd, DWORD dwFlags);
    virtual STDMETHODIMP GetState(LPSMDATA psmd);
    virtual STDMETHODIMP SetMenuToolbar(IUnknown* punk, DWORD dwFlags);

    // *** ITrackShellMenu methods ***
    virtual STDMETHODIMP SetObscured(HWND hwndTB, IUnknown* punkBand, DWORD dwSMSetFlags);
    virtual STDMETHODIMP Popup(HWND hwnd, POINTL *ppt, RECTL *prcExclude, DWORD dwFlags);

    // *** IObjectWithSite methods ***
    virtual STDMETHODIMP SetSite(IUnknown* punkSite);
    virtual STDMETHODIMP GetSite(REFIID ridd, void** ppvObj) { *ppvObj = NULL; return E_NOTIMPL; };

    // *** IServiceProvider methods ***
    virtual STDMETHODIMP QueryService(REFGUID guidService,
                                  REFIID riid, void **ppvObj);

    // *** IShellMenu2 methods ***
    virtual STDMETHODIMP GetSubMenu(UINT idCmd, REFIID riid, void **ppvObj);
    virtual STDMETHODIMP SetToolbar(HWND hwnd, DWORD dwFlags);
    virtual STDMETHODIMP SetMinWidth(int cxMenu);
    virtual STDMETHODIMP SetNoBorder(BOOL fNoBorder);
    virtual STDMETHODIMP SetTheme(LPCWSTR pszTheme);

    CTrackShellMenu();
private:
    virtual ~CTrackShellMenu();

    IShellMenu*     _psmClient;
    IShellMenu*     _psm;
    IShellMenu2*    _psm2;
    IUnknown*       _punkSite;
    int             _cRef;
    HMENU           _hmenu;
    BITBOOL         _fDestroyTopLevel : 1;
};


typedef struct
{
    WNDPROC pfnOriginal;
    IMenuBand* pmb;
} MENUHOOK;

#define SZ_MENUHOOKPROP TEXT("MenuHookProp")

LRESULT CALLBACK MenuHookWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);

    if (pmh)
    {
        MSG msg;
        LRESULT lres;

        msg.hwnd = hwnd;
        msg.message = uMsg;
        msg.wParam = wParam;
        msg.lParam = lParam;

        if (pmh->pmb->TranslateMenuMessage(&msg, &lres) == S_OK)
            return lres;

        wParam = msg.wParam;
        lParam = msg.lParam;
        return CallWindowProc(pmh->pfnOriginal, hwnd, uMsg, wParam, lParam);
    }

    return 0;
}

HRESULT HookMenuWindow(HWND hwnd, IMenuBand* pmb)
{
    HRESULT hres = E_FAIL;

    ASSERT(IsWindow(hwnd));

    // make sure we haven't already hooked this window
    if (GetProp(hwnd, SZ_MENUHOOKPROP) == NULL)
    {
        MENUHOOK* pmh = new MENUHOOK;
        if (pmh)
        {
            pmh->pfnOriginal = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
            pmh->pmb = pmb;

            SetProp(hwnd, SZ_MENUHOOKPROP, pmh);

            SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MenuHookWndProc);

            hres = S_OK;
        }
    }
    return hres;
}

void UnHookMenuWindow(HWND hwnd)
{

    MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);
    if (pmh)
    {
        SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pmh->pfnOriginal);
        SetProp(hwnd, SZ_MENUHOOKPROP, NULL);
        delete pmh;
    }

}


// This class is here to implement a "Menu Filter". We need this because the old style of 
// implementing obscured Menus does not work because user munges the WM_INITMENUPOPUP information
// based on the relative position within the HMENU. So here we keep that information, we just hide the item.

class CShellMenuCallbackWrapper : public IShellMenuCallback,
                                  public CObjectWithSite
{
    int _cRef;
    IShellMenuCallback* _psmc;
    HWND    _hwnd;
    RECT    _rcTB;
    ~CShellMenuCallbackWrapper()
    {
        ATOMICRELEASE(_psmc);
    }

public:
    CShellMenuCallbackWrapper(HWND hwnd, IShellMenuCallback* psmc) : _cRef(1)
    {
        _psmc = psmc;
        if (_psmc)
            _psmc->AddRef();
        _hwnd = hwnd;
        GetClientRect(_hwnd, &_rcTB);
    }

    // *** IUnknown methods ***
    STDMETHODIMP QueryInterface (REFIID riid, LPVOID * ppvObj)
    {
        static const QITAB qit[] = 
        {
            QITABENT(CShellMenuCallbackWrapper, IShellMenuCallback),
            QITABENT(CShellMenuCallbackWrapper, IObjectWithSite),
            { 0 },
        };

        return QISearch(this, qit, riid, ppvObj);
    }

    STDMETHODIMP_(ULONG) AddRef()
    {
        _cRef++;
        return _cRef;
    }

    STDMETHODIMP_(ULONG) Release()
    {
        ASSERT(_cRef > 0);
        _cRef--;

        if (_cRef > 0)
            return _cRef;

        delete this;
        return 0;
    }

    // *** CObjectWithSite methods (override)***
    STDMETHODIMP SetSite(IUnknown* punk)            {   IUnknown_SetSite(_psmc, punk); return S_OK;  }
    STDMETHODIMP GetSite(REFIID riid, void** ppObj) {   return IUnknown_GetSite(_psmc, riid, ppObj); }

    // *** IShellMenuCallback methods ***
    STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        HRESULT hres = S_FALSE;
        
        if (_psmc)
            hres = _psmc->CallbackSM(psmd, uMsg, wParam, lParam);

        if (uMsg == SMC_GETINFO)
        {
            SMINFO* psminfo = (SMINFO*)lParam;
            int iPos = (int)SendMessage(_hwnd, TB_COMMANDTOINDEX, psmd->uId, 0);

            if (psminfo->dwMask & SMIM_FLAGS &&
                iPos >= 0 && 
                !SHIsButtonObscured(_hwnd, &_rcTB, iPos))
            {
                psminfo->dwFlags |= SMIF_HIDDEN;
                hres = S_OK;
            }
        }

        return hres;
    }
};

STDAPI  CTrackShellMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
{
    HRESULT hres = E_OUTOFMEMORY;
    CTrackShellMenu* pObj = new CTrackShellMenu();
    if (pObj)
    {
        hres = pObj->QueryInterface(riid, ppv);
        pObj->Release();
    }

    return hres;
}


CTrackShellMenu::CTrackShellMenu() : _cRef(1)
{ 
    if (SUCCEEDED(CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu, &_psm))))
    {
        _psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &_psm2));
    }
}

CTrackShellMenu::~CTrackShellMenu()
{
    ATOMICRELEASE(_psm2);
    ATOMICRELEASE(_psm);
    ATOMICRELEASE(_psmClient);
    ASSERT(!_punkSite);     // else someone neglected to call matching SetSite(NULL)
}

ULONG CTrackShellMenu::AddRef()
{
    _cRef++;
    return _cRef;
}

ULONG CTrackShellMenu::Release()
{
    ASSERT(_cRef > 0);
    _cRef--;

    if (_cRef > 0)
        return _cRef;

    delete this;
    return 0;
}

HRESULT CTrackShellMenu::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENTMULTI(CTrackShellMenu, IShellMenu, ITrackShellMenu),
        QITABENT(CTrackShellMenu, ITrackShellMenu),
        QITABENT(CTrackShellMenu, IShellMenu2),
        QITABENT(CTrackShellMenu, IObjectWithSite),
        QITABENT(CTrackShellMenu, IServiceProvider),
        { 0 },
    };

    HRESULT hres = QISearch(this, qit, riid, ppvObj);

    return hres;
}

// *** IServiceProvider methods ***
HRESULT CTrackShellMenu::QueryService(REFGUID guidService,
                                  REFIID riid, void **ppvObj)
{
    return IUnknown_QueryService(_psm, guidService, riid, ppvObj);
}

// *** IShellMenu methods ***
STDMETHODIMP CTrackShellMenu::Initialize(IShellMenuCallback* psmc, UINT uId, UINT uIdAncestor, DWORD dwFlags)
{ SMFORWARD(Initialize(psmc, uId, uIdAncestor, dwFlags)); }

STDMETHODIMP CTrackShellMenu::GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId, UINT* puIdAncestor, DWORD* pdwFlags)
{ SMFORWARD(GetMenuInfo(ppsmc, puId, puIdAncestor, pdwFlags)); }

STDMETHODIMP CTrackShellMenu::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hkey, DWORD dwFlags)
{ SMFORWARD(SetShellFolder(psf, pidlFolder, hkey, dwFlags)); }

STDMETHODIMP CTrackShellMenu::GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj)
{ SMFORWARD(GetShellFolder(pdwFlags, ppidl, riid, ppvObj)); }

STDMETHODIMP CTrackShellMenu::SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags)
{ SMFORWARD(SetMenu(hmenu, hwnd, dwFlags)); }

STDMETHODIMP CTrackShellMenu::GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags)
{ SMFORWARD(GetMenu(phmenu, phwnd, pdwFlags)); }

STDMETHODIMP CTrackShellMenu::InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
{ SMFORWARD(InvalidateItem(psmd, dwFlags)); }

STDMETHODIMP CTrackShellMenu::GetState(LPSMDATA psmd)
{ SMFORWARD(GetState(psmd)); }

STDMETHODIMP CTrackShellMenu::SetMenuToolbar(IUnknown* punk, DWORD dwFlags)
{ SMFORWARD(SetMenuToolbar(punk, dwFlags)); }

STDMETHODIMP CTrackShellMenu::GetSubMenu(UINT idCmd, REFIID riid, void **ppvObj)
{
    if (_psm2)
    {
        return _psm2->GetSubMenu(idCmd, riid, ppvObj);
    }
    else
    {
        return E_NOTIMPL;
    }
}

STDMETHODIMP CTrackShellMenu::SetToolbar([in] HWND hwnd, [in] DWORD dwFlags)
{
    if (_psm2)
    {
        return _psm2->SetToolbar(hwnd, dwFlags);
    }
    else
    {
        return E_NOTIMPL;
    }
}

STDMETHODIMP CTrackShellMenu::SetMinWidth([in] int cxMenu)
{
    if (_psm2)
    {
        return _psm2->SetMinWidth(cxMenu);
    }
    else
    {
        return E_NOTIMPL;
    }
}

STDMETHODIMP CTrackShellMenu::SetNoBorder([in] BOOL fNoBorder)
{
    if (_psm2)
    {
        return _psm2->SetNoBorder(fNoBorder);
    }
    else
    {
        return E_NOTIMPL;
    }
}

STDMETHODIMP CTrackShellMenu::SetTheme([in] LPCWSTR pszTheme)
{
    if (_psm2)
    {
        return _psm2->SetTheme(pszTheme);
    }
    else
    {
        return E_NOTIMPL;
    }
}

// *** ITrackShellMenu methods ***
HRESULT CTrackShellMenu::SetObscured(HWND hwndTB, IUnknown* punkBand, DWORD dwSMSetFlags)
{
    HRESULT hr = E_OUTOFMEMORY;

    // Make sure we created the Inner Shell Menu
    if (!_psm)
        return hr;

    if (punkBand && 
        SUCCEEDED(punkBand->QueryInterface(IID_PPV_ARG(IShellMenu, &_psmClient))))
    {
        UINT uId, uIdAncestor;
        DWORD dwFlags;
        IShellMenuCallback* psmcb;

        hr = _psmClient->GetMenuInfo(&psmcb, &uId, &uIdAncestor, &dwFlags);
        if (SUCCEEDED(hr))
        {
            IShellMenuCallback* psmcbClone = NULL;
            if (psmcb)
            {
                if (S_FALSE == psmcb->CallbackSM(NULL, SMC_GETOBJECT, 
                    (WPARAM)&IID_IShellMenuCallback,
                    (LPARAM)(LPVOID*)&psmcbClone))
                {
                    psmcbClone = psmcb;
                    psmcbClone->AddRef();
                }
            }

            dwFlags &= ~SMINIT_HORIZONTAL;

            CShellMenuCallbackWrapper* psmcw = new CShellMenuCallbackWrapper(hwndTB, psmcbClone);

            // We want the bands to think it is:
            // Top level - because it has no menuband parent
            // Vertical  - because it's not a menubar
            dwFlags |= SMINIT_TOPLEVEL | SMINIT_VERTICAL;
            hr = _psm->Initialize(psmcw, uId, ANCESTORDEFAULT, dwFlags);

            if (SUCCEEDED(hr))
            {
                HWND hwndOwner;
                HMENU hmenuObscured;
                hr = _psmClient->GetMenu(&hmenuObscured, &hwndOwner, NULL);
                if (SUCCEEDED(hr))
                {
                    hr = _psm->SetMenu(hmenuObscured, hwndOwner, dwSMSetFlags | SMSET_DONTOWN);   // Menuband takes ownership;
                }
            }

            if (psmcb)
                psmcb->Release();

            if (psmcbClone)
                psmcbClone->Release();

            if (psmcw)
                psmcw->Release();

        }
    }
    else
    {
        IShellMenu2 *psm2;
        hr = _psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &psm2));
        if (SUCCEEDED(hr))
        {
            hr = psm2->SetToolbar(hwndTB, dwSMSetFlags);
            psm2->Release();
        }
    }

    return hr;
}

HRESULT CTrackShellMenu::Popup(HWND hwnd, POINTL *ppt, RECTL *prcExclude, DWORD dwFlags)
{
    IMenuBand* pmb;
    HRESULT hres = E_INVALIDARG;


    if (!_psm)
        return hres;

    hres = _psm->QueryInterface(IID_PPV_ARG(IMenuBand, &pmb));
    if (FAILED(hres))
        return hres;

    HWND hwndParent = GetTopLevelAncestor(hwnd);

    // Did the user set a menu into the Shell Menu?
    HWND hwndSubclassed = NULL;
    GetMenu(NULL, &hwndSubclassed, NULL);
    if (hwndSubclassed == NULL)
    {
        // No; We need to artificially set one so that the message filtering and stuff works
        SetMenu(NULL, hwndParent, 0);
    }

    SetForegroundWindow(hwndParent);

    IMenuPopup* pmp;
    hres = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMenuPopup, &pmp));
    if (SUCCEEDED(hres))
    {
        IBandSite* pbs;
        hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandSite, &pbs));
        if (SUCCEEDED(hres)) 
        {
            hres = pmp->SetClient(pbs);
            if (SUCCEEDED(hres)) 
            {
                IDeskBand* pdb;
                hres = _psm->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb));
                if (SUCCEEDED(hres)) 
                {
                    hres = pbs->AddBand(pdb);
                    pdb->Release();
                }
            }
            pbs->Release();
        }

        // If we've got a site ourselves, have MenuDeskBar use that.
        if (_punkSite)
            IUnknown_SetSite(pmp, _punkSite);

        if (SUCCEEDED(hres))
        {
            CMBMsgFilter* pmf = GetMessageFilter();
            void* pvContext = GetMessageFilter()->GetContext();
            hres = HookMenuWindow(hwndParent, pmb);
            if (SUCCEEDED(hres))
            {
                // This collapses any modal menus before we proceed. When switching between
                // Chevron menus, we need to collapse the previous menu. Refer to the comment
                // at the function definition.
                pmf->ForceModalCollapse();

                pmp->Popup(ppt, (LPRECTL)prcExclude, dwFlags);

                pmf->SetModal(TRUE);

                MSG msg;
                while (GetMessage(&msg, NULL, 0, 0)) 
                {
                    HRESULT hres = pmb->IsMenuMessage(&msg);
                    if (hres == E_FAIL)
                    {
                        // menuband says it's time to pack up and go home.
                        // re-post this message so that it gets handled after
                        // we've cleaned up the menu (avoid re-entrancy issues &
                        // let rebar restore state of chevron button to unpressed)
                        PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
                        break;
                    }
                    else if (hres != S_OK) 
                    {
                        // menuband didn't handle this one
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                    }
                }

                hres = S_OK;
                UnHookMenuWindow(hwndParent);
                // We cannot change the context when modal, so unset the modal flag so that we can undo the context block.
                pmf->SetModal(FALSE); 
                pmf->SetContext(pvContext, TRUE);
            }
            pmb->Release();
        }

        if (_psmClient)
        {
            // This is to fix a bug where if there is a cached ISHellMenu in the submenu,
            // and you share the callback (For example, Broweser menu callback and the
            // favorites menu being shared between the browser bar and the chevron menu)
            // when on menu collapsed, we were destroying the sub menu by doing a set site.
            // since we no longer do the set site on the sub menu, we need a way to say "Reset
            // your parent". and this is the best way.
            IUnknown_Exec(_psmClient, &CGID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
        }

        // This call is required regardless of whether we had a _punkSite above;
        // MenuDeskBar does its cleanup on SetSite(NULL).
        IUnknown_SetSite(pmp, NULL);
        pmp->Release();
    }

    return hres;
}

// *** IObjectWithSite methods ***
HRESULT CTrackShellMenu::SetSite(IUnknown* punkSite)
{
    ASSERT(NULL == punkSite || IS_VALID_CODE_PTR(punkSite, IUnknown));

    ATOMICRELEASE(_punkSite);

    _punkSite = punkSite;

    if (punkSite)
        punkSite->AddRef();

    return S_OK;
}

BOOL IsISFBand(IUnknown* punk)
{
    OLECMD rgCmds[] = {
        ISFBID_PRIVATEID, 0,
    };

    IUnknown_QueryStatus(punk, &CGID_ISFBand, SIZEOF(rgCmds), rgCmds, NULL);

    return BOOLIFY(rgCmds[0].cmdf);
}

HRESULT DoISFBandStuff(CTrackShellMenu* ptsm, IUnknown* punk)
{
    HRESULT hr = E_INVALIDARG;

    if (punk && ptsm)
    {
        IShellFolderBand* psfb;
        hr = punk->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb));

        if (SUCCEEDED(hr))
        {
            BANDINFOSFB bi;
            bi.dwMask = ISFB_MASK_IDLIST | ISFB_MASK_SHELLFOLDER;

            hr = psfb->GetBandInfoSFB(&bi);

            if (SUCCEEDED(hr))
            {
                CISFMenuCallback* pCallback = new CISFMenuCallback();

                if (pCallback)
                {
                    hr = pCallback->Initialize(punk);

                    if (SUCCEEDED(hr))
                    {
                        ptsm->Initialize(SAFECAST(pCallback, IShellMenuCallback*), 0, 
                            ANCESTORDEFAULT, SMINIT_VERTICAL | SMINIT_TOPLEVEL);

                        hr = ptsm->SetShellFolder(bi.psf, bi.pidl, NULL, SMSET_COLLAPSEONEMPTY);
                    }
                    pCallback->Release();
                }
                else
                {
                    hr = E_OUTOFMEMORY;
                }

                bi.psf->Release();
                ILFree(bi.pidl);
            }

            psfb->Release();
        }
    }

    return hr;
}


// An API for internal use.
HRESULT ToolbarMenu_Popup(HWND hwnd, LPRECT prc, IUnknown* punk, HWND hwndTB, int idMenu, DWORD dwFlags)
{
    HRESULT hres = E_OUTOFMEMORY;
    CTrackShellMenu* ptsm = new CTrackShellMenu();

    if (ptsm)
    {
        hres = S_OK;
        if (IsISFBand(punk))
        {
            hres = DoISFBandStuff(ptsm, punk);
        }
        else if (hwndTB)
        {
            ptsm->Initialize(NULL, 0, ANCESTORDEFAULT, SMINIT_TOPLEVEL | SMINIT_VERTICAL | SMINIT_RESTRICT_DRAGDROP);
            hres = ptsm->SetObscured(hwndTB, punk, SMSET_TOP);
        }

        IUnknown* punkSite;
        if (SUCCEEDED(IUnknown_GetSite(punk, IID_PPV_ARG(IUnknown, &punkSite))))
            ptsm->SetSite(punkSite);

        HMENU hmenu = idMenu ? SHLoadMenuPopup(HINST_THISDLL, idMenu) : NULL;

        if (SUCCEEDED(hres) && hmenu)
            hres = ptsm->SetMenu(hmenu, hwnd, SMSET_BOTTOM);

        if (SUCCEEDED(hres))
        {
            DWORD dwPopupFlags = MPPF_BOTTOM;

            // select first/last menu item if specified
            if (dwFlags == DBPC_SELECTFIRST)
            {
                dwPopupFlags |= MPPF_INITIALSELECT;
            }
            else if (dwFlags == DBPC_SELECTLAST)
            {
                dwPopupFlags |= MPPF_FINALSELECT;
            }
            else if (dwFlags != 0)
            {
                VARIANT var;
                var.vt = VT_I4;
                var.lVal = dwFlags;
                IUnknown* punk;
                if (SUCCEEDED(ptsm->QueryInterface(IID_PPV_ARG(IUnknown, &punk))))
                {
                    IUnknown_QueryServiceExec(punk, SID_SMenuBandChild, &CGID_MenuBand, MBANDCID_SELECTITEM, 0, &var, NULL);
                    punk->Release();
                }
            }

            POINTL ptPop = {prc->left, prc->bottom};
            hres = ptsm->Popup(hwnd, &ptPop, (LPRECTL)prc, dwPopupFlags);
        }

        ptsm->SetSite(NULL);
        ptsm->Release();
    }
    return hres;
}
