Windows Vista Forums
Vista Forums Home Join Vista Forums Windows 7 Forum Vista Tutorials Tags
Welcome to Windows Vista Forums. Our forum is dedicated to helping you find solutions with any problems, errors or issues you are experiencing with Windows Vista. The Vista forum also covers news and updates and has an extensive Windows Vista tutorial section that covers a wide range of tips and tricks.

Go Back   Vista Forums > Misc Newsgroups > PowerShell

Vista - Using an interop assembly with interop dependencies

Reply
 
Old 10-05-2007   #1 (permalink)
Jeff


 
 

Using an interop assembly with interop dependencies

Using TlbImp.exe, I have created an interop assembly for a COM DLL I
am working with. This assembly has a dependency on MSXML2.dll, so I
created an interop assembly for it, too. Both assemblies are in the
same directory, and I have loaded them both using
[Reflection.Assembly]::LoadFile(). When I try to use a type created
from my assembly, I get an error saying that the MSXML2 assembly
cannot be loaded, because the file cannot be found.

After some experimentation, I was able to get everything to work by
copying MSXML2 interop DLL to $PSHOME. I would rather not do that, if
I don't have to. I also tried adding the two assemblies' directory to
$env:Path, but that didn't work. Other than adding the MSXML2
assembly to the GAC, what are my options?

Jeff


My System SpecsSystem Spec
Old 10-05-2007   #2 (permalink)
Oisin Grehan


 
 

Re: Using an interop assembly with interop dependencies

On Oct 5, 6:56 am, Jeff <jeff.hill...@xxxxxx> wrote:
Quote:

> Using TlbImp.exe, I have created an interop assembly for a COM DLL I
> am working with. This assembly has a dependency on MSXML2.dll, so I
> created an interop assembly for it, too. Both assemblies are in the
> same directory, and I have loaded them both using
> [Reflection.Assembly]::LoadFile(). When I try to use a type created
> from my assembly, I get an error saying that the MSXML2 assembly
> cannot be loaded, because the file cannot be found.
>
> After some experimentation, I was able to get everything to work by
> copying MSXML2 interop DLL to $PSHOME. I would rather not do that, if
> I don't have to. I also tried adding the two assemblies' directory to
> $env:Path, but that didn't work. Other than adding the MSXML2
> assembly to the GAC, what are my options?
>
> Jeff
This is a large and complex topic; start here:
http://groups.google.ca/group/micros...9416d6e857ebb8

- Oisin

My System SpecsSystem Spec
Old 10-05-2007   #3 (permalink)
Jeff


 
 

Re: Using an interop assembly with interop dependencies

On Oct 5, 10:04 pm, Oisin Grehan <ois...@xxxxxx> wrote:
Quote:

>
> This is a large and complex topic; start here:http://groups.google.ca/group/micros...powershell/msg...
>
> - Oisin
I think I have read all of those articles in the past, but I learned a
few new things this time around. Thanks for pointing them out
again.

I won't be able to try anything new until next week, but if I have any
success, I will post back here.

Thanks.

Jeff

My System SpecsSystem Spec
Old 10-08-2007   #4 (permalink)
Jeff


 
 

Re: Using an interop assembly with interop dependencies

On Oct 6, 6:39 am, Jeff <jeff.hill...@xxxxxx> wrote:
Quote:

> On Oct 5, 10:04 pm, Oisin Grehan <ois...@xxxxxx> wrote:
>
>
>
Quote:

> > This is a large and complex topic; start here:http://groups.google.ca/group/micros...powershell/msg...
>
Quote:

> > - Oisin
>
> I think I have read all of those articles in the past, but I learned a
> few new things this time around. Thanks for pointing them out
> again.
>
> I won't be able to try anything new until next week, but if I have any
> success, I will post back here.
>
> Thanks.
>
> Jeff
I ended up writing a few functions to take care of most of the work of
dealing with COM interop assemblies for me, as I do this sort of
thing a lot.

