Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Cannot convert "System.__ComObject"

485 views
Skip to first unread message

Hagus

unread,
Jan 13, 2008, 10:06:00 PM1/13/08
to
I have a C# program that accesses a COM+ server application. There is an
Interop assembly that exposes an interface to the COM+ application. The
interface is ISegmentCycle. An outline of the code is:
Object mySegmentCycleObj;
NGLINC.ISegmentCycle mySegmentCycle;
...
Type typeApp = Type.GetTypeFromCLSID(guidApp, "localhost", false);
mySegmentCycleObj = Activator.CreateInstance(typeApp, true);
mySegmentCycle = (NGLINC.ISegmentCycle) mySegmentCycleObj;

That code works just fine. I would like to be able to access the component
from PowerShell. My best effort so far is:
$mytype = [System.Type]::GetTypeFromCLSID($myclsid, "localhost", $false)
[reflection.assembly]::LoadWithPartialName("Interop.NGLINC")
[System.Object] $myobj = [System.Activator]::CreateInstance($mytype, $true)
[NGLINC.ISegmentCycle] $myseg = [NGLINC.ISegmentCycle] $myobj

I get an error:
Cannot convert "System.__ComObject" to "NGLINC.ISegmentCycle".
At \powershell\test.ps1:13 char:62
+ [NGLINC.ISegmentCycle] $myseg = [NGLINC.ISegmentCycle] $myobj <<<<

$myobj is indeed System.__ComObject.

Is there a way to do this with a PowerShell script?

If not, do you think I would stand a better chance be creating a PSSnapin?

Thanks,
Mike


Oisin Grehan [MVP]

unread,
Jan 13, 2008, 11:16:55 PM1/13/08
to

Hi Mike,

This can be a tricky area, one that demands more time to explain that
I can spare at the moment, but hopefully this snippet will point you
in the right direction:

$myType= [Type]::GetTypeFromCLSID($guid)
$com = [Activator]::CreateInstance($myType)
$obj = [Marshal]::CreateWrapperOfType($com, [NGLINC.ISegmentCycle])

Suffice it to say, powershell's specialised com adapters get in the
way here - it's a lot easy to work with when you've got proper primary
interop assemblies (PIAs), but hopefully this will work for you.

Hope this helps,

- Oisin / x0n

Hagus

unread,
Jan 14, 2008, 1:28:11 PM1/14/08
to
Oisin,

Thanks for the suggestion.

My code now looks like:


$mytype = [System.Type]::GetTypeFromCLSID($myclsid, "localhost", $false)
[reflection.assembly]::LoadWithPartialName("Interop.NGLINC")
[System.Object] $myobj = [System.Activator]::CreateInstance($mytype, $true)
[NGLINC.ISegmentCycle] $myseg =

[System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($myobj,
[NGLINC.ISegmentCycle])

I still get an error:
Exception calling "CreateWrapperOfType" with "2" argument(s): "The type must
be __ComObject or be derived from __ComObj
ect.
Parameter name: t"
At \powershell\test.ps1:15 char:94
+ [NGLINC.ISegmentCycle] $myseg =
[System.Runtime.InteropServices.Marshal]::CreateWrapperOfType( <<<< $myobj,
[NGLINC.ISegmentCycle])

I would appreciate more suggestons and comments.

Thanks,
Mike

Oisin Grehan [MVP]

unread,
Jan 14, 2008, 7:13:02 PM1/14/08
to
Hi Hagus,

Try not to translate the C# so literally. Using prefixed casts in
powershell are rarely required since it usually just does the Right
Thing when converting arguments for target methods. The casts are
kinda weird in posh as they are applied at runtime at various points
in a variable's life and may interfere with the normal adapter
mechanisms. So, drop the [object] and [isegmentcycle] cast prefixes:

$mytype = [Type]::GetTypeFromCLSID($myclsid, "localhost", $false)
[reflection.assembly]::LoadWithPartialName("Interop.NGLINC")
$myobj = [Activator]::CreateInstance($mytype, $true)
$myseg =
[Runtime.InteropServices.Marshal]::CreateWrapperOfType($myobj,
[NGLINC.ISegmentCycle])

Note that you can also drop the "system" prefix for namespaces. I'm
also not sure why you insist on using the GetTypeFromCLSID overload
that requires a server name? if you're working against localhost, then
just use the overload I supplied.

- Oisin

> > - Oisin / x0n- Hide quoted text -
>
> - Show quoted text -

Hagus

unread,
Jan 14, 2008, 8:35:01 PM1/14/08
to
Oisin

I changed my code as you recommended:
$mytype = [Type]::GetTypeFromCLSID($myclsid)
[reflection.assembly]::LoadWithPartialName("Interop.NGLINC")
$myobj = [Activator]::CreateInstance($mytype)


$myseg = [Runtime.InteropServices.Marshal]::CreateWrapperOfType($myobj,
[NGLINC.ISegmentCycle])

I'm seeing two errors. The first one is:
Exception calling "CreateInstance" with "2" argument(s): "Cannot load type
'I2_tManager, RECABS, Version=1.0.1.0, Cultu
re=neutral, PublicKeyToken=23a330035d5d6b0d'."
At \powershell\test.ps1:7 char:37
+ $myobj = [Activator]::CreateInstance( <<<< $mytype, $true)

The next is as I last reported:


Exception calling "CreateWrapperOfType" with "2" argument(s): "The type must
be __ComObject or be derived from __ComObject.
Parameter name: t"

At \powershell\test.ps1:16 char:64
+ $myseg = [Runtime.InteropServices.Marshal]::CreateWrapperOfType( <<<<
$myobj, [NGLINC.ISegmentCycle])

The first error goes away if I change the GetTypeFromCLSID back to the way
it was:


$mytype = [Type]::GetTypeFromCLSID($myclsid, "localhost", $false)

Thanks,
Mike

Oisin Grehan [MVP]

unread,
Jan 14, 2008, 9:29:30 PM1/14/08
to

Hi Mike

The reason the error goes away is because the final argument of $false
prevents the method from displaying the first error ;-) Change it to
$true (meaning "display errors when trying to create the type") and
you will see why you have a problem - the second error message occurs
because $null is indeed not derived from __ComObject. It appears that
a dependency (i2_tmanager) is failing to load, probably because it is
just plain missing. I have no clue what this assembly is for, so I
hope you can figure out the rest! I suggest loading your interop
assembly into Lutz Roeder's free Reflector tool (google it) and
examining the dependencies.

