I've been struggling with this for hours, and I haven't made any progress.
I am creating COM objects in c#. My objects will available for use right
next to a collection of c++ COM objects. What makes this challenging, is
that there are existing applications, both managed and unmanaged, that load
and use these objects.
For 3 of the 4 permutations, this works correctly (Managed/unmanaged,
client/server).
The problem comes when a managed app tries to use my managed COM object. In
an effort to ensure timely releases of the objects, these applications use
Marshal.ReleaseComObject to free the COM object. When the COM object is
managed code, this throws a "Specified cast is not valid" exception.
For interfaces returned from the interface methods, I can address this with
EnterpriseServicesHelper.WrapIUnknownWithComObject. However, I don't know
what to do with the instantiation of the COM object itself.
Changing the client apps isn't a practical solution. There are existing
application. I need for the COM object to behave like a true COM object
here.
I've looked at ExtensibleClassFactory class, and ICustomFactory. If the
solution is there, I can't understand it. I've also looked at application
domains. This doesn't seem to contain the solution either.
Is this a solvable problem?
lgs.lgs - 09 May 2006 01:26 GMT
With further investigation, I also find that: attempting to instantiate the
COM object using this code fails with "771A6CA6-937C-4593-BF1B-C4CB81F3EECA
is either not valid or not registered"
[ComImport,
Guid("771A6CA6-937C-4593-BF1B-C4CB81F3EECA")]
public class CPullSource
{
}
CPullSource cps = new CPullSource();
Even though I use this format all the time for creating instances of
unmanaged COM objects. Note that this code DOES work:
Guid g3 = new Guid("771A6CA6-937C-4593-BF1B-C4CB81F3EECA");
Type comtype2 = Type.GetTypeFromCLSID(g3);
object oo = Activator.CreateInstance( comtype2);
In summary: It almost looks like the managed client is using the managed COM
object as more of an assembly then a true COM object. I'm sure that does
great things for performance and all, but it is just not going to work for
my application.
Is there some way to "cut the cord" between the client and the COM object?
I need the managed client to treat the managed COM object the same way it
treats unmanaged COM objects. And I need to do this by changing the COM
object, rather than the clients.
I'm using vs 2003, but 2005 is an option if it will solve these
problems.
Phil Wilson - 09 May 2006 22:02 GMT
It's not clear to me that this will work - you're trying to connect a
runtime callable wrapper to a Com callable wrapper, and I don't believe this
is a supported scenario. You have managed client code calling a (managed)
interop assembly calling a CCW generated to let unmanaged code call a
managed Com client. I don't know how to break that chain without changing
the client code (by using late binding or having the client call an
unmanaged Com Dll as a shim).

