///////////////////////////////////////////////////////////////////////////////
//
//  Copyright 1999 American Power Conversion, All Rights Reserved
//
//  Name:   upsapplet.cpp
//
//  Author: Noel Fegan
//
//  Description
//  ===========
//  
//  Revision History
//  ================
//  04 May 1999 - nfegan@apcc.com : Added this comment block.
//  04 May 1999 - nfegan@apcc.com : Preparing for code inspection
//

#include "upstab.h"

#include <objbase.h>
#include <shlobj.h>
#include <initguid.h>
#include "upsapplet.h"
#pragma hdrstop

extern "C" HINSTANCE   g_theInstance = 0;
UINT        g_cRefThisDll = 0;          // Reference count for this DLL

// {DE5637D2-E12D-11d2-8844-00600844D03F}
DEFINE_GUID(CLSID_ShellExtension, 
0xde5637d2, 0xe12d, 0x11d2, 0x88, 0x44, 0x0, 0x60, 0x8, 0x44, 0xd0, 0x3f);

//
// DllMain is the DLL's entry point.
//
// Input parameters:
//   hInstance  = Instance handle
//   dwReason   = Code specifying the reason DllMain was called
//   lpReserved = Reserved (do not use)
//
// Returns:
//   TRUE if successful, FALSE if not
//

///////////////////////////////////////////////////////////////////////////////

