|
-
Aug 31st, 2002, 04:41 PM
#1
Thread Starter
Frenzied Member
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.
-
Sep 2nd, 2002, 10:44 AM
#2
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.
-
Sep 2nd, 2002, 11:08 AM
#3
Thread Starter
Frenzied Member
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.
-
Sep 3rd, 2002, 03:42 AM
#4
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.
-
Sep 3rd, 2002, 03:45 AM
#5
Thread Starter
Frenzied Member
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.
-
Sep 9th, 2002, 04:31 AM
#6
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.
-
Sep 9th, 2002, 04:50 AM
#7
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.
-
Sep 9th, 2002, 05:15 AM
#8
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.
-
Sep 9th, 2002, 06:44 AM
#9
Thread Starter
Frenzied Member
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.
-
Sep 9th, 2002, 06:56 AM
#10
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|