FIGURE 2.

---------



# MAKEFILE for George's Bitmap Control OCX

#

# George Cross, Borland C++ Technical Support, July 1997

#

# Built with Borland C++Builder 1.0 (with patch)

# 

# Not dependent on BUILTINS.MAK, BCC32.CFG, TLINK32.CFG, PATH nor 

# any other environment variables.

#



.autodepend



TARGET = bmpctrl

OBJS = $(TARGET).obj dllentry.obj ioleobj.obj bmpctrl_i.obj irunnabl.obj

RESES = $(TARGET).res



$(TARGET).ocx:  bcc.cfg $(TARGET).tlb $(OBJS) $(RESES)

  $(MAKEDIR)\tlink32.exe @&&|

    /Tpd /v /x /L$(MAKEDIR)\..\lib  +

    c0d32 $(OBJS)

    $.



    import32 cw32



    $&

|



bcc.cfg:

  copy &&|

    -DUNICODE -DSTRICT -DINC_OLE2 

    -I$(MAKEDIR)\..\include

    -Hc -H"$(TARGET).h" -H=$(TARGET).csm

    -c -v

| $.

  

.odl.tlb:

  $(MAKEDIR)\midl.exe @&&|

    /win32 /c_ext /ms_ext /I$(MAKEDIR)\..\include\idl

    /h bmpctrl_ifc.h

    /cpp_cmd $(MAKEDIR)\cpp32.exe 

    /cpp_opt "-oCON -I$(MAKEDIR)\..\include -D__MKTYPLIB__"

| $.



.cpp.obj:

  $(MAKEDIR)\bcc32 +bcc.cfg { $& }



.c.obj:

  $(MAKEDIR)\bcc32 +bcc.cfg $.



.rc.res:

  $(MAKEDIR)\brcc32 $.



clean:

  del *.obj *.csm *.?0? *.t?2 *.dll *.ocx *.tlb \

      $(TARGET)_i.c $(TARGET)_ifc.h *.res *.cfg



-------------------------------------------------------------------------------

FIGURE 3.

---------



[

uuid(8E020160-F600-11D0-A9C4-00C04FD20A3D),

     helpstring("George's Bitmap Control's Type Library"),

     lcid(0x0000),

     version(1.0)

]

library TBmpCtrlLibrary

{

  importlib("STDOLE32.TLB");

  importlib("oc30.dll");



  [

  uuid(EA266FA1-F608-11D0-A9C4-00C04FD20A3D),

       helpstring("Dispatch Interface DITBmpCtrl")

  ]

  dispinterface DITBmpCtrl

    {

    }



  [

  uuid(AF534240-F4BA-11D0-A9C2-00C04FD20A3D),

       helpstring("George's Bitmap Control"),

       control

  ]

  coclass TBmpCtrl

  {

    dispinterface DITBmpCtrl; 

  }

};

      

-------------------------------------------------------------------------------

FIGURE 4.

---------



#if !defined(BMPCTRL_H)

#define BMPCTRL_H



#include "bmpctrl_ifc.h"



class TImpIClassFactory : public IClassFactory

{

  protected:

      ULONG           m_cRef;



  public:

      TImpIClassFactory(void);

      ~TImpIClassFactory(void);



      //IUnknown members

      STDMETHODIMP         QueryInterface(REFIID, void**);

      STDMETHODIMP_(ULONG) AddRef(void);

      STDMETHODIMP_(ULONG) Release(void);



      //IClassFactory members

      STDMETHODIMP     CreateInstance(LPUNKNOWN, REFIID, void**);

      STDMETHODIMP     LockServer(BOOL);

};





//forward declarations

class TImpIOleObject;

class TImpIRunnableObject;



//BMPCTRL.CPP

class TBmpCtrl : public IUnknown

{

  friend class TImpIOleObject;

  friend class TImpIRunnableObject;



  private:

    ULONG             m_cRef;

    DWORD             m_dwConn;

    LPUNKNOWN         m_pDefIUnknown;    //IUnknown of Ole Data Cache

    LPUNKNOWN         m_pUnkOuter;       //IUnknown of outer aggregating object

    LPOLEOBJECT       m_pImpIOleObject;       //Aggregated

    LPRUNNABLEOBJECT  m_pImpIRunnableObject;

    LPOLECLIENTSITE   m_pIOleClientSite;      //From client

    LPOLEADVISEHOLDER m_pIOleAdviseHolder;    //From client

    CLSID             m_Clsid;

    void (*m_pfnDestroyed)();



