Results 1 to 10 of 10

Thread: Implementing IUnkown and accessing Interfaces

  1. #1

    Thread Starter
    Frenzied Member Magiaus's Avatar
    Join Date
    Mar 2002
    Location
    swamp land
    Posts
    1,267

    Implementing IUnkown and accessing Interfaces

    ok, i ave 3 c++ books open and a shell programming in vb book and i can't find a good explination of how to impament an interface. can some one please give me a run down on it or a crash course. i am using vc++ 7 but am willing to open 6 if it will get me going easier

    thanks in advance
    Magiaus

    If I helped give me some points.

  2. #2
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    You have two options, one easier but less flexible than the other. I will explain only the easier one now. See this URL for an explanation of the other method.

    Code:
    // A implementation is usually a C++ class that provides
    // implementation for all methods of all base interfaces.
    // In this case we want to implement the IStream interface, which
    // provides methods for file-like reading and writing.
    
    // This is our class
    class CByteArrayStream : public IStream, IClassFactory
    {
      // This is the reference count: it counts how many pointers to
      // this object exist.
      UINT m_uiRefC;
    public:
      // IUnknown methods:
      STDMETHOD_(UINT, AddRef)();
      STDMETHOD_(UINT, Release)();
      STDMETHOD(QueryInterface)(REFIID iid, void** ppv);
    
      // IClassFactory methods:
      STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID iid, void **ppvObject);
      STDMETHOD(LockServer)(BOOL fLock);
    
      // IStream methods:
      STDMETHOD(Read)(void *pv, ULONG cb, ULONG *pcbRead);
      STDMETHOD(Write)(void const* pv, ULONG cb, ULONG *pcbWritten);
      STDMETHOD(Seek)(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *pLibNewPosition);
      STDMETHOD(SetSize)(ULARGE_INTEGER libNewSize);
      // and and and...
    };
    
    // Implement the functions. This is a simple object which doesn't
    // support aggregation, so we can keep the IUnknown functions
    // very simple.
    STDMETHODIMP_(UINT) CByteArrayStream::AddRef()
    {
    // The purpose of AddRef is to increment the reference count
    // so that the object only destroys itself when it is no longer needed.
      return ++m_uiRefC;
    }
    
    STDMETHODIMP_(UINT) CByteArrayStream::Release()
    {
    // Release inverts the behavior of AddRef and destroys the object
    // if it is no longer needed.
      if(--m_uiRefC == 0)
      {
        UINT ret = m_uiRefC;
        delete this;
        return ret;
      }
      else
        return m_uiRefC;
    }
    
    STDMETHODIMP CByteArrayStream::QueryInterface(REFIID iid, void **ppv)
    {
    // See if interface supported and return pointer.
      if(iid == IID_IUnknown)
      {
    // A new copy of the pointer
        AddRef();
    // The casting is necessary!!!
        *ppv = (void*)((IUnknown*)this);
        return S_OK;
      }
      else if(iid == IID_IClassFactory)
      {
        AddRef();
        *ppv = (void*)((IClassFactory*)this);
        return S_OK;
      }
      else if(iid == IID_IStream)
      {
        AddRef();
        *ppv = (void*)((IStream*)this);
        return S_OK;
      }
      else
      {
    // interface not supported
        *ppv = NULL;
        return E_NOINTERFACE;
      }
    }
    
    // The IClassFactory is necessary because CoCreateInstance first calls CoGetClassObject which obtains a IClassFactory pointer on
    // this object and then uses the CreateInstance method to get
    // more objects
    
    // I must admit that I'm very unsure whether this
    // implementation makes sense.
    STDMETHODIMP CByteArrayStream::CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
    {
    // Don't support aggregation
      if(pUnkOuter != NULL)
      {
        *ppv = NULL;
        return CLASS_E_NOAGGREGATION;
      }
      IUnknown *pNew = new CByteArrayStream;
      hr=pNew->QueryInterface(iid, ppv);
      pNew->Release(); // We don't need this pointer anymore
      return hr;
    }
    
    // I have no idea what IClassFactory::LockServer is for
    STDMETHODIMP CByteArrayStream::LockServer(BOOL bLock)
    {
      return E_NOTIMPLEMENTED;
    }
    
    // Implement the functionality of IStream - this is the
    // actual thing your object should do!
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  3. #3

    Thread Starter
    Frenzied Member Magiaus's Avatar
    Join Date
    Mar 2002
    Location
    swamp land
    Posts
    1,267
    ok. I understand that fairly well. i think... going to play with it and see what i can get working.
    Magiaus

    If I helped give me some points.

  4. #4
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    If I had my reference with me I could help you more, but I don't have it, sorry. (I'd need the MFC source)
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  5. #5

    Thread Starter
    Frenzied Member Magiaus's Avatar
    Join Date
    Mar 2002
    Location
    swamp land
    Posts
    1,267
    yeah i have everything but a good mfc and atl book. i thought i would never need mfc shows what i know.
    Magiaus

    If I helped give me some points.

  6. #6
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    Big mistake on my part!!!
    Your COM object doesn't need to implement IClassFactory.
    Instead, there is a "class object" associated with your COM object which only implements IClassFactory, whose CreateInstance methode returns a pointer to your COM object and which is somehow obtained by calling CoGetClassObject with your class id. But I don't know how, must research into it (stupid MSDN's explanations are so bad...).
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  7. #7
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    I think I've found it.

    Any DLL that contains COM objects must implement and export the function DllGetClassObject, which allocates a class object (an object that implements IClassFactory) and returns a pointer to it.

    Any EXE that contains COM objects must call CoRegisterClassObject on startup, passing the CLSID of the COM object, an IUnknown pointer to the class object, context, flags and a pointer to the return value which can be used to unregister the class object.

    CoGetClassObject will determine which context the COM object uses and act appropriatly: a DLL context triggers these actions:
    a) Load the DLL (if not loaded)
    b) Blend into app mem space
    c) Call DllGetClassObject and return

    An EXE context triggers these actions:
    a) Start the exe
    b) Wait until it has registered it's class object
    c) Obtain the class object and return
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  8. #8
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    So here comes a full COM object DLL source.
    Code:
    class CByteArrayStream : public IStream
    {
      UINT m_uiRefC;
    public:
      // IUnknown methods:
      STDMETHOD_(UINT, AddRef)();
      STDMETHOD_(UINT, Release)();
      STDMETHOD(QueryInterface)(REFIID iid, void** ppv);
    
      // IStream methods:
      STDMETHOD(Read)(void *pv, ULONG cb, ULONG *pcbRead);
      STDMETHOD(Write)(void const* pv, ULONG cb, ULONG *pcbWritten);
      STDMETHOD(Seek)(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *pLibNewPosition);
      STDMETHOD(SetSize)(ULARGE_INTEGER libNewSize);
      // and and and...
    };
    
    STDMETHODIMP_(UINT) CByteArrayStream::AddRef()
    {
      return ++m_uiRefC;
    }
    
    STDMETHODIMP_(UINT) CByteArrayStream::Release()
    {
      if(--m_uiRefC == 0)
      {
        delete this;
        return 0;
      }
      else
        return m_uiRefC;
    }
    
    STDMETHODIMP CByteArrayStream::QueryInterface(REFIID iid, void **ppv)
    {
      if(ppv == NULL)
        return E_INVALIDARG;
      if(iid == IID_IUnknown)
      {
        AddRef();
        *ppv = (void*)((IUnknown*)this);
        return S_OK;
      }
      else if(iid == IID_IStream)
      {
        AddRef();
        *ppv = (void*)((IStream*)this);
        return S_OK;
      }
      else
      {
        *ppv = NULL;
        return E_NOINTERFACE;
      }
    }
    
    // Implement the functionality of IStream - this is the
    // actual thing your object should do
    
    // Here is the class object
    class CMyClassObject : public IClassFactory
    {
      UINT m_uiRefC;
      UINT m_bLockCount;
    
    public:
      CMyClassObject() : m_bLockCount(0), m_uiRefC(0) {};
      ~CMyClassObject() { pCO = NULL; };
    
      // IUnknown methods:
      STDMETHOD_(UINT, AddRef)();
      STDMETHOD_(UINT, Release)();
      STDMETHOD(QueryInterface)(REFIID iid, void** ppv);
    
      // IClassObject methods
      STDMETHOD(CreateInstance)(IUnknown *pOut, REFIID iid, void **ppvObject);
      STDMETHOD(LockServer)(BOOL bLock);
    
      // This is the only object of this type
      static CMyClassObject *pCO;
    };
    
    CMyClassObject * CMyClassObject::pCO = NULL;
    
    STDMETHODIMP_(UINT) CMyClassObject::AddRef()
    {
      return ++m_uiRefC;
    }
    
    STDMETHODIMP_(UINT) CMyClassObject::Release()
    {
      if(--m_uiRefC == 0)
      {
        delete this;
        return 0;
      }
      else
        return m_uiRefC;
    }
    
    STDMETHODIMP CMyClassObject::QueryInterface(REFIID iid, void **ppv)
    {
      if(ppv == NULL)
        return E_INVALIDARG;
      if(iid == IID_IUnknown)
      {
        AddRef();
        *ppv = (void*)((IUnknown*)this);
        return S_OK;
      }
      else if(iid == IID_IClassFactory)
      {
        AddRef();
        *ppv = (void*)((IClassFactory*)this);
        return S_OK;
      }
      else
      {
        *ppv = NULL;
        return E_NOINTERFACE;
      }
    }
    
    STDMETHODIMP CMyClassObject::CreateInstance(IUnknown *pOut, REFIID iid, void **ppvObject)
    {
      if(ppvObject == NULL)
        return E_INVALIDARG;
      if(pOut != NULL)
        return CLASS_E_NOAGGREGATION;
      CByteArrayStream *p = new CByteArrayStream;
      if(p == NULL)
        return E_OUTOFMEMORY;
      HRESULT hr =  p->QueryInterface(iid, ppvObject);
      p->Release();
      return hr;
    }
    
    STDMETHODIMP CMyClassObject::LockServer(BOOL bLock)
    {
      if(bLock)
      {
        AddRef();
        m_bLockCount++;
      }
      else if(!bLock && m_bLockCount > 0)
      {
        Release();
        m_bLockCount--;
      }
      else
        return E_UNEXPECTED;  // too many calls
      return S_OK;
    }
    
    STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv)
    {
      if(ppv == NULL)
        return E_INVALIDARG;
      *ppv = NULL;
      if(clsid != Id_Of_CByteArrayStream)
        return CLASS_E_CLASSNOTAVAILABLE;
      pCO = new CMyClassObject();
      if(!pCO)
        return E_OUTOFMEMORY;
      return pCO->QueryInterface(iid, ppv);
    }
    
    STDAPI DllCanUnloadNow()
    {
      return CMyClassObject::pCO != NULL;
    }
    Last edited by CornedBee; Sep 9th, 2002 at 05:28 AM.
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  9. #9

    Thread Starter
    Frenzied Member Magiaus's Avatar
    Join Date
    Mar 2002
    Location
    swamp land
    Posts
    1,267
    I think I get it. Windows uses the class the inherits IClassFactory and gives a pointer to my object to manage the instacing of my object and all that jazz. vb6 made it so easy. i get the idea but i am going to have to do some heavy study before i feel confident about building a big object.

    thanks CornedBee
    Magiaus

    If I helped give me some points.

  10. #10
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    Yep.

    I'm now going to build a COM object for me. Exactly what I posted here: it implements IStream.
    It's purpose is to offer an IStream interface for a CByteArray - I need this to load an image from a DAO DB binary blob using CImage...

    Currently I load the data, copy to a HGLOBAL block and call CreateIStreamOnHGlobal, but that's a waste.
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width