DATATRAN.CPP
/* 
 * DATATRAN.CPP 
 * Data Transfer Object Chapter 12 
 * 
 * Transfer data object implemented in a DLL.  This data object will 
 * cache specific formats and renderings such that its IDataObject 
 * interface could be plopped on the clipboard or used in drag-drop. 
 * 
 * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved 
 * 
 * Kraig Brockschmidt, Microsoft 
 * Internet  :  kraigb@microsoft.com 
 * Compuserve:  >INTERNET:kraigb@microsoft.com 
 */ 
 
 
#define INITGUIDS 
#include "datatran.h" 
 
 
//Count number of objects and number of locks. 
ULONG       g_cObj=0; 
ULONG       g_cLock=0; 
 
//Make this global for the data object to create listboxes. 
HINSTANCE   g_hInst=NULL; 
 
 
 
/* 
 * LibMain(32) 
 * 
 * Purpose: 
 *  Entry point conditionally compiled for Windows NT and Windows 
 *  3.1.  Provides the proper structure for each environment. 
 */ 
 
#ifdef WIN32 
BOOL WINAPI LibMain32(HINSTANCE hInstance, ULONG ulReason 
    , LPVOID pvReserved) 
    { 
    if (DLL_PROCESS_DETACH==ulReason) 
        { 
        return TRUE; 
        } 
    else 
        { 
        if (DLL_PROCESS_ATTACH!=ulReason) 
            return TRUE; 
        } 
 
    return TRUE; 
    } 
#else 
int PASCAL LibMain(HINSTANCE hInstance, WORD wDataSeg 
    , WORD cbHeapSize, LPSTR lpCmdLine) 
    { 
    if (0!=cbHeapSize) 
        UnlockData(0); 
 
    g_hInst=hInstance; 
    return (int)hInstance; 
    } 
#endif 
 
 
 
 
 
 
/* 
 * DllGetClassObject 
 * 
 * Purpose: 
 *  Provides an IClassFactory for a given CLSID that this DLL is 
 *  registered to support.  This DLL is placed under the CLSID 
 *  in the registration database as the InProcServer. 
 * 
 * Parameters: 
 *  clsID           REFCLSID that identifies the class factory 
 *                  desired.  Since this parameter is passed this 
 *                  DLL can handle any number of objects simply 
 *                  by returning different class factories here 
 *                  for different CLSIDs. 
 * 
 *  riid            REFIID specifying the interface the caller wants 
 *                  on the class object, usually IID_ClassFactory. 
 * 
 *  ppv             PPVOID in which to return the interface 
 *                  pointer. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR on success, otherwise an error code. 
 */ 
 
HRESULT APIENTRY DllGetClassObject(REFCLSID rclsid 
    , REFIID riid, PPVOID ppv) 
    { 
    //Check that we can provide the interface 
    if (IID_IUnknown!=riid && IID_IClassFactory!=riid) 
        return ResultFromScode(E_NOINTERFACE); 
 
    *ppv=NULL; 
 
    //Check each of the three CLSIDs we support. 
    if (CLSID_DataTransferObject==rclsid) 
        *ppv=new CDataTransferClassFactory(); 
 
    if (NULL==*ppv) 
        return ResultFromScode(E_OUTOFMEMORY); 
 
    ((LPUNKNOWN)*ppv)->AddRef(); 
    g_cObj++; 
    return NOERROR; 
    } 
 
 
 
 
 
/* 
 * DllCanUnloadNow 
 * 
 * Purpose: 
 *  Answers if the DLL can be freed, that is, if there are no 
 *  references to anything this DLL provides. 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  BOOL            TRUE if nothing is using us, FALSE otherwise. 
 */ 
 
STDAPI DllCanUnloadNow(void) 
    { 
    SCODE   sc; 
 
    //Our answer is whether there are any object or locks 
    sc=(0L==g_cObj && 0L==g_cLock) ? S_OK : S_FALSE; 
    return ResultFromScode(sc); 
    } 
 
 
 
 
/* 
 * ObjectDestroyed 
 * 
 * Purpose: 
 *  Function for the DataObject object to call when it gets 
 *  destroyed.  Since we're in a DLL we only track the number 
 *  of objects here letting DllCanUnloadNow take care of the rest. 
 */ 
 