    HRESULT GrabFromClipboard();



  public:

    TBmpCtrl(LPUNKNOWN, void(*pfnDestroyed)());

    ~TBmpCtrl();

    HRESULT Init();



    //IUnknown members

    STDMETHODIMP         QueryInterface(REFIID, void**);

    STDMETHODIMP_(ULONG) AddRef(void);

    STDMETHODIMP_(ULONG) Release(void);

  

};

  



//IOLEOBJ.CPP

class TImpIOleObject: public IOleObject

{

  private:

      ULONG           m_cRef;

      TBmpCtrl*       m_pObj;

      LPUNKNOWN       m_pUnkOuter;



  public:

      TImpIOleObject(TBmpCtrl*, LPUNKNOWN);

      ~TImpIOleObject();



      //IUnknown members that delegate to m_pUnkOuter.

      STDMETHODIMP         QueryInterface(REFIID, void**);

      STDMETHODIMP_(ULONG) AddRef(void);

      STDMETHODIMP_(ULONG) Release(void);



      //IOleObject members

      STDMETHODIMP SetClientSite(LPOLECLIENTSITE);

      STDMETHODIMP GetClientSite(LPOLECLIENTSITE *);

      STDMETHODIMP SetHostNames(LPCOLESTR, LPCOLESTR);

      STDMETHODIMP Close(DWORD);

      STDMETHODIMP SetMoniker(DWORD, LPMONIKER);

      STDMETHODIMP GetMoniker(DWORD, DWORD, LPMONIKER *);

      STDMETHODIMP InitFromData(LPDATAOBJECT, BOOL, DWORD);

      STDMETHODIMP GetClipboardData(DWORD, LPDATAOBJECT *);

      STDMETHODIMP DoVerb(LONG, LPMSG, LPOLECLIENTSITE, LONG

		       , HWND, LPCRECT);

      STDMETHODIMP EnumVerbs(LPENUMOLEVERB *);

      STDMETHODIMP Update(void);

      STDMETHODIMP IsUpToDate(void);

      STDMETHODIMP GetUserClassID(CLSID *);

      STDMETHODIMP GetUserType(DWORD, LPOLESTR *);

      STDMETHODIMP SetExtent(DWORD, LPSIZEL);

      STDMETHODIMP GetExtent(DWORD, LPSIZEL);

      STDMETHODIMP Advise(LPADVISESINK, DWORD *);

      STDMETHODIMP Unadvise(DWORD);

      STDMETHODIMP EnumAdvise(LPENUMSTATDATA *);

      STDMETHODIMP GetMiscStatus(DWORD, DWORD *);

      STDMETHODIMP SetColorScheme(LPLOGPALETTE);

};



//IRUNNABL.CPP

class TImpIRunnableObject : public IRunnableObject

    {

    protected:

        ULONG           m_cRef;

        TBmpCtrl*       m_pObj;

        LPUNKNOWN       m_pUnkOuter;



    public:

        TImpIRunnableObject(TBmpCtrl*, LPUNKNOWN);

        ~TImpIRunnableObject();



        STDMETHODIMP QueryInterface(REFIID, void**);

        STDMETHODIMP_(ULONG) AddRef(void);

        STDMETHODIMP_(ULONG) Release(void);



        STDMETHODIMP GetRunningClass(LPCLSID);

        STDMETHODIMP Run(LPBINDCTX);

        STDMETHODIMP_(BOOL) IsRunning(void);

        STDMETHODIMP LockRunning(BOOL, BOOL);

        STDMETHODIMP SetContainedObject(BOOL);

    };

#endif //BMPCTRL_H





-------------------------------------------------------------------------------

FIGURE 5.

---------



#include <ole2.h>

#include "bmpctrl.h"

#define __TRACE

#include <checks.h>



// Count number of objects and number of locks.



ULONG       g_cObj=0;

ULONG       g_cLock=0;







STDAPI __declspec(dllexport) DllGetClassObject(REFCLSID rclsid, REFIID riid

    , void** ppv)

    {

    TRACE("Inside DllGetClassObject");

    TImpIClassFactory* pCF;

    HRESULT         hr;



    if (CLSID_TBmpCtrl!=rclsid)

        return E_FAIL;



    //Check that we can provide the interface

    if (IID_IUnknown!=riid && IID_IClassFactory!=riid)

        return E_NOINTERFACE;



    //Return our factory's IClassFactory

    pCF=new TImpIClassFactory();



    if (NULL==pCF)

        return ResultFromScode(E_OUTOFMEMORY);



    //If the factory hasn't the interface, delete it

    hr=pCF->QueryInterface(riid, ppv);



    if (FAILED(hr))

        delete pCF;



    return hr;

    }





