//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       ext.cxx
//
//  Contents:   Shell extension handler for network objects
//
//  Classes:    CNetObj
//
//  History:    26-Sep-95 BruceFo  Created
//
//--------------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop

#include <pages.hxx>

#define DONT_WANT_SHELLDEBUG
#include <shsemip.h>

#include "resource.h"
#include "ext.hxx"
#include "util.hxx"

//--------------------------------------------------------------------------
//
// Shell DLL communication
//

typedef
UINT
(WINAPI *SHELLGETNETRESOURCE)(
    HNRES hnres,
    UINT iItem,
    LPNETRESOURCE pnresOut,
    UINT cbMax
    );

HINSTANCE g_hShellLibrary = NULL;
SHELLGETNETRESOURCE g_pFuncGNR = NULL;
UINT g_cfNetResource = 0;

BOOL LoadShellDllEntries(VOID);

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


/*
 * Helper functions used to communicate with shell32.dll
 */

BOOL LoadShellDllEntries(VOID)
{
    if (g_hShellLibrary)
    {
        return TRUE;
    }

    g_hShellLibrary = LoadLibrary(TEXT("shell32.dll"));
    if (NULL == g_hShellLibrary)
    {
        return FALSE;
    }

    g_pFuncGNR = (SHELLGETNETRESOURCE)GetProcAddress(g_hShellLibrary, (LPSTR)(MAKELONG(SHGetNetResourceORD, 0)));
    if (NULL == g_pFuncGNR)
    {
        return FALSE;
    }

    return TRUE;
}

//+-------------------------------------------------------------------------
//
//  Member:     CNetObj::CNetObj
//
//  Synopsis:   Constructor
//
//  History:    4-Apr-95    BruceFo  Created
//
//--------------------------------------------------------------------------

CNetObj::CNetObj(
    VOID
    )
    :
    _uRefs(0),
    _pDataObject(NULL),
    _hkeyProgID(NULL)
{
    INIT_SIG(CNetObj);

    AddRef(); // give it the correct initial reference count. add to the DLL reference count
}


//+-------------------------------------------------------------------------
//
//  Member:     CNetObj::~CNetObj
//
//  Synopsis:   Destructor
//
//  History:    4-Apr-95    BruceFo  Created
//
//--------------------------------------------------------------------------

CNetObj::~CNetObj()
{
    CHECK_SIG(CNetObj);

    if (_pDataObject)
    {
        _pDataObject->Release();
        _pDataObject = NULL;
    }

    if (_hkeyProgID)
    {
        LONG l = RegCloseKey(_hkeyProgID);
        if (l != ERROR_SUCCESS)
        {
            appDebugOut((DEB_ERROR, "CNetObj::destructor. Error closing registry key, 0x%08lx\n", l));
        }
        _hkeyProgID = NULL;
    }
}


//+-------------------------------------------------------------------------
//
//  Member:     CNetObj::Initialize
//
//  Derivation: IShellExtInit
//
//  Synopsis:   Initialize the shell extension. Stashes away the argument data.
//
//  History:    4-Apr-95    BruceFo  Created
//
//  Notes:      This method can be called more than once.
//
//--------------------------------------------------------------------------

STDMETHODIMP
CNetObj::Initialize(
    LPCITEMIDLIST   pidlFolder,
    LPDATAOBJECT    pDataObject,
    HKEY            hkeyProgID
    )
{
    CHECK_SIG(CNetObj);

    CNetObj::~CNetObj();

    if (!LoadShellDllEntries())
    {
        return E_FAIL;
    }

    // Duplicate the pDataObject pointer
    _pDataObject = pDataObject;
    if (pDataObject)
    {
        pDataObject->AddRef();
    }

    // Duplicate the handle
    if (hkeyProgID)
    {
        LONG l = RegOpenKeyEx(hkeyProgID, NULL, 0L, MAXIMUM_ALLOWED, &_hkeyProgID);
        if (l != ERROR_SUCCESS)
        {
            appDebugOut((DEB_ERROR, "CNetObj::Initialize. Error duplicating registry key, 0x%08lx\n", l));
        }
    }

    return S_OK;
}


//+-------------------------------------------------------------------------
//
//  Member:     CNetObj::AddPages
//
//  Derivation: IShellPropSheetExt
//
//  Synopsis:   (from shlobj.h)
//              "The explorer calls this member function when it finds a
//              registered property sheet extension for a particular type
//              of object. For each additional page, the extension creates
//              a page object by calling CreatePropertySheetPage API and
//              calls lpfnAddPage.
//
//  Arguments:  lpfnAddPage -- Specifies the callback function.
//              lParam -- Specifies the opaque handle to be passed to the
//                        callback function.
//
//  History:    4-Apr-95    BruceFo  Created
//
//--------------------------------------------------------------------------