Here they are:

$SdkPath = "C:\Program Files\Microsoft.NET\SDK\v2.0\Bin"

function Create-InteropAssembly(
[string] $progId = $(throw "You must specify a ProgId"),
[string] $assemblyName = $(throw "You must specify a name"),
[string] $path = ( Get-Location ) )
{
$classes = "HKLM:\SOFTWARE\Classes"

# find the path to the type library
$clsid = ( Get-Item `
$classes\$progId\CLSID ).GetValue( "" )
$tlbPath = ( Get-Item `
$classes\CLSID\$clsid\InProcServer32 ).GetValue( "" )

# create a strong name key/value pair
if ( !( Test-Path "$path\keyPair.snk" ) )
{
& "$SdkPath\sn.exe" -q -k "$path\keyPair.snk"
}

# create the assembly
& "$SdkPath\TlbImp.exe" /silent /keyfile:$path\keyPair.snk `
/out:$assemblyName $tlbPath

# add the the path to the assembly to the pipeline
"$path\$assemblyName"
}

function Gac-Assembly( [string] $assemblyPath )
{
& "$SdkPath\gacutil.exe" /silent /i $assemblyPath
}

function UnGac-Assembly( [string] $assemblyName )
{
& "$SdkPath\gacutil.exe" /silent /u $assemblyName
}

function Get-AppDomainedType(
[string] $assemblyPath = $(throw "You must specify an assembly"),
[string] $typeName = $(throw "You must specify a name") )
{
# put the assembly in the GAC
Gac-Assembly $assemblyPath

# create a temporary AppDomain
$appDomain = [System.AppDomain]::CreateDomain( "TempAppDomain" )
$appDomain.AppendPrivatePath(
( Get-ChildItem $assemblyPath ).DirectoryName )

# create a new AssemblyName
$assemblyName = New-Object System.Reflection.AssemblyName
$assemblyName.Name =
( Get-ChildItem $assemblyPath ).Name -replace ".dll"
$assemblyName.CodeBase = $assemblyPath

# load the assembly into the new AppDomain
$assembly = $appDomain.Load( $assemblyName )

# create the type
$type = $appDomain.CreateInstanceFromAndUnwrap( $assemblyPath,
$typeName )

# put all the information about the type and AppDomain into
# an object that can be used to clean up later
$appDomainedTypeInfo = New-Object System.Object

$appDomainedTypeInfo | Add-Member -MemberType NoteProperty `
-Name AppDomain `
-Value $appDomain
$appDomainedTypeInfo | Add-Member -MemberType NoteProperty `
-Name AssemblyPath `
-Value $assemblyPath
$appDomainedTypeInfo | Add-Member -MemberType NoteProperty `
-Name AssemblyName `
-Value $assemblyName.Name

# add the type and the info object to the pipeline
$type
$appDomainedTypeInfo
}

function CleanUp-AppDomainedType(
[PSObject] $appDomainedTypeInfo,
[switch] $DeleteAssembly )
{
# unload the temporary AppDomain
[System.AppDomain]::Unload( $appDomainedTypeInfo.AppDomain )

# remove the assembly from the GAC
UnGac-Assembly $appDomainedTypeInfo.AssemblyName

if ( $DeleteAssembly )
{
# delete the assembly
Remove-Item $appDomainedTypeInfo.AssemblyPath
}
}

Create-InteropAssembly uses the ProgId of a COM object to determine
the location of a Type Library that can be used to generate an
assembly using TlbImp. Get-AppDomainedType takes a path to an
assembly and the name of a type. It puts the assembly in the GAC,
creates a new AppDomain, loads the assembly into the new AppDomain,
and uses AppDomain.CreateInstanceFromAndUnwrap() to get an instance of
the specified type. Besides the created type, the function adds a
custom object to the pipeline that can be used to clean up. CleanUp-
AppDomainedType uses the custom object to unload the temporary
AppDomain, remove the assembly from the GAC, and optionally delete the
assembly.

This how I use these functions:

# create an interop dll, if it doesn't already exist
if ( !( Test-Path "$( Get-Location )\InteropAssembly.dll" ) )
{
Create-InteropAssembly "InteropAssembly.SomeClass" `
"InteropAssembly.dll" | Out-Null
}