STDAPI __declspec(dllexport) DllCanUnloadNow()

    {

    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);

    }





void ObjectDestroyed(void)

    {

    g_cObj--;

    return;

    }





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

//

// IClassFactory

//



TImpIClassFactory::TImpIClassFactory(void)

    {

    m_cRef=0L;

    return;

    }



TImpIClassFactory::~TImpIClassFactory(void)

    {

    return;

    }



STDMETHODIMP TImpIClassFactory::QueryInterface(REFIID riid, void** ppv)

    {

    *ppv=NULL;



    if (IID_IUnknown==riid || IID_IClassFactory==riid)

        *ppv=this;



    if (NULL!=*ppv)

        {

        ((LPUNKNOWN)*ppv)->AddRef();

        return NOERROR;

        }



    return E_NOINTERFACE;

    }





STDMETHODIMP_(ULONG) TImpIClassFactory::AddRef(void)

    {

    return ++m_cRef;

    }





STDMETHODIMP_(ULONG) TImpIClassFactory::Release(void)

    {

    if (0!=--m_cRef)

        return m_cRef;



    delete this;

    return 0;

    }





STDMETHODIMP TImpIClassFactory::CreateInstance(LPUNKNOWN pUnkOuter

    , REFIID riid, void** ppvObj)

    {

    TBmpCtrl*        pObj;

    HRESULT            hr;



    *ppvObj=NULL;

    hr=E_OUTOFMEMORY;



    //No aggregation supported

    if (NULL!=pUnkOuter && IID_IUnknown!=riid)

        return CLASS_E_NOAGGREGATION;



    //Create the object passing function to notify on destruction.

    pObj=new TBmpCtrl(pUnkOuter, ObjectDestroyed);



    if (NULL==pObj)

        return hr;



    hr = pObj->Init();

    if (SUCCEEDED(hr))

        hr=pObj->QueryInterface(riid, ppvObj);

    else {

        delete pObj;

        return hr;

    }



    //Kill the object if initial creation or Init failed.

    if (FAILED(hr))

        delete pObj;

    else

        g_cObj++;



    return hr;

    }





STDMETHODIMP TImpIClassFactory::LockServer(BOOL fLock)

    {

    if (fLock)

        g_cLock++;

    else

        g_cLock--;



    return NOERROR;

    }





-------------------------------------------------------------------------------

FIGURE 6.

---------



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

// 

// IUnknown members:  QueryInterface, AddRef, Release

//



STDMETHODIMP TBmpCtrl::QueryInterface(REFIID riid, void** ppv)

{

  *ppv = NULL;

  

  if (IID_IUnknown==riid)

    *ppv=this;



  if (IID_IOleObject==riid)

  *ppv=m_pImpIOleObject;



  if (IID_IRunnableObject==riid)

  *ppv=m_pImpIRunnableObject;



  if (IID_IViewObject==riid       || IID_IViewObject2==riid ||

      IID_IOleCache==riid         || IID_IOleCache2==riid   ||

      IID_IOleCacheControl==riid  ||

      IID_IDataObject==riid       || IID_IPersistStorage==riid)

    return m_pDefIUnknown->QueryInterface(riid, ppv);



  if (NULL!=*ppv)

  {

    ((LPUNKNOWN)*ppv)->AddRef();

    return NOERROR;

  }



  return E_NOINTERFACE;

}





STDMETHODIMP_(ULONG) TBmpCtrl::AddRef()

{

  return ++m_cRef;

}





STDMETHODIMP_(ULONG) TBmpCtrl::Release()

{

  --m_cRef;

  if (0L!=--m_cRef)

      return m_cRef;



  if (NULL!=m_pfnDestroyed)

      (*m_pfnDestroyed)();



  delete this;

  return 0L;

}





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

// 

// 

//



HRESULT TBmpCtrl::Init()