STDMETHODIMP
CNetObj::AddPages(
    LPFNADDPROPSHEETPAGE lpfnAddPage,
    LPARAM               lParam
    )
{
    CHECK_SIG(CNetObj);

    //
    // Call IDataObject::GetData asking for a g_cfNetResource (i.e., HNRES).
    //
    STGMEDIUM medium;
    FORMATETC fmte =
    {
        g_cfNetResource
            ? g_cfNetResource
            : (g_cfNetResource = RegisterClipboardFormat(CFSTR_NETRESOURCES)),
        NULL,
        DVASPECT_CONTENT,
        -1,
        TYMED_HGLOBAL
    };
    appAssert(NULL != _pDataObject);
    HRESULT hr = _pDataObject->GetData(&fmte, &medium);
    CHECK_HRESULT(hr);
    if (FAILED(hr))
    {
        return hr;
    }

    ///////////////// Now I have a 'medium' to release

    hr = S_OK;

    HNRES hnres = medium.hGlobal;

    // Get number of selected items
    if (NULL != g_pFuncGNR)
    {
        UINT cItems = (*g_pFuncGNR)(hnres, (UINT)-1, NULL, 0);
        if (cItems > 0)
        {
            // Retrieve NETRESOURCE object from clipboard
            LPNETRESOURCE pNetRes = (LPNETRESOURCE)_bufNetResource;
            UINT ret = (*g_pFuncGNR)(hnres, 0, pNetRes, MAX_ONE_RESOURCE); // Get first item
            if (ret == 0)
            {
                // bad hnres?
                appDebugOut((DEB_TRACE, "CNetObj::AddPages. SHGetNetResource returned 0\n"));
                // NOTE: this is really error
            }
            else if (ret > MAX_ONE_RESOURCE)
            {
                // FEATURE: Resize the buf and try again
                appDebugOut((DEB_TRACE, "CNetObj::AddPages. buffer too small, needs to be %d\n", ret));
            }
            else
            {
                LPTSTR pszTemplate = NULL;

                if (RESOURCEDISPLAYTYPE_NETWORK == pNetRes->dwDisplayType)
                {
                    pszTemplate = MAKEINTRESOURCE(IDD_NETWORK_SUMMARYINFO);
                }
                else if (RESOURCEDISPLAYTYPE_DOMAIN == pNetRes->dwDisplayType)
                {
                    pszTemplate = MAKEINTRESOURCE(IDD_WRKGRP_SUMMARYINFO);
                }
                else if (RESOURCEDISPLAYTYPE_SERVER == pNetRes->dwDisplayType)
                {
                    pszTemplate = MAKEINTRESOURCE(IDD_SERVER_SUMMARYINFO);
                }
                else if (RESOURCEDISPLAYTYPE_SHARE == pNetRes->dwDisplayType)
                {
                    pszTemplate = MAKEINTRESOURCE(IDD_SHARE_SUMMARYINFO);
                }
                else
                {
                    appDebugOut((DEB_TRACE, "CNetObj::AddPages. Unknown net resource type!\n"));
                }

                //
                //  Create a property sheet page object from a dialog box.
                //

                if (NULL != pszTemplate)
                {
                    hr = FillAndAddPage(
                            lpfnAddPage,
                            lParam,
                            pszTemplate);
                }
            }
        }
        else
        {
            appDebugOut((DEB_TRACE, "CNetObj::AddPages. NO net resources!\n"));
            // NOTE: this is really error
        }
    }
    else
    {
        appDebugOut((DEB_TRACE, "CNetObj::AddPages. No SHGetNetResource function!\n"));
        // NOTE: this is really error
    }

    ReleaseStgMedium(&medium);
    return hr;
}

HRESULT
CNetObj::FillAndAddPage(
    LPFNADDPROPSHEETPAGE lpfnAddPage,
    LPARAM lParam,
    LPTSTR pszTemplate
    )
{
    PROPSHEETPAGE psp;
    HPROPSHEETPAGE hpage;

    CPage* pPage = new CPage(this);
    if (NULL == pPage)
    {
        return E_OUTOFMEMORY;
    }

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

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

    hpage = CreatePropertySheetPage(&psp);
    if (NULL == hpage)
    {
        delete pPage;
        return E_OUTOFMEMORY;
    }

    BOOL fAdded = (*lpfnAddPage)(hpage, lParam);
    if (!fAdded)
    {
        DestroyPropertySheetPage(hpage);
        return E_OUTOFMEMORY;
    }

    return S_OK;
}


//+-------------------------------------------------------------------------
//
//  Member:     CNetObj::ReplacePages
//
//  Derivation: IShellPropSheetExt
//
//  Synopsis:   (From shlobj.h)
//              "The explorer never calls this member of property sheet
//              extensions. The explorer calls this member of control panel
//              extensions, so that they can replace some of default control
//              panel pages (such as a page of mouse control panel)."
//
//  Arguments:  uPageID -- Specifies the page to be replaced.
//              lpfnReplace -- Specifies the callback function.
//              lParam -- Specifies the opaque handle to be passed to the
//                        callback function.
//
//  History:    4-Apr-95    BruceFo  Created
//
//--------------------------------------------------------------------------

STDMETHODIMP
CNetObj::ReplacePage(
    UINT                 uPageID,
    LPFNADDPROPSHEETPAGE lpfnReplaceWith,
    LPARAM               lParam
    )
{
    CHECK_SIG(CNetObj);

    appAssert(!"CNetObj::ReplacePage called, not implemented");
    return E_NOTIMPL;
}


// dummy function to export to get linking to work
HRESULT PropDummyFunction()
{
    return S_OK;
}
