Solved

Passing custom interface pointer

Posted on 2003-11-03
29
515 Views
Last Modified: 2013-11-25
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?
0
Comment
Question by:ranadhir
  • 8
  • 6
  • 5
  • +2
29 Comments
 
LVL 9

Expert Comment

by:_ys_
ID: 9670430
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
 
LVL 19

Expert Comment

by:Dexstar
ID: 9671831
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
 
LVL 12

Expert Comment

by:andrewjb
ID: 9671920
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
 
LVL 19

Expert Comment

by:Dexstar
ID: 9673349
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
 
LVL 9

Expert Comment

by:_ys_
ID: 9673558
>> 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
 

Author Comment

by:ranadhir
ID: 9676342
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
 
LVL 12

Expert Comment

by:andrewjb
ID: 9677467
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
 
LVL 12

Expert Comment

by:andrewjb
ID: 9677508
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
 

Author Comment

by:ranadhir
ID: 9677656
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
 
LVL 12

Expert Comment

by:andrewjb
ID: 9677673
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
 
LVL 9

Expert Comment

by:_ys_
ID: 9677971
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
 
LVL 12

Expert Comment

by:andrewjb
ID: 9677982
_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
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 9

Expert Comment

by:_ys_
ID: 9678067
>> _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
 

Author Comment

by:ranadhir
ID: 9683451
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
 
LVL 9

Expert Comment

by:_ys_
ID: 9685587
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
 
LVL 12

Expert Comment

by:andrewjb
ID: 9746288
Yup - don't delete it. Could be useful in the future!
0
 
LVL 9

Expert Comment

by:_ys_
ID: 9746684
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
 
LVL 19

Expert Comment

by:Dexstar
ID: 9749357
I vote against deleting it too...  Didn't the asker get a helpful answer?

Dex*
0
 

Author Comment

by:ranadhir
ID: 9768127
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
 
LVL 9

Accepted Solution

by:
_ys_ earned 40 total points
ID: 9770506
>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
 
LVL 9

Expert Comment

by:tinchos
ID: 10242483
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
 
LVL 19

Expert Comment

by:Dexstar
ID: 10244661
@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
 
LVL 9

Expert Comment

by:tinchos
ID: 10245429
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
 
LVL 19

Expert Comment

by:Dexstar
ID: 10245660
Upon reviewing the discussion, I agree...

Dex*
0
 
LVL 9

Expert Comment

by:_ys_
ID: 10254124
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

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Suggested Solutions

With most software applications trying to cater to multiple user needs nowadays, the focus is to make them as configurable as possible. For e.g., when creating Silverlight applications which will connect to WCF services, the service end point usuall…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

23 Experts available now in Live!

Get 1:1 Help Now