extern "C" int APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason,
    LPVOID lpReserved)
{
    //
    // If dwReason is DLL_PROCESS_ATTACH, save the instance handle so it
    // can be used again later.
    //
    if (dwReason == DLL_PROCESS_ATTACH) 
    {
        g_theInstance = hInstance;
        DisableThreadLibraryCalls(g_theInstance);
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// In-process server functions

//
// DllGetClassObject is called by the shell to create a class factory object.
//
// Input parameters:
//   rclsid = Reference to class ID specifier
//   riid   = Reference to interface ID specifier
//   ppv    = Pointer to location to receive interface pointer
//
// Returns:
//   HRESULT code signifying success or failure
//

STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
    *ppv = NULL;
    //
    // Make sure the class ID is CLSID_ShellExtension. Otherwise, the class
    // factory doesn't support the object type specified by rclsid.
    //
    if (!IsEqualCLSID (rclsid, CLSID_ShellExtension))
  {
    //Error
    return ResultFromScode (CLASS_E_CLASSNOTAVAILABLE);
  }

    //
    // Instantiate a class factory object.
    //
    CClassFactory *pClassFactory = new CClassFactory ();

    if (pClassFactory == NULL)
  {
    //Error
        return ResultFromScode (E_OUTOFMEMORY);
  }

    //
    // Get the interface pointer from QueryInterface and copy it to *ppv.
    //

    HRESULT hr = pClassFactory->QueryInterface (riid, ppv);
    pClassFactory->Release ();
    return hr;
}

//
// DllCanUnloadNow is called by the shell to find out if the DLL can be
// unloaded. The answer is yes if (and only if) the module reference count
// stored in g_cRefThisDll is 0.
//
// Input parameters:
//   None
//
// Returns:
//   HRESULT code equal to S_OK if the DLL can be unloaded, S_FALSE if not
//

STDAPI DllCanUnloadNow (void)
{
    return ResultFromScode ((g_cRefThisDll == 0) ? S_OK : S_FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// CClassFactory member functions

CClassFactory::CClassFactory ()
{
    m_cRef = 1;
    g_cRefThisDll++;
}

CClassFactory::~CClassFactory ()
{
    g_cRefThisDll--;
}

STDMETHODIMP CClassFactory::QueryInterface (REFIID riid, LPVOID FAR *ppv)
{

    if (IsEqualIID (riid, IID_IUnknown)) {
        *ppv = (LPUNKNOWN) (LPCLASSFACTORY) this;
        m_cRef++;
    return NOERROR;
    }

    else if (IsEqualIID (riid, IID_IClassFactory)) {
        *ppv = (LPCLASSFACTORY) this;
        m_cRef++;
        return NOERROR;
    }

    else {  
        *ppv = NULL;
        return ResultFromScode (E_NOINTERFACE);


    }
}

STDMETHODIMP_(ULONG) CClassFactory::AddRef ()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CClassFactory::Release ()
{
    if (--m_cRef == 0)
        delete this;
    return m_cRef;
}

//
// CreateInstance is called by the shell to create a shell extension object.
//
// Input parameters:
//   pUnkOuter = Pointer to controlling unknown
//   riid      = Reference to interface ID specifier
//   ppvObj    = Pointer to location to receive interface pointer
//
// Returns:
//   HRESULT code signifying success or failure
//

STDMETHODIMP CClassFactory::CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid,
    LPVOID FAR *ppvObj)
{
    *ppvObj = NULL;

    //
    // Return an error code if pUnkOuter is not NULL, because we don't
    // support aggregation.
    //
    if (pUnkOuter != NULL)
        return ResultFromScode (CLASS_E_NOAGGREGATION);

    //
    // Instantiate a shell extension object.
    //
    CShellExtension *pShellExtension = new CShellExtension ();

    if (pShellExtension == NULL)
        return ResultFromScode (E_OUTOFMEMORY);

    // Get the interface pointer from QueryInterface and copy it to *ppvObj.
    //
    HRESULT hr = pShellExtension->QueryInterface (riid, ppvObj);
    pShellExtension->Release ();
    return hr;
}

//
// LockServer increments or decrements the DLL's lock count.
//

STDMETHODIMP CClassFactory::LockServer (BOOL fLock)
{
    return ResultFromScode (E_NOTIMPL);
}

/////////////////////////////////////////////////////////////////////////////
// CShellExtension member functions

CShellExtension::CShellExtension ()
{
    m_cRef = 1;
    g_cRefThisDll++;
}

CShellExtension::~CShellExtension ()
{
    g_cRefThisDll--;
}

STDMETHODIMP CShellExtension::QueryInterface (REFIID riid, LPVOID FAR *ppv)
{
    if (IsEqualIID (riid, IID_IUnknown)) {
        *ppv = (LPUNKNOWN) (LPSHELLPROPSHEETEXT) this;
        m_cRef++;
        return NOERROR;
    }

    else if (IsEqualIID (riid, IID_IShellPropSheetExt)) {
        *ppv = (LPSHELLPROPSHEETEXT) this;
        m_cRef++;
        return NOERROR;
    }

    else if (IsEqualIID (riid, IID_IShellExtInit)) {
        *ppv = (LPSHELLEXTINIT) this;
        m_cRef++;
        return NOERROR;
    }
    
    else {
        *ppv = NULL;  
        return ResultFromScode (E_NOINTERFACE);
    }
}

STDMETHODIMP_(ULONG) CShellExtension::AddRef ()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CShellExtension::Release () {
  if (--m_cRef == 0) {
    delete this;
    }

  return(m_cRef);
  }

//
// AddPages is called by the shell to give property sheet shell extensions
// the opportunity to add pages to a property sheet before it is displayed.
//
// Input parameters:
//   lpfnAddPage = Pointer to function called to add a page
//   lParam      = lParam parameter to be passed to lpfnAddPage
//
// Returns:
//   HRESULT code signifying success or failure
//

STDMETHODIMP CShellExtension::AddPages (LPFNADDPROPSHEETPAGE lpfnAddPage,
                                        LPARAM lParam) {
  PROPSHEETPAGE psp;
  HPROPSHEETPAGE hUPSPage = NULL;
  HMODULE hModule = GetUPSModuleHandle();
  
  ZeroMemory(&psp, sizeof(psp));

  psp.dwSize = sizeof(psp);
  psp.dwFlags = PSP_USEREFPARENT;
  psp.hInstance = hModule;
  psp.pszTemplate = TEXT("IDD_UPS_EXT");
  psp.pfnDlgProc = UPSMainPageProc;
  psp.pcRefParent = &g_cRefThisDll;
  
  hUPSPage = CreatePropertySheetPage (&psp);

  //
  // Add the pages to the property sheet.
  //
  if (hUPSPage != NULL) {      
    if (!lpfnAddPage(hUPSPage, lParam)) {
      DestroyPropertySheetPage(hUPSPage);
      }
    }

  return(NOERROR);
  }

//
// ReplacePage is called by the shell to give control panel extensions the
// opportunity to replace control panel property sheet pages. It is never
// called for conventional property sheet extensions, so we simply return
// a failure code if called.
//
// Input parameters:
//   uPageID         = Page to replace
//   lpfnReplaceWith = Pointer to function called to replace a page
//   lParam          = lParam parameter to be passed to lpfnReplaceWith
//
// Returns:
//   HRESULT code signifying success or failure
//

STDMETHODIMP CShellExtension::ReplacePage (UINT uPageID,
                       LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
{
    return ResultFromScode (E_FAIL);
}

//
// Initialize is called by the shell to initialize a shell extension.
//
// Input parameters:
//   pidlFolder = Pointer to ID list identifying parent folder
//   lpdobj     = Pointer to IDataObject interface for selected object(s)
//   hKeyProgId = Registry key handle
//
// Returns:
//   HRESULT code signifying success or failure
//

STDMETHODIMP CShellExtension::Initialize (LPCITEMIDLIST pidlFolder,
    LPDATAOBJECT lpdobj, HKEY hKeyProgID)
{
  return ResultFromScode (NO_ERROR);
}