void ObjectDestroyed(void) 
    { 
    g_cObj--; 
    return; 
    } 
 
 
 
 
 
/* 
 * CDataTransferClassFactory::CDataTransferClassFactory 
 * CDataTransferClassFactory::~CDataTransferClassFactory 
 * 
 * Constructor Parameters: 
 *  None 
 */ 
 
CDataTransferClassFactory::CDataTransferClassFactory(void) 
    { 
    m_cRef=0L; 
    return; 
    } 
 
 
CDataTransferClassFactory::~CDataTransferClassFactory(void) 
    { 
    return; 
    } 
 
 
 
 
 
 
/* 
 * CDataTransferClassFactory::QueryInterface 
 * CDataTransferClassFactory::AddRef 
 * CDataTransferClassFactory::Release 
 */ 
 
STDMETHODIMP CDataTransferClassFactory::QueryInterface 
    (REFIID riid, PPVOID ppv) 
    { 
    *ppv=NULL; 
 
    //Any interface on this object is the object pointer. 
    if (IID_IUnknown==riid || IID_IClassFactory==riid) 
        *ppv=this; 
 
    if (NULL!=*ppv) 
        { 
        ((LPUNKNOWN)*ppv)->AddRef(); 
        return NOERROR; 
        } 
 
    return ResultFromScode(E_NOINTERFACE); 
    } 
 
 
STDMETHODIMP_(ULONG) CDataTransferClassFactory::AddRef(void) 
    { 
    return ++m_cRef; 
    } 
 
 
STDMETHODIMP_(ULONG) CDataTransferClassFactory::Release(void) 
    { 
    if (0!=--m_cRef) 
        return m_cRef; 
 
    delete this; 
    ObjectDestroyed(); 
    return 0; 
    } 
 
 
 
 
 
 
 
/* 
 * CDataTransferClassFactory::CreateInstance 
 * 
 * Purpose: 
 *  Instantiates a DataObject object that supports the IDataObject 
 *  and IUnknown interfaces.  If the caller asks for a different 
 *  interface than these two then we fail. 
 * 
 * Parameters: 
 *  pUnkOuter       LPUNKNOWN to the controlling IUnknown if we 
 *                  are being used in an aggregation. 
 *  riid            REFIID identifying the interface the caller 
 *                  desires to have for the new object. 
 *  ppvObj          PPVOID in which to store the desired 
 *                  interface pointer for the new object. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR if successful, otherwise E_NOINTERFACE 
 *                  if we cannot support the requested interface. 
 */ 
 
STDMETHODIMP CDataTransferClassFactory::CreateInstance 
    (LPUNKNOWN pUnkOuter, REFIID riid, PPVOID ppvObj) 
    { 
    PCDataObject        pObj; 
    HRESULT             hr; 
 
    *ppvObj=NULL; 
    hr=ResultFromScode(E_OUTOFMEMORY); 
 
    if (NULL!=pUnkOuter && IID_IUnknown!=riid) 
        return ResultFromScode(E_NOINTERFACE); 
 
    //Create the object telling it the data size to work with 
    pObj=new CDataObject(pUnkOuter, ObjectDestroyed); 
 
    if (NULL==pObj) 
        return hr; 
 
    if (pObj->Init()) 
        hr=pObj->QueryInterface(riid, ppvObj); 
 
    //Kill the object if initial creation or Init failed. 
    if (FAILED(hr)) 
        delete pObj; 
    else 
        g_cObj++; 
 
    return hr; 
    } 
 
 
 
 
 
 
/* 
 * CDataTransferClassFactory::LockServer 
 * 
 * Purpose: 
 *  Increments or decrements the lock count of the DLL.  If the lock 
 *  count goes to zero and there are no objects, the DLL is allowed 
 *  to unload.  See DllCanUnloadNow. 
 * 
 * Parameters: 
 *  fLock           BOOL specifying whether to increment or 
 *                  decrement the lock count. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR always. 
 */ 
 
STDMETHODIMP CDataTransferClassFactory::LockServer(BOOL fLock) 
    { 
    if (fLock) 
        g_cLock++; 
    else 
        g_cLock--; 
 
    return NOERROR; 
    }