Passing custom interface pointer

I have an ATL exe project which exposes a COM object.The aim is to return the interface pointer of another COM object as the return of a method call.

So if I have 2 COM objects
1.ObjInvoked housed in a DLL,exposing a custom interface IObjWire

2.ObjInvoker housed within the ATL exe project,contains a method GetpIf(IObjwire** ptr).
This idl imports the idl of the Dll to allow a successful compilation/linking of the project.

Now when a client tries to instantiate ObjInvoker it fails.However,if i change the GetIpf to return IUnknown**,the object is invoked successfully.

Is the presence of a custom interface in the method semantics causing th efailure in instantiation?If so,is there some legal way to make the return of a custom interface pointer possible?
ranadhirAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

_ys_Commented:
Why not just mimick the Qi mechanism. It's also extensible, obviously.

Within your IDL declare your method as
HRESULT GetpIf([in] REFIID riid, [out, iid_is(riid)] void** ptr)
0
DexstarCommented:
ranadhir:

> Now when a client tries to instantiate ObjInvoker it fails.However,if i change
> the GetIpf to return IUnknown**,the object is invoked successfully.

When you say "it fails", what do you mean?  What is the HRESULT that gets returned?  Does it fail on your call to CoCreateInstance?

In my experience, using IUnknown** is good, because then you can return whatever interface you want, and your client can QI for the interface that they want.

Hope that helps,
Dex*
0
andrewjbCommented:
I'm using this, so it _can_ work.

I presume you're passing an IObjwire** to the function and not an IUnknown** or somthing?

Your are AddRef-ing the returned value?

I think _ys_'s suggestion is bad. A void pointer doesn't get marshalled, so it won't work if you're cross process or probably even cross thread. It's not actuall legal. I still don't understand how QueryInterface manages it - I presume it's 'magic' inside COM somewhere.

So,

No reason why you can't return an IObjwire** - as Dexstar asks, in what way does it fail?

and

if you give up, change the function to reutrn an IUnknown** and then QueryInterface. But DON'T use a (void**).
0
Cloud Class® Course: SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

DexstarCommented:
Actually, a void pointer WILL get marshalled if it is declared in the IDL like "([in] REFIID riid, [out, iid_is(riid)] void** ptr)".  If that weren't true, then QueryInterface wouldn't work either...  :)

Dex*
0
_ys_Commented:
>> I think _ys_'s suggestion is bad.
Who are we to disagree with Don Box ... when he suggested it ...

>> Is the presence of a custom interface in the method semantics causing th efailure in instantiation?
The universal marshaller won't know what to do with out, so unless you've rolled your own proxy/stub, it has little or no option.

>> [Dexstar] a void pointer WILL get marshalled if it is declared in the IDL like "([in] REFIID riid, [out, iid_is(riid)] void** ptr)
ditto.

Saying that, a lot of people prefer to write:
HRESULT GetpIf([in] REFIID riid, [out, iid_is(riid)] IUnknown** ptr);

>> [andrewjb] change the function to reutrn an IUnknown** and then QueryInterface.
>> [Dexstar] In my experience, using IUnknown** is good, because then you can return whatever interface
>> you want, and your client can QI for the interface that they want.

Sorry, but two round trips _cannot_ be better than one. [iid_is] eliminates the second round trip. Round trips are evil. Minimise them.

>> [andrewjb] It's not actuall legal.
The most basic of COM interface IUnknown uses this syntax within QueryInterface. Also, all IEnumXxxx implementations use it as well. It's out there. Accept it.

>> Now when a client tries to instantiate ObjInvoker it fails.
Soundds like a VB client - [in, out] usually circumvents this error with custom interfaces.
0
ranadhirAuthor Commented:
Nice discussion mates.Thanks for the inputs - but in my case it was just a case of improper registration of the proxy stub - The second interface worked with Iunknow because it inherited from IDispatch and hence used the OLE marshalling.Sorry for the over-sight.
0
andrewjbCommented:
Could we just continue for a moment, on the discussion about void ** and marshalling.

