I found a pretty good tutorial at http://www.catch22.net/tuts/dragdrop.asp
that covers some of the basics. But while it states it already explained how
to implement IUnknown members, it does not.
I've been searching the Web for the past 20 minutes and I don't understand
why I can't find a simple implementation of the IUnknown members. Can anyone
help me understand why I'm not finding them?
I had Inside OLE 2 but, after being forced to move during some financial
problems, my search through packed boxes failed to turn up this book.
Surely, people are implementing IUnknown all the time. Can anyone point me
to where some examples are given?
Thanks!
Jonathan
I strongly urge you to pick up a copy of ATL Internals by Sells & Rector,
which explains the ATL plumbing in detail.
Brian
Then another good resource is Inside COM by Rogerson.
It's pretty painful to do this without the help of a framework. ATL is very
convenient and has a very small footprint.
I'm curious why you are precluding ATL.
Brian
> Oops... did you mean non-ATL?
Yup.
> Then another good resource is Inside COM by Rogerson.
Yeah, it's looking more and more like I need to pick up a book.
Unfortunately, the local Borders (2 of them) and others don't seem to stock
these books. At Amazon, I may be looking at a week or more before delivery.
> It's pretty painful to do this without the help of a framework. ATL is
very
> convenient and has a very small footprint.
>
> I'm curious why you are precluding ATL.
I'm beginning to get a little curious about that myself. But, as a
consultant, I have to meet the requirements of the client.
Thanks.
--
Jonathan Wood
SoftCircuits
http://www.softcircuits.com
Available for consulting: http://www.softcircuits.com/jwood/resume.htm
class CObj : public I1, public I2
{
...
public:
STDMETHOD(QueryInterface)(REFIID riid, void** ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
...
private:
volatile LONG m_nRef;
}
STDMETHODIMP CObj::QueryInterface(REFIID riid, void** ppv)
{
if (ppv == NULL) {
return E_POINTER;
}
if (IsEqualIID(riid, IID_I1) || IsEqualIID(riid, IID_IUnknown)) {
*ppv = reinterpret_cast<void*>(static_cast<I1*>(this));
} else if (IsEqualIID(riid, IID_I2)) {
*ppv = reinterpret_cast<void*>(static_cast<I2*>(this));
} else {
*ppv = NULL;
}
if (*ppv != NULL) {
(void)reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
} else {
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CObj::AddRef()
{
return (ULONG)InterlockedIncrement(&m_nRef);
}
STDMETHODIMP_(ULONG) CObj::Release()
{
LONG ret = InterlockedDecrement(&m_nRef);
if (ret == 0) {
delete this;
}
return (ULONG)ret;
}
Note I just typed it here, so it may not compile right away.
If you don't need thread-safe reference count, you can use simply
++m_nRef and --m_nRef in place of the interlocked functions.
Here's how you use the class:
CObj* pObj = new CObj;
pObj->AddRef();
I1 *p1 = NULL; // or this is returned
pObj->QueryInterface(IID_I1, (void**)&p1);
pObj->Release();
pObj = NULL;
// use p1 from now on
--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnic...@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:%23$xukQijF...@TK2MSFTNGP10.phx.gbl...
I think I may still be in over my head with this (I've ordered both Inside
OLE--which I owned at one time--and Essential COM but they could be weeks
getting here) but I'm willing to spend a little time trying to learn if I
can get some additional information.
I guess I1 and I2 are just placeholders for interfaces that I may want to
implement, and not actual symbols. Is that right?
Other code I've seen just uses if (riid == IDD_I1). Can you tell me why you
prefer to use IsEqualIID()?
So, I guess I need to implement IDataObject, IDropSource, and IDropTarget
(for starters). Do I just repeat the code for each interface as shown below,
reimplementing all needed methods for each one?
class CDataObject : public IDataObject
{
...
}
class CDropSource : public IDropSource
{
...
}
class CDropTarget : public IDropTarget
{
...
}
One thing I don't get: all the code I've seen reimplements QueryInterface,
AddRef, and Release for each and every interface. Shouldn't there be a way
to inherit this functionality, only adding the methods not included in
IUnknown?
--
Jonathan Wood
SoftCircuits
http://www.softcircuits.com
Available for consulting: http://www.softcircuits.com/jwood/resume.htm
"Alexander Nickolov" <agnic...@mvps.org> wrote in message
news:%23ns0QBu...@TK2MSFTNGP15.phx.gbl...
--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnic...@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:OAze$NujFH...@TK2MSFTNGP10.phx.gbl...
> Thanks Alexander,
>
> I think I may still be in over my head with this (I've ordered both Inside
> OLE--which I owned at one time--and Essential COM but they could be weeks
> getting here) but I'm willing to spend a little time trying to learn if I
> can get some additional information.
>
> I guess I1 and I2 are just placeholders for interfaces that I may want to
> implement, and not actual symbols. Is that right?
Correct. My example showed an object implementing two interfaces
in addition to IUnknown. Your objects can implement any number
of interfaces, just add if clauses. The first if always serves IUnknown
as well, and is thus special. Note, all your objects for supporting
drag and drop will most likely implement a single interface.
>
> Other code I've seen just uses if (riid == IDD_I1). Can you tell me why
> you
> prefer to use IsEqualIID()?
Either way is fine. the Platform SDK has an even faster version with
InlineIsEqualIID. Note, if you you program in C you cannot compare
structs, not that this is an issue here...
>
> So, I guess I need to implement IDataObject, IDropSource, and IDropTarget
> (for starters). Do I just repeat the code for each interface as shown
> below,
> reimplementing all needed methods for each one?
Yes, these are all diferent objects. It makes no sense to have a single
object implement all three of these interfaces. Note that IDataObject
and IDropSource objects are provided by the originator of drag and
drop, while the IDropTarget object is implemented by the target.
E.g. if you want to act as a drop target, you only need to implement
an object with IDropTarget and associate it with your window. If,
however, you want to act as a drag source, you need to implement
the other two objects as well.
Chapters 10-13 in "Inside OLE" should give you all the info you
need to implement OLE drag and drop.
>
> class CDataObject : public IDataObject
> {
> ...
> }
>
> class CDropSource : public IDropSource
> {
> ...
> }
>
> class CDropTarget : public IDropTarget
> {
> ...
> }
>
> One thing I don't get: all the code I've seen reimplements QueryInterface,
> AddRef, and Release for each and every interface. Shouldn't there be a way
> to inherit this functionality, only adding the methods not included in
> IUnknown?
Of course. That's what ATL gives you in the first place. You can create
your own table driven IUnkown implementation as well. My example
wasn't table-driven - it was simple code for illustration.
> > One thing I don't get: all the code I've seen reimplements
QueryInterface,
> > AddRef, and Release for each and every interface. Shouldn't there be a
way
> > to inherit this functionality, only adding the methods not included in
> > IUnknown?
>
> Of course. That's what ATL gives you in the first place. You can create
> your own table driven IUnkown implementation as well. My example
> wasn't table-driven - it was simple code for illustration.
To make sure I understand then, you're saying that I can use IUnknown as a
base classe. But that the only way the QueryInterface method will work
correctly for all derived classes is if QueryInterface uses a table lookup
for supported interfaces, which derived classes may modify.
That makes sense if that's what you mean.
--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnic...@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:eNe2S4u...@TK2MSFTNGP09.phx.gbl...
> IUnknown is the base interface for all COM interfaces.
> It _has_ to be implemented for each object separately.
> However, you can have a helper class implement it based
> on a table provided by its derived class. For example in
> ATL IUnknown is "implemented" in CComObjectRootEx<>
> and the table located in the derived class is comprised of
> COM_INTERFACE_ENTRY macros. ATL uses another class
> derived from your COM class to actually implement
> IUnknown (so you can have several lifetime models) by
> delegating to CComObjectRootEx<>. The most popular
> version is CComObject<>, but there are others as well.
> If you are interested in ATL, I will also recommend you to
> read "ATL Internals" by Brent Rector and Chris Sells.
I'm still waiting for my COM/OLE books so I won't worry too much about ATL
just yet.
I've been using an online tutorial that has some errors and is somewhat
incomplete. I've obviously still got a long way to go but, based on the
tutorial and your own code, here's the class declarations I've come up with.
Perhaps you'd let me know if you'd do something different. Thanks.
#pragma once
#include "objidl.h"
class CDataObject : public IDataObject
{
public:
// Construction
CDataObject();
~CDataObject();
// IUnknown members
STDMETHOD(QueryInterface)(REFIID iid, void **ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IDataObject members
STDMETHOD(GetData)(FORMATETC *pFormatEtc, STGMEDIUM *pmedium);
STDMETHOD(GetDataHere)(FORMATETC *pFormatEtc, STGMEDIUM *pmedium);
STDMETHOD(QueryGetData)(FORMATETC *pFormatEtc);
STDMETHOD(GetCanonicalFormatEtc)(FORMATETC *pFormatEct, FORMATETC
*pFormatEtcOut);
STDMETHOD(SetData)(FORMATETC *pFormatEtc, STGMEDIUM *pMedium, BOOL
fRelease);
STDMETHOD(EnumFormatEtc)(DWORD dwDirection, IEnumFORMATETC
**ppEnumFormatEtc);
STDMETHOD(DAdvise)(FORMATETC *pFormatEtc, DWORD advf, IAdviseSink *, DWORD
*);
STDMETHOD(DUnadvise)(DWORD dwConnection);
STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppEnumAdvise);
protected:
// Any private members and functions
ULONG m_lRefCount;
};
class CDropSource : public IDropSource
{
public:
// Construction
CDropSource();
~CDropSource();
// IUnknown members
STDMETHOD(QueryInterface)(REFIID iid, void **ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IDropSource members
STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
STDMETHOD(GiveFeedback)(DWORD dwEffect);
protected:
// Any private members and functions
ULONG m_lRefCount;
};
class CDropTarget : public IDropTarget
{
public:
// Construction
CDropTarget();
~CDropTarget();
// IUnknown members
STDMETHOD(QueryInterface)(REFIID iid, void **ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IDropTarget members
STDMETHOD(DragEnter)(IDataObject *pDataObject, DWORD grfKeyState, POINTL
pt, DWORD *pdwEffect);
STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
STDMETHOD(DragLeave)(void);
STDMETHOD(Drop)(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt,
DWORD *pdwEffect);
protected:
// Any private members and functions
ULONG m_lRefCount;
};
One other things:
Given the declarations I just posted, let's say I have a class CMainWin the
derives from CWin (my own classes). Can I do the following:
class CMainWin : public CDropTarget, public CWin
{
...
};
Would this work as expect, and allow CMainWin to use "this" as a pointer to
a IDropTarget interface?
Thanks!
--
Jonathan Wood
SoftCircuits
http://www.softcircuits.com
Available for consulting: http://www.softcircuits.com/jwood/resume.htm
"Alexander Nickolov" <agnic...@mvps.org> wrote in message
news:uoGVC$ujFHA...@tk2msftngp13.phx.gbl...
--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnic...@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:ebDQjuvj...@TK2MSFTNGP15.phx.gbl...
I forgot about Release deleting the object--obviously that would change.
So what would you normally do? Just add a pointer in the CDropTarget class
that references the object window or whatever that is responsible for
pasting data?
Thanks.
--
Jonathan Wood
SoftCircuits
http://www.softcircuits.com
Available for consulting: http://www.softcircuits.com/jwood/resume.htm
"Alexander Nickolov" <agnic...@mvps.org> wrote in message
news:eSHxj0Tk...@TK2MSFTNGP10.phx.gbl...
--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnic...@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:%23Gcy23T...@tk2msftngp13.phx.gbl...
--
Jonathan Wood
SoftCircuits
http://www.softcircuits.com
Available for consulting: http://www.softcircuits.com/jwood/resume.htm
"Alexander Nickolov" <agnic...@mvps.org> wrote in message
news:uoYEmMUk...@TK2MSFTNGP12.phx.gbl...