{

  LPUNKNOWN pIUnknown = this;

  HRESULT hr;

  

  if (NULL!=m_pUnkOuter)

    pIUnknown = m_pUnkOuter;    



  m_pImpIOleObject = new TImpIOleObject(this, pIUnknown);



  if (NULL==m_pImpIOleObject)

    return E_OUTOFMEMORY;



  m_pImpIRunnableObject = new TImpIRunnableObject(this, pIUnknown);



  if (NULL==m_pImpIRunnableObject){

    delete m_pImpIOleObject;

    return E_OUTOFMEMORY;

  }

  hr=CreateDataCache(NULL, CLSID_TBmpCtrl, 

                     IID_IUnknown, &static_cast<void*>(m_pDefIUnknown));



  if (FAILED(hr)){

    delete m_pImpIOleObject;

    delete m_pImpIRunnableObject;

    return hr;

  }

  

  //At this point we are at reference count 0.  The next block will 

  //possibly call TBmpCtrl::Release, so we make an artificial reference

  //count so we don't unload prematurely.

  m_cRef++;

  hr = GrabFromClipboard();

  m_cRef--;

  

  if (FAILED(hr)){

    delete m_pImpIOleObject;

    delete m_pImpIRunnableObject;

    m_pDefIUnknown->Release();

    return hr;

  }

  

  return S_OK;

}





HRESULT TBmpCtrl::GrabFromClipboard()

{

  unsigned int cf = 0;

  HGLOBAL hMem = NULL;

  HRESULT  hr;

  LPUNKNOWN pIUnknown;

  LPPERSISTSTORAGE pIPersistStorage;

  LPOLECACHE pIOleCache;

  STGMEDIUM stm;

  FORMATETC fe;

  DWORD dwConn; 



  if (!OpenClipboard(NULL))

    return E_FAIL;



  stm.tymed = TYMED_MFPICT;

  stm.pUnkForRelease = NULL;



  hMem = GetClipboardData(CF_METAFILEPICT);

  if (NULL!=hMem)

    cf = CF_METAFILEPICT;

  

  if (0==cf) {

    stm.tymed = TYMED_HGLOBAL;

    hMem = GetClipboardData(CF_DIB);

  

    if (NULL!=hMem)

      cf = CF_DIB;

  }  

  if (0==cf){

    stm.tymed = TYMED_GDI;

    hMem = GetClipboardData(CF_BITMAP);

   

    if (NULL!=hMem)

      cf=CF_BITMAP;

  }

  

  if (0==cf)

  {

    stm.tymed=TYMED_HGLOBAL;

    hMem=GetClipboardData(CF_UNICODETEXT);



    if (NULL!=hMem)

      cf=CF_UNICODETEXT;

  }



  stm.hGlobal = OleDuplicateData(hMem,cf,NULL);

  CloseClipboard();





  //Didn't get anything?  Then we're finished.

  if (0==cf)

      return E_FAIL;



  //This now describes the data we have.

  fe.cfFormat = cf;

  fe.dwAspect = DVASPECT_CONTENT;

  fe.ptd = NULL;

  fe.tymed = stm.tymed;

  fe.lindex = -1;



  //By aggregation rules, after calling QueryInterface on an aggregated 

  //object, we must call Release after using the interface.  See

  //Brockshmidt, chapter 2. 



  m_pDefIUnknown->QueryInterface(IID_IOleCache, 

                                 &static_cast<void*>(pIOleCache));

  pIOleCache->Cache(&fe, ADVF_PRIMEFIRST, &dwConn);



  hr=pIOleCache->SetData(&fe, &stm, TRUE);

  pIOleCache->Release();



  if (FAILED(hr))

  {

    ReleaseStgMedium(&stm);

    return E_FAIL;

  }



  return S_OK;

};



  

-------------------------------------------------------------------------------

FIGURE 7.

---------


STDMETHODIMP TImpIOleObject::DoVerb(LONG iVerb, LPMSG pMSG
    , LPOLECLIENTSITE pActiveSite, LONG lIndex, HWND hWndParent
    , LPCRECT pRectPos)
    {
    HRESULT     hr;

    switch (iVerb)
        {
        case OLEIVERB_HIDE:
            break;

        case OLEIVERB_PRIMARY:
        case OLEIVERB_OPEN:
        case OLEIVERB_SHOW:
            /*
             * If we're not running, make sure we are.  In any
             * case, make the dialog visible and insure it has
             * the right parent now.
             */
            hr=NOERROR;
            hr=m_pObj->m_pImpIRunnableObject->Run(NULL);

            if (FAILED(hr)) 
                return E_OUTOFMEMORY;

            break;

        default:
            return OLEOBJ_S_INVALIDVERB;
        }

    return NOERROR;
    }

-------------------------------------------------------------------------------

FIGURE 8.

---------


STDMETHODIMP TImpIRunnableObject::Run(LPBINDCTX pBindCtx)
    {
    return m_pObj->GrabFromClipboard();
    }


