In an application I'm involved with we're trying to get a drag-and-
drop functionality to work between a mfc dialog and a .NET forms
within the same process. Here is an slightly abbreviate code snippet
from mfc part:
COleDataSource * pDataSource = new COleDataSource;
// fill-in code
pDataSource->CacheGlobalData(...);
pDataSource->DoDragDrop ();
This code works! The correct data is transferred to the destination!
The problem now is cleaning up.
Adding a delete pDataSource causes an access violation in destination
code.
What I've noticed is that pDataSource->m_dwRef is increment to 1 on
creation and increased to 3 if OnDragEnter\OnDragDrop or OnDragEnter
\OnDragLeave occurs in destination code.
Seen some code example use the following pattern:
pDataSource->DoDragDrop ();
if ( pDataSource->m_dwRef <= 1)
delete pDataSource;
else
pDataSource->ExternalRelease();
Does this make sense?
After a bit of digging I found out that COleDataSource (actually
CCmdTarget ) contains several sort of similar methods:
* InternalRelease
* ExternalRelease
* ExternalDisconnect
What is the difference between Internal and External Release?
What is the difference between Release and Disconnect?
Thanks for any help.
> COleDataSource * pDataSource = new COleDataSource;
> // fill-in code
> pDataSource->CacheGlobalData(...);
> pDataSource->DoDragDrop ();
>
> This code works! The correct data is transferred to the destination!
> The problem now is cleaning up.
>
> Adding a delete pDataSource causes an access violation in destination
> code.
>
> What I've noticed is that pDataSource->m_dwRef is increment to 1 on
> creation and increased to 3 if OnDragEnter\OnDragDrop or OnDragEnter
> \OnDragLeave occurs in destination code.
Those OLE objects are reference counted, so I would not call 'delete' on
them...
I would "respect" their contract of AddRef/Release (these are methods
present in IUknown, the root interface for COM/OLE objects).
> Seen some code example use the following pattern:
>
> pDataSource->DoDragDrop ();
> if ( pDataSource->m_dwRef <= 1)
> delete pDataSource;
> else
> pDataSource->ExternalRelease();
I would just call ->ExternalRelease (or InternalRelease, if there is no
aggregation).
I think that the difference between External and Internal is about
aggregation: External methods like ExternalRelease are to properly manage
aggregation.
pDataSource->DoDragDrop();
pDataSource->ExternalRelease();
// or
// pDataSource->InternalRelease();
// if there's no aggregation
I believe that Internal/ExternalRelease will decrement the reference count,
and call C++ 'delete' if reference count is 0.
However, I'm not sure about that, and I would prefer experimenting with some
real code, also considering that you are mixing MFC and managed world of
.NET here... (I think that computer science is an "experimental" science :)
we need compilers, debuggers, log files, etc. to diagnose things and try to
find solutions to bug.)
BTW: I would also post this question in the ATL group.
There is a COM very expert there whose name is Igor (T.): he knows a lot
about COM and OLE and I believe he will give you a detailed answer...
Giovanni
> I would just call ->ExternalRelease (or InternalRelease, if there is no
> aggregation).
>
> I think that the difference between External and Internal is about
> aggregation: External methods like ExternalRelease are to properly manage
> aggregation.
>
> pDataSource->DoDragDrop();
> pDataSource->ExternalRelease();
> // or
> // pDataSource->InternalRelease();
> // if there's no aggregation
>
> I believe that Internal/ExternalRelease will decrement the reference
> count, and call C++ 'delete' if reference count is 0.
I stepped into source code of CCmdTarget::ExternalRelease, and it calls
InternalRelease, which in turns calls OnFinalRelease virtual method if
reference count reaches value of 0.
OnFinalRelease does a 'delete this' as last statement.
Giovanni
So you are saying this would be a good solution:
if ( pDataSource->m_dwRef <= 1)
pDataSource->InternalRelease();
else
pDataSource->ExternalRelease();
> I think that the difference between External and Internal is about
> aggregation: External methods like ExternalRelease are to properly manage
> aggregation.
>
> pDataSource->DoDragDrop();
> pDataSource->ExternalRelease();
> // or
> // pDataSource->InternalRelease();
> // if there's no aggregation
>
> I believe that Internal/ExternalRelease will decrement the reference count,
> and call C++ 'delete' if reference count is 0.
>
> However, I'm not sure about that, and I would prefer experimenting with some
> real code, also considering that you are mixing MFC and managed world of
> .NET here... (I think that computer science is an "experimental" science :)
> we need compilers, debuggers, log files, etc. to diagnose things and try to
> find solutions to bug.)
>
Will continue to experiment.
> BTW: I would also post this question in the ATL group.
> There is a COM very expert there whose name is Igor (T.): he knows a lot
> about COM and OLE and I believe he will give you a detailed answer...
>
> Giovanni
Will post it there aswell.
Thanks for your help.
No, I would just do:
pDataSource->InternalRelease();
(or pDataSource->ExternalRelease() if you are using aggregation.)
Internal/ExternalRelease will do 'delete this' if reference count is 0.
Giovanni
Thanks for your excellent help.
Please excuse my ignorance but I do not understand what aggregation
means in the context of COM.
> Thanks for your excellent help.
You're welcome.
> I do not understand what aggregation
> means in the context of COM.
This is a good article about COM Aggregation:
"COM Concept : Unleashing Aggregation"
http://www.codeproject.com/KB/COM/unleashingaggregation.aspx
Giovanni
Thanks.
I actually found the article a bit difficult to understand [a lack of
experience with COM is probably a factor].
To me Aggragation is similar to composition but without ownership of
the object in question.
In the code I do not make any explicit calls to EnableAggregation \
QueryAggregates or simliar. Only used CacheGlobalData, DoDragDrop and
InternalRelease so far...
The data being drag-and-drop'ed contains text if that makes any
difference.
> In the code I do not make any explicit calls to EnableAggregation \
> QueryAggregates or simliar. Only used CacheGlobalData, DoDragDrop and
> InternalRelease so far...
As I wrote, I would suggest you to do just:
pDataSource->InternalRelease();
(No explicit 'delete' - the object will delete itself when its reference
count reaches 0.)
I would also put a:
pDataSource = NULL;
after InternalRelease() call, so that in the code that follows you don't
have dangling references (after InternalRelease, you have no more control
over the object).
Try that way.
You may also do some tracing in debug builds (use TRACE macro or write
something to a log file...), to check that the object actually gets deleted.
For example, you can put these tracing messages in your object destructor,
so you can see when the object gets deleted.
Note that the object may be destructed also after you call InternalRelease.
In fact, you pass the object to the OLE engine, so OLE can use that object
in other contexts you can't control. What is important is that you are
coherent with OLE specifications, calling InternalRelease when *you* don't
need the object anymore. When also OLE doesn't need the object, it will
decrease the reference count, and only when the reference count gets 0, a
'delete this;' statement will be executed, cleaning up the object.
If you put a tracing/log message in your object destructor, when 'delete
this;' is executed (when reference count gets 0), that message will be
printed/written to your log file, so you are sure that the object is cleaned
up correctly.
Giovanni
Thought I'd thank you again for your excellent help.
Performed various test on the updated code and it is working as
expected.
Cheers :)
> Thought I'd thank you again for your excellent help.
>
> Performed various test on the updated code and it is working as
> expected.
>
> Cheers :)
You're very welcome :)
I learnt a lot in these newsgroups (and I still learn), and I enjoy helping
and giving back when I can.
Cheers
Giovanni