if ( Test-Path "$( Get-Location )\InteropAssembly.dll" )
{
# create a SomeClass in a temporary AppDomain
$someObject, $appDomainedTypeInfo = Get-AppDomainedType `
"$( Get-Location )\InteropAssembly.dll" `
"InteropAssembly.SomeClass"

# do some stuff with $someObject...

# clean up the temporary AppDomain
CleanUp-AppDomainedType $appDomainedTypeInfo -DeleteAssembly
}

This still has a problem: when the assembly containing the type is a
COM interop assembly, the COM server continues to be locked up by
powershell.exe even after the AppDomain is unloaded. The whole reason
I started down this road was to try to use a COM server through an
interop assembly without locking it up. I don't know what else to
try.

Jeff

My System SpecsSystem Spec
Old 10-09-2007   #5 (permalink)
Oisin Grehan


 
 

Re: Using an interop assembly with interop dependencies

On Oct 8, 2:32 am, Jeff <jeff.hill...@xxxxxx> wrote:
Quote:

> On Oct 6, 6:39 am, Jeff <jeff.hill...@xxxxxx> wrote:
>
>
>
>
>
Quote:

> > On Oct 5, 10:04 pm, Oisin Grehan <ois...@xxxxxx> wrote:
>
Quote:
Quote:

> > > This is a large and complex topic; start here:http://groups.google.ca/group/micros...powershell/msg...
>
Quote:
Quote:

> > > - Oisin
>
Quote:

> > I think I have read all of those articles in the past, but I learned a
> > few new things this time around. Thanks for pointing them out
> > again.
>
Quote:

> > I won't be able to try anything new until next week, but if I have any
> > success, I will post back here.
>
Quote:

> > Thanks.
>
Quote:

> > Jeff
>
> I ended up writing a few functions to take care of most of the work of
> dealing with COM interop assemblies for me, as I do this sort of
> thing a lot.
>
> Here they are:
>
> $SdkPath = "C:\Program Files\Microsoft.NET\SDK\v2.0\Bin"
>
> function Create-InteropAssembly(
> [string] $progId = $(throw "You must specify a ProgId"),
> [string] $assemblyName = $(throw "You must specify a name"),
> [string] $path = ( Get-Location ) )
> {
> $classes = "HKLM:\SOFTWARE\Classes"
>
> # find the path to the type library
> $clsid = ( Get-Item `
> $classes\$progId\CLSID ).GetValue( "" )
> $tlbPath = ( Get-Item `
> $classes\CLSID\$clsid\InProcServer32 ).GetValue( "" )
>
> # create a strong name key/value pair
> if ( !( Test-Path "$path\keyPair.snk" ) )
> {
> & "$SdkPath\sn.exe" -q -k "$path\keyPair.snk"
> }
>
> # create the assembly
> & "$SdkPath\TlbImp.exe" /silent /keyfile:$path\keyPair.snk `
> /out:$assemblyName $tlbPath
>
> # add the the path to the assembly to the pipeline
> "$path\$assemblyName"
>
> }
>
> function Gac-Assembly( [string] $assemblyPath )
> {
> & "$SdkPath\gacutil.exe" /silent /i $assemblyPath
>
> }
>
> function UnGac-Assembly( [string] $assemblyName )
> {
> & "$SdkPath\gacutil.exe" /silent /u $assemblyName
>
> }
>
> function Get-AppDomainedType(
> [string] $assemblyPath = $(throw "You must specify an assembly"),
> [string] $typeName = $(throw "You must specify a name") )
> {
> # put the assembly in the GAC
> Gac-Assembly $assemblyPath
>
> # create a temporary AppDomain
> $appDomain = [System.AppDomain]::CreateDomain( "TempAppDomain" )
> $appDomain.AppendPrivatePath(
> ( Get-ChildItem $assemblyPath ).DirectoryName )
>
> # create a new AssemblyName
> $assemblyName = New-Object System.Reflection.AssemblyName
> $assemblyName.Name =
> ( Get-ChildItem $assemblyPath ).Name -replace ".dll"
> $assemblyName.CodeBase = $assemblyPath
>
> # load the assembly into the new AppDomain
> $assembly = $appDomain.Load( $assemblyName )
>
> # create the type
> $type = $appDomain.CreateInstanceFromAndUnwrap( $assemblyPath,
> $typeName )
>
> # put all the information about the type and AppDomain into
> # an object that can be used to clean up later
> $appDomainedTypeInfo = New-Object System.Object
>
> $appDomainedTypeInfo | Add-Member -MemberType NoteProperty `
> -Name AppDomain `
> -Value $appDomain
> $appDomainedTypeInfo | Add-Member -MemberType NoteProperty `
> -Name AssemblyPath `
> -Value $assemblyPath
> $appDomainedTypeInfo | Add-Member -MemberType NoteProperty `
> -Name AssemblyName `
> -Value $assemblyName.Name
>
> # add the type and the info object to the pipeline
> $type
> $appDomainedTypeInfo
>
> }
>
> function CleanUp-AppDomainedType(
> [PSObject] $appDomainedTypeInfo,
> [switch] $DeleteAssembly )
> {
> # unload the temporary AppDomain
> [System.AppDomain]::Unload( $appDomainedTypeInfo.AppDomain )
>
> # remove the assembly from the GAC
> UnGac-Assembly $appDomainedTypeInfo.AssemblyName
>
> if ( $DeleteAssembly )
> {
> # delete the assembly
> Remove-Item $appDomainedTypeInfo.AssemblyPath
> }
>
> }
>
> Create-InteropAssembly uses the ProgId of a COM object to determine
> the location of a Type Library that can be used to generate an
> assembly using TlbImp. Get-AppDomainedType takes a path to an
> assembly and the name of a type. It puts the assembly in the GAC,
> creates a new AppDomain, loads the assembly into the new AppDomain,
> and uses AppDomain.CreateInstanceFromAndUnwrap() to get an instance of
> the specified type. Besides the created type, the function adds a
> custom object to the pipeline that can be used to clean up. CleanUp-
> AppDomainedType uses the custom object to unload the temporary
> AppDomain, remove the assembly from the GAC, and optionally delete the
> assembly.
>
> This how I use these functions:
>
> # create an interop dll, if it doesn't already exist
> if ( !( Test-Path "$( Get-Location )\InteropAssembly.dll" ) )
> {
> Create-InteropAssembly "InteropAssembly.SomeClass" `
> "InteropAssembly.dll" | Out-Null
>
> }
>
> if ( Test-Path "$( Get-Location )\InteropAssembly.dll" )
> {
> # create a SomeClass in a temporary AppDomain
> $someObject, $appDomainedTypeInfo = Get-AppDomainedType `
> "$( Get-Location )\InteropAssembly.dll" `
> "InteropAssembly.SomeClass"
>
> # do some stuff with $someObject...
>
> # clean up the temporary AppDomain
> CleanUp-AppDomainedType $appDomainedTypeInfo -DeleteAssembly
>
> }
>
> This still has a problem: when the assembly containing the type is a
> COM interop assembly, the COM server continues to be locked up by
> powershell.exe even after the AppDomain is unloaded. The whole reason
> I started down this road was to try to use a COM server through an
> interop assembly without locking it up. I don't know what else to
> try.
>
> Jeff- Hide quoted text -
>
> - Show quoted text -
Woah, getting hairy I see ;-) ok, try creating your interop assembly
again, but this time add the /primary switch to tlbimp.exe, along
with /keyfile. Then, register this assembly in the gac. Next, do NOT
load this interop assembly directly. Instead, instantiate your com
object with new-object directly, e.g.