Hagus

unread,
Jan 15, 2008, 12:21:01 PM1/15/08
to
Oisin,

Thanks for the reference to Reflector. I’ve had the tool for quite a while
and use it regularly. I2_tManager is class within the assembly RECABS.
RECABS is a COM+ server component; it uses System.EnterpriseServices.

The use of $false in GetTypeFromCLSID is not masking an error in this case.
If I change it to $true, I still only get the one error on
CreateWrapperOfType. If I use GetTypeFromCLSID with either $true or $false,
after [Activator].CreateInstance $myobj is not $null. $myobj.PSObject shows:
Members : {Get_SessnInfo, HUBSTAT, IsLocal}
Properties : {}
Methods : {Get_SessnInfo, HUBSTAT, IsLocal}
ImmediateBaseObject : System.__ComObject
BaseObject : System.__ComObject
TypeNames :
{System.__ComObject#{8c5032a6-3bd7-3995-a73b-c8abe1920336},
System.__ComObject, System.MarshalByR
efObject, System.Object}

System.__ComObject is the same thing I have in my C# code after calling
Activator.CreateInstance. The difference is that in C#, I can cast
System.__ComObject to NGLINC.ISegmentCycle and then make calls using methods
described by the ISegmentCycle interface. In PowerShell that fails.

I’m coming to the conclusion that there is either a problem or limitation
with PowerShell that precludes scripting against my COM+ server application.
It’s not a big deal; VBScript and WSH can’t handle it either. It’s just that
I started out hopeful that PowerShell included enough additional flexibility
to allow me to script against a COM+ server of this type.

Thanks,
Mike

Oisin Grehan [MVP]

unread,
Jan 16, 2008, 5:04:39 PM1/16/08
to
> > - Oisin- Hide quoted text -

>
> - Show quoted text -

Ok, I just realised that you probably wrote the COM server yourself,
and as such you probably also generated the interop assembly with
tlbimp.exe; read up on the /primary and /key(container) switches on
tlbimp. Sorry for the terseness, but you seem relatively technical and
can probably infer (or discover) what I don`t mention here. If not,
post back and I can elaborate. Ultimately, instead of using a clsid or
progid to instantiate your com object which indirectly loads a primary
interop assembly (or dynamically creates an interop assembly if it
cannot find a PIA in the GAC), you can indirectly create your com
server by instantiating the interop class directly. If your com class
is MyNamespace.I2_tManager, the managed interop class will named the
same, e.g. MyNamespace.I2_tManager; however, there is an additional
class created in the interop assembly that is suffed with "Class" -
e.g. "MyNamspace.I2_tManagerClass" you can also new-up this class if
you load the interop assembly directly. However, this alternate class
imports and implements the imported COM interfaces as managed
interfaces and you should be able to cast to obtain the COM interface
you need. It`s a really complex area as you can see, and when viewed
through the "let me look after that for you sir" lens of powershell,
it`s murkier still.

0 new messages