Signature
Phil Wilson [MVP Windows Installer]
----
> I've been struggling with this for hours, and I haven't made any progress.
>
[quoted text clipped - 24 lines]
>
> Is this a solvable problem?
lgs.lgs - 09 May 2006 23:22 GMT
> It's not clear to me that this will work - you're trying to connect a
> runtime callable wrapper to a Com callable wrapper, and I don't believe
[quoted text clipped - 3 lines]
> changing the client code (by using late binding or having the client call
> an unmanaged Com Dll as a shim).
Yes, that's pretty much what I'm trying to do.
I've done this quite often with the COM objects in this framework:
c#->rcw->c++ code
And this seems to work with the COM object I'm trying to add to the
framework:
c++->ccw->c# code
And this DOES work:
c#->rcw->ccw->c# code
It just appears that interop uses special knowledge of the fact that we are
going from managed to managed to pass things along, rather than doing all
the rcw/ccw marshaling. It almost seems like when it recognizes that's
what's happening, it shortcuts it to:
c#->c#
> and I don't believe this is a supported scenario.
It's not clear why it wouldn't be. One of the main benefits of COM is that
you don't need to care about the language of the client vs server, what
allocators they use, etc. Why should the ccw care where it's being called
from? And why should the rcw care that the COM object is managed? They
just need to carry on a normal, per-spec COM conversation.
Indeed, the fact that it DOESN'T work the way normal COM objects do suggests
that someone has gone out of their way to write code to make this work
"better." And truly, I'm sure it -is- much better performance-wise. Just
not compatibility-wise, which is what I need here.
I wonder if there is an answer in the System.Runtime.Remoting.Services
namespace? I have found that wrapping objects with
EnterpriseServicesHelper.WrapIUnknownWithComObject before I return them
changes them into System.__ComObjects, which is what I believe I want.
dw
Bob Eaton - 10 May 2006 11:53 GMT
I'm not at all sure this is what you're talking about, but I have a managed
class (a collection) which instantiates (and then "collects") different COM
servers. Some of the COM servers are unmanaged (written in C++ using
MFC/ATL) and some of them are managed (written in C# in the same assembly as
the client collection object even).
The way I made this work was to define the interface above the managed
server class base class. Then the unmanaged COM servers implement that
interface as acquired via an "#import" statement of the managed tlb file.
Managed servers:
// define interface
public interface IFoo
{
string Bar
{
get;
}
};
// define managed class which implements interface defined above
class ManagedFoo : public IFoo
{
string Bar
{
get { return "asdg"; };
}
};
Then my collection client app creates these things (regardless of whether
they were managed or unmanged) by using their ProgID:
string strProgId = "MyNamespace.ManagedFoo";
// or "MyNamespace.UnmanagedFoo" for the unmanaged flavor
Type typeFoo = Type.GetTypeFromProgID(strProgId);
IFoo rThing = (IFoo) Activator.CreateInstance(typeFoo);
Now the managed client can play with both managed and unmanaged servers
according only to the interface definition.
I specifically remember there was no way to do this without the interface
definition, because there was no way I could get the managed client to think
of the unmanaged servers as being of type ManagedFoo (i.e. a .Net class
definition). So the "least common denominator" was the COM interface, which
.Net supports the specifying of via "interface" definitions.
Bob
> I've been struggling with this for hours, and I haven't made any progress.
>
[quoted text clipped - 10 lines]
> use Marshal.ReleaseComObject to free the COM object. When the COM object
> is managed code, this throws a "Specified cast is not valid" exception.
lgs.lgs - 11 May 2006 00:41 GMT
Hey Bob, thanks for the response.
As it happens, these objects do inherit from a common interface
(IBaseFilter). While I'm not currently maintaining a collection of them, it
is certainly possible that client apps will be doing eactly that.
And when they do, I want them to be able to call Marshal.ReleaseComObject on
all of them, without having to special case my managed ones. After all,
these client apps may be already released applications. And it's quite
reasonable of them to expect that new COM objects will behave the same way
as existing ones.
I haven't tried Type.GetTypeFromProgID, but I have tried
Type.GetTypeFromCLSID, as will as calling PInvoke with CoCreateInstance.
.NET always recognizes that it's .net calling .net, and refuses to treat
them like other COM objects.
dw
Bob Eaton - 12 May 2006 04:38 GMT
What's the need for clients to call RelaseComObject? I don't do that, and
nothing seems like it's leaking.
In my managed clients, I always deal with the interface pointer (i.e. IFoo)
rather than the managed class thingy (i.e. _ManagedFoo or ManagedFoo) and
when I stop refering to them, they seem to go away just fine... regardless
of whether they were managed or not...
At least I don't get any warnings when I quit debugging that something
leaked...
Bob
> Hey Bob, thanks for the response.
>
[quoted text clipped - 14 lines]
>
> dw
lgs.lgs - 12 May 2006 05:51 GMT
> What's the need for clients to call RelaseComObject? I don't do that, and
> nothing seems like it's leaking.
It's a question of what resources the object contains. If the object is
holding open a file, a network connection, or some limited resource (such as
a device), you need the object to be released on demand so that you can do
other things with it. If it isn't holding open anything in particular, you
can wait for the GC to collect it.
Whether it's a good idea or a bad idea, I need to work with existing
applications which expect this to work.
dw