ps> $obj = new-object -COM myprogid.name

The /primary switch marks the assembly with a special attribute
(primary interop assembly attribute), which links that interop
assembly to the com object by the typelib's guid. when you use new-
object, the loader will automatically load your interop assembly into
the default context (instead of probably into the loadfrom context
which isolates your interop assembly from the default context which
powershell sits in) and create your com object for you. additionally,
if your com object is STA, new-object will ensure it is created in an
STA for you.

try this and let me know how you get on,

- Oisin / x0n

My System SpecsSystem Spec
Old 10-09-2007   #6 (permalink)
Jeff


 
 

Re: Using an interop assembly with interop dependencies

On Oct 10, 12:13 am, Oisin Grehan <ois...@xxxxxx> wrote:
Quote:

> Woah, getting hairy I see ;-) ok, try creating your interop assembly
> again, but this time add the /primary switch to tlbimp.exe, along
> with /keyfile. Then, register this assembly in the gac. Next, do NOT
> load this interop assembly directly. Instead, instantiate your com
> object with new-object directly, e.g.
>
> ps> $obj = new-object -COM myprogid.name
>
> The /primary switch marks the assembly with a special attribute
> (primary interop assembly attribute), which links that interop
> assembly to the com object by the typelib's guid. when you use new-
> object, the loader will automatically load your interop assembly into
> the default context (instead of probably into the loadfrom context
> which isolates your interop assembly from the default context which
> powershell sits in) and create your com object for you. additionally,
> if your com object is STA, new-object will ensure it is created in an
> STA for you.
>
> try this and let me know how you get on,
>
> - Oisin / x0n
Thanks for the help Oisin. Unfortunately, your suggestion doesn't
seem to make much of a difference. My COM DLL is still locked by
powershell.exe after my script runs. The only other difference, other
than not being able to use an enumeration, defined in the COM DLL,
directly, since the assembly isn't loaded, is that the COM server
seems to be the only file in the powershell process' Modules list that
is related to what I am trying to do. When I load the interop
assembly directly into the new AppDomain, there is also an entry in
the Modules list for the assembly in the GAC:

C:\WC:\WINDOWS\assembly\GAC_MSIL\...\InteropAssembly.dll

This file isn't in the list when I create the object using New-Object -
Com rather than AppDomain.CreateInstanceFromAndUnwrap().

Any other ideas?
Jeff

My System SpecsSystem Spec
Old 10-10-2007   #7 (permalink)
Oisin Grehan


 
 

Re: Using an interop assembly with interop dependencies

On Oct 9, 10:22 pm, Jeff <jeff.hill...@xxxxxx> wrote:
Quote:

> On Oct 10, 12:13 am, Oisin Grehan <ois...@xxxxxx> wrote:
>
>
>
>
>
Quote:

> > Woah, getting hairy I see ;-) ok, try creating your interop assembly
> > again, but this time add the /primary switch to tlbimp.exe, along
> > with /keyfile. Then, register this assembly in the gac. Next, do NOT
> > load this interop assembly directly. Instead, instantiate your com
> > object with new-object directly, e.g.
>
Quote:

> > ps> $obj = new-object -COM myprogid.name
>
Quote:

> > The /primary switch marks the assembly with a special attribute
> > (primary interop assembly attribute), which links that interop
> > assembly to the com object by the typelib's guid. when you use new-
> > object, the loader will automatically load your interop assembly into
> > the default context (instead of probably into the loadfrom context
> > which isolates your interop assembly from the default context which
> > powershell sits in) and create your com object for you. additionally,
> > if your com object is STA, new-object will ensure it is created in an
> > STA for you.
>
Quote:

> > try this and let me know how you get on,
>
Quote:

> > - Oisin / x0n
>
> Thanks for the help Oisin. Unfortunately, your suggestion doesn't
> seem to make much of a difference. My COM DLL is still locked by
> powershell.exe after my script runs. The only other difference, other
> than not being able to use an enumeration, defined in the COM DLL,
> directly, since the assembly isn't loaded, is that the COM server
> seems to be the only file in the powershell process' Modules list that
> is related to what I am trying to do. When I load the interop
> assembly directly into the new AppDomain, there is also an entry in
> the Modules list for the assembly in the GAC:
>
> C:\WC:\WINDOWS\assembly\GAC_MSIL\...\InteropAssembly.dll
>
> This file isn't in the list when I create the object using New-Object -
> Com rather than AppDomain.CreateInstanceFromAndUnwrap().
>
> Any other ideas?
> Jeff- Hide quoted text -
>
> - Show quoted text -
Jeff,

Ahhhh, when you said your "com server is locked up," I thought you
meant frozen/unresponsive! I understand now that you meant the
*physical DLL* is locked access-wise on disk. Sorry man, I was
wandering down the wrong way completely (although the PIA stuff is
still relevant). This is a different kettle of fish entirely...
however:

First off, *two* collections of the GC are needed to completely free
up the COM reference after you've freed any psvariable references to
it. Normally this should happen automatically (eventually), but
regardless try calling [gc]::collect() explicitly twice and then
checking to see if the com server dll has been unloaded - tbh, I'm not
100% sure if the .net interop layer will call FreeLibrary() for you
(it should though, right people?). Perhaps someone from MSFT will
chime in here. If this is not the case, you can probably call
FreeLibrary yourself to unload the DLL from powershell.exe's process,
but this is the filthiest of filthy hacks.

Hope this helps,

- Oisin / x0n



My System SpecsSystem Spec
Old 10-10-2007   #8 (permalink)
Jeff


 
 

Re: Using an interop assembly with interop dependencies

On Oct 10, 11:36 pm, Oisin Grehan <ois...@xxxxxx> wrote:
Quote:

> On Oct 9, 10:22 pm, Jeff <jeff.hill...@xxxxxx> wrote:
>
>
>
Quote:

> > On Oct 10, 12:13 am, Oisin Grehan <ois...@xxxxxx> wrote:
>
Quote:
Quote:

> > > Woah, getting hairy I see ;-) ok, try creating your interop assembly
> > > again, but this time add the /primary switch to tlbimp.exe, along
> > > with /keyfile. Then, register this assembly in the gac. Next, do NOT
> > > load this interop assembly directly. Instead, instantiate your com
> > > object with new-object directly, e.g.
>
Quote:
Quote:

> > > ps> $obj = new-object -COM myprogid.name
>
Quote:
Quote:

> > > The /primary switch marks the assembly with a special attribute
> > > (primary interop assembly attribute), which links that interop
> > > assembly to the com object by the typelib's guid. when you use new-
> > > object, the loader will automatically load your interop assembly into
> > > the default context (instead of probably into the loadfrom context
> > > which isolates your interop assembly from the default context which
> > > powershell sits in) and create your com object for you. additionally,
> > > if your com object is STA, new-object will ensure it is created in an
> > > STA for you.
>
Quote:
Quote:

> > > try this and let me know how you get on,
>
Quote:
Quote:

> > > - Oisin / x0n
>
Quote:

> > Thanks for the help Oisin. Unfortunately, your suggestion doesn't
> > seem to make much of a difference. My COM DLL is still locked by
> > powershell.exe after my script runs. The only other difference, other
> > than not being able to use an enumeration, defined in the COM DLL,
> > directly, since the assembly isn't loaded, is that the COM server
> > seems to be the only file in the powershell process' Modules list that
> > is related to what I am trying to do. When I load the interop
> > assembly directly into the new AppDomain, there is also an entry in
> > the Modules list for the assembly in the GAC:
>
Quote:

> > C:\WC:\WINDOWS\assembly\GAC_MSIL\...\InteropAssembly.dll
>
Quote:

> > This file isn't in the list when I create the object using New-Object -
> > Com rather than AppDomain.CreateInstanceFromAndUnwrap().
>
Quote:

> > Any other ideas?
> > Jeff- Hide quoted text -
>
Quote:

> > - Show quoted text -
>
> Jeff,
>
> Ahhhh, when you said your "com server is locked up," I thought you
> meant frozen/unresponsive! I understand now that you meant the
> *physical DLL* is locked access-wise on disk. Sorry man, I was
> wandering down the wrong way completely (although the PIA stuff is
> still relevant). This is a different kettle of fish entirely...
> however:
>
> First off, *two* collections of the GC are needed to completely free
> up the COM reference after you've freed any psvariable references to
> it. Normally this should happen automatically (eventually), but
> regardless try calling [gc]::collect() explicitly twice and then
> checking to see if the com server dll has been unloaded - tbh, I'm not
> 100% sure if the .net interop layer will call FreeLibrary() for you
> (it should though, right people?). Perhaps someone from MSFT will
> chime in here. If this is not the case, you can probably call
> FreeLibrary yourself to unload the DLL from powershell.exe's process,
> but this is the filthiest of filthy hacks.
>
> Hope this helps,
>
> - Oisin / x0n
I was thinking as I was writing "locked up" that those words might not
be the best choice, and I guess I was right. Sorry for the
confusion. I will try your latest suggestion and report back.

Again, thanks for your time.

Jeff

My System SpecsSystem Spec
Old 10-11-2007   #9 (permalink)
Jeff


 
 

Re: Using an interop assembly with interop dependencies

On Oct 11, 6:49 am, Jeff <jeff.hill...@xxxxxx> wrote:
Quote:

> On Oct 10, 11:36 pm, Oisin Grehan <ois...@xxxxxx> wrote:
>
>
>
Quote:

> > On Oct 9, 10:22 pm, Jeff <jeff.hill...@xxxxxx> wrote:
>
Quote:
Quote:

> > > On Oct 10, 12:13 am, Oisin Grehan <ois...@xxxxxx> wrote:
>
Quote:
Quote:

> > > > Woah, getting hairy I see ;-) ok, try creating your interop assembly
> > > > again, but this time add the /primary switch to tlbimp.exe, along
> > > > with /keyfile. Then, register this assembly in the gac. Next, do NOT
> > > > load this interop assembly directly. Instead, instantiate your com
> > > > object with new-object directly, e.g.
>
Quote:
Quote:

> > > > ps> $obj = new-object -COM myprogid.name
>
Quote:
Quote:

> > > > The /primary switch marks the assembly with a special attribute
> > > > (primary interop assembly attribute), which links that interop
> > > > assembly to the com object by the typelib's guid. when you use new-
> > > > object, the loader will automatically load your interop assembly into
> > > > the default context (instead of probably into the loadfrom context
> > > > which isolates your interop assembly from the default context which
> > > > powershell sits in) and create your com object for you. additionally,
> > > > if your com object is STA, new-object will ensure it is created in an
> > > > STA for you.
>
Quote:
Quote:

> > > > try this and let me know how you get on,
>
Quote:
Quote:

> > > > - Oisin / x0n
>
Quote:
Quote:

> > > Thanks for the help Oisin. Unfortunately, your suggestion doesn't
> > > seem to make much of a difference. My COM DLL is still locked by
> > > powershell.exe after my script runs. The only other difference, other
> > > than not being able to use an enumeration, defined in the COM DLL,
> > > directly, since the assembly isn't loaded, is that the COM server
> > > seems to be the only file in the powershell process' Modules list that
> > > is related to what I am trying to do. When I load the interop
> > > assembly directly into the new AppDomain, there is also an entry in
> > > the Modules list for the assembly in the GAC:
>
Quote:
Quote:

> > > C:\WC:\WINDOWS\assembly\GAC_MSIL\...\InteropAssembly.dll
>
Quote:
Quote:

> > > This file isn't in the list when I create the object using New-Object -
> > > Com rather than AppDomain.CreateInstanceFromAndUnwrap().
>
Quote:
Quote:

> > > Any other ideas?
> > > Jeff- Hide quoted text -
>
Quote:
Quote:

> > > - Show quoted text -
>
Quote:

> > Jeff,
>
Quote:

> > Ahhhh, when you said your "com server is locked up," I thought you
> > meant frozen/unresponsive! I understand now that you meant the
> > *physical DLL* is locked access-wise on disk. Sorry man, I was
> > wandering down the wrong way completely (although the PIA stuff is
> > still relevant). This is a different kettle of fish entirely...
> > however:
>
Quote:

> > First off, *two* collections of the GC are needed to completely free
> > up the COM reference after you've freed any psvariable references to
> > it. Normally this should happen automatically (eventually), but
> > regardless try calling [gc]::collect() explicitly twice and then
> > checking to see if the com server dll has been unloaded - tbh, I'm not
> > 100% sure if the .net interop layer will call FreeLibrary() for you
> > (it should though, right people?). Perhaps someone from MSFT will
> > chime in here. If this is not the case, you can probably call
> > FreeLibrary yourself to unload the DLL from powershell.exe's process,
> > but this is the filthiest of filthy hacks.
>
Quote:

> > Hope this helps,
>
Quote:

> > - Oisin / x0n
>
> I was thinking as I was writing "locked up" that those words might not
> be the best choice, and I guess I was right. Sorry for the
> confusion. I will try your latest suggestion and report back.
>
> Again, thanks for your time.
>
> Jeff
The filthy hack (FreeLibrary) does free up the COM DLL, but it has the
side effect of making the DLL unusable for the remainder of the
session. I'm really not sure what is going on. If I try the whole
process again, when I try to create the type, using either New-Object
or AppDomain.CreateInstanceFromAndUnwrap(), I get this error:

Exception calling "CreateInstanceFromAndUnwrap" with "2" argument(s):
"Retrieving the COM class factory for component with CLSID {...}
failed due to the following error: 80010105."

80010105 is the code for "Unknown OLE Error"; I didn't like that
approach anyway.

Calling [gc]::Collect() didn't seem to have an effect, but after
trying a few other things, I'm not really sure what to think. I tried
several different combinations of [gc]::Collect(),
[System.Runtime.InteropServices.Marshal]::ReleaseComObject(), and
CoFreeUnusedLibraries in ole32.dll, and eventually, the DLL would be
freed. I was unable to consistently make it happen though, so I don't
know which and in what order these need to be called.
ReleaseComObject, Collect, and CoFreeUnusedLibraries makes the most
sense to me, but that never seemed to work, at least not right away.
It seems like a certain amount of waiting is also necessary, so one or
more of these steps might not even be necessary, once the mysterious
correct step is taken.

If any of this raises any flags for you, please let me know. I would
love to find a consistent resolution for this.

Jeff

My System SpecsSystem Spec
Reply

Thread Tools


Similar Threads
Thread Forum
Interop .NET General
Microsoft.VirtualServer.Interop.dll and the Microsoft.VMRCClientControl.Interop.dll Virtual Server
To Reference Interop.XXX.dll or XXX.dll .NET General
Why can't I use Interop assembly instead of COM? PowerShell


Vista Forums is an independent web site and has not been authorized,
sponsored, or otherwise approved by Microsoft Corporation.
"Windows Vista", the Start Orb, and related materials are trademarks of Microsoft Corp.
© Designer Media Ltd

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46