I think I'm right. void pointers (void* or void**) are _Not_ marshalled. QueryInterface is a magic exception. Yes, I know that it's fundamental to COM that the pointer needs to be marshalled. But I think that's the point - it's fundamental, and someone in the depths of COM knows that it needs to marshall this particular void**, but no others.

Any comments?
0
andrewjbCommented:
Got it!

From MSDN:

"Custom Interfaces and Standard Marshaling"

Do not use void *. If you need a pointer to a generic interface pointer, use IUnknown **. The void ** arguments used in QueryInterface are an exception because MIDL inherently understands the semantics of the function, but it doesn't understand the semantics of your own function.
0
ranadhirAuthor Commented:
It is really a very interesting discussion - could you providethe link to the above write-up?A simple search in MSDN for the above did not return the relevant article.It would be much appreciated.
0
andrewjbCommented:
Hmmm.. I actually got it from the version you get with VC++.

Books : "Inside OLE" Chapter 6; "Standard Marshalling Architecture"

Does that exist under MSDN on the 'net?
0
_ys_Commented:
Sorry to have to reiterate:

>> a void pointer WILL get marshalled if it is declared in the IDL like "([in] REFIID riid, [out, iid_is(riid)] void** ptr)

This statement is claused:
>> if it is declared in the IDL like [...]

At no point did we (Dexstar and myself) state that void* values are marshalled correctly. Apologies for any confusion we may have caused - it did make for an interesting read though.


To clear things up with IUnknown:
IUnknown doesn't get marshalled. It's decorated within IDL with the [local] attribute. IRemUnknown is where everything gets marshalled.
0
andrewjbCommented:
_ys_ : I'm not trying to argue, just understand what happens and how it all works :-)

Could you expand on your statement "IUnknown doesn't get marshalled."

If I pass an IUnknown * or IUnknown ** cross-process, then it _is_ marshalled, isn't it? It works in both processes, so it must be? Or am I wrong? And what's IRemUnknown? Never heard of that one, yet...


Andrew.
0
_ys_Commented:
>> _ys_ : I'm not trying to argue, just understand what happens and how it all works :-)
Nor I - just sharing knowledge.

As you pointed out:
>> MIDL inherently understands the semantics of the function

Ever wondered why AddRef and Release return unsigned long values rather than the 'mandatory' HRESULTS.

[local] is a powerful attribute. It allows you to produce seperate function prototypes for local and remote invocations - remote being cross-apartment / machine / whatever.

Have a look inside any custom proxy/stub code and you'll undoubtably find IRemUnknown.
0
ranadhirAuthor Commented:
I am just trying to summarize the discussion till now , so that I can put it down into test proto-types to enhance my understanding on this issue:
1. Wrapping a interface within a void** out parameter would fail in a cross-process/cross-apartment invocation, unless it is explicitly decorated with the [out, iid_is(riid)]  property in the IDL.
2. IUnknown** is marshalled properly even without this explicit decoration.
3. Provided proper stub/proxy marshalling support exists,even a custom interface pointer reference should be marshalled properly , without requiring the [out, iid_is(riid)]  specification in the IDL(well no-one commented on this,but I dared to put it down here on the basis of my experience till now)

A follow-up to  be tested on my part -
If a custom interface is mentioned as local in the IDL , it should not be allowed to be marshalled .
So any cross-process attempt to instantiate an object which hands out such a interface pointer through a method call,should meet with failure.

Right?
0
_ys_Commented:
Nice summary ranadhir!


Couldn't resisit a couple of answers/comments.

1. Yes. However a custom proxy/stub could rewrite this - which no-one should ever do (it breaks the language neutrality of COM)

2. Yes.

3. Yes. But why would anyone want to. If IMyInterface was updated sometime in the future and IMyInterface2 was created to replace it, what would clients do? QI - second round trip. The [iid_is] attribute circumvents this problem. by removing the need for the extra QI.

>> A follow-up to  be tested on my part -
Look forward to your findings.

>> If a custom interface is mentioned as local in the IDL , it should not be allowed to be marshalled .
Yes. The auto-generated proxy/stub code produced by the MIDL compiler should not allow [local]ly declared classes to be remoted.

But there's nothing preventing a customised proxy/stub to cloak such an invocation and give out a valid remoted interface pointer - just consider IUnknown for a classic example.


