Powershell: Using external types / classes in Custom PowerShell Classes (v5) results in Parsing Error

Created on 26 Aug 2016  路  8Comments  路  Source: PowerShell/PowerShell

The Issue

I am trying to create a PowerShell-Project with my own custom PowerShell-Classes.
I use explicit type defintion for all attributes of my custom classes. For all integrated .Net-Classes there is now problem, but for other classes (for example external dll) it does not work.
It seems like, that you have to create a Wrapper script which loads the dll / Imports the module and afterwards starts the actual script project (with custom classes) by dotsourcing.

Unfortunately this workaround (Wrapper-Script) does not work out in my scenario --> A automation / orchestration engine only accepts one script...

So is there a way to fix this? The Parser should not throw an error, as you declare (using statement) that you will use classes out of this namespace. If there is something wrong with the class itself.. .this should be a runtime exception, or am I wrong?

Steps to reproduce

using namespace Microsft.ActiveDirectory.Management
#These two do not work, too 
#using module ActiveDirectory
#Import-Module ActiveDirectory

class Test
{
    [Microsoft.ActiveDirectory.Management.ADDomain] $domain;

}
 ...

Expected behavior

no Error Message

Actual behavior

At line:9 char:6
+     [Microsoft.ActiveDirectory.Management.ADDomain] $domain;
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [Microsoft.ActiveDirectory.Management.ADDomain].
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : TypeNotFound

In this case with an integrated .NET class it perfectly works.

using namespace System.DirectoryServices.ActiveDirectory 

Class Test 
{ 
    [Domain] $dom = [Domain]::GetCurrentDomain() 
} 
Issue-Enhancement WG-Language

Most helpful comment

@dpurge Yes the PowerShell class has to be parsed after the type is added. So having the class in the same file as the Add-Type won't work (because the whole file, including the class, is read before the Add-Type is executed).

Putting the class into an external file will work because it delays reading the class until script execution time.

See also: https://github.com/PowerShell/PowerShell/issues/6652.

To fix this particular issue, the plan is to allow using assembly, which will read DLL metadata safely (i.e. without loading it) at parse-time. However this is a large work item.

All 8 comments

I believe we implemented 'using assembly' specifically for this use case

According to @lzybkr, using assembly is meant for this, but it has not been implemented for this use case yet.

Same problem here in DSC resource (classes are recomended way to implement them, I heard)

I have tried to "Add-Type -AssemblyName System.IO.Compression.FileSystem" before class definition and inside class method and also "using namespace System.IO.Compression" before defining the class, but none of these worked.

A workaround was to dot-source an external script file in class method where Add-Type worked as usual.

@dpurge Yes the PowerShell class has to be parsed after the type is added. So having the class in the same file as the Add-Type won't work (because the whole file, including the class, is read before the Add-Type is executed).

Putting the class into an external file will work because it delays reading the class until script execution time.

See also: https://github.com/PowerShell/PowerShell/issues/6652.

To fix this particular issue, the plan is to allow using assembly, which will read DLL metadata safely (i.e. without loading it) at parse-time. However this is a large work item.

@rjmholt correct me if I'm wrong, but my understanding is that in order for this to work then at least the following would need to be done:

  1. Classes would need to be defined with MetadataBuilder instead of the System.Reflection.Emit API's
  2. Not yet loaded assemblies specified by using assembly would need to be analyzed via a MetadataReader if classes are defined in the file

Has there been any discussion on if this would even be accepted as a contribution? As you know, code that utilizes those API's would be exponentially more complex than the current code.

I'd like to take on at some point if no one beats me to it, but I'm unsure if it would be considered worth maintaining.

@SeeminglyScience - Using MetadataReader was my original plan when I introduced using assembly - I even wanted to use it for command analysis to avoid loading assemblies.

I can't comment on MetadataBuilder. I thought Reflection.Emit was up to the task, but I could easily be mistaken. One important aspect of defining types though - it should ideally be done in a collectible assembly.

Using MetadataReader was my original plan when I introduced using assembly - I even wanted to use it for command analysis to avoid loading assemblies.

Yes! I've been on and off working on a metadata only version of CmdletInfo for that 馃檪

I can't comment on MetadataBuilder. I thought Reflection.Emit was up to the task, but I could easily be mistaken.

It is as long as we can load the assembly prior to actually defining the type which should be doable. Now that I think about it, I think I started down that path because I wanted the exceptions TypeBuilder.CreateType() throws, but at parse time. MetadataBuilder doesn't really do any validation though, so any of that logic would need to be written from scratch utilizing MetadataReader anyway.

One important aspect of defining types though - it should ideally be done in a collectible assembly.

Ah yeah, MetadataBuilder is out then. I thought they weren't collectible for some reason.

Should be a lot less complicated with just MetadataReader then. Thanks @lzybkr!

Was this page helpful?
0 / 5 - 0 ratings