Everything answered with a yes. Nice summary ranadhir.
0
andrewjbCommented:
Yup - don't delete it. Could be useful in the future!
0
_ys_Commented:
Another vote for PAQ-ing this, rather than deletion.

Posts contain good imformation. Diverged a little from the initial question, but fuelled by it.
0
DexstarCommented:
I vote against deleting it too...  Didn't the asker get a helpful answer?

Dex*
0
ranadhirAuthor Commented:
Sorry mates - I am not much aware of the modularities;but if deletion flushes out this discussion, i would hate it too.In that case,maybe we can put it in a state whether it stays in the database for further relelvant updations later(is that PAQ?).
Anyway,a quick test based on my summary above shows that all the cases are successful(IUnknown*,void** and custom interface pointer).

I had a DLL which implemented custom marshalling (IMarshal).This was loaded by an EXE to hand out the queried interface to the client.The EXE provided 3 methods to hand out the interface pointers(based on the summary above),and they all got through.

A couple of  clarifications relelvant to the discussion above :

1. comment from _y_s ...
 >> Now when a client tries to instantiate ObjInvoker it fails.
Soundds like a VB client - [in, out] usually circumvents this error with custom interfaces.

Could not grasp the relevance  this statement. I thought that [in,out] was relevant to the memory handling rather, than marshalling.
Or you meant a IDispatch scripting client like VBScript ( because  I thought VB is almost as good as native C++ in handling custom interfaces even through non-OLE marshalling ).

2. Though for all practical purposes IUnknown*& is equivalent to IUnknown** and much cleaner in semantics,why has COM ignored references ?

About the points , I would request the points to be split equally between all who have participated to make this thread so exciting;and to keep this question alive to let everyone give inputs to this basic yet so important aspect of COM.Does that sound fine?
0
_ys_Commented:
>why has COM ignored references ?
Interesting question.

Two possible arguments.

1.IDL itself uses a pure C syntax. References don't exist in C.

2.COM is supposed to be language independent. Not all languages have the concept of references, I guess. Addresses can be conceptualised easy enough in any language - taking VB as an example (no _direct_ support).

>Sounds like a VB client - [in, out] usually circumvents this error with custom interfaces.
I purposefully avoided explaining this one, as it's related to VB internals - this is a C++ forum.

Here goes:
For an [out] parameter VB delays determining the actual type definition until an instance is actully requested. When you request this interface, it enumerates it's methods determining what the VB equivalents should look like. It hits this [out] parameters and throws up - it doesn't know what this _exact_ interface is (could be a base pointer etc.). It could reasonably accomodate it, but chooses not to (treats it as a void* --> could be anything). Had it been typed IDispatch it would accept it.

For an [in, out] parameter VB _has_ to now the exact makeup of the interface beforehand. That way it can decide whether or not it can accomodate it.

The existence of [iid_is] allows VB to accept a pure [out] parameter, as it at least has a hint on what the _exact_ interface is.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
tinchosCommented:
No comment has been added lately, so it's time to clean up this TA.
I will leave the following recommendation for this question in the Cleanup topic area:

Accept: _ys_ {http:#9770506}

Please leave any comments here within the next seven days.
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Tinchos
EE Cleanup Volunteer
0
DexstarCommented:
@tinchos : The Asker specifically requested that the points be split.  In their last comment, they wrote "About the points , I would request the points to be split equally between all who have participated to make this thread so exciting;and to keep this question alive to let everyone give inputs to this basic yet so important aspect of COM.Does that sound fine? "

They even increased the number of points as requested.

Dex*
0
tinchosCommented:
Dex

I was quite aware of it , but the fact that it was just a 40 points question and 3 users posted comments made me unable to split between all of you (The minimun split is of 20 points).

Given the fact that I couldn't fullfill the askers request, I felt that the most fair thing to do was to award _ys_ the points.

Tincho
0
DexstarCommented:
Upon reviewing the discussion, I agree...

Dex*
0
_ys_Commented:
Is it Ok if I agree too ...

Just reread this entire post. Certainly one of the more interesting EE posts I've been involved in.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.